171
The Grinder Table of contents 1 Project........................................................................................................................... 7 1.1 The Grinder, a Java Load Testing Framework........................................................ 7 1.1.1 What is The Grinder?.......................................................................................... 7 1.1.2 Authors................................................................................................................ 7 1.1.3 Credits................................................................................................................. 8 1.2 The Grinder License................................................................................................ 9 1.2.1 The Grinder......................................................................................................... 9 1.2.2 HTTPClient....................................................................................................... 10 1.2.3 Jython................................................................................................................ 10 1.2.4 Jakarta ORO...................................................................................................... 10 1.2.5 jEdit Syntax....................................................................................................... 10 1.2.6 Apache XMLBeans........................................................................................... 10 1.2.7 PicoContainer.................................................................................................... 10 1.2.8 License text........................................................................................................ 10 1.3 Downloading The Grinder..................................................................................... 25 1.3.1 Download.......................................................................................................... 25 1.4 Support................................................................................................................... 25 1.4.1 Mailing lists....................................................................................................... 25 1.5 External references.................................................................................................26 1.5.1 Articles.............................................................................................................. 26 1.5.2 Commercials...................................................................................................... 27 2 Development.............................................................................................................. 29 2.1 The Grinder Change Log....................................................................................... 29 2.1.1 The Grinder 3.0-beta33..................................................................................... 29 2.1.2 The Grinder 3.0-beta32..................................................................................... 30 2.1.3 The Grinder 3.0-beta31..................................................................................... 30 Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

The Grinder

Embed Size (px)

Citation preview

Page 1: The Grinder

The Grinder

Table of contents

1 Project...........................................................................................................................7

1.1 The Grinder, a Java Load Testing Framework........................................................ 7

1.1.1 What is The Grinder?.......................................................................................... 7

1.1.2 Authors................................................................................................................ 7

1.1.3 Credits................................................................................................................. 8

1.2 The Grinder License................................................................................................ 9

1.2.1 The Grinder......................................................................................................... 9

1.2.2 HTTPClient....................................................................................................... 10

1.2.3 Jython................................................................................................................ 10

1.2.4 Jakarta ORO...................................................................................................... 10

1.2.5 jEdit Syntax....................................................................................................... 10

1.2.6 Apache XMLBeans........................................................................................... 10

1.2.7 PicoContainer.................................................................................................... 10

1.2.8 License text........................................................................................................10

1.3 Downloading The Grinder..................................................................................... 25

1.3.1 Download.......................................................................................................... 25

1.4 Support...................................................................................................................25

1.4.1 Mailing lists.......................................................................................................25

1.5 External references.................................................................................................26

1.5.1 Articles.............................................................................................................. 26

1.5.2 Commercials......................................................................................................27

2 Development.............................................................................................................. 29

2.1 The Grinder Change Log....................................................................................... 29

2.1.1 The Grinder 3.0-beta33..................................................................................... 29

2.1.2 The Grinder 3.0-beta32..................................................................................... 30

2.1.3 The Grinder 3.0-beta31..................................................................................... 30

Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 2: The Grinder

2.1.4 The Grinder 3.0-beta30..................................................................................... 31

2.1.5 The Grinder 3.0-beta29..................................................................................... 32

2.1.6 Grinder 3.0-beta28.............................................................................................33

2.1.7 Grinder 3.0-beta27.............................................................................................34

2.1.8 Grinder 3.0-beta26.............................................................................................34

2.1.9 Grinder 3.0-beta25.............................................................................................35

2.1.10 Grinder 3.0-beta24...........................................................................................36

2.1.11 Grinder 3.0-beta23...........................................................................................36

2.1.12 Grinder 3.0-beta22...........................................................................................37

2.1.13 Grinder 3.0-beta21...........................................................................................37

2.1.14 Grinder 3.0-beta20...........................................................................................37

2.1.15 Grinder 3.0-beta19...........................................................................................37

2.1.16 Grinder 3.0-beta18...........................................................................................38

2.1.17 Grinder 3.0-beta17...........................................................................................38

2.1.18 Grinder 3.0-beta16...........................................................................................39

2.1.19 Grinder 3.0-beta15...........................................................................................40

2.1.20 Grinder 3.0-beta14...........................................................................................40

2.1.21 Grinder 3.0-beta13...........................................................................................41

2.1.22 Grinder 3.0-beta12...........................................................................................41

2.1.23 Grinder 3.0-beta11...........................................................................................42

2.1.24 Grinder 3.0-beta10...........................................................................................43

2.1.25 Grinder 3.0-beta9.............................................................................................43

2.1.26 Grinder 3.0-beta8.............................................................................................44

2.1.27 Grinder 3.0-beta7.............................................................................................44

2.1.28 Grinder 3.0-beta6.............................................................................................44

2.1.29 Grinder 3.0-beta5.............................................................................................44

2.1.30 Grinder 3.0-beta4.............................................................................................45

2.1.31 Grinder 3.0-beta3.............................................................................................45

2.1.32 Grinder 3.0-beta2.............................................................................................45

2.1.33 Grinder 3.0-beta1.............................................................................................45

2.1.34 Grinder 3.0-beta0.............................................................................................46

2.1.35 Grinder 2.8.6....................................................................................................46

The Grinder

Page 2Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 3: The Grinder

2.1.36 Grinder 2.8.5....................................................................................................46

2.1.37 Grinder 2.8.4....................................................................................................46

2.1.38 Grinder 2.8.3....................................................................................................46

2.1.39 Grinder 2.8.2....................................................................................................47

2.1.40 Grinder 2.8.1....................................................................................................47

2.1.41 Grinder 2.8.......................................................................................................48

2.1.42 Grinder 2.7.2....................................................................................................48

2.1.43 Grinder 2.7.1....................................................................................................49

2.1.44 Grinder 2.7.......................................................................................................49

2.1.45 Grinder 2.6.5....................................................................................................50

2.1.46 Grinder 2.6.4....................................................................................................50

2.1.47 Grinder 2.6.3....................................................................................................51

2.1.48 Grinder 2.6.2....................................................................................................51

2.1.49 Grinder 2.6.1....................................................................................................52

2.1.50 Grinder 2.6.......................................................................................................52

2.1.51 Grinder 2.5.......................................................................................................52

2.1.52 Grinder 2.4.......................................................................................................53

2.1.53 Grinder 2.3.2....................................................................................................53

2.1.54 Grinder 2.3.1....................................................................................................53

2.1.55 Grinder 2.3.......................................................................................................54

2.1.56 Grinder 2.2.......................................................................................................54

2.1.57 Grinder 2.1.......................................................................................................54

2.1.58 Grinder 2.0.......................................................................................................55

2.1.59 Grinder 0.9.......................................................................................................55

2.1.60 Grinder 0.8.......................................................................................................56

2.2 The Grinder To Do list...........................................................................................56

2.2.1 Enhancements....................................................................................................56

2.2.2 Bugs...................................................................................................................64

2.2.3 Build.................................................................................................................. 64

2.2.4 Documentation.................................................................................................. 64

2.3 Contributing........................................................................................................... 65

2.3.1 The Source.........................................................................................................65

The Grinder

Page 3Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 4: The Grinder

2.3.2 What do I need to build The Grinder?...............................................................65

2.3.3 What do I need to test and package The Grinder?.............................................66

2.3.4 How to give back...............................................................................................66

2.3.5 Documentation help wanted..............................................................................66

2.3.6 Internationalisation help wanted....................................................................... 67

2.4 How The Grinder records test times - a problem?.................................................68

2.4.1 Timing tests....................................................................................................... 68

3 All documentation...................................................................................................... 69

4 The Grinder 2............................................................................................................. 69

4.1 Getting started........................................................................................................69

4.1.1 Getting started................................................................................................... 70

4.1.2 Core properties.................................................................................................. 72

4.1.3 The Console.......................................................................................................74

4.1.4 The TCPSniffer................................................................................................. 75

4.2 Plug-ins.................................................................................................................. 81

4.2.1 The HTTP Plug-in............................................................................................. 81

4.2.2 The JUnit Plug-in.............................................................................................. 86

5 The Grinder 3............................................................................................................. 87

5.1 What's new in The Grinder 3?............................................................................... 87

5.1.1 Jython................................................................................................................ 87

5.1.2 New distribution packaging...............................................................................88

5.1.3 Other changes.................................................................................................... 88

5.1.4 Roadmap............................................................................................................89

5.2 Features of The Grinder 3...................................................................................... 89

5.2.1 Capabilities of The Grinder...............................................................................89

5.2.2 Open Source...................................................................................................... 90

5.2.3 Standards........................................................................................................... 90

5.2.4 The Grinder Architecture.................................................................................. 90

5.2.5 Console..............................................................................................................91

5.2.6 Statistics, Reports, Charts..................................................................................91

5.2.7 Script................................................................................................................. 91

5.2.8 The Grinder Plug-ins......................................................................................... 92

The Grinder

Page 4Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 5: The Grinder

5.2.9 HTTP Plug-in.................................................................................................... 92

5.2.10 TCP Proxy....................................................................................................... 93

5.2.11 Documentation.................................................................................................93

5.2.12 Support.............................................................................................................93

5.3 Getting started........................................................................................................93

5.3.1 Getting started................................................................................................... 93

5.3.2 The Grinder 3 Properties File..........................................................................100

5.3.3 The Console.....................................................................................................103

5.3.4 The TCPProxy.................................................................................................107

5.4 Plug-ins................................................................................................................ 117

5.4.1 The HTTP Plug-in........................................................................................... 117

5.5 Statistics............................................................................................................... 122

5.5.1 Standard statistics............................................................................................ 122

5.5.2 Distribution of statistics...................................................................................123

5.5.3 Querying and updating statistics..................................................................... 123

5.5.4 Registering new expressions........................................................................... 123

5.6 Scripts.................................................................................................................. 124

5.6.1 Scripts..............................................................................................................124

5.6.2 A Step-By-Step Script Tutorial....................................................................... 127

5.6.3 Script Gallery.................................................................................................. 132

5.7 SSL Support......................................................................................................... 149

5.7.1 Before we begin...............................................................................................149

5.7.2 Controlling when new SSL sessions are created.............................................149

5.7.3 Using client certificates................................................................................... 150

5.7.4 FAQ................................................................................................................. 150

5.7.5 Picking a certificate from a key store [Advanced].......................................... 151

5.7.6 Debugging....................................................................................................... 152

6 Questions.................................................................................................................. 152

6.1 Frequently Asked Questions................................................................................ 152

6.1.1 Questions......................................................................................................... 152

6.2 Common...............................................................................................................170

6.3 The Grinder 2.......................................................................................................171

The Grinder

Page 5Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 6: The Grinder

6.4 The Grinder 3.......................................................................................................171

The Grinder

Page 6Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 7: The Grinder

1. Project

1.1. The Grinder, a Java Load Testing Framework

1.1.1. What is The Grinder?

The Grinder is a JavaTM load-testing framework. It is freely available under a BSD-styleopen-source license.

The Grinder makes it easy to orchestrate the activities of a test script in many processesacross many machines, using a graphical console application. Test scripts make use ofclient code embodied in Java plug-ins. Most users of The Grinder do not write plug-insthemselves, instead they use one of the supplied plug-ins. The Grinder comes with amature plug-in for testing HTTP services, as well as a tool which allows HTTP scripts tobe automatically recorded.

The next major version of The Grinder, The Grinder 3 is currently available as a betaquality release. The Grinder 3 uses the powerful scripting language Jython, and allowsany Java code to be tested without the need to write a plug-in. New users are advised tostart with The Grinder 3.

The Grinder was originally developed for the book Professional Java 2 EnterpriseEdition with BEA WebLogic Server by Paco Gómez and Peter Zadrozny. Philip Astontook ownership of the code and reworked it to create The Grinder 2. Philip continues toenhance and maintain The Grinder, and welcomes all contributions. Peter, Philip and TedOsborne published the book J2EE Performance Testing which makes extensive use ofThe Grinder.

The latest news, downloads, and mailing list archives can be found on SourceForge.net.

1.1.2. Authors

Paco Gómez

Philip Aston

Phil Dawes

Paddy Spencer

Richard Wallace

Stuart Tily

Phillip Burgess

Christian Nedregard

The Grinder

Page 7Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 8: The Grinder

Mikael Suokas

David Freels

Kalle Burbeck

Dirk Feufel

Kjetil JD

Todd Wasson

Jose Antonio Zapata Rey

Richard Perks

Manuel Silva

Bill Schnellinger

Bertrand Ave

Huibert Alblas

Kalyanaraman Venkatasubramaniy

John Stanford White

Slavik Gnatenko

Marc Born

Andreas Schmidt

Calum Fitzgerald

Sebastián Fontana

KArol Muszynski

Meo Bogliolo

Konstantin Ignatyev

Martin Wagner

Venelin Mitov

### (Huifeng Chen)

1.1.3. Credits

I wish to thank Paco Gómez and Peter Zadrozny for the key ideas embodied in theoriginal version of The Grinder.

The Grinder

Page 8Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 9: The Grinder

I am grateful to VA Software for SourceForge.net.

This site was put together with txt2html, XEmacs and Apache Forrest.

Philip Aston

1.2. The Grinder License

The Grinder is free software. It also repackages other free software. This section explainswhat you can and cannot do with The Grinder and the software included with it.

1.2.1. The Grinder

Copyright (c) 2000 Paco GómezCopyright (c) 2000-2006 Philip AstonAll rights reserved.

Additional contributions have been made by individuals listed in the AUTHORS filesupplied with this distribution. Each individual's claim to copyright is asserted in the filesto which they contributed.

Redistribution and use in source and binary forms, with or without modification, arepermitted provided that the following conditions are met:

• Redistributions of source code must retain the above copyright notice, this list ofconditions and the following disclaimer.

• Redistributions in binary form must reproduce the above copyright notice, this list ofconditions and the following disclaimer in the documentation and/or other materialsprovided with the distribution.

• Neither the names of the copyright holders nor the names of the contributors may beused to endorse or promote products derived from this software without specific priorwritten permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ANDCONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OFMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE AREDISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORSBE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOTLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER INCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OROTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THISSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

The Grinder

Page 9Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 10: The Grinder

1.2.2. HTTPClient

The Grinder includes Ronald Tschalär's HTTPClient library(http://www.innovation.ch/java/HTTPClient/index.html). The HTTPClient library isdistributed under the GNU Lesser Public License. Under the term 6 of the GNU LesserPublic License, The Grinder is a "work that uses the Library".

1.2.3. Jython

The Grinder includes the software Jython, created by Jim Hugunin, Barry Warsaw andthe Jython team (http://www.jython.org/). This is distributed under the terms of theJython and JPython software licenses.

1.2.4. Jakarta ORO

The Grinder includes software developed by the Apache Software Foundation(http://www.apache.org/), specifically the Jakarta ORO regular expression library. This isdistributed under the terms of the Apache Software License.

1.2.5. jEdit Syntax

The Grinder includes the jEdit Syntax highlighting package (http://syntax.jedit.org/). Thisis distributed according to the jEdit Syntax copyright and usage statement.

1.2.6. Apache XMLBeans

The Grinder includes Apache XMLBeans (http://xmlbeans.apache.org/), under the termsof the Apache Software License Version 2.0. See the XMLBeans NOTICE.

1.2.7. PicoContainer

The Grinder includes PicoContainer (http://picocontainer.codehaus.org/). This isdistributed under the terms of the PicoContainer license.

1.2.8. License text

1.2.8.1. The GNU Lesser General Public License

GNU LESSER GENERAL PUBLIC LICENSEVersion 2.1, February 1999

Copyright (C) 1991, 1999 Free Software Foundation, Inc.59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

Everyone is permitted to copy and distribute verbatim copies

The Grinder

Page 10Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 11: The Grinder

of this license document, but changing it is not allowed.

[This is the first released version of the Lesser GPL. It also countsas the successor of the GNU Library Public License, version 2, hencethe version number 2.1.]

Preamble

The licenses for most software are designed to take away yourfreedom to share and change it. By contrast, the GNU General PublicLicenses are intended to guarantee your freedom to share and changefree software--to make sure the software is free for all its users.

This license, the Lesser General Public License, applies to somespecially designated software packages--typically libraries--of theFree Software Foundation and other authors who decide to use it. Youcan use it too, but we suggest you first think carefully about whetherthis license or the ordinary General Public License is the betterstrategy to use in any particular case, based on the explanations below.

When we speak of free software, we are referring to freedom of use,not price. Our General Public Licenses are designed to make sure thatyou have the freedom to distribute copies of free software (and chargefor this service if you wish); that you receive source code or can getit if you want it; that you can change the software and use pieces ofit in new free programs; and that you are informed that you can dothese things.

To protect your rights, we need to make restrictions that forbiddistributors to deny you these rights or to ask you to surrender theserights. These restrictions translate to certain responsibilities foryou if you distribute copies of the library or if you modify it.

For example, if you distribute copies of the library, whether gratisor for a fee, you must give the recipients all the rights that we gaveyou. You must make sure that they, too, receive or can get the sourcecode. If you link other code with the library, you must providecomplete object files to the recipients, so that they can relink themwith the library after making changes to the library and recompilingit. And you must show them these terms so they know their rights.

We protect your rights with a two-step method: (1) we copyright thelibrary, and (2) we offer you this license, which gives you legalpermission to copy, distribute and/or modify the library.

To protect each distributor, we want to make it very clear thatthere is no warranty for the free library. Also, if the library ismodified by someone else and passed on, the recipients should knowthat what they have is not the original version, so that the originalauthor's reputation will not be affected by problems that might beintroduced by others.

Finally, software patents pose a constant threat to the existence ofany free program. We wish to make sure that a company cannoteffectively restrict the users of a free program by obtaining arestrictive license from a patent holder. Therefore, we insist thatany patent license obtained for a version of the library must beconsistent with the full freedom of use specified in this license.

The Grinder

Page 11Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 12: The Grinder

Most GNU software, including some libraries, is covered by theordinary GNU General Public License. This license, the GNU LesserGeneral Public License, applies to certain designated libraries, andis quite different from the ordinary General Public License. We usethis license for certain libraries in order to permit linking thoselibraries into non-free programs.

When a program is linked with a library, whether statically or usinga shared library, the combination of the two is legally speaking acombined work, a derivative of the original library. The ordinaryGeneral Public License therefore permits such linking only if theentire combination fits its criteria of freedom. The Lesser GeneralPublic License permits more lax criteria for linking other code withthe library.

We call this license the "Lesser" General Public License because itdoes Less to protect the user's freedom than the ordinary GeneralPublic License. It also provides other free software developers Lessof an advantage over competing non-free programs. These disadvantagesare the reason we use the ordinary General Public License for manylibraries. However, the Lesser license provides advantages in certainspecial circumstances.

For example, on rare occasions, there may be a special need toencourage the widest possible use of a certain library, so that itbecomesa de-facto standard. To achieve this, non-free programs must beallowed to use the library. A more frequent case is that a freelibrary does the same job as widely used non-free libraries. In thiscase, there is little to gain by limiting the free library to freesoftware only, so we use the Lesser General Public License.

In other cases, permission to use a particular library in non-freeprograms enables a greater number of people to use a large body offree software. For example, permission to use the GNU C Library innon-free programs enables many more people to use the whole GNUoperating system, as well as its variant, the GNU/Linux operatingsystem.

Although the Lesser General Public License is Less protective of theusers' freedom, it does ensure that the user of a program that islinked with the Library has the freedom and the wherewithal to runthat program using a modified version of the Library.

The precise terms and conditions for copying, distribution andmodification follow. Pay close attention to the difference between a"work based on the library" and a "work that uses the library". Theformer contains code derived from the library, whereas the latter mustbe combined with the library in order to run.

GNU LESSER GENERAL PUBLIC LICENSETERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

0. This License Agreement applies to any software library or otherprogram which contains a notice placed by the copyright holder orother authorized party saying it may be distributed under the terms ofthis Lesser General Public License (also called "this License").Each licensee is addressed as "you".

The Grinder

Page 12Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 13: The Grinder

A "library" means a collection of software functions and/or dataprepared so as to be conveniently linked with application programs(which use some of those functions and data) to form executables.

The "Library", below, refers to any such software library or workwhich has been distributed under these terms. A "work based on theLibrary" means either the Library or any derivative work undercopyright law: that is to say, a work containing the Library or aportion of it, either verbatim or with modifications and/or translatedstraightforwardly into another language. (Hereinafter, translation isincluded without limitation in the term "modification".)

"Source code" for a work means the preferred form of the work formaking modifications to it. For a library, complete source code meansall the source code for all modules it contains, plus any associatedinterface definition files, plus the scripts used to control compilationand installation of the library.

Activities other than copying, distribution and modification are notcovered by this License; they are outside its scope. The act ofrunning a program using the Library is not restricted, and output fromsuch a program is covered only if its contents constitute a work basedon the Library (independent of the use of the Library in a tool forwriting it). Whether that is true depends on what the Library doesand what the program that uses the Library does.

1. You may copy and distribute verbatim copies of the Library'scomplete source code as you receive it, in any medium, provided thatyou conspicuously and appropriately publish on each copy anappropriate copyright notice and disclaimer of warranty; keep intactall the notices that refer to this License and to the absence of anywarranty; and distribute a copy of this License along with theLibrary.

You may charge a fee for the physical act of transferring a copy,and you may at your option offer warranty protection in exchange for afee.

2. You may modify your copy or copies of the Library or any portionof it, thus forming a work based on the Library, and copy anddistribute such modifications or work under the terms of Section 1above, provided that you also meet all of these conditions:

a) The modified work must itself be a software library.

b) You must cause the files modified to carry prominent noticesstating that you changed the files and the date of any change.

c) You must cause the whole of the work to be licensed at nocharge to all third parties under the terms of this License.

d) If a facility in the modified Library refers to a function or atable of data to be supplied by an application program that usesthe facility, other than as an argument passed when the facilityis invoked, then you must make a good faith effort to ensure that,in the event an application does not supply such function ortable, the facility still operates, and performs whatever part ofits purpose remains meaningful.

The Grinder

Page 13Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 14: The Grinder

(For example, a function in a library to compute square roots hasa purpose that is entirely well-defined independent of theapplication. Therefore, Subsection 2d requires that anyapplication-supplied function or table used by this function mustbe optional: if the application does not supply it, the squareroot function must still compute square roots.)

These requirements apply to the modified work as a whole. Ifidentifiable sections of that work are not derived from the Library,and can be reasonably considered independent and separate works inthemselves, then this License, and its terms, do not apply to thosesections when you distribute them as separate works. But when youdistribute the same sections as part of a whole which is a work basedon the Library, the distribution of the whole must be on the terms ofthis License, whose permissions for other licensees extend to theentire whole, and thus to each and every part regardless of who wroteit.

Thus, it is not the intent of this section to claim rights or contestyour rights to work written entirely by you; rather, the intent is toexercise the right to control the distribution of derivative orcollective works based on the Library.

In addition, mere aggregation of another work not based on the Librarywith the Library (or with a work based on the Library) on a volume ofa storage or distribution medium does not bring the other work underthe scope of this License.

3. You may opt to apply the terms of the ordinary GNU General PublicLicense instead of this License to a given copy of the Library. To dothis, you must alter all the notices that refer to this License, sothat they refer to the ordinary GNU General Public License, version 2,instead of to this License. (If a newer version than version 2 of theordinary GNU General Public License has appeared, then you can specifythat version instead if you wish.) Do not make any other change inthese notices.

Once this change is made in a given copy, it is irreversible forthat copy, so the ordinary GNU General Public License applies to allsubsequent copies and derivative works made from that copy.

This option is useful when you wish to copy part of the code ofthe Library into a program that is not a library.

4. You may copy and distribute the Library (or a portion orderivative of it, under Section 2) in object code or executable formunder the terms of Sections 1 and 2 above provided that you accompanyit with the complete corresponding machine-readable source code, whichmust be distributed under the terms of Sections 1 and 2 above on amedium customarily used for software interchange.

If distribution of object code is made by offering access to copyfrom a designated place, then offering equivalent access to copy thesource code from the same place satisfies the requirement todistribute the source code, even though third parties are notcompelled to copy the source along with the object code.

5. A program that contains no derivative of any portion of theLibrary, but is designed to work with the Library by being compiled or

The Grinder

Page 14Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 15: The Grinder

linked with it, is called a "work that uses the Library". Such awork, in isolation, is not a derivative work of the Library, andtherefore falls outside the scope of this License.

However, linking a "work that uses the Library" with the Librarycreates an executable that is a derivative of the Library (because itcontains portions of the Library), rather than a "work that uses thelibrary". The executable is therefore covered by this License.Section 6 states terms for distribution of such executables.

When a "work that uses the Library" uses material from a header filethat is part of the Library, the object code for the work may be aderivative work of the Library even though the source code is not.Whether this is true is especially significant if the work can belinked without the Library, or if the work is itself a library. Thethreshold for this to be true is not precisely defined by law.

If such an object file uses only numerical parameters, datastructure layouts and accessors, and small macros and small inlinefunctions (ten lines or less in length), then the use of the objectfile is unrestricted, regardless of whether it is legally a derivativework. (Executables containing this object code plus portions of theLibrary will still fall under Section 6.)

Otherwise, if the work is a derivative of the Library, you maydistribute the object code for the work under the terms of Section 6.Any executables containing that work also fall under Section 6,whether or not they are linked directly with the Library itself.

6. As an exception to the Sections above, you may also combine orlink a "work that uses the Library" with the Library to produce awork containing portions of the Library, and distribute that workunder terms of your choice, provided that the terms permitmodification of the work for the customer's own use and reverseengineering for debugging such modifications.

You must give prominent notice with each copy of the work that theLibrary is used in it and that the Library and its use are covered bythis License. You must supply a copy of this License. If the workduring execution displays copyright notices, you must include thecopyright notice for the Library among them, as well as a referencedirecting the user to the copy of this License. Also, you must do oneof these things:

a) Accompany the work with the complete correspondingmachine-readable source code for the Library including whateverchanges were used in the work (which must be distributed underSections 1 and 2 above); and, if the work is an executable linkedwith the Library, with the complete machine-readable "work thatuses the Library", as object code and/or source code, so that theuser can modify the Library and then relink to produce a modifiedexecutable containing the modified Library. (It is understoodthat the user who changes the contents of definitions files in theLibrary will not necessarily be able to recompile the applicationto use the modified definitions.)

b) Use a suitable shared library mechanism for linking with theLibrary. A suitable mechanism is one that (1) uses at run time acopy of the library already present on the user's computer system,

The Grinder

Page 15Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 16: The Grinder

rather than copying library functions into the executable, and (2)will operate properly with a modified version of the library, ifthe user installs one, as long as the modified version isinterface-compatible with the version that the work was made with.

c) Accompany the work with a written offer, valid for atleast three years, to give the same user the materialsspecified in Subsection 6a, above, for a charge no morethan the cost of performing this distribution.

d) If distribution of the work is made by offering access to copyfrom a designated place, offer equivalent access to copy the abovespecified materials from the same place.

e) Verify that the user has already received a copy of thesematerials or that you have already sent this user a copy.

For an executable, the required form of the "work that uses theLibrary" must include any data and utility programs needed forreproducing the executable from it. However, as a special exception,the materials to be distributed need not include anything that isnormally distributed (in either source or binary form) with the majorcomponents (compiler, kernel, and so on) of the operating system onwhich the executable runs, unless that component itself accompaniesthe executable.

It may happen that this requirement contradicts the licenserestrictions of other proprietary libraries that do not normallyaccompany the operating system. Such a contradiction means you cannotuse both them and the Library together in an executable that youdistribute.

7. You may place library facilities that are a work based on theLibrary side-by-side in a single library together with other libraryfacilities not covered by this License, and distribute such a combinedlibrary, provided that the separate distribution of the work based onthe Library and of the other library facilities is otherwisepermitted, and provided that you do these two things:

a) Accompany the combined library with a copy of the same workbased on the Library, uncombined with any other libraryfacilities. This must be distributed under the terms of theSections above.

b) Give prominent notice with the combined library of the factthat part of it is a work based on the Library, and explainingwhere to find the accompanying uncombined form of the same work.

8. You may not copy, modify, sublicense, link with, or distributethe Library except as expressly provided under this License. Anyattempt otherwise to copy, modify, sublicense, link with, ordistribute the Library is void, and will automatically terminate yourrights under this License. However, parties who have received copies,or rights, from you under this License will not have their licensesterminated so long as such parties remain in full compliance.

9. You are not required to accept this License, since you have notsigned it. However, nothing else grants you permission to modify ordistribute the Library or its derivative works. These actions are

The Grinder

Page 16Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 17: The Grinder

prohibited by law if you do not accept this License. Therefore, bymodifying or distributing the Library (or any work based on theLibrary), you indicate your acceptance of this License to do so, andall its terms and conditions for copying, distributing or modifyingthe Library or works based on it.

10. Each time you redistribute the Library (or any work based on theLibrary), the recipient automatically receives a license from theoriginal licensor to copy, distribute, link with or modify the Librarysubject to these terms and conditions. You may not impose any furtherrestrictions on the recipients' exercise of the rights granted herein.You are not responsible for enforcing compliance by third parties withthis License.

11. If, as a consequence of a court judgment or allegation of patentinfringement or for any other reason (not limited to patent issues),conditions are imposed on you (whether by court order, agreement orotherwise) that contradict the conditions of this License, they do notexcuse you from the conditions of this License. If you cannotdistribute so as to satisfy simultaneously your obligations under thisLicense and any other pertinent obligations, then as a consequence youmay not distribute the Library at all. For example, if a patentlicense would not permit royalty-free redistribution of the Library byall those who receive copies directly or indirectly through you, thenthe only way you could satisfy both it and this License would be torefrain entirely from distribution of the Library.

If any portion of this section is held invalid or unenforceable underanyparticular circumstance, the balance of the section is intended toapply,and the section as a whole is intended to apply in other circumstances.

It is not the purpose of this section to induce you to infringe anypatents or other property right claims or to contest validity of anysuch claims; this section has the sole purpose of protecting theintegrity of the free software distribution system which isimplemented by public license practices. Many people have madegenerous contributions to the wide range of software distributedthrough that system in reliance on consistent application of thatsystem; it is up to the author/donor to decide if he or she is willingto distribute software through any other system and a licensee cannotimpose that choice.

This section is intended to make thoroughly clear what is believed tobe a consequence of the rest of this License.

12. If the distribution and/or use of the Library is restricted incertain countries either by patents or by copyrighted interfaces, theoriginal copyright holder who places the Library under this License mayaddan explicit geographical distribution limitation excluding thosecountries,so that distribution is permitted only in or among countries not thusexcluded. In such case, this License incorporates the limitation as ifwritten in the body of this License.

13. The Free Software Foundation may publish revised and/or newversions of the Lesser General Public License from time to time.

The Grinder

Page 17Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 18: The Grinder

Such new versions will be similar in spirit to the present version,but may differ in detail to address new problems or concerns.

Each version is given a distinguishing version number. If the Libraryspecifies a version number of this License which applies to it and"any later version", you have the option of following the terms andconditions either of that version or of any later version published bythe Free Software Foundation. If the Library does not specify alicense version number, you may choose any version ever published bythe Free Software Foundation.

14. If you wish to incorporate parts of the Library into other freeprograms whose distribution conditions are incompatible with these,write to the author to ask for permission. For software which iscopyrighted by the Free Software Foundation, write to the FreeSoftware Foundation; we sometimes make exceptions for this. Ourdecision will be guided by the two goals of preserving the free statusof all derivatives of our free software and of promoting the sharingand reuse of software generally.

NO WARRANTY

15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NOWARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OROTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANYKIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THEIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULARPURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THELIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUMETHE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO INWRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFYAND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOUFOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL ORCONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THELIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEINGRENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR AFAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IFSUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCHDAMAGES.

END OF TERMS AND CONDITIONS

How to Apply These Terms to Your New Libraries

If you develop a new library, and you want it to be of the greatestpossible use to the public, we recommend making it free software thateveryone can redistribute and change. You can do so by permittingredistribution under these terms (or, alternatively, under the terms oftheordinary General Public License).

To apply these terms, attach the following notices to the library. Itissafest to attach them to the start of each source file to mosteffectivelyconvey the exclusion of warranty; and each file should have at least the

The Grinder

Page 18Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 19: The Grinder

"copyright" line and a pointer to where the full notice is found.

<one line to give the library's name and a brief idea of what itdoes.>Copyright (C) <year> <name of author>

This library is free software; you can redistribute it and/ormodify it under the terms of the GNU Lesser General PublicLicense as published by the Free Software Foundation; eitherversion 2 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNULesser General Public License for more details.

You should have received a copy of the GNU Lesser General PublicLicense along with this library; if not, write to the Free SoftwareFoundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307

USA

Also add information on how to contact you by electronic and paper mail.

You should also get your employer (if you work as a programmer) or yourschool, if any, to sign a "copyright disclaimer" for the library, ifnecessary. Here is a sample; alter the names:

Yoyodyne, Inc., hereby disclaims all copyright interest in thelibrary `Frob' (a library for tweaking knobs) written by James Random

Hacker.

<signature of Ty Coon>, 1 April 1990Ty Coon, President of Vice

That's all there is to it!

1.2.8.2. The Jython and JPython Licenses

HISTORY OF THE SOFTWARE=======================

JPython was created in late 1997 by Jim Hugunin. Jim was also theprimary developer while he was at CNRI. In February 1999 Barry Warsawtook over as primary developer and released JPython version 1.1.In October 2000 Barry helped move the software to SourceForgewhere it was renamed to Jython. Jython 2.0 is developed by a groupof volunteers.

The zxJDBC package was written by Brian Zimmer and originally licensedunder the GNU Public License. The package is now covered by the JythonSoftware License.

Jython changes Software License.================================

Copyright (c) 2000, Jython DevelopersAll rights reserved.

The Grinder

Page 19Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 20: The Grinder

Redistribution and use in source and binary forms, with or withoutmodification, are permitted provided that the following conditionsare met:

- Redistributions of source code must retain the above copyrightnotice, this list of conditions and the following disclaimer.

- Redistributions in binary form must reproduce the above copyrightnotice, this list of conditions and the following disclaimer inthe documentation and/or other materials provided with the

distribution.

- Neither the name of the Jython Developers nor the names ofits contributors may be used to endorse or promote productsderived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOTLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FORA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS ORCONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, ORPROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORYOF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDINGNEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THISSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

JPython Software License.=========================

______________________________________________________________________

IMPORTANT: PLEASE READ THE FOLLOWING AGREEMENT CAREFULLY.

BY CLICKING ON THE "ACCEPT" BUTTON WHERE INDICATED, OR BY INSTALLING,COPYING OR OTHERWISE USING THE SOFTWARE, YOU ARE DEEMED TO HAVE AGREEDTOTHE TERMS AND CONDITIONS OF THIS AGREEMENT.

______________________________________________________________________

JPython version 1.1.x

1. This LICENSE AGREEMENT is between the Corporation for NationalResearch

Initiatives, having an office at 1895 Preston White Drive, Reston,VA

20191 ("CNRI"), and the Individual or Organization ("Licensee")accessing and using JPython version 1.1.x in source or binary form

andits associated documentation as provided herein ("Software").

2. Subject to the terms and conditions of this License Agreement, CNRIhereby grants Licensee a non-exclusive, non-transferable,

royalty-free,

The Grinder

Page 20Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 21: The Grinder

world-wide license to reproduce, analyze, test, perform and/ordisplay

publicly, prepare derivative works, distribute, and otherwise usethe

Software alone or in any derivative version, provided, however,that

CNRI's License Agreement and CNRI's notice of copyright, i.e.,"Copyright ©1996-1999 Corporation for National Research

Initiatives;All Rights Reserved" are both retained in the Software, alone or in

anyderivative version prepared by Licensee.

Alternatively, in lieu of CNRI's License Agreement, Licensee maysubstitute the following text (omitting the quotes), provided,

however,that such text is displayed prominently in the Software alone or in

anyderivative version prepared by Licensee: "JPython (Version 1.1.x)

ismade available subject to the terms and conditions in CNRI's

LicenseAgreement. This Agreement may be located on the Internet using thefollowing unique, persistent identifier (known as a handle):1895.22/1006. The License may also be obtained from a proxy server

onthe Web using the following URL:

http://hdl.handle.net/1895.22/1006."

3. In the event Licensee prepares a derivative work that is based onor

incorporates the Software or any part thereof, and wants to makethe

derivative work available to the public as provided herein, thenLicensee hereby agrees to indicate in any such work, in a

prominentlyvisible way, the nature of the modifications made to CNRI's

Software.

4. Licensee may not use CNRI trademarks or trade name, includingJPython

or CNRI, in a trademark sense to endorse or promote products orservices of Licensee, or any third party. Licensee may use the markJPython in connection with Licensee's derivative versions that arebased on or incorporate the Software, but only in the form"JPython-based ___________________," or equivalent.

5. CNRI is making the Software available to Licensee on an "AS IS"basis.

CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BYWAY

OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND DISCLAIMS ANYREPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANYPARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT

INFRINGEANY THIRD PARTY RIGHTS.

6. CNRI SHALL NOT BE LIABLE TO LICENSEE OR OTHER USERS OF THE SOFTWAREFOR

The Grinder

Page 21Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 22: The Grinder

ANY INCIDENTAL, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOSS AS ARESULT OF

USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY DERIVATIVETHEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. SOME STATES DO

NOTALLOW THE LIMITATION OR EXCLUSION OF LIABILITY SO THE ABOVE

DISCLAIMERMAY NOT APPLY TO LICENSEE.

7. This License Agreement may be terminated by CNRI (i) immediatelyupon

written notice from CNRI of any material breach by the Licensee, ifthe

nature of the breach is such that it cannot be promptly remedied;or

(ii) sixty (60) days following notice from CNRI to Licensee of amaterial remediable breach, if Licensee has not remedied such

breachwithin that sixty-day period.

8. This License Agreement shall be governed by and interpreted in allrespects by the law of the State of Virginia, excluding conflict of

lawprovisions. Nothing in this Agreement shall be deemed to create anyrelationship of agency, partnership, or joint venture between CNRI

andLicensee.

9. By clicking on the "ACCEPT" button where indicated, or byinstalling,

copying or otherwise using the Software, Licensee agrees to bebound by

the terms and conditions of this License Agreement.

[ACCEPT BUTTON]

1.2.8.3. The Apache Software License (Jakarta ORO)

/* ====================================================================* The Apache Software License, Version 1.1** Copyright (c) 2000-2002 The Apache Software Foundation. All rights* reserved.** Redistribution and use in source and binary forms, with or without* modification, are permitted provided that the following conditions* are met:** 1. Redistributions of source code must retain the above copyright* notice, this list of conditions and the following disclaimer.** 2. Redistributions in binary form must reproduce the above copyright* notice, this list of conditions and the following disclaimer in* the documentation and/or other materials provided with the* distribution.** 3. The end-user documentation included with the redistribution,* if any, must include the following acknowledgment:

The Grinder

Page 22Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 23: The Grinder

* "This product includes software developed by the* Apache Software Foundation (http://www.apache.org/)."* Alternately, this acknowledgment may appear in the softwareitself,* if and wherever such third-party acknowledgments normally appear.** 4. The names "Apache" and "Apache Software Foundation", "Jakarta-Oro"* must not be used to endorse or promote products derived from this* software without prior written permission. For written* permission, please contact [email protected].** 5. Products derived from this software may not be called "Apache"* or "Jakarta-Oro", nor may "Apache" or "Jakarta-Oro" appear intheir* name, without prior written permission of the Apache SoftwareFoundation.** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF* SUCH DAMAGE.* ====================================================================** This software consists of voluntary contributions made by many* individuals on behalf of the Apache Software Foundation. For more* information on the Apache Software Foundation, please see* <http://www.apache.org/>.** Portions of this software are based upon software originally written* by Daniel F. Savarese. We appreciate his contributions.*/

1.2.8.4. jEdit Syntax copyright and usage statement

The jEdit 2.2.1 syntax highlighting package contains code that isCopyright 1998-1999 Slava Pestov, Artur Biesiadowski, Clancy Malcolm,Jonathan Revusky, Juha Lindfors and Mike Dillon.

You may use and modify this package for any purpose. Redistribution ispermitted, in both source and binary form, provided that this noticeremains intact in all source distributions of this package.

-- Slava Pestov25 September 2000<[email protected]>

1.2.8.5. XMLBeans NOTICE

=========================================================================

The Grinder

Page 23Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 24: The Grinder

== NOTICE file corresponding to section 4(d) of the Apache License,==== Version 2.0, in this case for the Apache XmlBeans distribution.===========================================================================

This product includes software developed by The Apache SoftwareFoundation (http://www.apache.org/).

Portions of this software were originally based on the following:- software copyright (c) 2000-2003, BEA Systems,<http://www.bea.com/>.

Aside from contributions to the Apache XMLBeans project, this softwarealso includes:

- one or more source files from the Apache Xerces-J and Apache Axisproducts, Copyright (c) 1999-2003 Apache Software Foundation

- W3C XML Schema documents Copyright 2001-2003 (c) World Wide WebConsortium (Massachusetts Institute of Technology, EuropeanResearch Consortium for Informatics and Mathematics, KeioUniversity)

- Piccolo XML Parser for Java from http://piccolo.sourceforge.net/,Copyright 2002 Yuval Oren under the terms of the Apache SoftwareLicense 2.0

- JSR-173 Streaming API for XML fromhttp://sourceforge.net/projects/xmlpullparser/,Copyright 2005 BEA under the terms of the Apache SoftwareLicense 2.0

1.2.8.6. PicoContainer License

Copyright (c) 2003-2005, PicoContainer OrganizationAll rights reserved.

Redistribution and use in source and binary forms, with or withoutmodification, are permitted provided that the following conditions aremet:

Redistributions of source code must retain the above copyright notice,thislist of conditions and the following disclaimer.

Redistributions in binary form must reproduce the above copyrightnotice,this list of conditions and the following disclaimer in the

documentationand/or other materials provided with the distribution.

Neither the name of the PicoContainer Organization nor the names ofitscontributors may be used to endorse or promote products derived from

thissoftware without specific prior written permission.

The Grinder

Page 24Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 25: The Grinder

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "ASIS"AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,THEIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULARPURPOSEARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BELIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, ORCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OFSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESSINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER INCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OFTHEPOSSIBILITY OF SUCH DAMAGE.

1.3. Downloading The Grinder

1.3.1. Download

The Grinder 2 and The Grinder 3 can be downloaded from SourceForge.net. The files arepackaged as zip format files which you should expand using unzip, WinZip or similar.

The Grinder 3 is distributed as two zip files. Everything required to run The Grinder is inthe zip file labelled grinder-3.0-version.zip. The remaining files that areneeded to build The Grinder are distributed in the zip file labelledgrinder-3.0-version-src.zip; these are mainly of interest to developerswanting to extend The Grinder.

1.3.1.1. What else do I need?

To run The Grinder:

Java 2 Standard Edition 1.3, equivalent, or later For The Grinder 2

Java 2 Standard Edition 1.4, equivalent, or later For The Grinder 3

and optionally:

JSSE (Java Secure Socket Extension) 1.0.2 for SSL support with The Grinder 2.

Note, JSSE is a standard part of Java 2 StandardEdition 1.4.1 and later.

1.4. Support

1.4.1. Mailing lists

Requests for help should be sent to [email protected].

The Grinder

Page 25Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 26: The Grinder

Note:To reduce spam, you must now subscribe to a list before you can send email to it. The email address you send mailfrom must be the one you used to subscribe.

[email protected] is a low-volume mailing list which is used toannounce new releases and other items of interest to users of The Grinder.

Please contribute bug fixes and enhancements [email protected].

Searchable archives of these mailing lists can be found on SourceForge.net. Gmaneprovides alternative searchable archives, together with NNTP feeds and an optionalBlog-like interface.

You can also subscribe and unsubscribe to the lists.

When Philip Aston finds the time to respond to mail about The Grinder, messages notcopied to one of the above mail lists are likely to be ignored. Philip freely copiesresponses to the lists; if there is a particular reason why you want to keep yourcommunication private you must say so. Finally, if you can provide answers yourself,please don't be shy!

Note:The Grinder is free software. No one is under any obligation to fix your problem. If all else fails, you have the source.

1.5. External references

1.5.1. Articles

The Black Anvil: Shootout: Load Runner vs TheGrinder vs Apache JMeter(http://blackanvil.blogspot.com/2006/06/shootout-load-runner-vs-grinder-vs.html#links)

Detailed comparison of The Grinder, JMeter,and Load Runner from Travis Bear."...I recommended The Grinder asthe tool to go forward with. Ithas a simple, clean UI thatclearly shows what is going onwithout trying to do too much, andoffers great power and simplicitywith its unique Jython-basedscripting approach. Jython allowscomplex scripts to be developedmuch more rapidly than in moreformal languages like Java, yet itcan access any Java library orclass easily, allowing us tore-use elements of our existingwork."

Travis has since assisted with the implementation ofslow socket support for The Grinder.

The Grinder

Page 26Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 27: The Grinder

Performance Testing using The Grinder(http://cdjdn.com/downloads/performancetesting-grinder.pdf)

A high-level overview of test methodology usingThe Grinder from Paul Evans/Blue SlateSolutions. Hosted by the Capital District JavaDevelopers Network.

Grinder Test Automation for the WebLogicServer(http://www.anser-e.com/testing/GrinderAutomationTutorial.html)

An custom automated test environment forWebLogic built on The Grinder.

Gash: Load Testing Java Applications(http://gashalot.com/writing/blog-grinder.php)

Replacing JMeter with The Grinder 3"I went from a freshly downloadedtarball to fully functional testenvironment in about 2.5 hours.That's powerful."

WikiWikiWeb(http://c2.com/cgi/wiki/wiki?TheGrinder)

Entry on the Wiki of Wiki's.

Stress Testing with The Grinder and Cactus(http://www.abcseo.com/papers/grinder.htm)

Using The Grinder 2's JUnit plug-in with Cactus.

The Grinder: Load Testing for Everyone(http://dev2dev.bea.com/articles/aston.jsp)

An introductory article on The Grinder 2 fromPhil Aston.

Anticlue(http://www.anticlue.net/archives/000395.htm)

Blog entry on The Grinder 3.

Load Testing Web Services with Grinder(http://www.oreillynet.com/pub/wlg/6743)

An article on testing Web Services with TheGrinder 3.

Massive Propeller: The Grinder(http://www.massivepropeller.com/users/austin/blogs/whatsnew/archive/000043.html)

Blog entry on The Grinder 2.

Mr Worm's GonePage: The Grinder(http://82.133.140.67/MrWorm/35)

Blog entry on The Grinder.

Dan Moore!: The Grinder(http://www.mooreds.com/weblog/archives/000111.html)

Blog entry on The Grinder 3.

1.5.2. Commercials

This section contains links to commercial products and services related to The Grinder.You should not assume any relationship other than those documented below between thelisted individuals and companies and The Grinder project. If you have a product orservice related to The Grinder and would like to add information to this page, pleaseemail details to grinder-use.

1.5.2.1. J2EE Performance Testing

I'm pleased to announce the availability of J2EE Performance Testing with BEA

The Grinder

Page 27Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 28: The Grinder

WebLogic Server by Peter Zadrozny, Philip Aston and Ted Osborne, originally publishedby Expert Press and now by APress.

This book uses The Grinder 2 throughout, and indeed was responsible for driving thedevelopment of many of The Grinder's features. The book shows how to performance testcomplete J2EE applications and how to explore key performance issues surrounding themost popular J2EE APIs. The performance tests are carried out using BEA WebLogicServer™, but are generally applicable to any J2EE application server.

Most importantly, the book contains in-depth coverage of The Grinder 2 including a fulluser guide and case studies showing how to apply The Grinder to real world problems.The testing approach is equally applicable when using The Grinder 3.

Following several requests, I've made the source code for the book available from TheGrinder SourceForge page. This source is supplied unsupported and with no warranty.

Philip Aston

1.5.2.2. Anser Enterprise

One of my consulting services is helping performance analysts to set up company-internalblogs on their performance activities to help them communicate better with theirdevelopers and management. As part of my consulting service I can offer usage andcustomization tips on The Grinder and a separate data visualization tool to show Grindertest results on their company's intranet. Much of this is in the area of test automation andmining test results.

Here's a link which provides several example web pages on communicating WebLogic8.1/Grinder testing results. It requires downloading the Java 1.5 plug-in for chartingGrinder test results.

Todd Nichols, Anser Enterprise

1.5.2.3. TestPros

TestPros provides load testing and performance tuning services using Grinder. We canprovide our services in one or a combination of three ways - remotely via our Internetserver farm, at our test labs, or at our customer's location.

For more information:

• 1-877-783-7855• [email protected]• www.TestPros.com

1.5.2.4. swtest-discuss

I run a mailing list for software testers called swtest-discuss. There are a few people there

The Grinder

Page 28Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 29: The Grinder

(including me) who are interested in talking about how people do testing for open sourceprojects. I haven't yet found a community of open source testers that cuts across multipletools/applications.

If you're interested in sharing your experiences in testing open source software, pleaseconsider joining swtest-discuss, at least long enough to see if there's any interest in havingan on-going forum on this topic. If you do subscribe, please either send me a privateemail or introduce yourself to the list so we know you're there.

Danny R. Faught, Tejas Software Consulting

2. Development

2.1. The Grinder Change Log

2.1.1. The Grinder 3.0-beta33

Fulfil Feature Request 1524827 - Mnemonics needed. Requested by Travis Bear.

Fulfil Feature Request 1484448 - Console window size doesn't persist. Requested byTravis Bear.

Fulfil Feature Request 1185967 - Insert comments while capturing with the TCPProxy.Patch implemented by Venelin Mitov.

Really fix bug 1495428 - Plugin thread context not correctly registered for events. Thisbug prevented custom plugins from receiving thread lifecycle events but did not affect theHTTP plugin. Thanks to Klaus-Peter Berg for reporting that this was still broken.

Fix bug 1638659 - Deadlock in SleeperImplementation. Thanks to Bruce Adams for thereport.

Change the console's monitoring of changes to files so that it no longer assumes systemtime matches the file system time. This should fix some subtle bugs, particularly for filesystems on shared storage.

Fix bug 1639383 - Extra lines added to Content-Disposition. Previously, The Grinderrelied on the script maintaining line endings for textual POST bodies, in particularmultipart/form-data. This was brittle, text editors, email, and cross platform transferfrequently alters line endings; and I'm sure the Jython interpreter also takes a liberalapproach where a file has mixed line endings. I've applied a simple escaping scheme tofix this. Thanks to Claes T Tenell for the report.

Fix bug 1228335 - File editing should consider end of line. The console editor nowcanonicalises line endings on input, and uses the host platform line ending on output.

The Grinder

Page 29Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 30: The Grinder

Fix bug 1619450 - Unit test HTTPClient.TestCookie.testParse fails. Thanks to BastiaanBakker for the report.

Fulfil Feature Request 1510072 - Save absolute timestamp somewhere. Log both thecurrent time zone and the start time in milliseconds since the Epoch in the output log.Requested by Vladimir Sitnikov.

Add process control methods to console API, and provide a factory for obtaining aConsoleClient.

Tentative fix to bug 1677087. Thanks to Matt Moran for the report.

Remove all support for implicit 'grinder' script context object. It was deprecated in beta15, and the implementation was broken.

New Chinese translation from Hugh Chen. If you use this, you'll also need to set up Javafor Chinese fonts. The following blog entry may help:http://blog.lizhao.net/2007/03/java-chinese-fonts-on-ubuntu.html

2.1.2. The Grinder 3.0-beta32

Fix bug 1577573 - valueFromBodyURI() could be optimised. TCPProxy recorded scriptsuse this method, and its previous inefficiency caused beta 30 and beta 31 to be overlydemanding of CPU and memory. Thanks to Andy Kim for the original bug report.

Fix bug 1579641 - Exceptions thrown by HTTPRequest.processResponse() were notlogged correctly. Thanks to Richard Taylor for the report.

Fix bug 1585912 - jdbc.py incorrectly indented. Thanks to Coey (cminear atusers.sourceforge.net) for the report.

Fix bug 1593169 - AssertionError thrown in instrumentation code. Thanks to PeterWickersham and Matt Schlobohm for the report and analysis.

Added MQ library and example code from Phillip Mayhew to the contrib directory.

Fix bug 1592664 - InterruptedException when grinder.processes > 1. This is due to JREbadness that I've had to work around. Thanks to Stephane Nicoll and whoever opened thebug for the reports.

Fix bug 1604111 - Deadlock on shutdown (TCPProxy). Thanks to Graham Sweetland forhis assistance in reproducing this problem.

Added an initial version of the console API. This API is under development and notstable. It will grow and change over the next few releases. Its not ready for massconsumption yet, but advanced users can check out the API(net.grinder.console.client.ConsoleClient).

The Grinder

Page 30Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 31: The Grinder

2.1.3. The Grinder 3.0-beta31

Fix bug 1199086 - Distribution to multiple agents hangs. Thanks [email protected] for analysing the problem.

Fix bug 1495428 - Plugin thread context not correctly registered for events.

Fix HTTPPlugin TCPProxy filter to remove extraneous quote from reference to POSTdata files. Thanks to Saurabh Sehgal for the report.

Fix bug 1525387 - Relax cookie parsing to cope with .NET nonsense. Requested andprototyped by John S. White.

Fulfil Feature Request 1482811 - slow socket connections. Requested by Travis Bear. SeeHTTPPluginConnection.setBandwidthLimit(). Travis and Leland Olney assisted in thetesting and fixing of this feature.

Fix console bug where the Total line in statistics tables could be incorrectly rendered inred.

Fix bug 1525628 - Throw NotWrappableTypeException not NullPointerException onattempt to wrap a null object.

Fix bug 1527299 - statistics.delayReports doesn't work with nested tests. This required asignificant change to the script statistics API. The script can now distinguish between thestatistics for the current test (statistics.forCurrentTest), and the statistics for the lastcompleted test (statistics.forLastTest). If there is a current test, it's statistics can bequeried and modified. If there is a last reported test, it's statistics can be queried and canbe modified if statistics reports are delayed.

The statistics for an erroneous test is now recorded in the data log, but not included in theaggregate statistics sent to the console and the summary table (only the number of errorsis incremented). This makes the aggregate statistics averages correct, whilst still allowingthe test time to be retrieved from the data log. (Previously, the test time was zeroed forerroneous tests, so both the data log and the aggregate statistics averages other than testtime were incorrect).

Fix bug 1484703 - Total statistics wrong for composite tests. This was most apparent withthe HTTP plugin - the total average test time also included the page tests so "doubleaccounted" for the time of individual tests. The statistics display has now been changed sothat the statistics for composite tests (tests which wrap calls of other tests) are no longerincluding in the totals.

2.1.4. The Grinder 3.0-beta30

Fix HTTPlugin TCPProxy filter so that scripts send the real token name back to theserver, not the token id. Thanks to VmasterZen for reporting this.

The Grinder

Page 31Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 32: The Grinder

Changes to the script statistics API:

Scripts now refer to statistics by name rather than indicies. This means they don't need touse StatisticsIndexMap. This addresses an issue with scripts being unable to accessStatisticsIndexMap - thanks to Stefan Grinsted for the report.

The ScriptContext registerSummaryStatisticsView() and registerDetailStatisticsView()methods have been replaced with Statistics.registerSummaryExpression() andStatistics.registerDataLogExpression(). The new methods use expression strings, ratherthan requiring the use StatisticsView and ExpressionView objects.

The scripts no longer need objects from the net.grinder.statistics package; consequentlythat package has been removed from the statistics API.

Fix scripts statistics so the query methods work outside of an open test dispatch context;they return values for the last test reported by the worker thread. This was broken by thesupport for nested tests introduced in the last beta.

The grinder.recordTime property has been replaced by a newgrinder.reportTimesToConsole property. Test times are now always recorded in theworker process data log, irrespective of the value of grinder.reportTimesToConsole.

2.1.5. The Grinder 3.0-beta29

Nested Tests are supported. That is, you can now use a Test to instrument script code thatcalls things wrapped by other Tests.

A __target__ field has been added to test proxy wrappers, allowing convenient access tothe wrapped object.

New implementation of the HTTPPlugin TCPProxy filter. Key new features over theprevious filter (which is deprecated):

• Records to a single script.• Basic Authorization is recorded.• Modular implementation based on an XSLT style sheet.• Requests are grouped together into pages, using simple rules that guess the resources

belonging to a page based on the resource type. Pages are instrumented as separatetests.

• Name value tokens are identified in URL path parameters and query strings andmapped to script variables. Most session tokens that encoded in URLs or present inhidden form fields will now be handled automatically.

Several changes to the script API, including: net.grinder.plugin.http.HTTPRequest:

• Removed addHeader(), deleteHeader()• Header arrays passed to GET, POST, etc. are now merged with the default values held

by the HTTPRequest. Previously they setting an array on the request overrode all the

The Grinder

Page 32Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 33: The Grinder

default values.

net.grinder.plugininterface.PluginThreadContext:

• getStartTime(), startTimedSection(), stopTimedSection() have been replaced withpauseClock(), resumeClock() to support nested tests. (Since now there can be multipleactive dispatch contexts).

net.grinder.script.Statistics:

• Query methods now return meaningful results within wrapped code.• Added getStatisticsIndexMap. Thanks to Beecher Greenman for reporting this

omission.

net.grinder.plugin.http.HTTPUtilities

• New class. This has methods that HTTP scripts might find useful.

The HTTPClient Authorization module is no longer enabled by default because itprevents raw Authorization headers being sent through. The module also slows thingsdown as HTTPClient must parse responses for challenges. Advanced users who still wishto use the HTTPClient Authorization module can enable it with the newHTTPPluginConnection.setUseAuthorizationModule() method. The followingconvenience wrappers around the HTTPClient API were also removed:addBasicAuthorization(), removeBasicAuthorization(), addDigestAuthorization(),removeDigestAuthorization().

The worker process to console communication has been made more efficient, and a racecondition which caused "Ignoring unknown test" messages has been removed. Thanks toDhinakar K for the report.

The Grinder can now optionally use J2SE 5 System.nanoTime() which might give moreprecision on some platforms. Times are still reported in milliseconds. See thegrinder.useNanoTime property. Thanks to Tom Barnes for requesting this feature.

Fix console file scanning to used the same filtering as the file distributon. Thanks towhoever reported this. (Bug 1447361).

The HTTP proxy code in the TCPProxy has been improved; this should address problemsseen when chaining TCPProxy's together as HTTPS proxies. Thanks to Justin Nowell forprompting me to look at this.

2.1.6. Grinder 3.0-beta28

Updates to the Italian translation from Meo Bogliolo.

Refactoring of statistics package API. This may affect existing custom plugins. SeePluginProcessContext.getStatistics.

The Grinder

Page 33Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 34: The Grinder

New Russian translation from Konstantin Ignatyev.

Fix the TCPProxy port forwarding mode to correctly generate https URLs. (Bug1324640).

Fix the exception logging to include Jython stack traces. Simplify the exception logformatting.

Java methods can now be wrapped. The resulting object should be invoked Python style,i.e. non-static methods must be supplied with a 'self' instance as the first argument.

New grinder.debug.singleprocess property. If set to true, the agent process spawnsengines in threads rather than processes, using special classloaders to isolate the engines.This allows the engine to be easily run in a debugger. This is primarily a tool fordebugging The Grinder engine, but it might also be useful to advanced users.

More relaxed handling of invalid dates in Cookies. Requested by Martin Wagner.

Nicer output for ConnectExceptions and UnknownHost exceptions. (Bug 927828).

Handle Cookies with empty expires attribute. (Bug 982834).

Added a pop-up menu to the editor file tree.

Support Jython 2.2a1. Simply add the Jython 2.2a1 jython.jar to the start of your classpath. Thanks to Martin Wagner for prompting me to do this.

The documentation is now distributed in a separate zip file.

2.1.7. Grinder 3.0-beta27

The TCPProxy is now relaxed about empty query strings. Thanks to John Sinclair for thesuggestion.

Updates to the Spanish translation from Jose Antonio Zapata.

New Polish translation from KArol Muszynski.

New Italian translation from Meo Bogliolo.

The documentation sources are now hosted on SourceForge CVS, and are no longer partof the source distribution. See http://sourceforge.net/cvs/?group_id=18598.

Modify HTTPClient code so that HTTPResponse.getText() works with more contenttypes. (Bug 1297821).

Fix out of date documentation references to getIndexForLong (it has been renamed togetLongIndex). Thanks to Dietrich Bollmann for the report.

2.1.8. Grinder 3.0-beta26

The Grinder

Page 34Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 35: The Grinder

The Grinder now requires J2SE 1.4.

The console now displays agent process status.

Fix the TCPProxy to cope correctly with 8 bit strings in all locales (Bug 1167731).Thanks to Anton Malykh for the report.

Fix a bug where system properties specified on the command line were incorrectly quotedwhen building the worker process command line. Thanks to Dietrich Bollmann for thereport.

Be more strict about matching line endings in the HTTP TCPProxy filters. Previouslythere was a platform specific bug. Thanks to Alex Bolgarov for the report.

Change to the script API: getGrinderID() has been renamed getProcessName().

Correct SSL documentation - The Grinder does not support the javax.net.ssl.* systemproperties.

Fix to the JMS receiver example from Dietrich Bollmann: use a queue to pass statebetween threads in a thread safe way.

Make the TCPProxy HTTP engine more resilient to connections being dropped. Thanksto Jon Weygandt for the suggestion.

2.1.9. Grinder 3.0-beta25

The statistics engine has been enhanced to report the standard deviation of the test times.This addresses Feature Request 1029224. The change requires some changes to thestatistics API - the timedTests and timedTestTime long statistics have been replaced witha single timedTests long sample statistic; there is currently no way for a script to directlyset, or query, timedTests.

Latest updates from Bertand Ave for the new HTTP Plugin TCPProxy filter. AddressesRFE 1041267, bug 1164319.

Change the HTTPlugin TCPProxy filters so that they no longer interpret ';' as a querystring separator. Thanks to Nichole Holthoefer for the problem report.

Fix a bug where the engine would incorrectly override the plugin if the plugin hadmarked a test as an error.

Correct a few typos. Thanks to Henry Chan for pointing then out.

Fix a bug so that Test invocation works from from __init__ or __del__. Thanks toBertrand Ave for the bug report.

Most source files have been converted to use the Unix line endings. This is bettersupported by the Eclipse CVS module and Cygwin tools.

The Grinder

Page 35Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 36: The Grinder

2.1.10. Grinder 3.0-beta24

The TCPProxy now returns HTML error messages to the browser where it can.

When used in the default HTTP proxy mode, the TCPProxy always listens as an HTTPSproxy as well. The TCPProxy -ssl option now only applies to port forwarding mode.

Fix a bug where the TCPProxy could raise an ArrayIndexOutOfBoundsException.Thanks to Alan Droege for the report.

Historically The Grinder has used the terms "transaction" and "test" synonymously - thisis inconsistent and confusing. "Test" is now used consistently everywhere.

Fix a bug with the new HTTP instrumentation. The statistics were incorrectly calculatedwhen more than one HTTPRequest was wrapped in a given Test. Thanks to BerndPressler and Sebastien Lemeunier for reporting the problem.

The distribution control in the console is now enabled only if the agent file caches mightbe out of date. This acts as a visual indication that the files need to be distributed. Addedwarning dialog about attempting to start the processes without distributing files.

The process controls in the console are now enabled only if an agent is connected. Addedwarning dialog about attempting to start the processes with unsaved buffers.

2.1.11. Grinder 3.0-beta23

Scripts can now control the SSL context used by each worker thread. This can be used tovary the client certficate based on the thread. Thanks to Ryan Whitaker and WilliamBabilon for requesting this.

Enhancement from Slavik Gnatenko that allows the grinder.threads,grinder.reportToConsole.interval, and grinder.duration properties to be overridden inscripts initialistion section.

Fix TCPProxy to make it more relaxed about 8 bit characters in HTTP URLs. Thanks toThomas Böhne for reporting problems.

Fix another bug in HTTPClient related to responses with a Content-Length of 0. Thanksto John Lee for the report.

German console translation updates from Marc Born and Andreas Schmidt.

French console translation updates from Bertrand Ave.

New documentation built with Apache Forrest.

Additional HTTP instrumentation from Calumn Fitzgerald. This adds new statisticsmeasuring time to resolve host names (DNS), time to establish the connection, and time

The Grinder

Page 36Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 37: The Grinder

to first byte.

XML-RPC example from Sebastián Fontana.

2.1.12. Grinder 3.0-beta22

Major work on console editing and script distribution features. These are undocumented,but now quite usable. See TODO for details of the enhancements to be done in theadditional releases before The Grinder 3.0 is promoted out of beta.

Fix bug in HTTPPlugin. Now The Grinder doesn't automatically follow redirects bydefault.

Fix bug in new HTTPPlugin filter which caused an exception when attempting to recordPOSTs that have query strings.

Fix bug in HTTPPlugin. Can now handle responses with a Content-Length of 0.

The TCPProxy now uses a built-in default key store if none is specified.

2.1.13. Grinder 3.0-beta21

New process ramp-up feature from Bertrand Ave and Phil Aston. New properties:grinder.initialProcesses, grinder.processIncrement, and grinder.processIncrementInterval.(Whilst this new feature is useful, process ramp-up and other scheduling will eventuallybe implemented by the console.)

2.1.14. Grinder 3.0-beta20

Fix bug 912050 - gr3-b19 doesn't playback recorded URL params. Fix bug 911966 -gr3-b19 doesn't playback User-Agent: header. Thanks to Koen Debruyckere for thetimely reports.

Simplify HTTPRequest API slightly by removing some redundant overloaded methodsand ordering parameters for consistently.

Other fixes to HTTPRequest:

• DELETE() mistakenly invoked a Trace() method.• GET() and OPTIONS() could pass header values to query strings.

The PluginRegistry class that a plugin must use has been moved to the plugininterfacepackage.

2.1.15. Grinder 3.0-beta19

Removed redundant getLogger() and getFilenameFactory() from PluginProcessContext

The Grinder

Page 37Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 38: The Grinder

and PluginThreadContext. Plug-ins can get these services from the script context objectand share them with worker threads.

The PluginRegistry.register() method now takes an instance of GrinderPlugin, rather thana class that implements GrinderPlugin, and no longer returns a PluginProcessContext.

Fix bug 820481 - StringIndexOutOfBoundsException when logging. Thanks to DragosCernahoschi and Gordon Reid for reports.

Fix bug 805013 - mean time is not correct. Thanks to all who reported this, and to JanWilmaers and Bart Bauwens for their investigation and analysis.

HTTP response errors statistic from John Stanford White.

Added a processResponse method to HTTPRequest so that scripts subclasses can processrespones.

The console can now be set to start as soon as the first worker process starts its threads.("Ignore 0 samples").

2.1.16. Grinder 3.0-beta18

Fix console column header resizing by working around Java bug 4760433.

Fix the TCPProxy SSL support that I inadvertently broke in beta 17.

Allow the console Look and Feel to be changed.

HTTPPlugin updates from Bertrand:

• Provide access to HTTPClient content and transfer decoding support.

• Minor updates to HTTPPluginTCPProxyFilter2.

Remove the Sniffer web app.

Fail hard if script attempts to register detail statistics view from worker thread.

Support scripts that sleep outside of worker threads.

Allow table cells to be copied to clipboard and (with J2SE 1.4 and later) copy dragged.

2.1.17. Grinder 3.0-beta17

Fix bug 872546 - using HTTPPlugin with no console hangs the worker processes. Thechange involves explicitly calling InetAddress.getLocalHost() up front, indicating that theunderlying cause is a very strange JVM issue.

Fix bug 876097 - ClonePyInstance not included in grinder.jar. Thanks to Karl Wyer for

The Grinder

Page 38Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 39: The Grinder

the report.

Fix implicit grinder object deprecation message. Thanks to Christian Sell for the report.

Minor updates and fixes to the HTTPPluginTCPProxyFilters from Bertrand Ave.

Fix bug 868587 - The HTTP plugin now uses a new SSL session for each thread and foreach run. This corresponds to the orthodox grinder model of one run corresponding to oneuser browser session. Previously SSL sessions were pooled across threads, leading tounrealistic results when using The Grinder to drive a cluster of servers through SSLsession-aware load balancers. More SSL sessions will be created, so expect high CPUload on both client and server, and average times for first contact with an HTTPS serverwithin a test run to be significantly higher. Thanks to Joshua Reedy for prompting me todo this, and for testing the change.

Example of how to use The Grinder with HTTPUnit from Tony Lodge.

Email script example from Tom Pittard.

Implement HTTPPluginConnection.removeBasicAuthorization() andHTTPPluginConnection.removeDigestAuthorization(). The parameters to this method,and semantics of the corresponding add methods, have been changed to matchHTTPClient. The clearAll methods have been removed from the API.

Add addValue() methods to the script Statistics API.

The HTTP plugin now records the length of responses and reports mean response lengthand response bytes per second to the console. Thanks to John Stafford White forprompting me to do this. I've altered the console tables to word wrap the column labels sothat more columns can be displayed neatly. This only appears to size correctly with J2SE1.3, and even then not every time. J2SE 1.4 only shows the first line. I hope to have thisswinging nicely by the next beta.

Spanish translation updates from Jose Antonio Zapata Rey.

2.1.18. Grinder 3.0-beta16

Multicast is no longer used for distributing console messages signals.

Multicast has been a frequent source of newbie problems, and has also made The Grinderunreasonably hard to use with some corporate network configurations. Console messagesare now sent over TCP connections to the agent processes, which fan-out the signals totheir worker processes. The processing is multi-threaded and asynchronous and I expect itto scale well.

IMPORTANT: The communication properties have changed. The following propertiescan be set in grinder.properties:

The Grinder

Page 39Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 40: The Grinder

grinder.consoleHost (previously grinder.consoleAddress) grinder.consolePortgrinder.useConsole (previously grinder.receiveConsoleSignals / grinder.reportToConsole)

The grinder.grinderAddress and grinder.grinderPort properties have been removed.Corresponding changes have been made to the console options dialog.

2.1.19. Grinder 3.0-beta15

The use of the implicit 'grinder' script context object is deprecated. You should explicitly"from net.grinder.Grinder import grinder" in your scripts to ensure they remaincompatible with future releases.

Fix bug 810532 - problems invoking methods. You can now wrap Jython objects that aresubclassed from Java objects. Thanks to Karl Wyer for the report, and also to JohnStanford White for requesting this.

HTTPPluginTCPProxyFilter now records the User-Agent header. Thanks to DannyFaught for pointing out the opportunity.

Fix bug 852217 - sample interval jumps after I set it. Thanks to Danny Faught for thereport.

Fix/enhance the TCPProxy's support for chained HTTPS proxies. This should now workwith a wider range of proxies.

Improved console error handling.

Fix bug 852298 - null pointer exception when multicast fails. Thanks to Danny Faught forthe report.

Remove support for grinder.appendLog, I suspect it was rarely used. Add new featurewhich keeps archives of old logs, see grinder.numberOfOldLogs. This should be muchmore useful when resetting processes with the console.

Add a confirm dialog to "Stop Processes" as its probably not what the user wants to do.

Bertrand Ave has added support for regular expressions to his newHTTPPluginTCPProxyFilter (see notes below). The implementation now has its ownresponse filter; use the TCPProxy "-newhttpplugin" as a short cut. An example propertiesfile can be found in contrib/httpplugin.

2.1.20. Grinder 3.0-beta14

Hack HTTPClient to provide a tentative fix to the "premature EOF" problem. This widelyreported problem occurs when a server times out and closes a connection, after whichwrites to the connection silently fail and we don't learn about the problem until we readfrom the connection.

The Grinder

Page 40Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 41: The Grinder

Clean up the Jython interpreter correctly so that scripts can use exit functions. (See thestandard Python atexit module).

Guarantee that the TestRunner __del__ call is called on shutdown so that scripts can useit to tidy up per-thread state. Thanks to Stefano Santoro for inspiration.

Add a new HTTPPluginTCPProxyFilter from Bertrand Ave. This is currentlyundocumented and should be considered beta, but is more feature rich than the defaultHTTPPluginTCPProxyFilter. Over time it is likely to become the default HTTPPluginfilter. If you'd like to try it out, you'll need to start the TCPProxy with "-requestFilternet.grinder.plugin.http.HTTPPluginTCPProxyFilter2 -responseFilternet.grinder.plugin.http.HTTPPluginTCPProxyReponseFilter". You'll also need theWebUtils Python module that can be found in contrib/httpplugin.

Fix bug 850939 - dead link in documentation. Thanks to Danny Faught for the report.

2.1.21. Grinder 3.0-beta13

Fix bug 847571 - console does not display image icons. Thanks to Stefano Santoro for thereport.

2.1.22. Grinder 3.0-beta12

Updates to the TCPProxy from Bertrand Ave:

• Added a "-console" switch. When used, the TCPProxy creates a simple Swing dialogthat allows it to be cleanly interrupted.

• Added a "-properties" switch to pass System properties to filter implementations.Removed the HTTPPluginTCPProxyFilter's "-initialTest" option in favour of thismore general mechanism.

• Added a shutdown hook to increase chance of clean shutdown.

Fix TCPProxy so that it supports HTTP/1.1 from the browser when acting as an HTTPProxy. Recording of cross-site links should now work. Thanks to Jimmy Wang for thereport.

Remove the -proxy option from the TCPProxy. The TCPProxy now listens as an HTTPproxy by default unless either -remoteHost or -remotePort is specified.

Add a new -httpproxy option to the TCPProxy. This option specifies that output should tobe directed through another HTTP/HTTPS proxy; this may help you reach the Internet.There is also a -httpsproxy option which allows you to have a different HTTPS proxy.These options are not supported in port forwarding mode.

The Grinder

Page 41Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 42: The Grinder

The TCPProxy now supports chains of filters. Some may find "-httpPlugin -requestFilterECHO -responseFilter ECHO" useful.

The TCPProxy now displays key store configuration when starting in SSL mode. Supportfor setting the key store using system properties has been removed.

Optimise TCPProxy SSL initialisation for speed rather than cryptographic strength. Donot rely on the TCPProxy to provide strong security.

Change the TCPProxy -timeout option so the TCPProxy will not exit if there are activeconnections.

Fix a TCPProxy HTTPS proxy bug that caused SSLHandshakeExceptions when usedwith Internet Explorer 6.0.

Enhancement from Richard Perks that allows scripts to specify the local interface to usefor HTTP connections. See HTTPPluginConnection.setLocalAddress().

HTTP Plugin enhancement: If the User-Agent header is specified, don't append theHTTPClient version string.

Fix bug 836028 - incorrect platform specific separator for classpath. Thanks to StefanoSantoro for the report.

2.1.23. Grinder 3.0-beta11

Updates to the French console translation from Betrand Ave.

German console translation from Huibert Alblas.

"grinder." properties specified on the agent command line are now passed through toworker processes. The original motivation for this was to allow the log directory to be setbased on the system date.

HTTPPlugin enhancements:

• Script API to set whether an exception should be thrown when establishing an HTTPSconnection if the server's certificate doesn't match the host name. The defaultbehaviour has been changed to disable this check.

Fix bug 804272 - console results table column order varies according to how classes arepackaged. Thanks to Karl Wyer for the report.

Updated TCPProxy documentation.

If console cannot be contacted, don't expect console signals.

The 'grinder' context object can now be explicitly imported fromnet.grinder.Grinder.grinder. It is good practice to add an explict import to the top of your

The Grinder

Page 42Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 43: The Grinder

scripts so that they can be called as Python modules from other scripts.

Fix bug so that TestRunners are now initialised using their worker thread.

New property, grinder.duration, from Kalyanaraman Venkatasubramaniy. This propertyallows an overall time limit to be set for worker processes.

Be more agressive about interrupting workers. Thanks to Venkat for the idea and originalimplementation.

Incorporated feature matrix into documentation. Thanks to Edwin DeSouza for his helpwith this.

2.1.24. Grinder 3.0-beta10

Fix saving of console data. (Bug 749461).

2.1.25. Grinder 3.0-beta9

Incorporate HTTPClient HTTPS patch into main distribution. The Grinder now*requires* the JSSE, so you must install it if you are using a version of the J2SE earlierthan 1.4.

Fix use of J2SE 1.4.1 Color constants. Should compile with J2SE 1.3.1 again. Thanks toKumar Mettu for the report.

Minor fixes to how the HTTP Plugin TCPProxy filter parses content length.

Minor fidling with timing.

Tutorial from Richard Perks.

Incorporate the jEdit Syntax package which will be used for development of a consolescript editor. The basics of the editor are there, but will not be functional until I'vereworked the communications layer to allow the script to be distributed.

The HTTP plugin now records the HTTP response status code to the data log forHTTPRequests that are wrapped in Tests. If multiple HTTPRequests are wrapped in aTest, the status code of the last response is recorded. Thanks to Carlos Franco for theidea.

Incorporate patch from Bill Schnellinger: Fix HTTPPluginTCPProxyFilter so that largedata uploads (>1K) are stored in separate data files rather than inlined in the script. Thisshould fix bug 702022.

Fulfil Feature Request 732676 raised by Bertrand Ave by adding a "milliseconds sincestart" column to the data file.

French console translation from Bertrand Ave.

The Grinder

Page 43Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 44: The Grinder

Example script that interprets an grinder.properties HTTP test script for The Grinder 2.

2.1.26. Grinder 3.0-beta8

Fix a bug in the TCPProxy which prevented the recording of 8-bit POST data. Thanks toJoakim Suarez for the report.

The TCPProxy now copes with query strings and form data that are not name value pairs.Thanks to Ryan Balsam for the report.

Tentative fix to bug 690639. (TCPProxy recorded HTTP script refers to header arraybefore declaration.)

Documentation updates. Correct JMS example scripts.

Spruce up console UI.

Allow the console to be optionally reset with the worker processes. (Feature request686632).

2.1.27. Grinder 3.0-beta7

Fix synchronisation of the console new tests event which was causing null pointerexceptions.

Fix synchronisation of TestStatisticsMap that caused potential data loss and workerprocess stack traces.

Fix handling of thread shutdown exceptions. Threads again now respond correctly toshutdown/reset requests.

Improve console handling of large numbers of tests.

2.1.28. Grinder 3.0-beta6

Enhanced statistics API for scripts.

Documentation.

Source code clean up.

2.1.29. Grinder 3.0-beta5

Expose script and document API to allow scripts to work with custom statistics.

Rationalise the plugin API so that PluginProcessContext and PluginThreadContext nolonger extend Logger and FilenameFactory.

The Grinder

Page 44Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 45: The Grinder

Add JMS examples.

Improve cleaning up of child processes when the agent is killed.

Some script documentation.

2.1.30. Grinder 3.0-beta4

Correct implementation to read grinder.consolePort rather thangrinder.console.consolePort. Thanks to Dave Tauzell for the report.

Replace mandatory properties with reasonable defaults.

Fix HTTPPlugin static initialisation order so that script settings are not overridden bydefault initialisation. Thanks to Mark Wilson and Brett Schmoll for reports.

Script API to provide access to The Grinder build version -net.grinder.common.GrinderBuild.

Script API to provide access to the HTTPClient connection context for a thread -net.grinder.plugin.http.HTTPPluginControl.getHTTPClientContext().

Script API to provide access to grinder.properties values -net.grinder.script.ScriptContext.getProperties(). Thanks to Dave Tauzell for featurerequest 672691.

2.1.31. Grinder 3.0-beta3

Fix bug in wrapping code that broke the hello-world examples. Thanks to Simon Cousinsfor finding this.

2.1.32. Grinder 3.0-beta2

Improved exception handling and logging. Any exception thrown by a wrapped test isrecorded as an "error", if the script doesn't catch the exception the run is aborted.

Jython instances can now be Test.wrap()'d.

First cut of HTTPPlugin TCPProxy filter.

HTTPRequest API now has overloaded GET and HEAD methods that take a query stringNVPair[].

2.1.33. Grinder 3.0-beta1

Fix bug 608392 - console data file field separator conflicts with localised decimal points.Fields are now tab separated. Thanks to Martin Vilcans for the report.

The Grinder

Page 45Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 46: The Grinder

Fix bug 631144 - worker processes sometimes fail to launch on real operating systems.Thanks to Martin Voss for the report.

2.1.34. Grinder 3.0-beta0

Jython meets The Grinder and the magic starts to happen.

2.1.35. Grinder 2.8.6

Fix bug 608392 - console data file field separator conflicts with localised decimal points.Fields are now tab separated. Thanks to Martin Vilcans for the report.

Fix bug 631144 - worker processes sometimes fail to launch on real operating systems.Thanks to Martin Voss for the report.

2.1.36. Grinder 2.8.5

Fix TCPSniffer bug for non-proxy, non-ssl startup.

2.1.37. Grinder 2.8.4

Unified HTTP/HTTPS proxy.

Allow spaces in grinder.properies file names and host names.

Documentation on The Grinder vs LoadRunner courtesy of Tom Braverman.

Spanish translation of the console from Jose Antonio Zapata Rey.

2.1.38. Grinder 2.8.3

String beans can now implement an interface that allows them to access the HTTPClientHTTPResponse object. This means that StringBeans can perform assertions on the HTTPresponse, and snarf data from that response for use in subsequent requests.

The HTTP plugin is now relaxed when it finds a partial string bean tag, it just outputs theliteral text. This allows string bean tags to be used within XML POST data.

Fix sleepTimeVariation documentation. Thanks to Mika Laitio for the report.

Allow query strings to be passed with POSTs. Thanks to Kjetil JD for the fix.

The TCP Sniffer parameters for passing keystore information have changed, seedoc/TCPSniffer.txt.

I've removed the TCP Sniffer URL rewriting filter; use -proxy instead.

The Grinder

Page 46Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 47: The Grinder

All of the TCP Sniffer engines have been reworked. This SSL proxy engine is moreefficient when making multiple requests. The TCP Sniffer -proxy mode can now be usedwith any filter. The TCP Sniffer should no longer spin CPU when idle.

Reworked HttpPluginSnifferFilter:

• Outputs a full usable test script.• Copes with POST data split across TCP packets.• Fixed bug where POST with multiple lines was read incorrectly, this required moving

to Jakarta ORO because Jakarta Regexp bombed with a trivial regexp.• Removed multipart form handling.• Records 401 (authentication) exchanges.

(A note is necessary on the multipart form handling. The more I looked at the existingsupport in the sniffer, the more dubious it looked. Here's what the HTTP specificationsays on the subject:

"In general, HTTP treats a multipart message-body no differently than any other mediatype: strictly as payload. The one exception is the "multipart/byteranges" type ..."

This means that the sniffer should just chuck the data directly into the POST file, which iswhat it now does. And no Cynthia, we do not support multipart/byteranges.)

Support for disabling persistent connections with the HTTPClient plugin courtesy ofTodd Wasson.

Other miscellaneous fixes and documentation.

2.1.39. Grinder 2.8.2

The Grinder is now released under a BSD style license.

New UI showing worker process status from Dirk Feufel.

Fix console communication bug where socket reader threads would sleep each time adead socket handle was polled.

Fix SocketPlugin bug where multiple request files were incorrectly handled. Thanks toHussein Badakhchani for the report.

2.1.40. Grinder 2.8.1

Communication from the worker processes to the Console now uses a unicast TCPconnection. Multicast has been used here since the beginning (it was cheap and cheerfulto implement) but caused several problems:

• Under heavy load, multicast packets are lost.• I've had a report of multicast packets being duplicated.

The Grinder

Page 47Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 48: The Grinder

Clearly neither of these are good for statistics reporting. Signals from the Console to theworker processes still use multicast.

IMPORTANT: The communication properties have changed, you must set the followingin grinder.properties:

grinder.consoleAddress (Address of machine running Console) grinder.consolePortgrinder.grinderAddress (Multicast address) grinder.grinderPort

[grinder.consoleAddress is a new property. grinder.multicastAddress andgrinder.multicastPort have been renamed to grinder.grinderAddress andgrinder.grinderPort.]

You should make the corresponding changes in the Console options dialog. Please referto the README file for more details.

Fix the data file title line.

Reinstate the hack to prevent the HTTPClient from sending HTTP trailers. Say-DHTTPClient.disableTrailers=true in your grinder.jvm.arguments.

2.1.41. Grinder 2.8

New heavy iron statistics engine. Allows plugins to dynamically add new statistics andderived statistic views.

As an example, I've used the new statistic engine to implement an additional "mean timeto first byte" for the HttpURLConnection implementation of the HTTPPlugin. Thisreplaces the old "timeIncludesTransaction" parameter. Support for HTTPClient is moreinvolved and is slated for a future release.

Console "about" dialog.

2.1.42. Grinder 2.7.2

Console enhancements:

• Console no longer reads grinder.properties:• New options dialog to set communication details.• Console responds dynamically new tests registered by Grinder processes.• Can no longer set ignore samples to 0, doing this used to cause incorrect triggering.• Colour coded state label.

Miscellaneous engine enhancements:

• Sleeping processes can now be interrupted by the Console, so they respond morequickly.

• Logging tweaks.

The Grinder

Page 48Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 49: The Grinder

• Add dropped message detection to the communications.

HTTPPlugin enhancements:

• Filenames of result pages now contain test description.

More changes to the plugin API:

• A better fix for non-contiguous test numbers that doesn't pollute the Test interface.• Coallese processID and hostID into a single grinderID.• Much repackaging and rationalisation. Plugins should only need to use the "common"

and "plugininterface" packages.

2.1.43. Grinder 2.7.1

Fix bug with non-contiguous test numbers.

Fix basic authentication bug.

2.1.44. Grinder 2.7

Console enhancements:

• All text is now read from Console.properties (think I18N).• Highlight errors labels if errors > 0.• Table view now has average TPS and peak TPS.• Removed individual sample TPS labels.• New last sample statistics tab.

Remove distinction between "abortions" and "errors" from the recorded statistics. Alltests now result in a "successful transaction" or an "error".

Removed deprecated BookHttpPlugin example. Use StringBeans instead.

Minor changes to the plugin interface.

New HTTP plugin sniffer filter option, -initialTest.

HTTPPlugin enhancements

• Can now send arbitrary headers. Note, the specific If-Modified-Since property hasbeen removed, use the arbitrary header support. The TCPSniffer filter has beenupdated accordingly.

• Most request strings, including the arbitrary header values, are now affected by theString Bean, see doc/HTTPPlugin.txt.

• HTTPClient is now the default implementation. The timeIncludesTransactionparameter (which is ignored by the HTTPClient implementation) now defaults to true.HttpURLConnection support is deprecated and will be removed in a future release.See doc/HTTPPlugin.txt for more information, including notes on HTTPS support.

The Grinder

Page 49Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 50: The Grinder

Features from Kalle Burbeck:

• HTTP plugin sniffer filter now has limited support for multipart forms.• HTTP plugin sniffer filter now generates test descriptions.

Features from Paddy Spencer:

• New "Sniff 'n' Grind" webapp (the management take no responsibility for Paddy'ssense of humour).

• Reworked HTTP proxy, added new HTTPS proxy

• TCPSniffer changes: -timeout to set the proxy to timeout; -localSSLPort for use withthe HTTPS proxy; -localHost for use with multiple local IP addresses; doesn't barf ifyou set remote host/port with proxy;

2.1.45. Grinder 2.6.5

New tabbed Console layout.

Arm/stop is now a single Console control.

New "reset" Console control. This allows you to start a single Grinder process permachine and control the whole show from the Console.

New Console control for the number of significant figures to display.

You can now save data from console.

HTTPClientHandler now supports multiple servers.

Graph colour is now based on response time.

Documentation fixes.

2.1.46. Grinder 2.6.4

Fix -colour.

Fix distribution: add HTTPClient classes to grinder.jar.

Patch HTTP to fix handling of query strings.

Fix: don't throw NPE when using HTTPClient to retrieve zero length body response.

Make the console "ignore samples" and "collect samples" controls text fields rather thansliders - allows big numbers for power users.

Hack a switch onto HTTPClient to disable the use of trailer-related headers, and use it inHTTPClientHandler. Better for the less well-travelled server implementations.

The Grinder

Page 50Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 51: The Grinder

Extra documentation. The changes to doc/Timing.txt are particular worth a scan.

2.1.47. Grinder 2.6.3

New socket plugin from David Freels.

Console summary table now uses a JTable.

Fix to recording of short response times from Mikael Suokas.

Thread safety fix from Mikael Suokas.

Fix basic authentication property names. Thanks to Andrew Sliwkowski for reportingthis.

Added new file doc/Problems.txt to hold wisdom from Mikael Suokas. Split thedocumentation of the plugins and the TCPSniffer into separate files.

The HTTP plugin now has experimental support for Ronald Tschalär's excellentHTTPClient library instead of HttpURLConnection. HTTPClient has many more featuresthan HttpURLConnection, seehttp://www.innovation.ch/java/HTTPClient/urlcon_vs_httpclient.html for a comparision.I hope to lever features such as proxy support, connection timeouts and persistent cookiesinto future versions of The Grinder. Unfortunately experimentation shows HTTPClientsometimes does not perform well in a multithreaded environment. I'm guessing thisshould be straightforward to fix. See the file doc/HTTPPlugin.txt for details on how toturn on the HTTPClient support.

The TCPSniffer echo filter now reports connection open and close events.

The TCPSniffer now has a funky "-colour" option.

2.1.48. Grinder 2.6.2

Added support for HTTP basic authentication. You can now use two new HTTP plugintest parameters, basicAuthenticationUser and basicAuthenticationPassword, to specify theappropriate parameters for each request. See README for details.

New HTTP plugin parameter: timeIncludesTransaction. By default the time that theHTTP plugin reports is the time it takes for the remote server to accept the connectionrequest. When using the times as an indicator of user experience, this is usually what youwant. Sometimes however you are more interested in the time the entire HTTPtransaction takes, including the time required to read the HTTP response over thenetwork. If so, set this parameter to true.

Removed property grinder.thread.beginCycleSleepTime, as its redundant. (Usegrinder.test0.sleepTime instead).

The Grinder

Page 51Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 52: The Grinder

New property: grinder.thread.initialSleepTime. See README for details.

When the property grinder.recordTime is false, the time is no longer logged to the datafile. (The prime purpose of recordTime is to control whether times are _reported_ to theconsole, in fact times are always recorded. This is too subtle for some users ;-) so I'vemade this change for an easy life).

Reworked the logging code. The visible change is improved logging of sub-processevents to the controlling terminal.

Changed the sense of the "-norewriteURLs" TCPSniffer option to make it"-rewriteURLs". Rewriting is no longer the default behaviour.

2.1.49. Grinder 2.6.1

Various tweaks to the timing code. The response times reported by your test scripts willalmost certainly be smaller and more correct with this version. See doc/Timing.txt forinformation.

New property: grinder.recordTime. See the file doc/Timing.txt for details.

New property: grinder.logProcessStreams. See README for details.

Handle multiple Set-Cookie headers, courtesy of Christian Nedregard.

2.1.50. Grinder 2.6

Console rework. Extra knobs and dials which allow the console to be set to capture acertain number of samples - see README for details. New average TPS reporting.

You can now use the Console to request that Grinder processes exit. ** IMPORTANT**The property "grinder.waitForConsoleSignal" has been renamed to"grinder.receiveConsoleSignals".

Cool HttpPluginSnifferFilter enhancements from Dr Paddy Spencer that rewrite absoluteURLs on the fly. This makes creating test scripts for sites that use absolute URL's muchless painful. The solution includes a new way of running the sniffer by defining it as aproxy to the browser. Paddy promises to document all of this RSN.

Several bug fixes including:

• Don't spin uselessly when not reporting to the console.• Don't broadcast needless cruft in the statistics report messages. (read: your network

will no longer creak, this explains the need to up the buffer in 2.5).• Log directory is no longer mandatory.

2.1.51. Grinder 2.5

The Grinder

Page 52Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 53: The Grinder

The HTTP plugin now supports "string beans". These allow URL's and POST data to beeasily parameterised.

I've up'ed the read buffer size to 64K to allow more test statistics to be sent in a singlemulticast message. If this is too low you'll get exceptions - either null pointer exceptionsin the Receiver or "can't bind to socket" exceptions in the Sender code. If any one is stillrunning into problems, please mail [email protected].

JRun friendly patch from Stuart Tily.

Better socket handling for TCPSniffer from Phillip Burgess.

2.1.52. Grinder 2.4

The console UI has been slightly tidied up. Graphs are now histograms.

The JUnit plugin should work again. It also has a new parameter (logStackTraces) andimproved logging.

You can now specify an explicit properties file to both the Grinder and the Console as thefirst argument. For example:

java net.grinder.Grinder myproperties

Credit to Marc Stogner for this idea and initial implementaion.

(More version musings: The last version was _meant_ to named be 2.4, not 2.3.2.According to my schem, increments the third number is meant to be reserved for bug fixreleases only. The new console funtionallity really deserves something more significant.Rather than rewriting history, I'm treating 2.3.2 as a "beta" of the console functionality,and upping the second number here.)

2.1.53. Grinder 2.3.2

A shiny new console, with true TPS measurement.

New communciations layer.

2.1.54. Grinder 2.3.1

Even more bug fixes to the HTTP plugin. The cookie handling has been reworked tomore accurately reflect RFC 2109. Multiple cookies should now work, with the exceptionthat only one Set-Cookie header is recognised per request. This is a limitation of thejava.net.HttpURLConnection class, which I will rant about at length to anyone who asks.

The HTTP plugin parameter 'grinder.plugin.parameter.keepSession' has been renamed to'grinder.plugin.parameter.useCookies'.

The Grinder

Page 53Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 54: The Grinder

The Grinder now has a logo, courtesy of Richard Wallace.

2.1.55. Grinder 2.3

A HTTP plugin bug fix release. See the ChangeLog for details.

2.1.56. Grinder 2.2

The promised JUnit plugin is here! See README for more information.

I've introduced the concept of a TestSetPlugin that defines the set of tests to perform. Thisis primarily to support the JUnit plugin.

Bug fix to SSLSnifferEngine from Paddy Spencer. (You're first name doesn't _have_ tobegin with 'P' to get into AUTHORS, but it helps :-)).

More tweaks to the logging and terminal output.

The Console _should_ work again. Can't verify this as multicast is currently not workingon my machine.

2.1.57. Grinder 2.1

The plugin interface now has a single "doTest" dispatch interface instead of usingreflection. This simplifies the writing of generic plugins. The "grinder.methods" propertyhas been removed. Instead tests are specified by a number of "grinder.testX" properties(where test is any number). See README for details.

[Support for testing classes using reflection will be provided by a JUnit plugin which willcome in 2.2].

The "initial sleep time" has been removed, a "begin cycle sleep time" has been added inits place. Individual method sleep times can now be specified. All sleep times are nowrandomly varied according to a Normal distribution. See README for details.

The HttpPlugin now supports an "ifModifiedSince" parameter. This translates to theHTTP "If-Modified-Since" request header. This allows the grinder to simulate themechanism browsers use to check for cached pages.

The HttpPluginSnifferFilter has been updated to record sleep times, If-Modified-Sinceheaders, and reflect the property format changes.

The logging of the HTTP plugin has been improved.

The output and error log format is now more useful. There was no point in displaying thehost and process for each line (each process gets its own output file). I've added thecurrent cycle and test number instead.

The Grinder

Page 54Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 55: The Grinder

I've removed the "simple" and "ejb" plugins as they are not useful in their own right and Igot fed up of maintaining them. Both would be best addressed using the planned JUnitplugin. Examples will come with Grinder 2.2.

We have our first JUnit test case. We need more.

The TCPSniffer is now silent about SocketExceptions.

Renamed "GrinderContext" to more correct "PluginContext". Renamed "method" to"test" throughout.

2.1.58. Grinder 2.0

"Huh - what happened to the version numbers?": This release of the Grinder should bepublished on http://developer.bea.com/ and its logically a "1.0" release. However, Paco'soriginal was released as 1.6 and I don't want to confuse the public too much as to thelatest and greatest.

New TCPSniffer - the SnifferServlets are dead! (Big thanks to Phil Dawes for the sweatbehind this).

The HTTP plugin now automatically follows redirects only if you set the property

grinder.plugin.parameter.followRedirects=true

See the README file for details.

The build auto-detects what modules to build by checking your classpath for classes themodule depends on. You may have to unset your classpath if this doesn't do what youwant. Currently the conditionally built modules are:

HTTP SSL plugin (Needs the JSSE) TCPSniffer SSL engine (Needs the JSSE)TCPSniffer HTTP plugin filter (Needs Apache Jakarta Regexp) WebLogic Server 5.1Trader EJB plugin (needs WLS Trader EJB)

Fixed a bug that caused the thread ID to be displayed incorrectly.

I've added a wrapper class for the Console to the net.grinder package. You should nowuse "java net.grinder.Console" instead of "java net.grinder.console.Console". All future"main" classes will belong to this package.

2.1.59. Grinder 0.9

I've finally got rid of PropsLoader. This entails a few changes to properties:

grinder.jvm.path - Specifies the java executable used for the child JVMs. Defaults to"java" so you do not need to specify this if your path is sensible.

grinder.jvm.classpath - Use to adjust the classpath used for the child VMs. Anything

The Grinder

Page 55Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 56: The Grinder

specified here will be prepended to the classpath used to start the grinder itself.

grinder.jvm.arguments - Additional arguments to child JVM's.

Additionally, grinder.properties does not have to be in your classpath anymore. (I know Isaid this before - I mean it this time).

PeterZ found the data file title line useful so I've reinstated it.

I'm now using Ant 1.2 as the build engine, and have decided to remove Ant from thedistrubution. See the build.xml file for more information.

2.1.60. Grinder 0.8

HTTPS support from Phil Dawes.

HTTP 302/307 handling from Phil Dawes.

2.2. The Grinder To Do list

This is The Grinder TODO list. Its a collection of my thoughts on the future of TheGrinder.

If you want to implement something off this wish list, let me know first by discussionyour ideas on the grinder-development list. There are instructions on how to contribute athttp://grinder.sourceforge.net/development/contributing.html.

The TODO list is part of the binary distribution. I'll accept patches against it too :-).

• Philip Aston

2.2.1. Enhancements

2.2.1.1. Console API

API

Process control

void startWorkerProcess(some id) void stopWorkerProcess(some id) List<ProcessStatus>getProcessStatus() - NB return value voidaddProcessEventListener(ProcessEventListener)

void setScript(...?)

Recording control (done).

The Grinder

Page 56Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 57: The Grinder

void startRecording() void stopRecording() void resetRecording()

what about access from grinder scripts?

sample log file

feedback from travis

Commnents on the API: -

* For sure there will need to be an ability to retrieve stats, as you mentioned.

* A file transfer API, or some other configuration management API will be needed so thata centrally managed config, such as might be included in grinder.properties, can bepushed out to the agents.

* Right now I'm using scripted ssh calls to start the agents on the remote machines. Itwould be better if there was a good, generic, cross-platform way the controller/consolecould initiate a connection to a given agent. This might mean having the agent processrunning as a server/service on the agent machine, so that it would always be available.

2.2.1.2. Script Distribution

Model

Agent process to manage file cache. (Need files as Jython can't have python path intomemory). Agent is responsible for local file store below a directory based on the agentname.

File cache should be up to date with directory structure below directory chosen byconsole. The root directory is part of python path.

Tasks

Release 1 (done)

Task: Agent receives asynchronously. Task: Console to broadcast. RemoveScriptDistributionFiles work. Task: Allow editing in the console. Task:DistributeFilesMessage -> DistributeFileMessage (send a single file). Send in abackground thread with a progress bar. Task: Console to maintain map of remote agentaddress to last update time. For now assume that agent cache should be overwritten. Fornow, find the oldest time and broadcast since then. Null -> send the lot. Task: Console toclear cache state on root change -> broadcast full cache on distribution. Need a special"clear cache" message. Task: Console to allow selection of script. Warn on distribution ifno script selected. Task: Agent to receive selection of script. Task: Agent FileStore to

The Grinder

Page 57Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 58: The Grinder

update copy of cache asynchronously. Main agent thread to move it into place at goodpoint in lifecycle to prevent locking of stale files by worker processes. Task: Agentshouldn't create filestore directory until necessary. Task: Setting a new directory shouldrequire agents to refresh their cache.

Release 2 (done)

Task: File Store directory should have a README describing what its for. Task: Guardagainst distributing agent cache directory. Can mark with a special file - maybe theREADME? Also prevent distribution of temporary files, CVS directories, grinder logfiles.

Release 3 (done)

Task: Mature FileDistributionHandler model. Extract from comms package, leaving thecomms bit of it behind. Task: Model agent cache state. Task: Console to indicate globaldirty state. (Distinct from buffer "dirty" state). Task: Console to check dirty state on playand warn. Task: Console should not enable worker process controls if no agent isconnected. Task: If dirty buffers on play, optionally warn.

Release 4 (done)

Task: Merge worker and agent process status models. Update process tab to show both.

Release 5 (done)

Task: Add warning if saving outside of distribution. Task: Console to watch local filesystem for edits, trigger need to distribute. Task: Regularly update the tree to match filesystem. Task: Write some tests for FileTree. Task: Warnings for buffers whose file ismodified outside of editor.

Release 6

Task: External editor integration. Task: The agent should pick up secondarygrinder.properties from file cache. Task: Console to optionally auto broadcast on play.Need some way of synchronising before starting run.

Release 7

Task: Addressed messaging. Every Acceptor.SocketResource to know its remoteEndPoint. Add FilteredFanOutServerSender (Extend FanOutServerSender, allowresourceToOutputStream to return null indicating the resource shouldn't be sent anything.Supplied Filter method is given a SocketResource and the Message and can then veto.)

Task: Add agent initiation channel so agent can send a checksum across file names/sizes.Console compares with its cache and generates a good last update time for the agent.Thus agents starting with a good cache skip sync. Can't rely on remote timestamps as

The Grinder

Page 58Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 59: The Grinder

clocks are not sync'd.

Task: Distribution file filter should be dynamically settable.

2.2.1.3. Console

Note: Klaus-Peter Berg is currently working on a major refactoring of the console.

Stop the recording when last thread terminates

Requested by Jürgen Weber

Refactoring

Consider moving overwrite / save before close / ... handling to the model. Needs somekind of command pattern to represent choices.

Add log panel

Report log messages that currently go to terminal, plus start, stop test runs etc. Logsshould be timestamped. Use log to replace use of System.err for warnings.

Future editor features

Crtl-S -> save. Revert file. Status bar. Undo. Copy and Paste menu items. File tree popupmenu.

Allow grinder properties to be set remotely.

• This could just be another view in the editor?

jEdit

Replace jEdit-syntax with new jEdit syntax package when available, if its license termsare acceptable. Apparently now available.

Clause 2b:

b) You must cause any work that you distribute or publish, that in whole or in partcontains or is derived from the Program or any part thereof, to be licensed as a whole atno charge to all third parties under the terms of this License.

10. If you wish to incorporate parts of the Program into other free programs whosedistribution conditions are different, write to the author to ask for permission. Forsoftware which is copyrighted by the Free Software Foundation, write to the FreeSoftware Foundation; we sometimes make exceptions for this. Our decision will beguided by the two goals of preserving the free status of all derivatives of our free software

The Grinder

Page 59Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 60: The Grinder

and of promoting the sharing and reuse of software generally.

2.2.1.4. Engine

There should be an ExternalFilenameFactory, cf ExternalLogger.

Log lines should not be truncated.

Interactive mode

Possible to have an interactive mode for debugging? E.g. a simple UI that can belaunched from a thread which drops the user into a console? Would need to launch withgrinder.debug.singleprocess=true.

Instrumentation API

Could we support a procedural "startTest()", "stopTest()" API?

Perhaps. This procedural style would certainly be more obvious to the average user than"wrapping". OTOH, wrapping is powerful and I'm not sure we want to support twomechanisms to instrument code.

2.2.1.5. Communication

Resolve anomaly that Senders throw exceptions but Receivers return null.

2.2.1.6. Statistics

Should TPS averages be harmonic means? http://en.wikipedia.org/wiki/Harmonic_mean

Split out serializeable kernel of TestStatisticsMap? Look hard at where the message isdeserialised.

public ExpressionView ctor should probably be deprecated.

Aligning data files

Requested by Jose Antonio Zapata:

Add an additional "milliseconds since the Epoch" entry to the data files.

Separate out console statistics views from summary statistics views

Needed for custom statistics that use 'period' so are valid in the console but not in theprocess logs.

max(), min()

The Grinder

Page 60Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 61: The Grinder

(Requested by Venelin Mitov).

Should store max, min values agains SampleStatistics, and add max(), min() toexpressions.

2.2.1.7. TCPProxy

Tim McNerney writes:> Obviously, I could edit the scripts by hand. But I'd like to have> TCPProxy do this for me. So is there some existing method for doing> such filtering? Say on target suffix ("filter=.gif,.js,.css")

Change TCPProxy filters to a stream oriented model. This should cure another one of theTCPSniffer/HTTPPlugin bug when recording large outputs with posts? Also, considerhaving a filter instance pair per connection.

Internationalise messages.

Support different client certificates for proxied connection.

Meo Bogliolo writes:> What do You think about adding the possibility to insert remarks> with the TCPProxy Console? Maybe it's usefull in complex> navigation... I currently look at think time in the scripts to> understand when the user "changes" page.

Allow the filters to optionally parse unknown command line options. They would alsothen have to be able to contribute to the help text. This would allow the http plugin to addoptions to specify a different filename, and also to specify stdout.

Failed connection events.

2.2.1.8. HTTP Plugin

Remove ParseException, ProtocolNotSuppException from public APIs.

Script support for HTTP "system property" options.

Pablo Estades Fernández says:> I need to use client cert on TCPProxy to be able to> record the test case but also I need to configure> grinder workers to run the test case presenting a> client cert.

2.2.1.9. Scripting

Consider forcing TestRunner to be registered with grinder.

The Grinder

Page 61Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 62: The Grinder

Add per-run statistics. This would also allow number of aborted runs to be recorded.

Script access to global statistics.

Alternate test languages

Should use AOP proxies (probably with CGLIB) to do test instrumentation. Shouldsupport Java, Groovy scripts.

Events

Idea from Nurul Choudhury:> Event counting - The Jython code can create a named event and fire> the event when some condition was met. When the console polls for> statistics the events and their count would be sent to the console.

Error reporting

> It would be possible for certain classes of error (AttributeError> being a good example) to spew out just>> Unknown attribute GETx at "http.py", line 9 in __call__.>> Is this what you're after?

To do this, might have to behave differently with 1 thread vs many.

2.2.1.10. Reports

Perhaps JasperReports?

2.2.1.11. Code

Review use of Kernel. Probably want to share them.

Remove ThreadLocal from RegisteredPlugin.

2.2.1.12. Other HTTP/HTML libraries

DeSouza, Edwin writes:> Instead of using:> http://www.innovation.ch/java/HTTPClient/index.html> <http://www.innovation.ch/java/HTTPClient/FAQ.html>>> How about using Jakarta Commons HttpClient (more popular and Apache> License):

The Grinder

Page 62Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 63: The Grinder

> http://jakarta.apache.org/commons/httpclient/index.html

Justin Spears writes:> I am new to grinder, however I found the built in HTTPClient a> little lacking in functionality. I might suggest using> httpunit (http://httpunit.sourceforge.net) instead. It> works well with jython, and has extremely powerful methods> for handling links, posts, gets, etc.>> It uses nekohtml to parse malformed HTML documents into> valid XML then exposes a useful DOM based on these results.>> It also handles HTTPS connections, forms, links and other> useful things in an easy to use easy to understand> manner, (no more NVPAIRS or regex's, or patching HTTPClient!).>> ...

OK, this amounts to a campaign against HTTPClient!

Reasons for HTTPClient:

• Its solid, (and not 'alpha' which is the case for HttpClient).• Its small and comprehensible.• It is efficient.• Its extremely well written.• Its the incumbent.

Reasons for Commons HttpClient:

• Its actively maintained.• It is more modular.• It is richer.

Reasons for HttpUnit:

• nekohtml, parsing support

I prefer HttpClient, HTTPClient over HttpUnit for The Grinder as they are "closer to thewire".

Specific responses to Justin:

>(no more NVPAIRS

NameValuePair = NVPair # More readable alias

> or regex's,

The Grinder

Page 63Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 64: The Grinder

But they're lovely!

> or patching HTTPClient!

In the next beta I'll supply HTTPClient pre-patched for JSSE.

Summary

On balance, yes HttpClient looks good and we should use it if it proves to be efficient. I'lladd it to the TODO, but its a significant change => low priority.

Update (Oct 05): Doubts about HttpClient's scalability:http://sourceforge.net/mailarchive/forum.php?thread_id=8372852&forum_id=2649

We perhaps need to look more closely at parsing support for functional assertions, but Idon't want to lose The Grinder's efficiency here.

Also http://www.nogoop.com/product_16.html#compare

2.2.2. Bugs

(In addition to those on Sourceforge).

2.2.2.1. Console/agent

Each time L&F changes, the saveAs dialog gets another All Files filter!

Add reset console action.

2.2.2.2. TCPProxy

Should listen on all interfaces if -localhost is not specified.

2.2.3. Build

How to build clover reports during full build?

• currently "with.clover" only works if clover is first thing on path, we need to do two"compile", "test" runs with different classes.dir.

• could have with ant-call, modify compile, compile-tests, test• better to ant ourself?

Include Clover history?

2.2.4. Documentation

grinder.debug.singleprocess

The Grinder

Page 64Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 65: The Grinder

HTTP plug-in.

Document G2 adapter.

2.2.4.1. Examples

HTTPS.

Basic authentication.

http://thread.gmane.org/gmane.comp.java.grinder.user/186

2.2.4.2. Forrest TODO

Create back links from javadoc. This isn't trivial, e.g. this:

<bottom> <![CDATA[ <a class="noborder" href="../whats-new.html"target="_top"><img src="../../images/grinder3.jpg"></img></a> ]]> </bottom>

only works for top level javadoc.

Create menu links to the javadoc.

How to include arbitrary HTML (e.g. for poll forms?)

2.3. Contributing

2.3.1. The Source

The Subversion repository for the source code and the documentation source is availablefor read-only anonymous access from SourceForge. To check out the source code, you'llwant to do something like this:

$ svn cohttps://grinder.svn.sourceforge.net/svnroot/grinder/trunk/source grinder

You can browse this repository, and monitor changes to the repository via CIA. Note, thepublished documentation refers to the latest released version of The Grinder, and so canlag Subversion by a month or two.

2.3.2. What do I need to build The Grinder?

If you want to build The Grinder from source, you'll need:

J2SE 1.4 or higher

J2SE 1.5 or higher To build compile optional J2SE 1.5 specific code

The Grinder

Page 65Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 66: The Grinder

Apache Jakarta Ant 1.6.2

Later versions of these packages should also work.

2.3.3. What do I need to test and package The Grinder?

The following optional packages are necessary to run The Grinder unit tests, reports, andrelease process.

JUnit 3.8.1 to run the unit tests

Checkstyle 3.4 to check the formatting of changes you maymake to The Grinder

Clover 1.3.5 to check unit test coverage

svnant 1.0.0 to run the Ant tasks that release The Grinder

Later versions of these packages should also work.

2.3.4. How to give back

If you feel you have something worth sharing, please first discuss your ideas on thegrinder-development list. If your ideas develop into code, you can submit patches to thislist. Patches should be in 'unified diff' (diff -u) format against either the latest releasedversion of The Grinder 3, or the Subversion trunk (please state which revision). Patchesshould also include updates to the relevant documentation sources. Please include astatement in your email that you, and where appropriate your employer, are happy foryour work to be distributed under the terms of The Grinder license.

Please pay attention to the existing coding style of The Grinder. There is a Checkstyleconfiguration file for The Grinder in etc which you should use to check the formattingof your code. You can do this using the checkstyle build target. Checkstyle helps tomake the code readable, catches quite a few silly errors, and makes applying patches amuch nicer experience. Patches that do not pass the Checkstyle rules will be rejected.Whilst we're considering coding style, please note that Philip considers abbreviatedvariable names to be evil.

I have a strong preference for classes that take all the things they need as parameters totheir constructors (like constructor injection), and for contracts to be specified in terms ofJava interfaces. This makes unit testing more powerful, and forces the developer to thinkprimarily about the purpose and contract of each class. I actively track code coverage, andwould expect full JUnit tests for new code. I use Clover; you can get a free Clover licensefor development of The Grinder.

2.3.5. Documentation help wanted

The Grinder

Page 66Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 67: The Grinder

Documenation can always be improved. Tutorials and examples from users of TheGrinder are always welcome. Please mail suggestions, corrections, and improvements tothe [email protected] mailing list.

The CVS repository for The Grinder documentation is public. Authors who have madevaluable contributions can ask for check-in rights via [email protected] mailing list.

2.3.6. Internationalisation help wanted

If you are bilingual you might fancy translating the console into a language of yourchoice. Jose Antonio Zapta Rey did just this and produced a Spanish translation.

This translation will be automaically be the default for users with their locale set correctlyfor the Spanish language. If your locale is set otherwise and you are curious to try thisout, specify the Java user.language system property.

java -Duser.language="es" net.grinder.Console

Bertrand Ave produced a translation for French speaking users(user.language="fr"), Huibert Alblas produced a German translation(user.language="de"), and Italian, Polish, Russian, and Chinese translations havesince followed.

2.3.6.1. How to provide a translation

Download and expand the latest source distribution of The Grinder. Look in thesrc/net/grinder/console/swingui/resources directory. The definition ofall English text used by the console is contained in the Console.properties file.Individual translations are contained in Console_XX.properties where XX is thetwo letter ISO 639 language code. For example, the Spanish translation is contained inConsole_es.properties.

Use a text editor to create a translated file for your language of choice. The existingtranslations will guide you as to which properties you need to translate. You should onlyinclude properties that have translated text. Don't include properties such asscriptTab.image that refer to images, or properties that refer to logical names suchas action.menu.

To test your file before contributing it, place the file in a directory structure matchingsrc/net/grinder/console/swingui/resources and add the directory thatcontains src to the start of the CLASSPATH that you use to start the console. You mayneed to set user.language as described above.

Post your translation to grinder-development list. Please also include a statement that youare happy for your work to be distributed under the terms of The Grinder license.

The Grinder

Page 67Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 68: The Grinder

2.4. How The Grinder records test times - a problem?

2.4.1. Timing tests

The Grinder records test times for each successful test. By default, this is done by asection of code that looks like:

m_context.startTimer(); // Critical section starts

try {// do test

}finally {

m_context.stopTimer(); // Critical section ends}

This is repeated for each test.

If there are many threads within the worker process, (and the sleep time is small or thetest takes a long time or the test performs I/O), it is highly likely that the JVM will swapthe thread out in the critical section causing an erroneously large test time to be reported.

Similarly, if the host machine that you are running The Grinder on is also running otheractive processes (such as other worker processes), it is highly likely that the JVM willswap the process out in the critical section, again causing an erroneously large test time tobe reported. If The Grinder is co-hosted with the target server, and the plug-in uses asynchronous protocol, (e.g. the HTTP plug-in), such swapping is a certainty.

Further, as the CPU utilisation rises the contention on the critical section risesnon-linearly and in a way that is difficult to quantify. The recorded time becomes more ameasure of how the OS and JVM can swap between multiple threads and less a measureof server performance.

This is a generic problem with all test harnesses and is not limited to The Grinder orJava. Within the scope of a single machine there is little that can be done about thiswhilst realistically using multiple threads and processes.

2.4.1.1. Fiddling with Thread scheduling - a partial fix

From The Grinder 2.6.1, the call to startTimer makes a Thread.yield() callbefore recording the start time which means that a thread is more likely to be swappedout/in just before the critical sections. It dramatically reduced the response times Imeasured (e.g. 30 ms to 3 ms). I consider this only an approximate fix to the problem - itdoes not prevent the OS from swapping the process out.

The recorded response time should always be considered an upper bound on the actualresponse time. Doing the yield() makes that bound more accurate.

The Grinder

Page 68Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 69: The Grinder

An argument against doing this is that it slightly alters the statistical distribution of theclient invocations. I'd counter that without the yield() the distribution is not even; itsdown to the OS and JVM scheduling so threads/processes are far likely to be swapped atsome points (e.g. waiting on I/O) than others. Because of this I decided there is little pointin making the yield() optional.

2.4.1.2. The Timer Client model - a solution?

One solution to this problem is to dedicate a single machine to the measuring of responsetimes. I call this the "timer client" model.

As of The Grinder 2.6.1 you can set a property (grinder.recordTime for The Grinder 2,renamed to grinder.reportTimesToConsole for The Grinder 3) to be false which willcause the worker processes that use that grinder.properties file not report the testtimes to the console. You should run all but one of your worker processes with thisproperty set to false. These are the load clients.

You should copy the grinder.properties file to a dedicated timer client machine, changethe grinder.recordTime/grinder.reportTimesToConsole property to betrue, and set grinder.processes and grinder.threads to 1. The single worker process willrun on the timing client, record all timing information and (optionally) report it to theconsole. The less other stuff you run on the timing client, the better.

The disadvantage of this method is that the statistical sample of the test times is muchsmaller.

2.4.1.3. An update after some experimentation

Testing has shown that the difference the timer client model makes is only measurablewhen those clients are co-hosted with the server. When you have separate server andclient machines its better to not use the timer client model because it decreases the samplesize of test times.

I recommend trying both models. If you discover something interesting, please report it togrinder-use.

3. All documentation

4. The Grinder 2

4.1. Getting started

The Grinder

Page 69Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 70: The Grinder

4.1.1. Getting started

Note:Please read Should I use The Grinder 2 or The Grinder 3?

4.1.1.1. The Grinder processes

The Grinder is composed of three key processes:

• Worker processes• Perform the tests using a plug-in

• Agent processes• Manage worker processes• A single agent process runs on each client machine

• The console• Coordinates the other processes• Collates and displays statistics

As The Grinder is written in Java, each of these processes is a Java Virtual Machine(JVM).

For heavy duty testing, you can start an agent process on each of several client machines.The worker processes they launch can be controlled and monitored using the console.There is little reason to run more than one agent on a single machine, but you can if youwish.

4.1.1.2. How do I start The Grinder?

Its easy:

1. Set your CLASSPATH to include the grinder.jar file in the lib directory.

2. Start the console:

java net.grinder.Console3. Create a grinder.properties file which defines the test you want to perform.

See the examples directory for inspiration.

4. Start an agent process:

java net.grinder.Grinder

The agent process forks child Java processes to do the work. You can also specify anexplicit properties file as the first argument. For example:

java net.grinder.Grinder myproperties

The Grinder

Page 70Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 71: The Grinder

The console does not read the grinder.properties file. It has its own options dialog (choosethe File/Options menu option), which you should use to set the communication addressesand ports to match those in the grinder.properties files.

When the child processes start, they inform the console of the tests they will run. If youstart the console after the agent process, you should press the Reset processes button. Thiswill cause the existing worker processes to exit and the agent process to start new childprocesses, which will update the console with the new test information.

Network addresses

The worker processes listen for console signals on a multicast address, by default this is228.1.1.1:1235. Each worker process sets up a TCP network connection to theconsole to report statistics. By default, the console listens on port 6372 on all localnetwork interfaces of the machine running the console.

If the default multicast addresses are not valid, alter following properties in thegrinder.properties file before starting the Grinder agents

grinder.consoleAddress (Address of machine running console)grinder.consolePortgrinder.grinderAddress (Multicast address)grinder.grinderPort

4.1.1.3. Output

Each worker process writes logging information to a file called out-host-n.log,where host is the machine host name and n is the worker process number. Errors will bewritten to error-host-n.log. If no errors occur, an error file will not be created.

Data about individual test invocations is written into a file called data-host-n.log.This can be imported into a spreadsheet tool such as Microsoft Excel™ for furtheranalysis.

The final statistics summary (in the out-* files of each process) looks something likethis:

Final statistics for this process:

SuccessfulTransactions Errors Average (ms)

Test 0 25 0 255.52Test 1 25 0 213.40Test 2 25 0 156.80 "Image"Test 3 25 0 90.48Test 4 25 0 228.68 "Login page"Test 5 25 0 86.12 "Securitycheck"Test 6 25 0 216.20Test 7 25 0 73.20

The Grinder

Page 71Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 72: The Grinder

Test 8 25 0 141.92Test 9 25 0 104.68 "Logout page"

Totals 250 0 156.70

The console displays a similar dynamic display of information collected from all theworker processes.

Each test has one of two possible outcomes:

1. Success. The number of Successful Transactions for that test is incremented The timetaken to perform the test is added to the Total.

2. Error. The exact interpretation of an error depends on the plug-in. The number ofErrors for the test is incremented.

The Total and Average figures are calculated based only on successful transactions.

4.1.2. Core properties

The Grinder is controlled by setting properties in the grinder.properties file.Here's a minimal grinder.properties file that uses the HttpPlugin:

grinder.plugin=net.grinder.plugin.http.HttpPlugingrinder.test0.parameter.url=http://localhost:80/index.html

If you start The Grinder using this properties file it will communicate with the consoleusing default addresses, use one worker process, one thread, and runs one cycle whichcontains once test. This is not too much use, so read on...

4.1.2.1. Table of core properties

This table lists the core properties understood by The Grinder engine. Refer to pluginspecific documentation for additional properties understood by the plugin you are using.

grinder.processes The number of worker processes to start.Default 1.

grinder.threads The number of aworker threads that eachworker process spawns. Default 1.

grinder.cycles The number of cycles of the test script eachthread performs. 0 means "run forever". Default1.

grinder.jvm Use an alternate JVM for worker processes.Defaults to java so you do not need to specifythis if your PATH is sensible.

grinder.jvm.classpath Use to adjust the classpath used for the workerprocess JVMs. Anything specified here will beprepended to the classpath used to start theGrinder processes.

The Grinder

Page 72Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 73: The Grinder

grinder.jvm.arguments Additional arguments to worker process JVM's.

grinder.logDirectory Directory to write log files to. Created if it doesn'talready exist.

grinder.appendLog Append to, rather than overwrite, existing logfiles.

grinder.hostID Override the "host" string used in log filenamesand logs. Defaults to the host name.

grinder.receiveConsoleSignals Set to true to respond to console signals.Default true.

grinder.reportToConsole Set to true to send statistics to the console.Default true.

grinder.consoleAddress The IP address or hostname to use forcommunication from the Grinder processes tothe console. Default is all the network interfacesof the local machine.

grinder.consolePort The IP port to use for communication from theGrinder processes to the console. Default is6372.

grinder.grinderAddress The multicast address to use for communicationfrom the console to the agent and workerprocesses. See What do I need to do to set upmulticast?. Default 228.1.1.1.

grinder.grinderPort The multicast port to use for communicationfrom the console to the agent and workerprocesses. Default 1234.

grinder.reportToConsole.interval The period at which each process sendsupdates to the console. This also controls thefrequency at which the data files are flushed.Default 500 ms.

grinder.thread.initialSleepTime The maximum time in milliseconds that eachthread waits before starting. Unlike the othersleep times, this is varied according to a flatrandom distribution. The actual sleep time willbe a random value between 0 and the specifiedvalue. Affected bygrinder.thread.sleepTimeFactor, butnotgrinder.thread.sleepTimeVariation.Default 0 ms.

grinder.thread.sleepTime The time in milliseconds to wait betweenindividual tests. Affected by bothgrinder.thread.sleepTimeFactor, and

The Grinder

Page 73Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 74: The Grinder

grinder.thread.sleepTimeVariation.Default 0 ms.

grinder.thread.sleepTimeFactor Apply a factor to all the sleep times you'vespecified. Setting this to 0.1 would run the scriptten times as fast. Default 1.

grinder.thread.sleepTimeVariation The Grinder varies the specified sleep timesaccording to a Normal distribution. This propertyspecifies a fractional range within which nearlyall (99.75%) of the times will lie. E.g., if the sleeptime is specified as 1000 and thesleepTimeVariation is set to 0.1, then 99.75%of the actual sleep times will be between 900and 1100 milliseconds. Default 0.2.

grinder.plugin The plugin class to use. Currently each scriptuses a single plugin.

grinder.plugin.parameter Prefix for properties interpreted by the plugin.See the the HTTP plugin documentation forexamples.

grinder.test0 Prefix for test 0 properties. Test 1 propertieshave a prefix of grinder.test1, and so on.The tests are run in numerical order.

grinder.test0.description Information string used in the log files and theconsole.

grinder.test0.sleepTime Override grinder.thread.sleepTime for aspecific test.

grinder.test0.parameter Prefix for test specific properties interpreted bythe plugin. See the the HTTP plugindocumentation for examples.

grinder.logProcessStreams Set to false to disable the logging of outputand error steams for worker processes. Youmight want to use this to reduce the overhead ofrunning a client thread. Default true.

grinder.recordTime Set to false to disable reporting of timinginformation; other statistics are still reported.Default true.

4.1.3. The Console

4.1.3.1. Process controls

Start processes, Reset processes and Stop processes send signals to Grinder processes thatare listening. (See grinder.receiveConsoleSignals, grinder.grinderAddress and

The Grinder

Page 74Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 75: The Grinder

grinder.grinderPort.)

Worker processes that are configured to receive console signals go through three states:

1. Initiated (waiting for a console signal)2. Running (performing tests, reporting to console)3. Finished (waiting for a console signal)

The Start processes control signals to worker processes that they should move into therunning state. Processes that are already running ignore this signal. Processes that are inthe finished state exit; the agent process will then reread the properties file and launchnew worker processes in the running state.

The Reset processes control signals all the worker processes to exit. The agent processwill then reread the properties file and launch new worker processes.

The Stop processes control signals all processes to exit.

Warning:Warning - unless grinder.appendLog is true, new worker process logs will overwrite those from previous runs.

4.1.3.2. Sample controls

The sample controls determine how the console captures reports from the workerprocesses. capture. It is important to understand that these control the console behaviouronly. For example, they do not adjust the frequency at which the worker processes sendreports (see grinder.reportToConsole.interval for that). Additionally, the sample controlsdo not interact in any way with the process controls.

The slider controls the period at which the console will take a sample. This involvesadding up all the reports received over that sample interval and calculating the TPS as(number of transactions that occurred)/(interval length). It is also the period at which theconsole graphs and statistics are updated.

By default, the console discards the first non-zero sample period, and starts updating thedisplay and calculating totals from the second sample. A non-zero sample period is one inwhich an update from a worker process was received. You can adjust how many non-zerosample periods the console ignores before starting capture with the ignore samples textfield.

The third control allows you to adjust how many samples the console will collect beforestopping capture.

You can also manually start and stop the sampling with the Capture statistics/Stopcapture control. Use the Save statistics control to save the current set of statistics to a file.

4.1.4. The TCPSniffer

The Grinder

Page 75Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 76: The Grinder

The TCPSniffer is misnamed: its not a sniffer (like snoop or Ethereal) at all, but rather aproxy that you can place between in a TCP stream. It filters the request and responsestreams, sending the results to stdout. You can control its behaviour by specifyingdifferent filters. Whilst the TCPSniffer is very useful in its own right, its main purpose asfar as The Grinder goes is to automatically generate scripts for the HTTP plugin.

Note:If you are not interested in the ability to record scripts for The Grinder 2, use the The Grinder 3's TCPProxy instead. Ithas more features and fixes.

4.1.4.1. Starting the TCPSniffer

You invoke the TCPSniffer with something like:

CLASSPATH=/opt/grinder/lib/grinder.jarexport CLASSPATH

java net.grinder.TCPSniffer

Say java net.grinder.TCPSniffer -? to get a list of the command lineoptions.

Suppose you want to capture a conversation with a server on host server, port 7001, youshould say something like:

java net.grinder.TCPSniffer -remoteHost server

The TCPSniffer will start and display the following information:

Initialising standard sniffer engine with the parameters:Request filter: net.grinder.tools.tcpsniffer.EchoFilterResponse filter: net.grinder.tools.tcpsniffer.EchoFilterLocal host: localhostLocal port: 8001Remote host: localhostRemote port: 7001

Engine initialised, listening on port 8001

You can then point your web browser at http://localhost:8001/ and exercisethe application through the browser. The TCPSniffer will echo your requests to theterminal and forward the requests to localhost:7001, as well as echoing responsefrom the server the terminal and returning them to the browser.

4.1.4.2. Using the TCPSniffer as an HTTP proxy

One problem of running the TCPSniffer as described above is that it only forwards to asingle remote host. Any links or redirects to other hosts that the application returns to thebrowser will bypass the TCPSniffer, meaning that they will not feature in the test script.This also applies to absolute URLs to the server.

The Grinder

Page 76Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 77: The Grinder

When recording browser traffic, a much better way to use the TCPSniffer is to run it as anHTTP proxy:

java net.grinder.TCPSniffer -proxy

This will make it listen as an HTTP proxy on port 8001 (the default, you can change itwith -localPort), and forward requests onto the relevant remote host, while echoingout the HTTP interactions.

You should set your browser connection settings to specify the TCP sniffer as the HTTPproxy (set host to be the host on which the TCPSniffer is running and port> to be 8001).You then use your browser as normal, e.g. in the example in the previous section youshould use the direct address http://localhost:7001 in your browser.

The TCPSniffer will run as a proxy for both HTTP and HTTPS if you specify -ssl.

4.1.4.3. Using the HttpPluginSnifferFilter

You can use the TCPSniffer to generate an HTTP plugin script segment suitable for usewith The Grinder.

java net.grinder.TCPSniffer -proxy -httpPluginFilter

The output of the HttpPluginSnifferFilter looks like:

Initialising standard sniffer engine with the parameters:Request filter: net.grinder.plugin.http.HttpPluginSnifferFilterResponse filter: net.grinder.tools.tcpsniffer.NullFilterLocal host: localhostLocal port: 8001Listening as an HTTP proxyEngine initialised, listening on port 8001

## The Grinder version 2.8.3## Script generated by the TCPSniffer at 25-Apr-02 08:17:57#

grinder.processes=1grinder.threads=1grinder.cycles=0

grinder.test0.sleepTime=11336grinder.test0.parameter.url=http://localhost:7001/grinder.test1.sleepTime=12168grinder.test1.parameter.url=http://localhost:7001/lah.htmlgrinder.test2.sleepTime=411grinder.test2.parameter.url=http://localhost:7001/test.gifgrinder.test3.sleepTime=4786grinder.test3.parameter.url=http://localhost:7001/lah.htmlgrinder.test3.parameter.header.If-Modified-Since=Tue, 16 Jan 2001

16:26:42 GMTgrinder.test4.sleepTime=311

The Grinder

Page 77Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 78: The Grinder

grinder.test4.parameter.url=http://localhost:7001/test.gifgrinder.test4.parameter.header.If-Modified-Since=Mon, 06 Nov 2000

08:35:58 GMT

The script part of this is sent to the stdout stream, whereas the information messagesare sent to stderr. You can redirect the script part to a file if you wish:

java net.grinder.TCPSniffer -proxy -httpPluginFilter >grinder.properties

You can then use this file with The Grinder.

4.1.4.4. SSL and HTTPS support

The TCPSniffer has SSL support. You must first install the JSSE.

SSL relationships are necessarily point to point. When you interpose the TCPSniffer youend up with:

Client <--- ssl1 ---> TCPSniffer <--- ssl2 ---> Server

Where ssl1 and ssl2 are two separate SSL connections. Each SSL connection has its ownset of client and server certificates (both of which are optional).

The TCPSniffer will negotiate appropriate certificates for both connections usingcertificates specified in a key store. See the JSSE documentation for how to set up a keystore. There are three parameters you can pass as command line options to the TCPSnifferto specify key store details:

-keyStore file The key store file.

-keyStorePassword password The password for the key store.

-keyStoreType type The type, defaults to jks.

You can also specify these with the corresponding javax.net.ssl.XXX properties.

Here's an example of starting the TCPSniffer as an HTTP/HTTPS proxy using thetestkeys key store provided with the JSSE samples:

java net.grinder.TCPSniffer -ssl -proxy -keyStore testkeys-keyStorePassword passphrase

Even if you are not using client certificates, you probably need to specify a key store.This is because the proxy needs a server certificate of its own:

Browser -----> [ServerCert] Proxy ----> [ServerCert2]Target

You need to start the proxy with a key store containing a self-signed server certificate.This is the certificate that the browser will be presented with. If you fail to provide aserver certificate, you will get a No available certificate corresponds to the SSL ciphersuites which are enabled exception. The easiest way to provide a certificate is to copy the

The Grinder

Page 78Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 79: The Grinder

testkeys file from the JSSE samples distribution and start the sniffer using:

java net.grinder.TCPSniffer -ssl -proxy -keyStore testkeys-keyStorePassword passphrase

Alternatively you might want to generate your own. Here's an example:

PASTON:philipa% keytool -genkey -keystore testkeys -storepasspassphrase -keyalg rsaWhat is your first and last name?[Unknown]: localhost

What is the name of your organizational unit?[Unknown]: Engineering

What is the name of your organization?[Unknown]: Grinders Inc

What is the name of your City or Locality?[Unknown]: Grindsville

What is the name of your State or Province?[Unknown]: Grindshire

What is the two-letter country code for this unit?[Unknown]: GR

Is <CN=localhost, OU=Engineering, O=Grinders Inc, L=Grindsville,ST=Grindshire, C=GR%gt; correct?

[no]: yes

Enter key password for <mykey>(RETURN if same as keystore password):

PASTON:philipa%

The first and last name ought to match the server which you run the proxy on, and youmust specify -keyalg rsa to generate a certificate that works with common browsers.See the keytool notes in the JDK documentation for how to do more tricks.

You may find NullPointerExceptions when using PKCS12 files generated bysome tools, e.g. openssl. Use the Java keytool utility to maintain key stores andyou'll be all right.

4.1.4.5. The Sniff 'n' Grind web application

Sniff 'n; Grind is Paddy Spencer's J2EE web application that automates the tasks involvedusing the TCPSniffer to record and replay HTTPPlugin scripts. This section containsrather minimal notes on its use.

Setup page

In the setup page enter the starting URL; this is usually the front page of the applicationyou're testing. You need to include the protocol in the URL, only HTTP and HTTPS arecurrently supported. Click on the button to go to...

Start TCPSniffer page

This page tells you what you need to change your browser proxy settings to. You need to

The Grinder

Page 79Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 80: The Grinder

so this before clicking on the link to start the page, otherwise your requests will gothrough the proxy you normally use and the sniffer won't pick them up. Once you've setthe proxy, click on the Click here to go to... link, and the start URL will be returned in anew window.

If you specify a certificate in the web.xml file, and a secure starting URL (an HTTPSrather than HTTP one) then the web application will let you sniff and grind your ssl-usingweb application. The only difference you'll notice is that you will be asked to accept anuntrusted certificate from the server. This is because the web application uses thecertificate you give it as a server certificate when you connect to it and as a clientcertificate when it connects to your web application. So you'd better make sure that it'sone your app will accept!

Do not close the sniffer window. If you do that you will not be able to end the test and getyour results.

In order not to be a resource hog, the sniffer proxy process will timeout after a givennumber of seconds (configurable via the web.xml), so if you don't do anything for awhile, you may find your proxy isn't there any more.

When you've finished the test, close the test window and rest your browser proxy to it'soriginal settings (you DID note down those settings, didn't you?) and then click on thestop button. You will be taken to...

TCPSniffer results page

The Sniff 'n' Grind generates a number of files. As a minimum there are two:

• httpsniffer.err - which contains the initial startup information as well as anyruntime errors that might have occurred.

• httpsniffer.out - which contains the details of the test(s).

If you wish to run the test manually, you need to copy these into yourgrinder.properties file and run The Grinder in the normal way. You will alsoneed to cut and paste the various http-plugin-sniffer-post files, if any.

If you instead want to run The Grinder right now, with your recorded results, then clickon the link and go to...

The Grinder setup page

Despite, or perhaps because of, the vast, bewildering array of properties that can be set tocontrol a Grinder session, the web application (in its current incarnation) only allows youto set the numbers of processes, threads and cycles from within the browser. Thesedefault to one of each and have maximum settings (currently hard-coded) of 5 processes,25 threads and 50 cycles.

The Grinder

Page 80Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 81: The Grinder

The reset button resets the values in the form (as you'd expect) and Grind me, baby! doeswhat it says, leading to...

Wait page

The patience page. If I was a real 31337 h4x0r d00d I'd have written some kewlapplet which would keep you entertained with graphical and highly amusing pr0nanimations to keep you entertained while sneakily querying the server for whether TheGrinder has finished. However, I'm not and so you've got a rather dull page with a fivesecond refresh on it and a note saying, "Wait."

The Grinder results page

The results page simply presents the contents of the files in the log directory (so ifyou've used some huge number of threads and cycles, this page will be BIG) and givesyou the options to re-run the grinder against the same test, but with different properties, orto record another test.

4.2. Plug-ins

4.2.1. The HTTP Plug-in

4.2.1.1. HTTP plug-in class

To use the HTTP plug-in, specify:

grinder.plugin=net.grinder.plugin.http.HttpPlugin

4.2.1.2. HTTP plug-in properties

This table lists the HTTP plug-in properties that you can set in grinder.propertiesin addition to the core properties. You can use the TCPSniffer to record HTTP plug-inscripts.

grinder.test0.parameter.url The URL to call. The HTTP GET method is usedunless grinder.test0.parameter.post isspecified. The contents of the file can be variedusing a string bean, see this FAQ.

grinder.test0.parameter.post Specify a file containing POST data to send. Thevalue can be varied using a string bean.

grinder.test0.parameter.header.name (Where name can be an arbitrary string). Add aname: header to the request with the specifiedvalue. The value can be varied using a stringbean.

The Grinder

Page 81Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 82: The Grinder

grinder.plugin.parameter.stringBean Fully qualified class name of a Java bean thatcan generate dynamic strings. See What's aString Bean?.

grinder.test0.parameter.ok Fail if the returned page doesn't contain thisstring. The value can be varied using a stringbean.

grinder.test0.parameter.basicAuthenticationRealmgrinder.test0.parameter.basicAuthenticationUsergrinder.test0.parameter.basicAuthenticationPassword

Used together, these specify an HTTP BASICauthentication header that will be sent with therequest. If you specify one of these values, youmust specify all three.

Note, the default HTTPClient implementation onlysends this if challenged by the server, as a browserwould, and the specified basicAuthenticationRealmmust match the realm required by theWWW-Authenticate header in the challenge. Thevalues can be varied using a string bean.

grinder.plugin.parameter.useCookies Set to false to disable cookie handling (itdefaults to true).

grinder.plugin.parameter.disablePersistentConnectionsSet to true to send a Connection: closemessage with every request. Seehttp://www.innovation.ch/java/HTTPClient/advanced_info.html#pers_con.Only works with the default HTTPClientimplementation. The default is false.

grinder.plugin.parameter.followRedirectsSet to true to automatically follow redirects, so youdon't have to have additional URLs in you scripts.The default is false. You should always set this tofalse for TCPSniffer generated scripts.

If you are using WebLogic Server and WebApplication form-based authentication you leave thisproperty set to false. This is because a redirectrequest that follows authentication contains a keycookie - setting the property to true prevents thecookie from being picked up by the plug-in.

grinder.useHTTPClient Controls which of the two different HTTPlibraries the HTTP plug-in uses. The defaultvalue is true, meaning that the HTTPClientimplementation should be used.

What's a String Bean?

When using the HTTP plug-in, it is often necessary to parametrise parts of URLs, POSTdata, and other request strings. The easy way to do this is to use a string bean - a JavaBean that returns Strings from its get methods.

The Grinder

Page 82Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 83: The Grinder

For example, suppose you wanted to modify the URLhttp://myhost/test.jsp?n=1 such that n is set to a random number each time?Easy! Here's how.

1. Write a simple bean:

// MyStringBean.javapackage mystuff;

import java.util.Random;

public class MyStringBean {private Random m_random = new Random();

public String getRandomInteger() {return Integer.toString(m_random.nextInt());

}}

2. Compile this and put it in your CLASSPATH. Then alter yourgrinder.properties to include this line:

grinder.plugin.parameter.stringBean=mystuff.MyStringBean3. Alter the test URL parameter as follows:

grinder.test0.parameter.url=http://myhost/test.jsp?n=<getRandomInteger>

The <beanMethodName> syntax can be used in URL strings, POST data files, HTTPheaders and in OK strings. It must correspond to a public method of the string beanthat takes no parameters and returns a String.

The HTTP plug-in is relaxed about a partial string bean tag matches (for example<notAMethod> or <abc<def>xyz>); if it can't find a suitable match it simplyoutputs the literal text. This allows string bean tags to be used within XML POST data. Ifyou find that your string bean is not invoked when you expect it to be, use the TCPSnifferto find out what is actually being sent and check your spelling.

Each string bean instance is instantiated per thread, and maintains its state betweeninvocations. If your bean needs additional information regarding the test life cycle (forexample, to reset a counter and the beginning of a cycle), it can implement thenet.grinder.plugin.http.StringBean interface. See the examples innet.grinder.plugin.http.example.

Advanced string beans can implement thenet.grinder.plugin.http.StringBean and/ornet.grinder.plugin.http.HTTPClientResponseListener interfaces toreceive callbacks about the test life cycle. See the examples insrc/net/grinder/plugin/http/example.

4.2.1.3. HTTPClient

The Grinder

Page 83Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 84: The Grinder

The HTTP plugin has two implementations. The default implementation is based onRonald Tschalär's excellent HTTPClient library. An alternative implementation whichuses the JDK's HttpURLConnection can also be used, but is deprecated and will beremoved from The Grinder in a future release. If you really want to you can specify thatthe HTTP plug-in should use HttpURLConnection instead of HTTPClient:

grinder.plugin.parameter.useHTTPClient=false

I highly recommend the HTTPClient implementation, see below for some reasons why.However the HttpURLConnection implementation has two features that the HTTPClientimplementation doesn't. The first feature is an additional parameter:

grinder.plugin.parameter.useCookiesVersionStringSet to false to remove the $Version stringfrom cookies (it defaults to true). This is to workaround broken (?) JRun 2.3.3. behaviour.

The HTTPClient cookie support is damn good, so this probably isn't an issue. It will befixed if it turns out to be a problem.

The second additional feature is the reporting of the mean time to first byte statistic inaddition to the normal total transaction time statistic. This will be supported by theHTTPClient implementation in a future release.

HTTPClient versus HttpURLConnection

HTTPClient has many more features than HttpURLConnection, seehttp://www.innovation.ch/java/HTTPClient/urlcon_vs_httpclient.html for a comparison. Ihope to lever features such as proxy support, connection timeouts and persistent cookiesinto future versions of The Grinder.

You can access many HTTPClient features by setting system properties. Seehttp://www.innovation.ch/java/HTTPClient/advanced_info.html for a list of properties.For example, you can force HTTPClient to use HTTP 1.0 instead of HTTP 1.1 with thefollowing parameter:

grinder.jvm.arguments=-DHTTPClient.forceHTTP_1.0=true

One of the key advantages for The Grinder is that HTTPClient allows explicit control ofconnection management, whereas HttpURLConnection uses connection pooling "underthe covers". Because HTTPClient uses extra connections, it may appear slower -particularly if the client and server are co-hosted. However, its a better model of reality(one cycle equals one browser session).

In my experience, HTTPClient is much more RFC compliant, and less buggy thanHttpURLConnection.

4.2.1.4. How do I use HTTPS?

The Grinder

Page 84Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 85: The Grinder

There are patches available to HTTPClient to work with several SSL implementations.See http://www.innovation.ch/java/HTTPClient/https.html for details. The instructionsthat follow assume you are using JSSE 1.0.2.

1. Install JSSE1.0.2. Follow the instructions inhttp://java.sun.com/products/jsse/INSTALL.html. I recommend installing the JSSE asan installed extension for simplicity.

2. Download the HTTPClient JSSE patch fromhttp://www.innovation.ch/java/HTTPClient/JSSE.zip

Extract the class files contained within the zip into a directory called HTTPClient,then create a jar containing that directory:

mkdir HTTPClient; cd HTTPClientjar xf /download/JSSE.zipcd ..jar cf HTTPClient-JSSE.jar HTTPClient

Add this jar to the start of your CLASSPATH before running The Grinder. Its worthreading the file README in JSSE.zip.

3. You can now use URLs that start with https: in your grinder.properties.

You may well need to create a trust store containing CA certificates that sign the servercertificate. See the JSSE documentation for full details, here's a quick hint:

keytool -import -v -keystore ./mycastore -file d:/wls5/myserver/ca.pem

You should then add -Djavax.net.ssl.trustStore=mycastore togrinder.jvm.arguments in your grinder.properties. Refer to the JSSEdocumentation for other useful properties. In particular -Djavax.net.debug=sslmight come in useful.

HTTPClient checks that the host name in each request URL matches the subject DN fieldin the certificate. If this isn't the case, you might need to add an entry to /etc/hosts,c:/WINNT40/system32/drivers/etc/, DNS or whatever, and then use thathostname in the request URLs.

The JSSE SSL implementation isn't quick. This should be taken into account whencomparing round trip times, as a compiled browser version is likely to be a lot faster.

4.2.1.5. How do I use the HTTPS plug-in? (HttpURLConnection implementation only)

Note:This information applies to the deprecated HttpURLConnection implementation. For information on using HTTPS withthe default HTTPClient implementation, see above.

1. Install JSSE1.0.2 as described above.2. In your grinder.properties, use the HttpsPlugin rather than HttpPlugin:

The Grinder

Page 85Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 86: The Grinder

grinder.plugin=net.grinder.plugin.http.HttpsPlugin3. You can now use URLs that start with https: in your grinder.properties.4. If you want two-way authentication, add the lines like:

grinder.plugin.parameter.clientCert=./philclient.p12grinder.plugin.parameter.clientCertPassword=acrobatYou can export a P12 certificate from Netscape.

4.2.2. The JUnit Plug-in

4.2.2.1. What's it for?

Why would you want to do use the JUnit plug-in? Three possible reasons spring to mind:

• To write a simple test case around arbitrary Java code, rather than creating a newplug-in. This is the recommended way to replace the functionality of the oldTraderEJBPlugin that was shipped with early versions of The Grinder. (As anadded bonus you end up with some JUnit test cases which you can use to impressesyour boss.

• To thrash the heck out of your code in an attempt to discover race conditions (bugs).

• To investigate the statistical effects of subatomic particles passing through yourhardware.

Looking for race conditions requires that each instance of the JUnit tests should runagainst a common fixture - how to achieve in general this is left as an exercise. Pleasesend any useful patterns to grinder-use. Additionally, because The Grinder currently runseach test cycle in the same order, most race conditions will be hidden. Perhaps a betterapproach would be to ditch The Grinder 2 and the JUnit plugin and instead use TheGrinder 3 to run scripts that exercise the test cases appropriately.

4.2.2.2. JUnit plug-in class

To use the JUnit plug-in, specify:

grinder.plugin=net.grinder.plugin.junit.JUnitPlugin

4.2.2.3. JUnit plug-in properties

This table lists the JUnit plug-in properties that you can set in grinder.propertiesin addition to the core properties.

grinder.plugin.parameter.testSuite The fully qualified name of the JUnit test suite class.

The class can be anything you can normally pass to aJUnit TestRunner. A quick summary: it can eitherhave a method, public staticjunit.framework.Test suite() which

The Grinder

Page 86Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 87: The Grinder

returns the TestSuite or it can define a number oftests methods which will be discovered throughintrospection - the method names must begin withtest. (Confusingly, it matters not whether the classdirectly implementsjunit.framework.TestSuite; this is a JUnitthing, so take any complaints there).

grinder.plugin.parameter.logStackTracesSet to true to produce stack traces for failuresand errors in the error log. The default is false.

grinder.plugin.parameter.initialTestNumberThe test number used for the first test,subsequent tests are numbered sequentially.This property is useful if you want to use severalworker processes to run different test suitesagainst the same console.The default is 0.

The tests to run are automatically sucked out of the test suite class - you shouldn't specifythem individually in grinder.properties. However, you can tweak with individual tests'sleep time as normal. For example, if you want to wait a second before the 6th test in thetest suite, you should say

grinder.test5.sleepTime=1000

JUnit has the concept of failures (which occur due to assertions failing) and errors (whichoccur when tests throw exceptions). The Grinder errors count for a test is incremented byone if the test causes either a failure or an error.

5. The Grinder 3

5.1. What's new in The Grinder 3?

This section summarises the changes between The Grinder 2 and The Grinder 3. If you'renot familiar with The Grinder 2, skim read this and move on to Getting Started.

Note:Please read Should I use The Grinder 2 or The Grinder 3?

5.1.1. Jython

The most significant change that The Grinder 3 introduces is a Jython scripting enginethat is used to execute test scripts. Jython is a Java implementation of the popular Pythonlanguage. Test scripts specify the tests to run. In The Grinder 2, tests were specified in thegrinder.properties file. The grinder.properties file is still used to specifygeneral control information (how to contact the console, how many worker processes to

The Grinder

Page 87Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 88: The Grinder

use, ..), as well as the name of the test script. Many other key features of The Grinder 2,such as the console and the process architecture, remain unchanged.

The scripting engine provides the following new capabilities:

Test any Java codeThe Grinder 3 allows any Java (or Jython) code to be encapsulated as a test. Thispractically removes the need to write custom plug-ins. Although plug-ins are nolonger responsible for performing tests, they can still be useful to manage objects thatthe tests use. For example, the HTTP plug-in manages a pool of connections for eachworker thread, and provides an HTTPRequest object that makes use of theseconnections.

Dynamic test scriptingThe Grinder 2 worker processes execute tests in the properties file sequentially in afixed order, and there is limited support in some of the The Grinder 2 plug-ins forchecking test results. The Grinder 3 allows arbitrary branching and looping and makestest results directly available to the test script, allowing different test paths to be takendepending on the outcome of each test.

The Grinder 2 HTTP plug-in's string bean feature provides simple support forrequests that contain dynamic data. The Grinder 3 can use the full power of Jython tocreate dynamic requests of arbitrary complexity.

Kind of dry, huh? If you never seen any Python or are wondering what was wrong withthe old grinder.properties approach then skip ahead to the Script Gallery whereyou can sample the power of The Grinder 3.

5.1.2. New distribution packaging

The Grinder is distributed in separate binary and source zip files. They can bedownloaded from SourceForge.net.

5.1.3. Other changes

• The TCPSniffer has been renamed TCPProxy for correctness. The TCPProxy can beused with other HTTP and HTTPS proxies. Many new features and fixes have beenadded to the TCPProxy.

• Console signals are now transmitted over a TCP socket connection. Multicast is nolonger used, removing a frequent source of setup problems.

• The interface that plug-ins must implement has changed significantly. Plug-inswritten for The Grinder 2 will not work with The Grinder 3.

• Many grinder.properties have been removed. The features formerly accessedthrough setting properties are now set by making calls to the plug-in from test scripts.Some of the remaining property names have been renamed.

• HTTP tests can now be directed through an HTTP proxy.

The Grinder

Page 88Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 89: The Grinder

• String beans and OK strings have been removed from the HTTP plug-in. String beansand OK strings are very limited in comparison to to the flexibility now provided byJython scripts.

• The HttpURLConnection implementation has been removed from the HTTPplug-in.

• Many other minor updates to HTTP testing functionality.• HTTPS and SSL contexts can now be controlled on a per thread basis.• The JUnit and Socket plug-ins have been removed. Their functionality can be

achieved directly by using the appropriate Java objects in scripts.

5.1.4. Roadmap

This section documents things that are planned for The Grinder 3. There are no currentrelease dates for these features.

5.1.4.1. Test script distribution

From the Grinder 3 console you will be able to edit test scripts and then distribute them tothe worker processes. It will no longer be necessary to copy grinder.properties filesaround or to use a shared disk.

5.2. Features of The Grinder 3

Thanks to Edwin DeSouza for his help in compiling this feature list.Last updated: 6 October 2003

5.2.1. Capabilities of The Grinder

Load Testing Load Testing determines if an application cansupport a specified load (for example, 500concurrent users) with specified response times.Load Testing is used to create benchmarks.

Capacity Testing Capacity Testing determines the maximum loadthat an application can sustain before systemfailure.

Functional Testing Functional Testing proves the correct behaviourof an application.

Stress Testing Stress Testing is load testing over an extendedperiod of time. Stress Testing determines if anapplication can meet specified goals for stabilityand reliability, under a specified load, for aspecified time period.

The Grinder

Page 89Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 90: The Grinder

5.2.2. Open Source

BSD style license The Grinder is distributed under a BSD stylelicense.

Jython The Grinder uses Jython (a Java Pythonimplementation) for scripting.

Jakarta ORO The Grinder uses Jakarta ORO for regularexpressions.

HTTPClient The Grinder uses HTTPClient for HTTP support.

jEdit Syntax The Grinder uses jEdit Syntax for script editing.

5.2.3. Standards

100% Pure Java The Grinder works on any hardware platformand any operating system that supports J2SE1.3 and above.

Web Browsers The Grinder can simulate web browsers andother devices that use HTTP, and HTTPS.

Web Services The Grinder can be used to test Web Serviceinterfaces using protocols such as SOAP andXML-RPC.

Database The Grinder can be used to test databasesusing JDBC.

Middleware The Grinder can be used to test RPC and MOMbased systems using protocols such as IIOP,RMI/IIOP, RMI/JRMP, and JMS.

Other Internet protocols The Grinder can be used to test systems thatutilise other protocols such as POP3, SMTP,FTP, and LDAP.

5.2.4. The Grinder Architecture

Goal Minimize system resource requirements whilemaximizing the number of test contexts ("virtualusers").

Multi-threaded, multi-process Each test context runs in its own thread. Thethreads can be split over many processesdepending on the requirements of the test andthe capabilities of the load injection machine.

Distributed The Grinder makes it easy to coordinate andmonitor the activity of processes across a

The Grinder

Page 90Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 91: The Grinder

network of many load injection machines from acentral console.

Scalable The Grinder typically can support severalhundred HTTP test contexts per load injectionmachine. (The number varies depending on thetype of test client). More load injection machinescan be added to generate bigger loads.

5.2.5. Console

Graphical Interface 100% Java Swing user interface.

Process coordination Worker processes can be started, stopped andreset from one central console.

Process monitoring Dynamic display of current worker processesand threads.

Internationalised and Localised English, French, Spanish, and Germantranslations are supplied. Users can add theirown translations.

Script editing Central editing and management of test scripts.(Future)

5.2.6. Statistics, Reports, Charts

Test monitoring Pre-defined charts for response time, testthroughput. Display the number of invocations,test result (pass/fail), average, minimum andmaximum values for response time and tests persecond for each test.

Data collation Collates data from worker processes. Data canbe saved for import into a spreadsheet or otheranalysis tool.

Instrument anything The Grinder records statistics about the numberof times each test has been called and theresponse times achieved. Any part of the testscript can be marked as a test.

Statistics engine Scripts can declare their own statistics andreport against them. The values will appear inthe console and the data logs. Compositestatistics can be specified as expressionsinvolving other statistics.

5.2.7. Script

The Grinder

Page 91Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 92: The Grinder

Record real users Scripts can be created by recording actions of areal user using the TCP Proxy. The script canthen be customised by hand.

Powerful scripting in Python Simple to use but powerful, fully object-orientedscripting.

Multiple scenarios Arbitrary looping and branching allows thesimulation of multiple scenarios. Simplescenarios can be composed into more complexscenarios. For example, you might allocate 10%of test contexts to a login scenario, 70% tosearching, 10% to browsing, and 10% to buying;or you might have different workloads forspecific times of a day.

Access to any Java API Jython allows any Java-based API to be useddirectly from the test script.

Parameterization of input data Input data (e.g. URL parameters, form fields)can be dynamically generated. The source ofthe data can be anything including flat files,random generation, a database, or previouslycaptured output.

Content Verification Scripts have full access to test results. In thefuture, The Grinder will include support forenhanced parsing of common results such asHTML pages.

5.2.8. The Grinder Plug-ins

HTTP The Grinder has special support for HTTP thatautomatically handles cookie and connectionmanagement for test contexts.

Custom Users can write their own plug-ins to adocumented interface; although this is rarelynecessary due to the powerful scripting facilities.

5.2.9. HTTP Plug-in

HTTP 1.0, HTTP 1.1 Support for both HTTP 1.0 and HTTP 1.1 isprovided.

HTTPS The Grinder supports HTTP over SSL.

Cookies Full support for Cookies is provided.

Multi-part forms The Grinder supports multi-part forms.

The Grinder

Page 92Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 93: The Grinder

5.2.10. TCP Proxy

TCP proxy A TCP proxy utility is supplied that can be usedto intercept system interaction at the protocollevel. It is useful for recording scripts and as adebugging tool.

HTTP Proxy The TCP proxy can be configured as anHTTP/HTTPS proxy for easy integration withweb browsers.

SSL Support The TCP proxy can simulate SSL sessions.

Filter-based architecture The TCP proxy has a pluggable filterarchitecture. Users can write their own filters.

5.2.11. Documentation

User Guide http://grinder.sourceforge.net/g3/whats-new.html

FAQs http://grinder.sourceforge.net/faq.html

Tutorial http://grinder.sourceforge.net/g3/tutorial-perks.html

Script Gallery http://grinder.sourceforge.net/g3/script-gallery.html

Articles http://grinder.sourceforge.net/press.html

Commercial books Professional Java 2 Enterprise Edition with BEAWebLogic ServerJ2EE Performance Testing

5.2.12. Support

Mailing Lists [email protected]@[email protected]

5.3. Getting started

5.3.1. Getting started

Note:This section takes a top down approach to The Grinder. If you are happy figuring things out for yourself and want to getyour hands dirty, you might like to read How do I start The Grinder? and then jump to the Script Gallery.

5.3.1.1. The Grinder processes

The Grinder

Page 93Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 94: The Grinder

The Grinder is a framework for running test scripts across a number of machines. Theframework is comprised of three types of process (or program). Worker processes, agentprocesses, and the console. The responsibilities of each of the process types are:

• Worker processes• Interpret Jython test scripts and performs tests using a number of worker threads

• Agent processes• Manage worker processes

• The console• Coordinates the other processes• Collates and displays statistics

As The Grinder is written in Java, each of these processes is a Java Virtual Machine(JVM).

For heavy duty testing, you start an agent process on each of several client machines. Theworker processes they launch can be controlled and monitored using the console. There islittle reason to run more than one agent on a single machine, but if you can if you wish.

5.3.1.2. Tests and test scripts

A test is a unit of work against which statistics are recorded. Users specify which tests torun using a Jython test script. Tests are uniquely defined by a test number and also have adescription. If you wish your scripts can report many different actions (e.g. different webpage requests) against the same test, The Grinder will aggregate the results.

The script is executed many times in a typical testing scenario. Each worker process has anumber of worker threads, and each worker thread calls the script a number of times. Asingle execution of a test script is called a run.

5.3.1.3. Network communication

Each worker process sets up a network connection to the console to report statistics. Eachagent process sets up a connection to the console to receive commands, which it passes onto its worker processes. The console listens for both types of connection on a particularaddress and port. By default, the console listens on port 6372 on all local networkinterfaces of the machine running the console.

If an agent process fails to connect to the console, or the grinder.useConsoleproperty is false, the agent will continue independently without the console andautomatically will start its worker processes. The worker processes will run to completionand not report to the console. This can be useful when you want to quickly try out a testscript without bothering to start the console.

Note:

The Grinder

Page 94Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 95: The Grinder

To change the console addresses, set the grinder.consoleHost and grinder.consolePort properties in thegrinder.properties file before starting The Grinder agents. The values must match those specified in the consoleoptions dialog.

5.3.1.4. Output

Each worker process writes logging information to a file called out-host-n.log,where host is the machine host name and n is the worker process number. Errors arewritten to error-host-n.log. If no errors occur, an error file will not be created.

Data about individual test invocations is written into a file called data-host-n.logthat can be imported into a spreadsheet tool such as Microsoft ExcelTM for furtheranalysis. The data file is the only place where information about individual tests isrecorded; the console displays only aggregate information.

The final statistics summary (in the out-* files of each process) looks something likethis:

Final statistics for this process:

SuccessfulTests Errors Mean Test Test Time

Time (ms) StandardDeviation(ms)

Test 0 25 0 255.52 22.52Test 1 25 0 213.40 25.15Test 2 25 0 156.80 20.81"Image"Test 3 25 0 90.48 14.41Test 4 25 0 228.68 23.97"Login page"Test 5 25 0 86.12 12.53"Security check"Test 6 25 0 216.20 8.89Test 7 25 0 73.20 12.83Test 8 25 0 141.92 18.36Test 9 25 0 104.68 19.86"Logout page"

Totals 250 0 156.70 23.32

The console has a dynamic display of similar information collected from all the workerprocesses. Plug-ins and advanced test scripts can provide additional statistics; forexample, the HTTP plug-in adds a statistic for the content length of the response body.

Each test has one of two possible outcomes:

1. Success. The number of Successful Tests for that test is incremented The time taken toperform the test is added to the Total.

2. Error. The execution of a test raised an exception. The number of Errors for the test isincremented. The time taken is discarded.

The Grinder

Page 95Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 96: The Grinder

The Total, Mean, and Standard Deviation figures are calculated based only on successfultests.

5.3.1.5. How do I start The Grinder?

Its easy:

1. Create a grinder.properties file. This file specifies general controlinformation (how the worker processes should contact the console, how many workerprocesses to use, ..), as well as the name of the Jython script that will be used to runthe tests.

2. Set your CLASSPATH to include the grinder.jar file which can be found in thelib directory.

3. Start the console on one of the test machines:

java net.grinder.Console4. For each test machine, do steps 1. and 2. and start an agent process:

java net.grinder.Grinder

You can also specify an explicit properties file as the first argument. For example:

java net.grinder.Grinder myproperties

The console does not read the grinder.properties file. It has its own optionsdialog (choose the File/Options menu option) which you should use to set thecommunication addresses and ports to match those in the grinder.properties files.The console controls can be used to trigger The Grinder test scenario. Each agent processthen creates child worker processes to do the work.

As the worker processes execute, they dynamically inform the console of the tests in thetest script. If you start the console after the agent process, you should press the Resetprocesses button. This will cause the existing worker processes to exit and the agentprocess to start fresh worker processes which will update the console with the new testinformation.

Included below are some sample shell scripts, for both unix/linux and windows, forstarting grinder agents, its console, and the HTTPProxy.

Windows:

• setGrinderEnv.cmd:

set GRINDERPATH=(full path to grinder install directory)set GRINDERPROPERTIES=(full path togrinder.properties)\grinder.propertiesset CLASSPATH=%GRINDERPATH%\lib\grinder.jar;%CLASSPATH%set JAVA_HOME=(full path to java install directory)PATH=%JAVA_HOME%\bin;%PATH%

• startAgent.cmd:

The Grinder

Page 96Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 97: The Grinder

call (path to setGrinderEnv.cmd)\setGrinderEnv.cmdecho %CLASSPATH%java -cp %CLASSPATH% net.grinder.Grinder %GRINDERPROPERTIES%

• startConsole.cmd:

call (path to setGrinderEnv.cmd)\setGrinderEnv.cmdjava -cp %CLASSPATH% net.grinder.Console

• startProxy.cmd:

call (path to setGrinderEnv.cmd)\setGrinderEnv.cmdjava -cp %CLASSPATH% net.grinder.TCPProxy -console -http > grinder.py

Unix:

• setGrinderEnv.sh:

#!/usr/bin/kshGRINDERPATH=(full path to grinder install directory)GRINDERPROPERTIES=(full path togrinder.properties)/grinder.propertiesCLASSPATH=$GRINDERPATH/lib/grinder.jar:$CLASSPATHJAVA_HOME=(full path to java install directory)PATH=$JAVA_HOME/bin:$PATHexport CLASSPATH PATH GRINDERPROPERTIES

• startAgent.sh:

#!/usr/bin/ksh. (path to setGrinderEnv.sh)/setGrinderEnv.shjava -cp $CLASSPATH net.grinder.Grinder $GRINDERPROPERTIES

• startConsole.sh:

#!/usr/bin/ksh. (path to setGrinderEnv.sh)/setGrinderEnv.shjava -cp $CLASSPATH net.grinder.Console

• startProxy.sh:

#!/usr/bin/ksh. (path to setGrinderEnv.sh)/setGrinderEnv.shjava -cp $CLASSPATH net.grinder.TCPProxy -console -http > grinder.py

5.3.1.6. How do I create a script?

You can manully create scripts for use with the Grinder and there are a number ofexamples of how to do this in the Script Gallery. See the Scripts section for more detailson how to create scripts.

If you are creating a script for a website or web application, you can use the TCPProxy togenerate an HTTPPlugin script suitable for use with The Grinder.

5.3.1.7. How should I set up a project structure for The Grinder?

Well the short answer is however works best for you. Many people will already know

The Grinder

Page 97Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 98: The Grinder

how they want to set up their directory structure and will have no issue implementing TheGrinder as one of their many tools. For those looking for a little guidance it is worthasking yourself questions like:

• How many projects will I be working on?• Will I need to revisit projects from time to time?• Do I need repeatability?• Is this a shared implementation?• ...etc.

Below is given an example of a directory structure for setting up The Grinder.

.`- Grinder

||-- bin| |-- setGrinderEnv.sh/cmd| |-- startAgent.sh/cmd| |-- startConsole.sh/cmd| `-- startProxy.sh/cmd||-- engine| |-- grinder-3.0-beta25| |-- grinder-3.0-beta26| `-- ...||-- etc| |-- grinder.properties| `-- ...||-- jvm| |-- jdk1.3| |-- jdk1.4.02| `-- ...||-- lib| |-- jython2.1| |-- jdom-1.0| |-- xerces_2_6_0| |-- xerces-2_6_2| |-- oracle| `-- ...||-- logs| `-- ...|`-- projects

|-- website_project| |-- httpscript.py| |-- httpscript_tests.py| `-- ...||-- db_project| |-- jdbc.py| `-- ...|

The Grinder

Page 98Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 99: The Grinder

`-- ...

First off the bin directory has been created for storing executable files for theimplementation. The sample start scripts from "How do I start The Grinder?" have beenincluded in this directory. The engine directory has been created for storing the versionsof The Grinder that may be used. Strictly speaking the versions of The Grinder could bestored under the lib directory but for this example The Grinder has been given its owndirectory. The etc directory has been created to store the configuration files for theimplementation such as the grinder.properties file. The jvm directory has been created tostore the various jdks and their versions that could be used in testing. The lib directoryhas been created to store the various third party libraries and their respective versions thatprojects may require. For example if you wanted to use the full set of libraries whichcome with jython then this is the directory into which you would install. Remember toupdate your CLASSPATH with the libraries you require. The logs has been created tostore the various logs that the grinder generates during its runs.The projects directory hasbeen created to store the scripts to be run by The Grinder and organise them byproject/body of work.

The above example would be useful as a simple implementation for one person whoworks on one project at a time. As the number of projects grows, more people share theimplementation, or projects need to be revisited with repeatability ensured, then it makessense, in this example, to modularize the implementation around the projects. To do thissimply create the bin, etc and logs directories under the respective projects like so:

|`-- projects

|-- website_project| |-- bin| | |-- setGrinderEnv.sh/cmd| | |-- startAgent.sh/cmd| | |-- startConsole.sh/cmd| | `-- startProxy.sh/cmd| |-- etc| | |-- grinder.properties| | `-- ...| |-- httpscript.py| |-- httpscript_tests.py| |-- logs| | `-- ...| `-- ...||-- db_project

Once this has been done the environment can be set to use the engine, jvm and librariesrequired by a particular project, rather than setting the environment for all the projects (aswould happen in the simple implementation). This allows you, for example, to retainprojects which were run using legacy versions of libraries and/or engine and re-run themat a later date with the same setup. Also different projects may require different versionsof the same library which would have caused issues when using an implementation-wideCLASSPATH. The grinder.properties file can also be customised on a per project basis.

The Grinder

Page 99Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 100: The Grinder

Modularizing the implementation like this gives greater flexibility and repeatability andopens up the prospect of multiple people using the implementation concurrently.

5.3.2. The Grinder 3 Properties File

The Grinder worker and agent processes are controlled by setting properties in thegrinder.properties file.

All properties have default values. If you start The Grinder agent process without agrinder.properties file it will communicate with the console using defaultaddresses, use one worker process, one thread, and make one run through the test scriptfound in the file grinder.py. This is not much use, so read on...

5.3.2.1. Table of properties

This table lists the properties understood by The Grinder engine.

Property Description Default

grinder.processes The number of workerprocesses the agent shouldstart.

1

grinder.threads The number of worker threadsthat each worker processspawns.

1

grinder.runs The number of runs of the testscript each thread performs. 0means "run forever", andshould be used when you areusing the console to controlyour test runs.

1

grinder.processIncrement If set, the agent will ramp upthe number of workerprocesses, starting the numberspecified everygrinder.processesIncrementIntervalmilliseconds.

Start all worker processestogether.

grinder.processIncrementIntervalUsed in conjunction withgrinder.processIncrement,this property sets the interval inmilliseconds at which the agentstarts new worker processes.

60000 ms

grinder.initialProcesses Used in conjunction withgrinder.processIncrement,this property sets the initialnumber of worker processes to

The value ofgrinder.processIncrement.

The Grinder

Page 100Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 101: The Grinder

start.

grinder.duration The maximum length of time inmilliseconds that each workerprocess should run for.grinder.duration can bespecified in conjunction withgrinder.runs, in which casethe worker processes willterminate if either the durationtime or the number of runs isexceeded.

Run forever.

grinder.script The file name of the Jythonscript to run.

grinder.py

grinder.jvm Use an alternate JVM forworker processes. Defaults tojava so you do not need tospecify this if your PATH issensible.

java

grinder.jvm.classpath Use to adjust the classpathused for the worker processJVMs. Anything specified herewill be prepended to theclasspath used to start theGrinder processes.

grinder.jvm.arguments Additional arguments to workerprocess JVMs.

grinder.logDirectory Directory to write log files to.Created if it doesn't alreadyexist.

The local directory.

grinder.numberOfOldLogs The number of archived logsfrom previous runs that shouldbe kept.

1

grinder.hostID Override the "host" string usedin log filenames and logs.

The host name.

grinder.consoleHost The IP address or hostname touse for communicationbetween the agent and workerprocesses and the console.

All the network interfaces of thelocal machine.

grinder.consolePort The IP port to use forcommunication that the agentand worker processes use tocontact to the console.

>6372

grinder.useConsole Set to false to set the agent true

The Grinder

Page 101Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 102: The Grinder

and worker processes not touse the console.

grinder.reportToConsole.intervalThe period at which eachprocess sends updates to theconsole. This also controls thefrequency at which the datafiles are flushed.

500 ms

grinder.initialSleepTime The maximum time inmilliseconds that each threadwaits before starting. Unlike thesleep times specified in scripts,this is varied according to a flatrandom distribution. The actualsleep time will be a randomvalue between 0 and thespecified value. Affected bygrinder.sleepTimeFactor,but notgrinder.sleepTimeVariation.

0 ms

grinder.sleepTimeFactor Apply a factor to all the sleeptimes you've specified, eitherthrough a property of in ascript. Setting this to 0.1 wouldrun the script ten times as fast.

1

grinder.sleepTimeVariationThe Grinder varies the sleeptimes specified in scriptsaccording to a Normaldistribution. This propertyspecifies a fractional rangewithin which nearly all (99.75%)of the times will lie. E.g., if thesleep time is specified as 1000and the sleepTimeVariation isset to 0.1, then 99.75% of theactual sleep times will bebetween 900 and 1100milliseconds.

0.2

grinder.logProcessStreamsSet to false to disable thelogging of output and errorsteams for worker processes.You might want to use this toreduce the overhead of runninga client thread.

true

grinder.reportTimesToConsoleSet to false to disablereporting of timing informationto the console; other statisticsare still reported.

true

The Grinder

Page 102Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 103: The Grinder

grinder.debug.singleprocessIf set to true, the agentprocess spawns engines inthreads rather than processes,using special classloaders toisolate the engines. This allowsthe engine to be easily run in adebugger. This is primarily atool for debugging The Grinderengine, but it might also beuseful to advanced users.

false

grinder.useNanoTime If set to true,System.nanoTime() is usedfor measuring time instead ofSystem.currentTimeMills().The Grinder will still reporttimes in milliseconds. Theprecision of these methodsdepends on the JVMimplementation and theoperating system. Settting totrue requires J2SE 5 or later.

false

5.3.2.2. Specifying properties on the command line

You can also specify these properties as Java system properties in the agent commandline. For example, on UNIX systems the following command line can be used to generatelog directories based on the current date.

java -Dgrinder.logDirectory="log/$(date +%y%m%d)" net.grinder.Grinder

5.3.3. The Console

5.3.3.1. Process controls

Start processes, Reset processes and Stop processes send signals to Grinder processes thatare listening. (See the properties grinder.useConsole, grinder.consoleHostand consolePort.)

Worker processes that are configured to receive console signals go through three states:

1. Initiated (waiting for a console signal)2. Running (performing tests, reporting to console)3. Finished (waiting for a console signal)

The Start processes control signals to worker processes that they should move into therunning state. Processes that are already running ignore this signal. Processes that are inthe finished state exit; the agent process will then reread the properties file and launchnew worker processes in the running state.

The Grinder

Page 103Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 104: The Grinder

The Reset processes control signals all the worker processes to exit. The agent processwill then reread the properties file and launch new worker processes.

The Stop processes control signals all processes, including the agent processes, to exit.You usually want to use Reset processes instead.

Warning:Each time the worker processes run, they generate a new set of logs. Logs from previous runs are "archived" by renamingthem. The number of logs that are kept from previous runs can be controlled with grinder.numberOfOldLogs.

5.3.3.2. Sample controls

The sample controls determine how the console captures reports from the workerprocesses. It is important to understand that these only control the console behaviour. Forexample, they do not adjust the frequency at which the worker processes send reports (seegrinder.reportToConsole.interval for that). Additionally, the samplecontrols do not interact in any way with the process controls.

The slider controls the period at which the console will take a sample. This involvesadding up all the reports received over that sample interval and calculating the TPS as(number of tests that occurred)/(interval length). It is also the period at which the consolegraphs and statistics are updated.

By default, the console discards the first non-zero sample period, and starts updating thedisplay and calculating totals from the second sample. A non-zero sample period is one inwhich an update from a worker process was received. You can adjust how many non-zerosample periods the console ignores before starting capture with the ignore samples textfield.

The third control allows you to adjust how many samples the console will collect beforestopping capture.

You can also manually start and stop the sampling with the Capture statistics/Stopcapture control. Use the Save statistics control to save the current set of statistics to a file.

5.3.3.3. Display

On the console there are a number of tabs which display information about The Grinderand its tests. These are detailed below:

Graphs

Each graph displays the 25 most recent Tests Per Second (TPS) values for a particulartest. A new value is added every console sample period. The y-axis is scaled so that thefull height represents the peak TPS value received for the test since the display was lastreset.

The Grinder

Page 104Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 105: The Grinder

The colours are based on the relative response time. Long response times are more red,short response times are more yellow. This acts as an eye-catcher, allowing expensivetests to be easily spotted.

Results

The Results tab shows the results from The Grinder instrumentation.

Instrument Description

Test The test number as specified in the test script,eg. tests[14000] will display as Test 14000.

Description The test description as specified in the testscript. If the HTTPProxy has been used togenerate the scripts the description field can befound in the httpscript_tests.py file, eg.tests[14000] = Test(14000, 'GETindex.jsp').wrap(request14000) will display as'Get index.jsp'.

Successful Tests The total number of iterations of the test thatwere successfully executed by The Grinderduring the test run.

Errors The total number of iterations of the test thatfailed to be fully executed by The Grinder duringthe test run.

Mean Time The mean time taken to execute the test andreceive the full response from the targetserver/application, in milliseconds.

Mean Time Standard Deviation The mean standard deviation of the time takento execute the test and receive the full responsefrom the target server/application, inmilliseconds.

TPS Transactions per second. The average numberof iterations of the test that successfully ran in aone second interval.

Peak TPS Peak Transactions per second. The maximumnumber of iterations of the test that successfullyran in a one second interval.

There is additional instrumentation for the HTTPPlugin.

Instrument Description

Mean Response Length The mean size of HTTP response from thetarget server/application in response to theexecuted test, in bytes.

The Grinder

Page 105Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 106: The Grinder

Response Bytes per Second The mean number of bytes per second receivedfrom the target server/application, in bytes persecond. This gives an indication of the amountof bandwidth being consumed by the test. Thisdoes not take into account the amount of trafficbeing sent to the target server/application.

Response Errors The total number of HTTP Response ErrorCodes (eg, 404, 500 etc) received during thetest run.

Mean Time to Resolve Host The mean time taken to resolve the ip addressof the target server from the Fully QualifiedDomain Name, via hosts file or DNS, inmilliseconds. This is the time relative to the startof the test iteration.

Mean Time to Establish Connection The mean time taken to establish a tcpconnection to the target server/application, inmilliseconds. This is the time relative to the startof the test iteration.

Mean Time to First Byte The mean time taken to receive the first byte ofresponse from the target server/application, inmilliseconds. This is the time relative to the startof the test iteration.

Processes

This tab displays information about the Agents, their worker processes and associatedthreads.

Header Description

Process The name of the process. A parent process willtake the hostname of the box on which it isrunning Its child processes take the name of theparent process and add a suffix of "-x" where xis an integer, eg. myserver-0.

Type The type of process, eg. Agent or Worker.

State Information about the state of the process, eg."Connected" for an agent process and "Running"and "Finished" for a Worker process.

Script

This tab contains the beginnings of console support for script editing. It is underconstruction and is not currently functional.

Also on this tab are controls for the script distribution system (Also accessible through

The Grinder

Page 106Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 107: The Grinder

the Distribute menu)

This gives you the ability to:

1. Set the root directory for script distributionThe directory on the console host that contains the scripts for distribution

2. Set the script to runThis selects the script from those in the distributed list that is to be run

3. Send changed files to worker processesThis pushes out the contents of the root directory to all connected worker processes

5.3.3.4. Internationalisation help wanted

If you are bilingual you might fancy translating the console into a language of yourchoice.

5.3.4. The TCPProxy

The TCPProxy is a proxy process that you can place in a TCP stream, such as the HTTPconnection between your browser and a server. It filters the request and response streams,sending the results to the terminal window (stdout). You can control its behaviour byspecifying different filters.

The TCPProxy's main purpose is to automatically generate HTTP test scripts that can bereplayed with The Grinder's HTTP plugin. Because the TCPProxy lets you see what'sgoing on at a network level it is also very useful as a debugging tool in its own right.

5.3.4.1. Starting the TCPProxy

The Grinder

Page 107Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 108: The Grinder

You start the TCPProxy with something like:

CLASSPATH=/opt/grinder/lib/grinder.jarexport CLASSPATH

java net.grinder.TCPProxy

Say java net.grinder.TCPProxy -? to get a full list of the command lineoptions.

With no additional options, the TCPProxy will start and display the followinginformation:

Initialising as an HTTP/HTTPS proxy with the parameters:Request filters: EchoFilterResponse filters: EchoFilterLocal address: localhost:8001

Engine initialised, listening on port 8001

This indicates that the TCPProxy is listening as an HTTP proxy on port 8001 (thedefault, but you can change it with -localPort).

The TCPProxy appears to your browser just like any other HTTP proxy server, and youcan use your browser as normal. If you type http://grinder.sourceforge.netinto your browser it will display The Grinder home page and the TCPProxy will outputall of the HTTP interactions between the browser and the SourceForge site.

The TCPProxy will proxy both HTTP and HTTPS. See below for details on customisingthe SSL configuration.

5.3.4.2. Preparing the Browser

You should now set your browser connection settings to specify the TCPProxy as theHTTP proxy. In the browser options dialog, set the proxy host to be the host on which theTCPProxy is running and proxy port to be 8001).

The relevant options dialog can be accessed by the following steps:

MSIE: Tools -> Internet Options -> Connections -> Local Area Network Settings.Mozilla/Netscape: Edit -> Preferences -> Advanced - Proxies.Mozilla/Firefox: Tools -> Options -> General -> Connection Settings.Opera: Tools -> Preferences -> Advanced -> Network -> Proxy Servers.

It is important to remember to remove any "bypass proxy server" or "No Proxy for"settings that you might have so that all the traffic flows through the TCPProxy and can becaptured.

Note:Firefox users will find Jeremy Gillick's SwitchProxy(https://addons.mozilla.org/quicksearch.php?q=switchproxy&section=A&application=firefox) extension useful forswitching to and from the TCPProxy.

The Grinder

Page 108Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 109: The Grinder

It might also be a good idea to clear out any cache/temporary Internet files that might beon your workstation. On the other hand, you might decide not to do this if you want torecord a script representing a frequent user to your site who has images are resources intheir browser cache. Also for IE users, changing the temporary Internet files settings tocheck for a newer version on every visit to a page can be useful.

5.3.4.3. Using the EchoFilter

The EchoFilter is the default filter used by the TCPProxy if no options are specified in thestartup command. The EchoFilter outputs the stream activity to the terminal. It can bevery useful for debugging as described in this FAQ.

Bytes that do not have a printable ASCII representation are displayed in hexadecimalbetween square brackets. Here's some example output:

------ 127.0.0.1:2263->ads.osdn.com:80 ------GET/?ad_id=5839&alloc_id=12703&site_id=2&request_id=8320720&1102173982760HTTP/1.1Host: ads.osdn.comUser-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.7.5)Gecko/20041107 Firefox/1.0Accept: image/png,*/*;q=0.5Accept-Language: en-gb,en-us;q=0.7,en;q=0.3Accept-Encoding: gzip,deflateAccept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7Keep-Alive: 300Proxy-Connection: keep-aliveReferer: http://sourceforge.net/projects/grinder

--- ads.osdn.com:80->127.0.0.1:2263 opened -------- ads.osdn.com:80->127.0.0.1:2273 ------HTTP/1.1 200 OKDate: Sat, 04 Dec 2004 15:26:27 GMTServer: Apache/1.3.29 (Unix) mod_gzip/1.3.26.1a mod_perl/1.29Pragma: no-cacheCache-control: privateConnection: closeTransfer-Encoding: chunkedContent-Type: image/gif

------ ads.osdn.com:80->127.0.0.1:2273 ------80BGIF89ae[00])[00D50000C3C3C3FEFDFD]hhhVVVyyy[F5CCD2D4D4D4CBCBCBD7]'F

Information lines are displayed to indicate the end point addresses and direction of theinformation flow and also whether a connection has just been opened or closed.

5.3.4.4. Using the HTTPPluginTCPProxyFilters

The Grinder

Page 109Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 110: The Grinder

You can use the TCPProxy to generate an HTTP script suitable for use with The Grinder.The Grinder provides a number of HTTP filters for this purpose. These filters arecontrolled by command line options:

-http (recommended)-httpplugin (deprecated)-newhttpplugin (deprecated)

What follows is a HOWTO detailing the use of the recommended HTTP filter.

The first step is to start the TCPProxy with an HTTP filter. A command like the followingshould be used to start the TCPProxy with the recommended HTTP filter:

java net.grinder.TCPProxy -console -http > grinder.py

The > grinder.py part of the line sends the script to a file called grinder.py.

The terminal output of the HTTPPluginTCPProxyFilter looks like:

14/03/06 17:04:25 (tcpproxy): Initialising as an HTTP/HTTPS proxy withtheparameters:

Request filters: HTTPRequestFilterResponse filters: HTTPResponseFilterLocal address: localhost:8001

14/03/06 17:04:27 (tcpproxy): Engine initialised, listening on port 8001

The console (initiated by -console) displays a simple control window that allows theTCPProxy to be shut down cleanly. This is needed because some terminal shells, e.g.Cygwin bash, do not allow Java processes to be interrupted cleanly, so filters cannot relyon standard shutdown hooks. The console looks like this:

The TCPProxy console will be incorporated into the main console in a future release.

Set your browser to use the TCPProxy as the HTTP proxy as described earlier), and runthrough your test scenario on your website.

Having finished your run through, press "Stop" on the TCPProxy console and thegenerated script will be written to grinder.py.

The grinder.py file contains headers, requests and a logical grouping of requests intopages, of the recorded tests.

For example, the headers section:

# The Grinder 3.0-beta29# HTTP script recorded by TCPProxy at 14-Feb-2006 16:25:38

from net.grinder.script import Testfrom net.grinder.script.Grinder import grinderfrom net.grinder.plugin.http import HTTPPluginControl, HTTPRequestfrom HTTPClient import NVPairconnectionDefaults = HTTPPluginControl.getConnectionDefaults()

The Grinder

Page 110Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 111: The Grinder

httpUtilities = HTTPPluginControl.getHTTPUtilities()

# These definitions at the top level of the file are evaluated once,# when the worker process is started.

connectionDefaults.defaultHeaders = \( NVPair('User-Agent', 'Mozilla/5.0 (Windows; U; Windows NT 5.0;

en-US; rv:1.8.0.1) Gecko/20060111 Firefox/1.5.0.1'),NVPair('Accept-Encoding', 'gzip,deflate'),NVPair('Accept-Language', 'en-us,en;q=0.5'),NVPair('Accept-Charset', 'ISO-8859-1,utf-8;q=0.7,*;q=0.7'), )

headers0= \( NVPair('Accept',

'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5'),)

headers1= \( NVPair('Accept', 'text/css,*/*;q=0.1'),NVPair('Referer', 'http://localhost/'), )

headers2= \( NVPair('Accept', 'text/css,*/*;q=0.1'),NVPair('Referer', 'http://localhost/css/product_styles.css'), )

headers3= \( NVPair('Accept', 'image/png,*/*;q=0.5'),NVPair('Referer', 'http://localhost/css/colours.css'), )

headers4= \( NVPair('Accept', 'image/png,*/*;q=0.5'),NVPair('Referer', 'http://localhost/'), )

#....

In the requests section each unique request is captured:

url0 = 'http://localhost:80'

# Create an HTTPRequest for each request, then replace the# reference to the HTTPRequest with an instrumented version.# You can access the unadorned instance using request101.__target__.request101 = HTTPRequest(url=url0, headers=headers0)request101 = Test(101, 'GET /').wrap(request101)

request102 = HTTPRequest(url=url0, headers=headers1)request102 = Test(102, 'GET product_styles.css').wrap(request102)

request103 = HTTPRequest(url=url0, headers=headers2)request103 = Test(103, 'GET tools.css').wrap(request103)

request104 = HTTPRequest(url=url0, headers=headers2)request104 = Test(104, 'GET colours.css').wrap(request104)

request105 = HTTPRequest(url=url0, headers=headers2)request105 = Test(105, 'GET forms.css').wrap(request105)

# ...

The Grinder

Page 111Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 112: The Grinder

Finally the TestRunner class. This section groups the requests into pages and defines eachpage as a method, sets sleep interval between requests and provides an instrumentedmethod for the return of data from the tests:

class TestRunner:"""A TestRunner instance is created for each worker thread."""

# A method for each recorded page.def page1(self):"""GET / (requests 101-121)."""result = request101.GET('/', (),( httpUtilities.basicAuthorizationHeader('user', 'pwd'), ))

self.token_database = \httpUtilities.valueFromBodyURI('database') # 'B'

grinder.sleep(171)request102.GET('/css/product_styles.css', (),( httpUtilities.basicAuthorizationHeader('user', 'pwd'), ))

grinder.sleep(50)request103.GET('/css/tools.css', (),( httpUtilities.basicAuthorizationHeader('user', 'pwd'), ))

request104.GET('/css/colours.css', (),( httpUtilities.basicAuthorizationHeader('user', 'pwd'), ))

request105.GET('/css/forms.css', (),( httpUtilities.basicAuthorizationHeader('user', 'pwd'), ))

#.....

return result

def page2(self):

#.....

def __call__(self):"""This method is called for every run performed by the worker

thread."""self.page1() # GET / (requests 101-121)

grinder.sleep(100)self.page2() # GET logging.html (requests 201-217)

#.....

def instrumentMethod(test, method_name, c=TestRunner):"""Instrument a method with the given Test."""unadorned = getattr(c, method_name)import newmethod = new.instancemethod(test.wrap(unadorned), None, c)setattr(c, method_name, method)

# Replace each method with an instrumented version.# You can call the unadorned method using self.page1.__target__().instrumentMethod(Test(100, 'Page 1'), 'page1')

The Grinder

Page 112Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 113: The Grinder

instrumentMethod(Test(200, 'Page 2'), 'page2')

#.....

Once you've recorded your script you have two methods that you can use to replay yourscript:

1. You can create a simple grinder.properties file and you can replay therecorded scenario with The Grinder. Your properties file should at least setgrinder.script to grinder.py.

2. Alternately you can use the Grinder console to distribute your script to an agent andset it as the script to run. Each agent will still need a simplegrinder.properties file , though you will not need to set thegrinder.script property.

The recorded script grinder.py can be edited by hand to suit your needs.

Altering the output with custom stylesheet

The recommended TCPProxy HTTP filter, -http, modifies the output of its request andresponse filters by way of an XLST stylesheet when producing the recorded script.

The standard/default stylesheet can be found in etc/httpToJythonScript.xsl.Replacing the standard stylesheet with one of your own making allows you to customisethe output of the filter. You shuould pass the file name of your custom stylesheet as acommand line argument directly after -http.

If you want to see the intermediate XML model you can use:java net.grinder.TCPProxy -http etc/httpToXML.xsl -console

The model confirms to the XML schema etc/tcpproxy-http.xsd.

5.3.4.5. SSL and HTTPS support

The TCPProxy has SSL support based on the JSSE. The JSSE is required by The Grinder,and is now part of Java 2 Standard Edition 1.4.1, see the Download page for furtherdetails.

SSL relationships are necessarily point to point. When you interpose the TCPProxy inSSL communications between a browser and a server you end up with two SSLconnections. Each SSL connection has its own set of client and server certificates (both ofwhich are optional).

The Grinder

Page 113Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 114: The Grinder

The TCPProxy will negotiate appropriate certificates for both connections using built-incertificates or in a user-specified Java key store. In particular, the TCPProxy needs aself-signed server certificate for the connection from the browser. By default, theTCPProxy will use a built-in certificate.

When first establishing a connection, your browser will present a warning andconfirmation dialog. This is because the built-in certificate isn't authorised by any of thecertificate authorities that the browser trusts. Additionally, the built-in certificateauthorises localhost so if your server doesn't listen at this address the browser willcomplain. Choose the "accept this certificate for this session" option.

Warning:The Grinder deliberately accelerates SSL initialisation by using a random number generator that is seeded with a fixednumber. This does not hinder SSL communication, but theoretically makes it less secure. No guarantee is made as to thecryptographic strength of any SSL communication using The Grinder.

Advanced users can specify their own server certificate for the connection from thebrowser, or add client certificates for the connection to the server, using the-keystore, -keystorepassword, and -keystoretype options. See theJ2SE/JSSE documentation for how to set up a key store.

If you fail to provide a key store with a valid server certificate (hard to do now TheGrinder has a built in certificate), you may get a No available certificate corresponds tothe SSL cipher suites which are enabled exception, and your browser may report that itcannot communicate as it has no common encryption algorithms. Internet Explorer likesto be different. If start the TCPProxy without a valid server certificate and then connectthrough it using Internet Explorer, the TCPProxy will report "SSL peer shut downincorrectly. The browser will just spin away until it times out. The easiest way to providea server certificate is to copy the testkeys file from the JSSE samples distribution and startthe proxy using:

java net.grinder.TCPProxy -keyStore testkeys -keyStorePasswordpassphrase

5.3.4.6. Using the TCPProxy with other proxies

The TCPProxy can be used with other HTTP/HTTPS proxies.

The Grinder

Page 114Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 115: The Grinder

Use the -httpproxy option to specify the host name and port of the proxy. Use the-httpsproxy option if your HTTPS proxy requires separate settings.

Note:The Grinder does not work with NTLM proxies

5.3.4.7. Using the TCPProxy as a port forwarder

It is normally most useful to use the TCPProxy in its HTTP Proxy mode as describedabove.

When using the TCPProxy as a debugging tool it occasionally is useful to use it in portforwarding mode. This mode is enabled when one or more of -remotehost and-remoteport are specified. In port forwarding mode, the TCPProxy simply listens onlocalhost:localport and forwards to remotehost:remoteport.

To understand why HTTP Proxy mode is usually better than port forwarding mode whenusing a browser, consider what happens if the remote server returns a page with anabsolute URL link back to itself. If you click on the link, the browser will contact theserver directly, bypassing the TCPProxy. Another disadvantage is that you can't use theTCPProxy with more than one remote sever.

5.3.4.8. Summary of TCPProxy options

Option Description

Common

-console Display a simple console that has a controlbutton that allows The TCPProxy to beshutdown cleanly. This can help in certainsituations where a hard kill of the TCPProxyprocess would loose output that is still bufferedin memory .

-http [stylesheet] Adds the recommended request filter and aresponse filter to produce a script for TheGrinder suitable for use with the HTTP plugin.The output can be customised by specifying thefile name of an alternative XSLT style sheet.

The Grinder

Page 115Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 116: The Grinder

-requestfilter filter Add a request filter. filter can be the name ofa class that implementsnet.grinder.tools.tcpproxy.TCPProxyFilteror one of NONE, ECHO. The option can bespecified multiple times, in which case the filtersare invoked one after another. If the notspecified, the default ECHO filter is used.

-responsefilter filter Add a response filter. filter can be the nameof a class that implementsnet.grinder.tools.tcpproxy.TCPProxyFilteror one of NONE, ECHO. The option can bespecified multiple times, in which case the filtersare invoked one after another. If the notspecified, the default ECHO filter is used.

-localhost host Set the host name or IP address to listen on.This must correspond to an interface of themachine the TCPProxy is started on. The defaultis localhost.

-localport port Set the port to listen on. The default is 8001.

-keystore file Specify a custom key store. Usually the built-inkeystore is good enough so -keystore doesnot need to be specified.

-keystorepassword password Set the key store password. Only used if-keystore is set. Optional for some key storetypes.

-keystoretype type Set the key store type. Only used if -keystoreis set. If not specified, the default value dependson JSSE configuration but is usually jks.

Additional

-properties file Specify a file containing properties that arepassed on to the filters.

-remotehost host Set the host name or port the TCPProxy shouldconnect to in port forwarding mode. TheTCPProxy starts in port forwarding mode ifeither -remotehost or -remoteport is set.The default is localhost.

-remoteport port Set the port the TCPProxy should connect to inport forwarding mode. The TCPProxy starts inport forwarding mode if either -remotehost or-remoteport is set. The default is 7001.

-timeout seconds Set an idle timeout. This is how long theTCPProxy will wait for a request before timingout and freeing the local port. The TCPProxy will

The Grinder

Page 116Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 117: The Grinder

not time out if there are active connections.

-httpproxy host port Specify that output should be directed throughanother HTTP/HTTPS proxy. This may help youreach the Internet. This option is not supportedin port forwarding mode.

-httpsproxy host port Specify that output should be directed through aHTTPS proxy. Overrides any -httpproxysetting. This option is not supported in portforwarding mode.

-ssl Use SSL in port forwarding mode. This will makeboth the TCPProxy's local socket and theconnections to the target server use SSL. Thedefault HTTP Proxy mode ignores this optionand always listens as an HTTP proxy and anHTTPS proxy.

-colour Specify that a simple colour scheme should beused to distinguish request streams fromresponse schemes. This uses terminal controlcodes that only work on ANSI compliantterminals.

-component class Register a component class with the filterPicoContainer.

-debug Make filter PicoContainer chatty.

Deprecated

-httpplugin Adds a deprecated request filter and a responsefilter to produce a script for The Grinder suitablefor use with the HTTP plugin. Use -httpinstead.

-newhttpplugin Adds a deprecated request filter and a responsefilter to produce a script for The Grinder suitablefor use with the HTTP plugin. Use -httpinstead.

5.4. Plug-ins

5.4.1. The HTTP Plug-in

FIXME (PA):Material to come! For now see the script API, the script gallery and Richard Perks' tutorial.

Warning:

The Grinder

Page 117Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 118: The Grinder

Draft material follows.

5.4.1.1. What's it for?

The HTTPPlugin is a mature plug-in for testing HTTP services. It has a number of utitliesuseful for HTTP scripts as well as a tool, TCPProxy, which allows HTTP scripts to beautomatically recorded.

5.4.1.2. Controlling the HTTPPlugin

The HTTPPlugin behaviour can be controlled from within scripts run by The Grinderthrough the use of the HTTPPluginControl facade.

Levels of Control

There are three levels of control of the behaviour of the HTTPPlugin that theHTTPPluginControl facade gives you access to:

1. Default Connection Behaviour• Method: getConnectionDefaults

• Returns a HTTPPluginConnection that can be used to set the defaultbehaviour of new connections.

2. Thread Connection Behaviour• Method: getThreadConnection

• Returns a HTTPPluginConnection for a particular URL.

• The resulting HTTPPluginConnection is valid for the current thread and thecurrent run. It can be used to set specific authentication details, default headers,cookies, proxy servers, and so on for the current thread/run on a per-URL basis.

• This method will throw a GrinderException if not called from a workerthread.

3. Thread HTTPClient Context Object Behaviour• Method: getThreadHTTPClientContext

• Returns the HTTPClient context object for the calling worker thread. This isuseful when calling HTTPClient methods directly, e.g.CookieModule.listAllCookies(Object).

• This method will throw a GrinderException if not called from a workerthread.

Importing the HTTPPluginControl

Place the following line at the top of your grinder script along with your other import

The Grinder

Page 118Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 119: The Grinder

statements

from net.grinder.plugin.http import HTTPPluginControl

5.4.1.3. Using the HTTPPlugin

Setting an HTTP proxy

Should you need to specify an HTTP proxy to route requests through the following codecan be used to specify the default proxy used.

control = HTTPPluginControl.getConnectionDefaults()control.setProxyServer("localhost", 8001)

HTTP proxies can also be specified at the thread connection level. This is useful tospecify proxies on a per URL basis.

control = HTTPPluginControl.getThreadConnection()proxyURL1 = control("http://(url1)")proxyURL2 = control("http://(url2)")proxyURL1.setProxyServer("localhost", 8001)proxyURL2.setProxyServer("localhost", 8002)

Setting HTTPClient Authorization module:

The HTTPClient Authorization module is no longer enabled by default because itprevents raw Authorization headers being sent through. The module also slows thingsdown as HTTPClient must parse responses for challenges.

Advanced users who still wish to use the HTTPClient Authorization module can enable itusing:

control = HTTPPluginControl.getConnectionDefaults()control.setUseAuthorizationModule(1)

Setting HTTP Headers

The HTTPlugin allows you to set the HTTP Headers sent with requests. The methodtakes the settings as header-name/value pairs

control = HTTPPluginControl.getConnectionDefaults()control.setDefaultHeaders(NVPair("(header-name)", "(value)"),))

Typical headers you might want to set here are Accept and its Accept-* relatives,Connection, From, User-Agent, etc.

For example to disable persistent connections:

control = HTTPPluginControl.getConnectionDefaults()control.setDefaultHeaders(NVPair("Connection", "close"),))

The Grinder

Page 119Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 120: The Grinder

Setting Encoding

Encoding for Content or for Transfer can be switched on and off using boolean flags

control = HTTPPluginControl.getConnectionDefaults()control.setUseContentEncoding(0)control.setUseTransferEncoding(1)

Setting Redirect Behaviour

Setting the HTTPPlugin behaviour with regards to following redirects can be switched onand off using boolean flags

control = HTTPPluginControl.getConnectionDefaults()control.setFollowRedirects(0)

Setting Local Address

Should you be conducting your tests on a server with multiple network interfaces you canset the local IP address used by the HTTPPlugin for outbound connections.

control = HTTPPluginControl.getConnectionDefaults()control.setLocalAddress("(local IP Address)")

Setting Timeout Value

The timeout value for used for creating connections and reading responses can becontrolled via the HTTPPlugin.

The following example sets a default timeout value of 30 seconds for all connections.

control = HTTPPluginControl.getConnectionDefaults()control.setTimeout(30)

Setting Cookie Behaviour

Setting the HTTPPlugin behaviour with regards to whether cookies are used or not can beswitched on and off using boolean flags

control = HTTPPluginControl.getConnectionDefaults()control.setUseCookies(0)

Automatic decompression of gzipped responses

For load testing, its often not practical to decompress the response. It's simply tooexpensive in CPU terms to do all that decompression in the client worker process. Thisdoesn't mean you can't test a server that compresses its responses, just that you can't parsethe responses in the script.

The Grinder

Page 120Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 121: The Grinder

On the other hand, there are times you may want to do this. The Grinder supportsdecompression which it inherits from the HTTPClient library, you just need to enableit. If your server encrypts the content and sets a Content-Encoding header that startswith one of { gzip, deflate, compress, identity }, you can automaticallydecrypt the responses by adding the following lines to the beginning of your script:

from net.grinder.plugin.http import HTTPPluginControlconnectionDefaults = HTTPPluginControl.getConnectionDefaults()connectionDefaults.useContentEncoding = 1

Similarly, if your server sets a Transfer-Encoding header that starts with one of {gzip, deflate, compress, chunked, identity }, you can enable theHTTPClient Transfer Encoding Module withconnectionDefaults.useTransferEncoding = 1.

There is no support for automatically decrypting things based on their Content-Type(as opposed to Content-Encoding, Transfer-Encoding). Your browser doesn'tdo this, so neither should The Grinder. If you really want to do this, you can use Java orJython decompression libraries from your script.

5.4.1.4. Using HTTPUtilities

The latest incarnation of the HTTPPlugin provides a new class:net.grinder.plugin.http.HTTPUtilities

This new class has several methods which are useful for HTTP scripts.

Setting Basic Authorization

The HTTPUtilities class can create an NVPair for an HTTP Basic Authorization headerusing the following method:

httpUtilities = HTTPPluginControl.getHTTPUtilities()httpUtilities.basicAuthorizationHeader('username', 'password')

Include the header with each HTTPRequest that requires the authentication.

request101.GET('/', (),( httpUtilities.basicAuthorizationHeader('prelive', 'g3tout'), ))

Getting the Last Response

The HTTPUtilities class can return the response for the last request made by the callingworker thread using the following method:

httpUtilities = HTTPPluginControl.getHTTPUtilities()httpUtilities.getLastResponse()

This returns the response, or null if the calling thread has not made any requests.

The Grinder

Page 121Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 122: The Grinder

This must be called from a worker thread, if not it throws a GrinderException.

Getting a Token Value from a Location URI

The HTTPUtilities class can return the value for a path parameter or query stringname-value token with the given tokenName in a Location header from the lastresponse. If there are multiple matches, the first value is returned. This utility can beinvoked using the following method:

httpUtilities = HTTPPluginControl.getHTTPUtilities()httpUtilities.valueFromLocationURI(tokenName)

If there is no match, an empty string is returned rather than null. This makes scriptsmore robust (as they don't need to check the value before using it), but they lose theability to distinguish between a missing token and an empty value.

This must be called from a worker thread, if not it throws a GrinderException.

Getting a Token Value from a URI in the Body of the Response

The HTTPUtilities class can return the value for a path parameter or query stringname-value token with the given tokenName in a URI in the body of the last response.If there are multiple matches, the first value is returned. This utility can be invoked usingthe following method:

httpUtilities = HTTPPluginControl.getHTTPUtilities()httpUtilities.valueFromBodyURI(tokenName)

This returns the first value if one is found, or null.

This must be called from a worker thread, if not it throws a GrinderException.

5.4.1.5. Recording of HTTP status codes

If an HTTPRequest is wrapped in a Test, the HTTP response status codes for eachcall of the HTTPRequest are recorded to the process data log. If multipleHTTPRequests are wrapped in a Test, the status code of the last response is recorded.

5.5. Statistics

5.5.1. Standard statistics

Details of the statistics provided by The Grinder can be found in the documentation of theStatistics interface. Scripts can use this interface to:

• Query whether a test was successful• Obtain statistic values, such as the test time of the last test

The Grinder

Page 122Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 123: The Grinder

• Modify or set a test's statistics before they are sent to the log and the console• Report custom statistics• Register additional views of standard and custom statistics

5.5.2. Distribution of statistics

All the statistics displayed in the console are aggregates (totals or averages) of a numberof tests received in the appropriate period. The reason for this is efficency. The Grinderwould not perform or scale if every data point was transfered back to the console.

The only place per-test statistics are available is in the process data_* files.

5.5.3. Querying and updating statistics

A script can query the statistics about the last completed test usinggrinder.statisticsForLastTest. Script code wrapped in a test can access information aboutthe statistics for the test (which may be incomplete) usinggrinder.statisticsForCurrentTest. For details of the query and update methods, seeStatisticsForTest. Refer to the documentation of the Statistics interface for other details.

An example script demonstrating these APIs can be found in the Script Gallery.

5.5.4. Registering new expressions

Custom statistic expressions can be added to console views and the worker processsummary tables (found in the out_* log files) using the registerSummaryExpressionmethod.

Custom expressions can be added to worker process data_* using theregisterDataLogExpression method.

Both methods take a displayName and an expression as parameters.

The displayName is the label used for the expression. For expressions displayed in theconsole, this string is converted to a key for an internationalised resource bundle look upby prefixing the string with statistic. and replacing any whitespace withunderscores; if no value for the key exists, the raw display name string is used.

Expressions are composed of statistic names (see Statistics) in a simple post-fix formatusing the symbols +, -, / and *, which have their usual meanings, in conjunction withsimple statistic names or sub-expressions. Precedence can be controlled by groupingexpressions in parentheses. For example, the error rate is (* (/ errors period)1000) errors per second.

Sample statistics, such as timedTests, must be introduced with one of sum, count,or variance, depending on the attribute of interest. For example, the statistic

The Grinder

Page 123Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 124: The Grinder

expression (/ (sum timedTests) (count timedTests)) gives themean test time in milliseconds.

5.6. Scripts

5.6.1. Scripts

This section describes The Grinder 3 scripting API. If you've used The Grinder 2 forHTTP testing and you're not a programmer, you might be a bit daunted. Don't worry, itsjust as easy to record and replay HTTP scripts with The Grinder 3.

5.6.1.1. Jython and Python

The Grinder 3 scripting engine is Jython - the Java implementation of Python. Python ispowerful, popular and easy on the eye. If you've never seen any Python before, don'tpanic. Take a look at the script gallery and Richard Perks' tutorial to get a taste of what itslike. There are plenty of resources on the web, here are a few of them to get you started:

• The Jython home page• The Python language web site• Ten Python pitfalls

I recommend the Jython Essentials book; you can read the introductory chapter for free.

5.6.1.2. Scripting for The Grinder

Script structure

Scripts must conform to a few conventions in order to work with The Grinder framework.I'll lay the rules out in fairly dry terms before proceeding with an example. Don't worry ifthis makes no sense to you at first, the examples are much easier to comprehend.

1. Scripts must define a class called TestRunnerWhen a worker process starts up it runs the test script once. The test script mustdefine a class called TestRunner. The Grinder engine then creates an instance ofTestRunner for each worker thread. A thread's TestRunner instance can be used tostore information specific to that thread.

Note:Although strongly recommended, strictly TestRunner doesn't need to be a class. See the Hello World with Functionsexample.

2. The TestRunner instance must be callableA Python object is callable if it defines a __call__ method. Each worker threadperforms a number of runs of the test script, as configured by the property

The Grinder

Page 124Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 125: The Grinder

grinder.runs. For each run, the worker thread calls its TestRunner; thus the__call__ method can be thought of as the definition of a run.

3. The test script can access services through the grinder objectThe engine makes an object called grinder available for the script to import. It canalso be imported by any modules that the script calls. This is an instance of theGrinder.ScriptContext class and provides access to context information (such as theworker thread ID) and services (such as logging and statistics).

Canonical test script structure

This is an example of a script that conforms to the rules above. It doesn't do very much -every run will log Hello World to the output log.

from net.grinder.script.Grinder import grinder

# An instance of this class is created for every thread.class TestRunner:

# This method is called for every run.def __call__(self):

# Per thread scripting goes here.grinder.logger.output("Hello World")

Automatically generating scripts

If you are creating a script for a website or web application, you can use the TCPProxy togenerate an HTTPPlugin script suitable for use with The Grinder.

5.6.1.3. Tests

Although our simple test script can be used with The Grinder framework and can easilybe started in many times in many worker processes on many machines, it doesn't reportany statistics. For this we need to create some tests. A Test has a unique test number anddescription. If you are using the console, it will automatically update to display newTests as they are created.

Let's add a Test to our script.

from net.grinder.script import Testfrom net.grinder.script.Grinder import grinder

# Create a Test with a test number and a description.test1 = Test(1, "Log method")

class TestRunner:def __call__(self):

log("Hello World")

Here we have created a single Test with the test number 1 and the description Logmethod. Note how must import the Test class in a similar manner to Java. We've alsoexplicitly imported the grinder object, this is good practice as it allows our script to be

The Grinder

Page 125Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 126: The Grinder

called from other scripts as a Python module.

Now the console knows about our Test, but we're still not using it to record anything.Let's record how long our grinder.logger.output method takes. To instrumentthe grinder.logger.output method we use our Test to wrap it with a proxywrapper. The wrapper object looks exactly like the grinder.logger.outputmethod and can be called in the same manner. If we call the wrapper the call will bedelegated through to the grinder.logger.output method but additionally the timetaken to do the call and the number of calls will be recorded and reported to the console.

from net.grinder.script import Testfrom net.grinder.script.Grinder import grinder

test1 = Test(1, "Log method")

# Wrap the log() method with our Test and call the result logWrapper.logWrapper = test1.wrap(grinder.logger.output)

class TestRunner:def __call__(self):

logWrapper("Hello World")

This is a fully functional test script that works within The Grinder framework and reportsresults to the console.

You're not restricted to wrapping method calls. In fact, its more common to wrap objects.Here's an example using The Grinder's HTTP plug-in.

# A simple example using the HTTP plugin that shows the retrieval of a# single page via HTTP.

from net.grinder.script import Testfrom net.grinder.script.Grinder import grinderfrom net.grinder.plugin.http import HTTPRequest

test1 = Test(1, "Request resource")request1 = test1.wrap(HTTPRequest())

class TestRunner:def __call__(self):

result = request1.GET("http://localhost:7001/")

5.6.1.4. The Grinder script API

With what you've seen already you have the full power of Jython at your finger tips. Youcan use practically any Java or Python code in your test scripts.

The Grinder script API can be used to access services from The Grinder. The Javadoccontains full information on all the packages, classes and interfaces that make up the coreAPI, as well as additional packages added by the shipped plug-ins. This section providesoverview information on various areas of the API. See also the HTTP plugindocumentation.

The Grinder

Page 126Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 127: The Grinder

The net.grinder.script packageAn instance of Grinder.ScriptContext called grinder is automatically available toall scripts. This object provides access to context information and acts a starting pointfor accessing other services. The instance can be explicitly imported from otherPython modules as net.grinder.script.Grinder.grinder.

We have described the use of the Test class above.

The Statistics interface allows scripts to query and modify statistics, provide customstatistics, and register additional views of standard and custom statistics.

The net.grinder.common packageThis package contains common interfaces and utility classes that are used throughoutThe Grinder and that are also useful to scripts.

5.6.1.5. The Jython distribution and installation

The Grinder is shipped with a version of Jython but does not package the Jythondistribution of the standard Python library. If you want to use the standard library, or ifyou want to use a different version of Jython, obtain and install Jython and add theinstallation's jython.jar to the start of the CLASSPATH used to launch The Grinder.

Jython picks up user and site preferences from several sources (seehttp://www.jython.org/docs/registry.html). A side effect of adding the installedjython.jar to the start of the CLASSPATH is that python.home can be correctlydetermined and the installed registry file will be used.

A useful thing that you can set in the registry is python.cachedir. This specifies aworking directory which Jython uses to store byte compiled python code. If you do nothave your own installed version of Jython in the CLASSPATH, the working directory willbe created in the directory which you run The Grinder. This is fine for most casual users;if you are are terribly worried about preserving disk space, read the information on theJython site.

5.6.2. A Step-By-Step Script Tutorial

5.6.2.1. Introduction

The is a step-by-step tutorial of how to write a number of dynamic HTTP tests usingvarious aspects of The Grinder and Jython APIs. The test script contains a number of teststhat are requests to the same URL. For each request, a different XML parameter isspecified. The resulting HTML data is checked on return and if the test was notsuccessful, the statistics API is used to mark that test as failed.

Richard Perks

The Grinder

Page 127Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 128: The Grinder

5.6.2.2. Script Imports

import stringimport randomfrom java.lang import Stringfrom java.net import URLEncoderfrom net.grinder.script import Testfrom net.grinder.plugin.http import HTTPRequestfrom net.grinder.common import GrinderException

Firstly when writing a script come the import statements. These include imports ofstandard Python modules such as string and random, and other Java importsincluding some language and network classes. Finally there are imports for Grinderspecific methods. A powerful feature of the Jython scripts that are used with The Grinderis the ability to take a mix and match approach to script programming. In some casesusing a Python API is quicker and easier than always using the corresponding Java APIcalls, so feel free to use whichever API makes most sense.

5.6.2.3. Test Definition

tests = {"News01" : Test(1, "News 1 posting"),"Sport01" : Test(2, "Sport 1 posting"),"Sport02" : Test(3, "Sport 2 posting"),"Trading01" : Test(4, "Trading 1 query"),"LifeStyle01" : Test(5, "LifeStyle 1 posting"),

}

To keep the script code easy to read, we next define all the tests we are going to berunning within this script. These are created as a Python dictionary and are name-valuepairs. The name is the name of the test and the value is a Test object with a test numericidentifier and description.

5.6.2.4. Bread crumbs

log = grinder.logger.outputout = grinder.logger.TERMINAL

# Server PropertiesSERVER = "http://serverhost:7001"URI = "/myServlet"

We next define some variables such as Grinder helper methods and server properties. Thelog variable is used to hold a reference to The Grinder logging mechanism and is usedthroughout the script. The out variable is also used in conjunction with logging. The twopossible values for this variable are TERMINAL and LOG. We have set the output toterminal for ease of debugging, which means that any log output goes to the terminalwindow used to start The Grinder test runs. The alternative is to switch this to logging theoutput to The Grinder log files.

The Grinder

Page 128Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 129: The Grinder

5.6.2.5. The Test Interface

class TestRunner:def __call__(self):

Here is the definition of our test class and the method called by The Grinder by each testthread. All scripts must define this class and method. Whilst we are discussing classesand methods, an important point to remember when new to Jython script development isthat Jython/Python code is scoped by indentation, rather than using braces like in alanguage like C or Java. The colon is used to delimit the start scope such as an if ormethod definition.

5.6.2.6. Using the Dictionary and Random Python Modules

for idx in range(len(tests)):testId = random.choice(tests.keys())log("Reading XML file %s " % testId, out)

As discussed earlier, the use of Python modules is encouraged during Grinder scriptdevelopment and I have used a few examples above when performing the test run. Withinthe test run, each of the tests defined in the test dictionary is looped round so that eachGrinder thread executes five separate tests. Within the loop, a test is chosen randomlyfrom one of the five tests. This prevents all threads of executing all the tests in the sameorder and helps simulate a more random load on the server.

Within the dictionary defined as tests, there are a number of useful methods such asare keys(), items() and sort(). We use the keys returned from the tests dictionaryas the parameter to the choice() method in the random module. This randomly selectsone of the tests keys as the current test identifier.

5.6.2.7. Forget the Java IO Package when Handling Files

file = open("./CAAssets/"+testId+".xml", 'r')fileStr = URLEncoder.encode(String(file.read()))file.close()

requestString = "%s%s%s%s" % (SERVER, URI, "?xmldata=", fileStr)

When having to retrieve the contents of files using Jython script, the use of the fileoperations blitz's Java IO for pure script development speed. In the code above, we needto open an XML document that has the name of a test, for example News01.xml. Thiswill be used as a request parameter for the News01 test. The file is opened for reading andencoded using the Java URLEncoder.

We next construct the request string to the server by concatenating the server, URI andXML documents together. Tip: if you need to remove spaces from within a string, youcan use a method like the following:

The Grinder

Page 129Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 130: The Grinder

requestString = string.join(requestString.split(), "")

5.6.2.8. Sending the Request and the Statistics API

grinder.statistics.delayReports = 1request = tests[testId].wrap(HTTPRequest())

log("Sending request %s " % requestString, out)result = request.GET(requestString)

As part of the test execution, we want the ability to check the result of the HTTP request.If the response back from the server is not one that we except, we want to mark the test asunsuccessful and not include the statistics in the test times. To do this, thedelayReports variable can be set to 1. Doing so will delay the reporting back of thestatistics until after the test has completed and we have had chance to check its operation.The default is to report back when the test returns control back to the script, i.e.immediately after a test has executed.

Next we wrap the HTTPRequest with the test being executed. This enables any callsthrough the test wrapper to be monitored by the Grinder. Only wrapped tests will be usedwhen collecting test statistics. Any other time spent within the script will not be recordedby The Grinder. Be careful not to include extra script processing within a test; doing sowill not give the correct statistics. Only test what is required.

The test itself is next executed which is a HTTP GET to the server using our previouslyconstructed test string. Remember - these tests execute in a loop for the number of testswe have defined, using a random test each time.

if string.find(result.getText(), "SUCCESS") < 1:grinder.statistics.forLastTest.setSuccess(0)writeToFile(result.getText(), testId)

On return from the HTTP GET, we check the result for the string "SUCCESS". If the testhas failed, this value will not be returned and the statistics object can be marked asunsuccessful. In the case of an unsuccessful test, we write the HTML output to a file forlater analysis:

def writeToFile(text, testId):filename = grinder.getFilenameFactory().createFilename(

testId + "-page", "-%d.html" % grinder.runNumber)

file = open(filename, "w")print >> file, textfile.close()

5.6.2.9. Full Script Listing

# Send an HTTP request to the server with XML request values

import string

The Grinder

Page 130Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 131: The Grinder

import randomfrom java.lang import Stringfrom java.net import URLEncoder

from net.grinder.script import Testfrom net.grinder.plugin.http import HTTPRequestfrom net.grinder.common import GrinderException

tests = {"News01" : Test(1, "News 1 posting"),"Sport01" : Test(2, "Sport 1 posting"),"Sport02" : Test(3, "Sport 2 posting"),"Trading01" : Test(4, "Trading 1 query"),"LifeStyle01" : Test(5, "LifeStyle 1 posting"),

}

log = grinder.logger.outputout = grinder.logger.TERMINAL

# Server Properties

SERVER = "http://serverhost:7001"URI = "/myServlet"

class TestRunner:def __call__(self):

for idx in range(len(tests)):

testId = random.choice(tests.keys())

log("Reading XML file %s " % testId, out)

file = open("./CAAssets/"+testId+".xml", 'r')fileStr = URLEncoder.encode(String(file.read()))file.close()

# Send the request to the serverrequestString = "%s%s%s%s" % (SERVER, URI, "?xmldata=",

fileStr)requestString = string.join(requestString.split(), "")

grinder.statistics.delayReports = 1request = tests[testId].wrap(HTTPRequest())

log("Sending request %s " % requestString, out)result = request.GET(requestString)

if string.find(result.getText(), "SUCCESS") < 1:grinder.statistics.forLastTest.setSuccess(0)writeToFile(result.getText(), testId)

# Write the responsedef writeToFile(text, testId):

filename = grinder.getFilenameFactory().createFilename(testId + "-page", "-%d.html" % grinder.runNumber)

file = open(filename, "w")

The Grinder

Page 131Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 132: The Grinder

print >> file, textfile.close()

5.6.3. Script Gallery

This page contains examples of Jython scripts and script snippets that can be used withThe Grinder 3. The scripts can also be found in the examples directory of thedistribution. To use one of these scripts, you'll need to set up a grinder.propertiesfile. Please also make sure you are using the latest beta version of The Grinder 3.

If you're new to Python, it might help to know that that blocks are delimited by lexicalindentation.

The scripts make use of The Grinder script API. The grinder object in the scripts is aninstance of ScriptContext through which the script can obtain contextualinformation (such as the worker process ID) and services (such as logging).

If you have a script that you would like to like to see to this page, please send it togrinder-use.

5.6.3.1. Hello World

# A minimal script that tests The Grinder logging facility.## This script shows the recommended style for scripts, with a# TestRunner class. The script is executed just once by each worker# process and defines the TestRunner class. The Grinder creates an# instance of TestRunner for each worker thread, and repeatedly calls# the instance for each run of that thread.

from net.grinder.script.Grinder import grinderfrom net.grinder.script import Test

# A shorter alias for the grinder.logger.output() method.log = grinder.logger.output

# Create a Test with a test number and a description. The test will be# automatically registered with The Grinder console if you are using# it.test1 = Test(1, "Log method")

# Wrap the log() method with our Test and call the result logWrapper.# Calls to logWrapper() will be recorded and forwarded on to the real# log() method.logWrapper = test1.wrap(log)

# A TestRunner instance is created for each thread. It can be used to# store thread-specific data.class TestRunner:

# This method is called for every run.def __call__(self):

logWrapper("Hello World")

The Grinder

Page 132Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 133: The Grinder

5.6.3.2. Simple HTTP example

# A simple example using the HTTP plugin that shows the retrieval of a# single page via HTTP. The resulting page is written to a file.## More complex HTTP scripts are best created with the TCPProxy.

from net.grinder.script.Grinder import grinderfrom net.grinder.script import Testfrom net.grinder.plugin.http import HTTPRequest

test1 = Test(1, "Request resource")request1 = test1.wrap(HTTPRequest())

class TestRunner:def __call__(self):

result = request1.GET("http://localhost:7001/")

# result is a HTTPClient.HTTPResult. We get the message body# using the getText() method.writeToFile(result.text)

# Utility method that writes the given string to a uniquely named file# using a FilenameFactory.def writeToFile(text):

filename = grinder.getFilenameFactory().createFilename("page", "-%d.html" % grinder.runNumber)

file = open(filename, "w")print >> file, textfile.close()

5.6.3.3. Recording many HTTP interactions as one test

# This example shows how many HTTP interactions can be grouped as a# single test by wrapping them in a function.

from net.grinder.script.Grinder import grinderfrom net.grinder.script import Testfrom net.grinder.plugin.http import HTTPRequestfrom HTTPClient import NVPair

# We declare a default URL for the HTTPRequest.request = HTTPRequest(url = "http://localhost:7001")

def page1():request.GET('/console')request.GET('/console/login/LoginForm.jsp')request.GET('/console/login/bea_logo.gif')

page1Test = Test(1, "First page").wrap(page1)

class TestRunner:def __call__(self):

page1Test()

The Grinder

Page 133Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 134: The Grinder

5.6.3.4. HTTP/J2EE form based authentication

# A more complex HTTP example based on an authentication conversation# with the server. This script demonstrates how to follow different# paths based on a response returned by the server and how to post# HTTP form data to a server.## The J2EE Servlet specification defines a common model for form based# authentication. When unauthenticated users try to access a protected# resource, they are challenged with a logon page. The logon page# contains a form that POSTs username and password fields to a special# j_security_check page.

from net.grinder.script.Grinder import grinderfrom net.grinder.script import Testfrom net.grinder.plugin.http import HTTPRequestfrom HTTPClient import NVPair

protectedResourceTest = Test(1, "Request resource")authenticationTest = Test(2, "POST to j_security_check")

class TestRunner:def __call__(self):

request = protectedResourceTest.wrap(HTTPRequest(url="http://localhost:7001/console"))

result = request.GET()

result = maybeAuthenticate(result)

result = request.GET()

# Function that checks the passed HTTPResult to see whether# authentication is necessary. If it is, perform the authentication# and record performance information against Test 2.def maybeAuthenticate(lastResult):

if lastResult.statusCode == 401 \or lastResult.text.find("j_security_check") != -1:

grinder.logger.output("Challenged, authenticating")

authenticationFormData = ( NVPair("j_username", "weblogic"),NVPair("j_password", "weblogic"),)

request = authenticationTest.wrap(HTTPRequest(url="%s/j_security_check" %

lastResult.originalURI))

return request.POST(authenticationFormData)

5.6.3.5. HTTP cookies

# HTTP example which shows how to access HTTP cookies.## By default, the HTTPClient library handles Cookie interaction and# removes the cookie headers from responses. If you want to access

The Grinder

Page 134Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 135: The Grinder

# them, one way is to define your own CookiePolicyHandler. This script# defines a CookiePolicyHandler that simply logs all cookies that are# sent or received.

from net.grinder.script.Grinder import grinderfrom net.grinder.script import Testfrom net.grinder.plugin.http import HTTPRequest, HTTPPluginControlfrom HTTPClient import CookieModule, CookiePolicyHandler

log = grinder.logger.output

class MyCookiePolicyHandler(CookiePolicyHandler):def acceptCookie(self, cookie, request, response):

log("accept cookie: %s" % cookie)return 1

def sendCookie(self, cookie, request):log("send cookie: %s" % cookie)return 1

CookieModule.setCookiePolicyHandler(MyCookiePolicyHandler())

test1 = Test(1, "Request resource")request1 = test1.wrap(HTTPRequest())

class TestRunner:def __call__(self):

result = request1.GET("http://localhost:7001/console")

5.6.3.6. HTTP multipart form submission

# This script uses the HTTPClient.Codecs class to post itself to the# server as a multi-part form. Thanks to Marc Gemis.

from net.grinder.script.Grinder import grinderfrom net.grinder.script import Testfrom net.grinder.plugin.http import HTTPRequestfrom HTTPClient import Codecs, NVPairfrom jarray import zeros

test1 = Test(1, "Upload Image")request1 = test1.wrap(HTTPRequest(url="http://localhost:7001/"))

class TestRunner:def __call__(self):

files = ( NVPair("self", "form.py"), )parameters = ( NVPair("run number", str(grinder.runNumber)), )

# This is the Jython way of creating an NVPair[] Java array# with one element.headers = zeros(1, NVPair)

# Create a multi-part form encoded byte array.data = Codecs.mpFormDataEncode(parameters, files, headers)grinder.logger.output("Content type set to %s" %

headers[0].value)

The Grinder

Page 135Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 136: The Grinder

# Call the version of POST that takes a byte array.result = request1.POST("/upload", data, headers)

5.6.3.7. Enterprise Java Beans

# Exercise a stateful session EJB from the BEA WebLogic Server 7.0# examples. Additionally this script demonstrates the use of the# ScriptContext sleep(), getThreadId() and getRunNumber() methods.## Before running this example you will need to add the EJB client# classes to your CLASSPATH.

from java.lang import Stringfrom java.util import Properties,Randomfrom javax.naming import Context,InitialContextfrom net.grinder.script.Grinder import grinderfrom net.grinder.script import Testfrom weblogic.jndi import WLInitialContextFactory

tests = {"home" : Test(1, "TraderHome"),"trade" : Test(2, "Trader buy/sell"),"query" : Test(3, "Trader getBalance"),}

# Initial context lookup for EJB home.p = Properties()p[Context.INITIAL_CONTEXT_FACTORY] = WLInitialContextFactory.name

home = InitialContext(p).lookup("ejb20-statefulSession-TraderHome")homeTest = tests["home"].wrap(home)

random = Random()

class TestRunner:def __call__(self):

log = grinder.logger.output

trader = homeTest.create()

tradeTest = tests["trade"].wrap(trader)

stocksToSell = { "BEAS" : 100, "MSFT" : 999 }for stock, amount in stocksToSell.items():

tradeResult = tradeTest.sell("John", stock, amount)log("Result of tradeTest.sell(): %s" % tradeResult)

grinder.sleep(100) # Idle a while

stocksToBuy = { "BEAS" : abs(random.nextInt()) % 1000 }for stock, amount in stocksToBuy.items():

tradeResult = tradeTest.buy("Phil", stock, amount)log("Result of tradeTest.buy(): %s" % tradeResult)

queryTest = tests["query"].wrap(trader)balance = queryTest.getBalance()log("Balance is $%.2f" % balance)

The Grinder

Page 136Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 137: The Grinder

trader.remove() # We don't record the remove()as a test

# Can obtain information about the thread context...if grinder.threadID == 0 and grinder.runNumber == 0:

# ...and navigate from the proxy back to the testd = queryTest.__test__log("Query test is test %d, (%s)" % (d.number,

d.description))

5.6.3.8. Grinding a database with JDBC

# Some simple database playing with JDBC.## To run this, set the Oracle login details appropriately and add the# Oracle thin driver classes to your CLASSPATH.

from java.sql import DriverManagerfrom net.grinder.script.Grinder import grinderfrom net.grinder.script import Testfrom oracle.jdbc import OracleDriver

test1 = Test(1, "Database insert")test2 = Test(2, "Database query")

# Load the Oracle JDBC driver.DriverManager.registerDriver(OracleDriver())

def getConnection():return DriverManager.getConnection(

"jdbc:oracle:thin:@127.0.0.1:1521:mysid", "wls", "wls")

def ensureClosed(object):try: object.close()except: pass

# One time initialisation that cleans out old data.connection = getConnection()statement = connection.createStatement()

try: statement.execute("drop table grinder_fun")except: pass

statement.execute("create table grinder_fun(thread number, run number)")

ensureClosed(statement)ensureClosed(connection)

class TestRunner:def __call__(self):

connection = Nonestatement = None

try:connection = getConnection()statement = connection.createStatement()

The Grinder

Page 137Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 138: The Grinder

testInsert = test1.wrap(statement)testInsert.execute("insert into grinder_fun values(%d, %d)"

%(grinder.threadID, grinder.runNumber))

testQuery = test2.wrap(statement)testQuery.execute("select * from grinder_fun where

thread=%d" %grinder.threadID)

finally:ensureClosed(statement)ensureClosed(connection)

5.6.3.9. Simple HTTP Web Service

# Calls an Amazon.com web service to obtain information about a book.## To run this script you must install the standard Python xml module.# Here's one way to do that:## 1. Download and install Jython 2.1# 2. Add the following line to grinder.properties (changing the pathappropriately):# grinder.jvm.arguments=-Dpython.home=c:/jython-2.1# 3. Add Jakarta Xerces (or one of the other parsers supported by# the xml module) to your CLASSPATH.## You may also need to obtain your own Amazon.com web service license# and replace the script text <insert license key here> with the# license key, although currently that doesn't appear to be necessary.

from net.grinder.script.Grinder import grinderfrom net.grinder.script import Testfrom net.grinder.plugin.http import HTTPRequestfrom HTTPClient import NVPairfrom xml.dom import javadomfrom org.xml.sax import InputSource

bookDetailsTest = Test(1, "Get book details from Amazon")parser = javadom.XercesDomImplementation()

class TestRunner:def __call__(self):

if grinder.runNumber > 0 or grinder.threadID > 0:raise RuntimeError("Use limited to one thread, one run; "

"see Amazon Web Services terms andconditions")

request = bookDetailsTest.wrap(HTTPRequest(url="http://xml.amazon.com/onca/xml"))

parameters = (NVPair("v", "1.0"),NVPair("f", "xml"),NVPair("t", "webservices-20"),NVPair("dev-t", "<insert license key here>"),NVPair("type", "heavy"),

The Grinder

Page 138Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 139: The Grinder

NVPair("AsinSearch", "1904284000"),)

bytes = request.POST(parameters).inputStream

# Parse resultsdocument = parser.buildDocumentUrl(InputSource(bytes))

result = {}

for details in document.getElementsByTagName("Details"):for detailName in ("ProductName", "SalesRank", "ListPrice"):

result[detailName] = details.getElementsByTagName(detailName)[0].firstChild.nodeValue

grinder.logger.output(str(result))

5.6.3.10. JAX-RPC Web Service

# Exercise a basic Web Service from the BEA WebLogic Server 7.0# examples.## Before running this example you will need to add the generated# JAX-RPC client classes and webserviceclient.jar to your CLASSPATH.

from net.grinder.script.Grinder import grinderfrom net.grinder.script import Testfrom examples.webservices.basic.javaclass import HelloWorld_Implfrom java.lang import System

System.setProperty( "javax.xml.rpc.ServiceFactory","weblogic.webservice.core.rpc.ServiceFactoryImpl")

webService =HelloWorld_Impl("http://localhost:7001/basic_javaclass/HelloWorld?WSDL")

port = webService.getHelloWorldPort()portTest = Test(1, "JAXP Port test").wrap(port)

class TestRunner:def __call__(self):

result = portTest.sayHello(grinder.threadID, grinder.grinderID)grinder.logger.output("Got '%s'" % result)

5.6.3.11. XML-RPC Web Service

# A server should be running on the localhost. This script uses the# example from#http://xmlrpc-c.sourceforge.net/xmlrpc-howto/xmlrpc-howto-java-server.html## Copyright (C) 2004 Sebasti�n Fontana# Distributed under the terms of The Grinder license.

from java.util import Vectorfrom java.lang import Integer

The Grinder

Page 139Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 140: The Grinder

from net.grinder.script.Grinder import grinderfrom net.grinder.script import Test

from org.apache.xmlrpc import XmlRpcClient

test1 = Test(1, "XML-RPC example test")server_url = "http://localhost:8080/RPC2"

serverWrapper = test1.wrap(XmlRpcClient(server_url))

class TestRunner:def __call__(self):

params = Vector()params.addElement(Integer(6))params.addElement(Integer(3))

result = serverWrapper.execute("sample.sumAndDifference",params)

sum = result.get("sum")

grinder.logger.output("SUM %d" % sum)

5.6.3.12. Hello World, with functions

# The Hello World example re-written using functions.## In previous examples we've defined TestRunner as a class; calling# the class creates an instance and calling that instance invokes its# __call__ method. This script is for the Luddites amongst you and# shows how The Grinder engine is quite happy as long as the script# creates a callable thing called TestRunner that can be called to# create another callable thing.

from net.grinder.script.Grinder import grinderfrom net.grinder.script import Test

test1 = Test(1, "Log method")logTest = test1.wrap(grinder.logger.output)

def doRun():logTest("Hello World")

def TestRunner():return doRun

5.6.3.13. The script life cycle

# A script that demonstrates how the various parts of a script and# their effects on worker threads.

# The "top level" of the script is called once for each worker# process. Perform any one-off initialisation here. For example,# import all the modules, set up shared data structures, and declare# all the Test objects you will use.

from net.grinder.script.Grinder import grinderfrom java.lang import System

The Grinder

Page 140Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 141: The Grinder

# The totalNumberOfRuns variable is shared by all worker threads.totalNumberOfRuns = 0

# An instance of the TestRunner class is created for each worker thread.class TestRunner:

# There's a runsForThread variable for each worker thread. This# statement specifies a class-wide initial value.runsForThread = 0

# The __init__ method is called once for each thread.def __init__(self):

# There's an initialisationTime variable for each worker thread.self.initialisationTime = System.currentTimeMillis()

grinder.logger.output("New thread started at time %s" %self.initialisationTime)

# The __call__ method is called once for each test run performed by# a worker thread.def __call__(self):

# We really should synchronise this access to the shared# totalNumberOfRuns variable. See JMS receiver example for how# to use the Python Condition class.global totalNumberOfRunstotalNumberOfRuns += 1

self.runsForThread += 1

grinder.logger.output("runsForThread=%d, totalNumberOfRuns=%d,

initialisationTime=%d" %(self.runsForThread, totalNumberOfRuns,

self.initialisationTime))

# You can also vary behaviour based on thread ID.if grinder.threadID % 2 == 0:

grinder.logger.output("I have an even thread ID.")

# Scripts can optionally define a __del__ method. The Grinder# guarantees this will be called at shutdown once for each thread# It is useful for closing resources (e.g. database connections)# that were created in __init__.def __del__(self):

grinder.logger.output("Thread shutting down")

5.6.3.14. Accessing test statistics

# Examples of using The Grinder statistics API with standard# statistics.

from net.grinder.script.Grinder import grinderfrom net.grinder.script import Testfrom net.grinder.plugin.http import HTTPRequest

The Grinder

Page 141Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 142: The Grinder

class TestRunner:def __call__(self):

request = Test(1, "Basic request").wrap(HTTPRequest(url = "http://localhost:7001"))

# Example 1. You can get the time of the last test as follows.result = request.GET("index.html")

grinder.logger.output("The last test took %d milliseconds" %grinder.statistics.forLastTest.time)

# Example 2. Normally test results are reported automatically# when the test returns. If you want to alter the statistics# after a test has completed, you must set delayReports = 1 to# delay the reporting before performing the test. This only# affects the current worker thread.grinder.statistics.delayReports = 1

result = request.GET("index.html")

if grinder.statistics.forLastTest.time > 5:# We set success = 0 to mark the test as a failure. The test# time will be reported to the data log, but not included# in the aggregate statistics sent to the console or the# summary table.grinder.statistics.forLastTest.success = 0

# With delayReports = 1 you can call report() to explicitly.grinder.statistics.report()

# You can also turn the automatic reporting back on.grinder.statistics.delayReports = 0

# Example 3.# getForCurrentTest() accesses statistics for the current test.# getForLastTest() accesses statistics for the last completed

test.

def page(self):resourceRequest = Test(2, "Request resource").wrap(

HTTPRequest(url ="http://localhost:7001"))

resourceRequest.GET("index.html");resourceRequest.GET("foo.css");

grinder.logger.output("GET foo.css returned a %d byte body"%grinder.statistics.forLastTest.getLong(

"httpplugin.responseLength"))

grinder.logger.output("Page has taken %d ms so far" %grinder.statistics.forCurrentTest.time)

if grinder.statistics.forLastTest.time > 10:grinder.statistics.forCurrentTest.success = 0

The Grinder

Page 142Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 143: The Grinder

resourceRequest.GET("image.gif");

instrumentedPage = Test(3, "Page").wrap(page)

instrumentedPage(self)

5.6.3.15. Java Message Service - Queue Sender

# JMS objects are looked up and messages are created once during# initialisation. This default JNDI names are for the WebLogic Server# 7.0 examples domain - change accordingly.## Each worker thread:# - Creates a queue session# - Sends ten messages# - Closes the queue session

from net.grinder.script.Grinder import grinderfrom net.grinder.script import Testfrom jarray import zerosfrom java.util import Properties, Randomfrom javax.jms import Sessionfrom javax.naming import Context, InitialContextfrom weblogic.jndi import WLInitialContextFactory

# Look up connection factory and queue in JNDI.properties = Properties()properties[Context.PROVIDER_URL] = "t3://localhost:7001"properties[Context.INITIAL_CONTEXT_FACTORY] =WLInitialContextFactory.name

initialContext = InitialContext(properties)

connectionFactory =initialContext.lookup("weblogic.examples.jms.QueueConnectionFactory")queue = initialContext.lookup("weblogic.examples.jms.exampleQueue")initialContext.close()

# Create a connection.connection = connectionFactory.createQueueConnection()connection.start()

random = Random()

def createBytesMessage(session, size):bytes = zeros(size, 'b')random.nextBytes(bytes)message = session.createBytesMessage()message.writeBytes(bytes)return message

class TestRunner:def __call__(self):

log = grinder.logger.output

log("Creating queue session")session = connection.createQueueSession(0,

The Grinder

Page 143Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 144: The Grinder

Session.AUTO_ACKNOWLEDGE)

sender = session.createSender(queue)instrumentedSender = Test(1, "Send a message").wrap(sender)

message = createBytesMessage(session, 100)

log("Sending ten messages")

for i in range(0, 10):instrumentedSender.send(message)grinder.sleep(100)

log("Closing queue session")session.close()

5.6.3.16. Java Message Service - Queue Receiver

# JMS objects are looked up and messages are created once during# initialisation. This default JNDI names are for the WebLogic Server# 7.0 examples domain - change accordingly.## Each worker thread:# - Creates a queue session# - Receives ten messages# - Closes the queue session## This script demonstrates the use of The Grinder statistics API to# record a "delivery time" custom statistic.## Copyright (C) 2003, 2004, 2005, 2006 Philip Aston# Copyright (C) 2005 Dietrich Bollmann# Distributed under the terms of The Grinder license.

from java.lang import Systemfrom java.util import Propertiesfrom javax.jms import MessageListener, Sessionfrom javax.naming import Context, InitialContextfrom net.grinder.script.Grinder import grinderfrom net.grinder.script import Testfrom threading import Conditionfrom weblogic.jndi import WLInitialContextFactory

# Look up connection factory and queue in JNDI.properties = Properties()properties[Context.PROVIDER_URL] = "t3://localhost:7001"properties[Context.INITIAL_CONTEXT_FACTORY] =WLInitialContextFactory.name

initialContext = InitialContext(properties)

connectionFactory =initialContext.lookup("weblogic.examples.jms.QueueConnectionFactory")queue = initialContext.lookup("weblogic.examples.jms.exampleQueue")initialContext.close()

# Create a connection.connection = connectionFactory.createQueueConnection()

The Grinder

Page 144Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 145: The Grinder

connection.start()

# Add two statistics expressions:# 1. Delivery time:- the mean time taken between the server sending# the message and the receiver receiving the message.# 2. Mean delivery time:- the delivery time averaged over all tests.# We use the userLong0 statistic to represent the "delivery time".

grinder.statistics.registerDataLogExpression("Delivery time","userLong0")grinder.statistics.registerSummaryExpression(

"Mean delivery time","(/ userLong0(+ timedTests untimedTests))")

# We record each message receipt against a single test. The# test time is meaningless.def recordDeliveryTime(deliveryTime):

grinder.statistics.forCurrentTest.setValue("userLong0",deliveryTime)

recordTest = Test(1, "Receive messages").wrap(recordDeliveryTime)

class TestRunner(MessageListener):

def __init__(self):self.messageQueue = [] # Queue of received messages not

yet recorded.self.cv = Condition() # Used to synchronise thread

activity.

def __call__(self):log = grinder.logger.output

log("Creating queue session and a receiver")session = connection.createQueueSession(0,

Session.AUTO_ACKNOWLEDGE)

receiver = session.createReceiver(queue)receiver.messageListener = self

# Read 10 messages from the queue.for i in range(0, 10):

# Wait until we have received a message.self.cv.acquire()while not self.messageQueue: self.cv.wait()# Pop delivery time from first message in message queuedeliveryTime = self.messageQueue.pop(0)self.cv.release()

log("Received message")

# We record the test a here rather than in onMessage# because we must do so from a worker thread.recordTest(deliveryTime)

log("Closing queue session")session.close()

The Grinder

Page 145Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 146: The Grinder

# Rather than over complicate things with explict message# acknowledgement, we simply discard any additional messages# we may have read.log("Received %d additional messages" % len(self.messageQueue))

# Called asynchronously by JMS when a message arrives.def onMessage(self, message):

self.cv.acquire()

# In WebLogic Server JMS, the JMS timestamp is set by the# sender session. All we need to do is ensure our clocks are# synchronised...deliveryTime = System.currentTimeMillis() -

message.getJMSTimestamp()

self.messageQueue.append(deliveryTime)

self.cv.notifyAll()self.cv.release()

5.6.3.17. Using The Grinder with other test frameworks

# Example showing how The Grinder can be used with HTTPUnit.## Copyright (C) 2003, 2004 Tony Lodge# Copyright (C) 2004 Philip Aston# Distributed under the terms of The Grinder license.

from net.grinder.script.Grinder import grinderfrom net.grinder.script import Test

from com.zaplet.test.frontend.http import HttpTest

# These correspond to method names on the test class.testNames = [ "testRedirect",

"testRefresh","testNegativeLogin","testLogin","testPortal","testHeader","testAuthoringLink","testTemplateDesign","testSearch","testPreferences","testAboutZaplet","testHelp","testLogoutLink","testNavigationFrame","testBlankFrame","testContentFrame","testLogout", ]

tests = [Test(i, name).wrap(HttpTest(name))for name, i in zip(testNames, range(len(testNames)))]

# A TestRunner instance is created for each thread. It can be used to# store thread-specific data.class TestRunner:

The Grinder

Page 146Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 147: The Grinder

def __call__(self):for t in tests:

result = t.run()

5.6.3.18. Email

# Send email using Java Mail (http://java.sun.com/products/javamail/)## This Grinder Jython script should only be used for legal email test# traffic generation within a lab testbed environment. Anyone using# this script to generate SPAM or other unwanted email traffic is# violating the law and should be exiled to a very bad place for a# very long time.## Copyright (C) 2004 Tom Pittard# Copyright (C) 2004 Philip Aston# Distributed under the terms of The Grinder license.

from net.grinder.script.Grinder import grinderfrom net.grinder.script import Test

from java.lang import Systemfrom javax.mail import Message, Sessionfrom javax.mail.internet import InternetAddress, MimeMessage

emailSendTest1 = Test(1, "Email Send Engine")

class TestRunner:def __call__(self):

smtpHost = "mailhost"

properties = System.getProperties()properties["mail.smtp.host"] = smtpHostsession = Session.getInstance(System.getProperties())session.debug = 1

message = MimeMessage(session)message.setFrom(InternetAddress("[email protected]"))

message.addRecipient(Message.RecipientType.TO,InternetAddress("[email protected]"))

message.subject = "Test email %s from thread %s" %(grinder.runNumber,grinder.threadID)

# One could vary this by pointing to various files for contentmessage.setText("SMTPTransport Email works from The Grinder!")

# Wrap transport object in a Grinder Jython Test Wrappertransport = emailSendTest1.wrap(session.getTransport("smtp"))

transport = emailSendTest1.wrap(transport)transport.connect(smtpHost, "username", "password")transport.send(message)transport.close()

5.6.3.19. Run test scripts in sequence

The Grinder

Page 147Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 148: The Grinder

# Scripts are defined in Python modules (helloworld.py, goodbye.py)# specified in grinder.properties:## script1=helloworld# script2=goodbye

from net.grinder.script.Grinder import grinder

from java.util import TreeMap

# TreeMap is the simplest way to sort a Java map.scripts = TreeMap(grinder.properties.getPropertySubset("script"))

# Ensure modules are initialised in the process thread.for module in scripts.values(): exec("import %s" % module)

def createTestRunner(module):exec("x = %s.TestRunner()" % module)return x

class TestRunner:def __init__(self):

self.testRunners = [createTestRunner(m) for m inscripts.values()]

# This method is called for every run.def __call__(self):

for testRunner in self.testRunners: testRunner()

5.6.3.20. Run test scripts in parallel

# Run TestScript1 in 50% of threads, TestScript2 in 25% of threads,# and TestScript3 in 25% of threads.

from net.grinder.script.Grinder import grinder

scripts = ["TestScript1", "TestScript2", "TestScript3"]

# Ensure modules are initialised in the process thread.for script in scripts: exec("import %s" % script)

def createTestRunner(script):exec("x = %s.TestRunner()" % script)return x

class TestRunner:def __init__(self):

tid = grinder.threadID

if tid % 4 == 2:self.testRunner = createTestRunner(scripts[1])

elif tid % 4 == 3:self.testRunner = createTestRunner(scripts[2])

else:self.testRunner = createTestRunner(scripts[0])

# This method is called for every run.

The Grinder

Page 148Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 149: The Grinder

def __call__(self):self.testRunner()

5.7. SSL Support

The Grinder 3 supports the use of SSL by scripts. The Grinder 3 implements SSL usingthe Java Secure Socket Extension (JSSE) included in the Java run time. When used withthe HTTP Plug-in, this is as simple as using https instead of http in URIs. Scripts canobtain a suitable SSLContext and hence a SSLSocketFactory for non-HTTP usecases, and can control the allocation of SSL sessions to worker threads.

5.7.1. Before we begin

5.7.1.1. Performance

Simulating multiple SSL sessions on a single test machine may or may not be realistic. Atypical browser running on a desktop PC has the benefit of a powerful CPU to run theSSL cryptography. Be careful that your results aren't constrained due to inadequate testclient CPU power.

5.7.1.2. The Grinder's SSL implementation is not secure

To reduce the client side performance overhead, The Grinder deliberately accelerates SSLinitialisation by using a random number generator that is seeded with a fixed number.Further, no validation of server certificates is performed. Neither of these hinder SSLcommunication, but they do make it less secure.

Warning:No guarantee is made as to the cryptographic strength of any SSL communication using The Grinder.

This acceleration affects initialisation time only and should not affect timing informationobtained using The Grinder.

5.7.2. Controlling when new SSL sessions are created

By default The Grinder creates a new SSL session for each run carried out by eachworker thread. This is in line with the usual convention of simulating a user session witha worker thread executing the part of the script defined byTestRunner.__call__().

Alternatively, scripts may wish to have an SSL session per worker thread, i.e. for eachthread to reuse SSL sessions on subsequent executions of TestRunner.__call__().This can be done with the SSLControl.setShareContextBetweenRuns()method:

The Grinder

Page 149Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 150: The Grinder

from net.grinder.script.Grinder import grindergrinder.SSLControl.shareContextBetweenRuns = 1

This will cause each worker thread to reuse SSL sessions between runs. SSL sessions willstill not be shared between worker threads. CallingsetShareContextBetweenRuns() affects all of the worker threads.

5.7.3. Using client certificates

If a server requests or requires a client certificate, The Grinder must have some way ofproviding one - this involves specifying a key store.

from net.grinder.script.Grinder import grinder

class TestRunner:def __call__(self):

grinder.SSLControl.setKeyStoreFile("mykeystore.jks","passphrase")

It is only valid to use setKeyStoreFile from a worker thread, and it only affects thatworker thread.

There is also a method called setKeyStore which takes a java.io.InputStreamwhich may be useful if your key store doesn't live on the local file system. Both methodshave an overloaded version that allows the key store type to be specified, otherwise thedefault type is used (normally jks).

Whenever setKeyStoreFile, setKeyStore, or setKeyManagers (see below)is called, the current SSL session for the thread is discarded. Consequently, you usuallywant to call these methods at the beginning of your __call__() method or from theTestRunner.__init__() constructor. Setting the thread's key store inTestRunner.__init__() is especially recommended if you callingsetShareContextBetweenRuns(true) to share SSL sessions between runs.

5.7.4. FAQ

The astute reader who is familiar with key stores may have a few questions. Here's a miniFAQ:

1. If I have several suitable certificates in my key store, how does The Grinder chosebetween them?

The Grinder relies on the JVM's default KeyManager implementations. This picks acertificate from the store based on SSL negotiation with the server. If there are severalsuitable certificates, the only way to control which is used is to provide your ownKeyManager.

2. setKeyStoreFile has a parameter for the key store password. What about thepass phrase that protects the private key in the key store?

The Grinder

Page 150Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 151: The Grinder

The pass phrases for keys must be the same as the key store password. This is arestriction of the default KeyManagers. If you don't like this, you can provide yourown KeyManager.

3. Shouldn't I need to specify a set of certificates for trusted Certificate Authorities?

No. The Grinder does not validate certificates received from the server, so does notneed a set of CA certificates.

4. Can I use the properties javax.net.ssl.keyStore,javax.net.ssl.keyStoreType, andjavax.net.ssl.keyStorePassword to specify a global keystore?

No. The Grinder does not use these properties, primarily because the JSSE does notprovide a way to access its default SSLContext.

5.7.5. Picking a certificate from a key store [Advanced]

Here's an example script that provides its own X509KeyManager implementationwhich controls which client certificate to use. The example is hard coded to always usethe certificate with the alias myalias.

from com.sun.net.ssl import KeyManagerFactory,X509KeyManagerfrom java.io import FileInputStreamfrom java.security import KeyStorefrom jarray import array

class MyManager(X509KeyManager):def __init__(self, keyStoreFile, keyStorePassword):

keyStore = KeyStore.getInstance("jks")keyStore.load(FileInputStream(keyStoreFile), keyStorePassword)

keyManagerFactory = \KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())

keyManagerFactory.init(keyStore, keyStorePassword)

# Assume we have one key manager.self._delegate = keyManagerFactory.keyManagers[0]

def __getattr__(self, a):"""Some Python magic to pass on all invocations of methods wedon't define on to our delegate."""

if self.__dict__.has_key(a): return self.__dict__[a]else: return getattr(self._delegate, a)

def chooseClientAlias(self, keyTypes, issuers):return "myalias"

myManager = MyManager("keystore.jks", "password")myManagerArray = array((myManager,), X509KeyManager)

class TestRunner:def __call__(self):

The Grinder

Page 151Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 152: The Grinder

grinder.SSLControl.setKeyManagers(myManagerArray)# ...

5.7.6. Debugging

When debugging SSL interactions, you may find it useful to set the following ingrinder.properties.

grinder.jvm.arguments=-Djavax.net.debug=ssl# or -Djavax.net.debug=all

6. Questions

6.1. Frequently Asked Questions

6.1.1. Questions

6.1.1.1. 1. Common Grinder Questions

1.1. General

1.1.1. So its called "The Grinder" then?

Yes. "The Grinder" is preferred over just plain "Grinder".

1.1.2. Should I use The Grinder 2 or The Grinder 3?

The Grinder 3. The Grinder 3 has many enhancements over The Grinder 2. The Grinder 3is in active development.

If you wish, you can continue to use The Grinder 2. You may find it initially easier to getuse for simple HTTP test scenarios. Be aware that defects in The Grinder 2 (including thisone and this one) will not be fixed.

1.1.3. Why is The Grinder 3 still labelled beta?

The following features need to be completed before I will remove the beta label from TheGrinder 3:

• Script editing and distribution.• Documentation.

These feature aside, The Grinder 3 is stable and usable.

1.1.4. How does The Grinder stack up against a commercial tool like Mercury Interactive's LoadRunner™?

The Grinder

Page 152Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 153: The Grinder

Here is an edited version of Tom Braverman's post to grinder-use.

A few reasons:• The Grinder is lightweight

Compared to setting up LoadRunner or some other full featured tools, TheGrinder is trivial to install and get running.

• The Grinder is a programmer's load tester

Too often, programmers defer load testing to some other group (e.g., QA) anddon't test their own components for scalability. The Grinder is designed for peoplewho understand the code that they're hitting - it's not just a "black box" with a setof associated response times.

Since tests can be coded - and not simply scripted, programmers get to test interiortiers of their application and not just response time via the user interface.

• The Grinder is freeI'm a consulting professional and I have to come up with solutions to deadlocksand slowdowns. Sometimes I only have hours to recreate a problem and thenattempt to resolve it. I can't count on my client having a given load testing tooland many (most?) development teams don't have any such tool (they defer thistype of testing to QA as mentioned above). I can bring The Grinder in and set it upand apply load quite quickly.

Summarizing: I don't try and persuade my clients that The Grinder is a replacementfor LoadRunner, etc. I tell them that The Grinder is for use by the developers and thatthey'll still want the QA team to generate scalability metrics using LoadRunner orsome other tool approved for the purpose by management.

Story: One client was attempting to determine scalability based on LoadRunner. TheLoadRunner team - with no understanding at all of the app - was telling them thatsome pages were giving response times of 30 seconds. The project manager knew thiswas patent BS since he could hit the enter key on his page and only count to 3 or 4before the page displayed. The client spent many resource days attempting tounderstand LoadRunner numbers. Within a few hours, The Grinder was up andrunning and reporting numbers that appeared to track with the user experience. TheGrinder became the gold standard that the client used to measure LoadRunner.

Its worth adding to this that many companies are using The Grinder for production loadtesting.

1.1.5. How do I subscribe/unsubscribe from The Grinder mailing lists?

You can subscribe and unsubscribe at Sourceforge. More details.

1.2. Running the tests

The Grinder

Page 153Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 154: The Grinder

1.2.1. How do I control the number of simulated users?

Typically a worker thread is thought of as a client context which can simulate aconcurrent user. You can control the number of worker processes per agent and thenumber of threads per process. You shouldn't need to run more than one agent process permachine. The number of simulated users is then

number of worker threads x number of worker processes x number testmachines

To simulate additional users it is usually better to increase the number of worker threadsrather than the number of processes except where:

• Your JVM threading implementation is particularly inefficient. This is not such aproblem nowadays. Running several hundred threads per process is usually fine.

• The protocol you are using will behave differently when distributed across multipleprocesses. For example, the WebLogic Server t3 protocol is optimised so that a singleTCP/IP connection is used between any two processes.

1.2.2. Can I run different test scripts against the same console?

Yes.

The console receives reports and updates the graph based on test number. Thus it ispossible to have different agent processes running scripts with different test numbers (e.g.one process running tests 1 to 5 and another running tests 6 to 10)., reporting to the sameconsole.

1.2.3. How do I run the tests for a specific period of time?

Use the console to control the test. If you want the tests to run for an hour and yoursample interval is set to 5 seconds, set the console to collect 720 samples.

If you're not using the console, see grinder.duration.

1.2.4. Is there any way to record CPU utilisation of the server?

No. The Grinder only measures the response of the test server as a black box. Look toother tools such as sar and vmstat to measure server CPU utilisation.

1.3. Statistics

1.3.1. Where is the raw data stored?

Liam Morley asked:

I understand that all the information from the workers is

The Grinder

Page 154Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 155: The Grinder

sent back to the client, but all I see thus far is roughstatistical data which seems to have been compiled fromthe raw data. When the data is sent back to the console,where is it stored?

For efficiency, the worker processes only send back aggregate information to the console,not the raw data. Each worker process sends the console a report every 500ms. The reportcontains information for each test that was invoked during the period which includes thenumber of invocations, the total time taken, the number of errors, and any other customstatistics.

Each worker process writes out the raw information to a process specific data file. Thisare the data_... files that are stored with the other log files.

Apart from the raw data, another advantage that these files give you that the consoledoesn't is a record of the testing time line. Whilst the console graphs present thisinformation graphically, the console only allows you to save snapshots of the currentstatistics.

1.3.2. How does the Grinder calculate the mean time and TPS?

Each worker process records the time taken by each thread for each test and regularlyreports the mean time for each test to the console. The console calculates the mean timefor each test across all worker processes. This is the mean time.

Tests per second (TPS) is calculated by the console. It is the sum of the number of testsperformed by all worker processes within the sampling period, divided by the duration ofthe sampling period. Using longer sampling periods will give better averages, but theconsole display will be updated less frequently.

1.3.3. What is the relation between mean time and TPS?

In general there is not a linear relationship between the test time and the number of testsper second.

Most time in a typical server-side application is spent waiting on I/O. Elapsed time is notthe sum the time spent by all of the threads. If I have a simple servlet or ejb that does thefollowing:

void doit() { Thread.sleep(1000); // 1 second }

Say with this I get 100 TPS and a 1.2 second response time. If I now change the code to:

void doit() { Thread.sleep(10000); // 10 seconds }

the system isn't doing any more work and I'd expect to get 100 TPS and a 10.2 secondrepsonse time.

Note:

The Grinder

Page 155Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 156: The Grinder

This is only true assuming we have infinite server side threads. In practice, the size of a server side thread pool has asignificant effect on the behaviour of a system. I hope that this discusssion helps you see that there is no direct connectionbetween TPS and test time.

1.4. HTTP

1.4.1. Why do POSTs take longer than GETs?

Dennis Linnell wondered this. This is what he found out:

A network protocol trace revealed that the differences in POST vs. GET times wereattributable to the following:

1. The Grinder (HTTPClient) sends a GET request as a single TCP protocol data unit(PDU), which requires a single acknowledgment (ACK). This is normal andexpected.

2. HTTPClient sends a POST as two PDUs: The first includes everything but theName/Value pairs and requires an ACK; the second includes only theName/Value pairs and also requires an ACK. This is a consequence of theconservative implementation of HTTP/1.1 pipelining in HTTPClient.

3. When the first PDU of the POST arrives at the Windows XP TCP/IP protocolstack on the server, the delayed ACK feature of TCP/IP RFC 1122 kicks in. Ifanother request were to arrive, the stack would send an ACK; otherwise it waitsup to 200 ms. and then sends an ACK. Since, in this case, no other request arrives,the stack delays the ACK. Naturally, I made the changes outlined in the Microsoftknowledge base and it solved the problem for me. Case closed? Maybe not.

4. Still, the question remains, Is the HTTPClient behavior reasonable?. I tracedMicrosoft Internet Explorer 6 (latest patch level) and it sends a POST in one PDUand thus does not invoke the dreaded delayed ACK. On the other hand, Firefox1.0.4 (oddly) requires 3 pairs of PDUs to get the job done. So HTTPClient issomewhere in between.

5. As HTTPClient's behavior is not in violation of the HTTP/1.1 RFC, I have noreason to complain. And, looking at the HTTPClient code, I'm certainly not boldenough to touch it. Still, it doesn't quite model the behavior of the market-leadingweb browser accurately.

1.4.2. For a given webpage, why does a Grinder script take a much longer time to complete than my browsertakes to display the page?

How your web application behaves under non-trivial load and how long it takes to displaya page are two distinct concerns.

The Grinder runs through test script elements sequentially while browsers, such as MSInternet Explorer, can request several elements at the same time using parallel threads.The number of parallel threads depends on the browser type, MS Internet Explorertypically uses 3. Therefore the browser should complete requesting the complete list ofelements from a webpage before the Grinder.

The Grinder

Page 156Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 157: The Grinder

If you are concerned over page download times rather than application behaviour underload then Peter Booth writes:

To understand how a typical browser will download and display *your* pageyou should use a tool like IBM's Page Detailer(http://www.alphaworks.ibm.com/tech/pagedetailer) which intercepts andinstruments all browser activity and visualises this in an easy to interpretfashion. It can highlight tuning opportunities like coalescing JavaScript,reducing GIF count, that are more in the page design than dynamicapplication design arena.

From the IBM Page Detailer Website

IBM Page Detailer places a probe in the Windows Socket Stack to capturedata about timing, size, and data flow, and then it presents the collected datain both graphical and tabular views, clearly showing how the page wasdelivered to the browser.

Tools like IBM Page Detailer are a useful addition to your arsenal when looking at thecomplete picture. The tamperdata (https://addons.mozilla.org/firefox/966/) add-on forFirefox is also worth a look.

1.5. Problems

1.5.1. I've seen The Grinder report negative test times?!

Are you running Linux kernel 2.2.18? Mikael Suokas reports:

In some load situations, on some hardware, older Linux kernels can generate systemtimes that jump backwards. I have experienced this problem on one machine (IntelPentium/150, 128M RAM, Adaptec AHA-2940, SCSI disks) running the stock RedHat Linux 7.0 kernel (2.2.16).

The Grinder occasionally reported negative response times in the -900 to -700 msrange whenever the load became high. Interestingly, the same OS + kernel version onseveral other machines never showed these negative response times. The problem wasnot Grinder or Java specific: a C program polling gettimeofday() also showed systemtime jumping backwards.

Upgrading to kernel 2.2.19 fixed this issue for me. Since the release notes for Linux2.2.18 mention time keeping locking fixes, I don't think that was a coincidence.

Of course, you should upgrade any 2.2.x kernel to 2.2.19 anyway, because because ofthe many security fixes.

1.5.2. What can I do about address in use exceptions on Microsoft Windows machines?

Answer courtesy of Venkat:

The Grinder

Page 157Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 158: The Grinder

Recently, there have been a couple posts from people facing the address in useexception when running The Grinder. I also faced the same issue and here is what Ifound and how I resolved it.

Windows OS TCP-IP system has a parameter that controls the maximum port numberused when an application requests any available user port from the system. Bydefault, ephemeral (that is, short-lived) ports are allocated between the values of 1024and 5000 inclusive. During a Grinder test, if we exceed this limit, we get the addressin use exception. To make it more complicated, there is another parameter that saysonce the application closes a TCP connection, how long the OS will wait beforereclaiming the port for the connection. The default value for this is 4 minutes.

Due to the above, often the default Windows configuration isn't sufficent to run loadtests. To change this, modify the following Registry values under the keyHKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters:

MaxUserPort = dword:00004e20 (20,000 decimal)TcpTimedWaitDelay = dword:0000001e (30 decimal)

The above was sufficient for my testing. Adjust the MaxUserPort to a largernumber if you still run into the exception. Of course, the OS needs to be rebootedonce the setting is changed. MS documentation says the MaxUserPort setting isavailable since Windows NT 3.51 Service Pack 5. I am not sure which versionintroduced the TcpTimedWaitDelayed setting. You can confirm whether yourOS version supports this by looking into the Registry location above.

1.5.3. How do I specify the ephemeral port range?

When a client connects to a TCP server, the client side port uses a temporary emphemeralport. Occasionally users want to set the ephemeral ports that are used for:

• Connections from the agent and worker processes to the console.• Connections created by the TCPProxy.• Connections created by the HTTP Plugin.

The Grinder does not specify the ephemeral port numbers, and instead uses valuesassigned by the operating system's TCP/IP stack. If you wish to control the ephemeralport numbers, please see NCFTP for a good HOWTO.

.

1.5.4. Why can't I create more than ten connections per second on Windows XP?

If you're using Windows XP SP2, this article(http://www.speedguide.net/read_articles.php?id=1497) may provide the answer.

6.1.1.2. 2. The Grinder 2

The Grinder

Page 158Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 159: The Grinder

2.1. General

2.1.1. What do I need to do to set up multicast?

You must set up multicast if you want to use the console with The Grinder 2. It is used tosend signals from the Console to the Grinder processes (start, stop). Multicast is no longernecessary for The Grinder 3.

Multicast addresses lie in the range 224.0.0.0 to 239.255.255.255. Ports lie inthe range 0 to 65535. You should ensure that the address and ports you chose does notclash with other applications running on your LAN. The example files uses the address228.1.1.1:1234:

grinder.receiveConsoleSignals=truegrinder.grinderAddress=228.1.1.1grinder.grinderPort=1234

For most modern TCP stacks, e.g Windows 95/98/NT, Linux, multicast works out of thebox.

Under Linux, you may also need to set up the routing table. Try:

route add -net 224.0.0.0 netmask 240.0.0.0 dev eth0

Some Windows VPN clients (e.g Bay Networks Extranet) interfere with multicast. Youmay need to disable them.

2.1.2. What do I need to do to set up multicast under Windows 2000?

With a stand alone Windows 2000 machine, you might experience similar grief to myself.I found that I could only get multicast to work if my LAN NIC had a carrier and the MSloop-back adapter is not installed (or disabled). The following links contain experiencesthat tally with mine:

• http://www.web3d.org/WorkingGroups/vrtp/dis-java-vrml/hypermail/2001/0021.html(http://web.archive.org/web/20030802192353/www.web3d.org/WorkingGroups/vrtp/dis-java-vrml/hypermail/2001/0021.html)

• http://www.web3d.org/WorkingGroups/vrtp/dis-java-vrml/hypermail/2001/0023.html(http://web.archive.org/web/20030802192353/www.web3d.org/WorkingGroups/vrtp/dis-java-vrml/hypermail/2001/0023.html)

• http://www.web3d.org/WorkingGroups/vrtp/dis-java-vrml/hypermail/2001/0025.html(http://web.archive.org/web/20030802192353/www.web3d.org/WorkingGroups/vrtp/dis-java-vrml/hypermail/2001/0025.html)

The last of these says:

I have been looking for a solution for this problem a longtime without success :-( I found several dummy loop-backIP stacks but none that supports multicast. The solution Ihave been using is to bring along a tiny hub and connectmy laptop to that hub when doing demos. Windows is a bitstupid in the way that it only checks if it has a carrier.

The Grinder

Page 159Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 160: The Grinder

I now use a slightly cheaper/lighter solution; namely I have cropped a network cable shortand twisted my own physical loop-back adapter. Needless to say, if anyone figures outhow to get multicast working with a stand alone W2K machine, I'm more than interested.

Note:Multicast is no longer required by The Grinder 3.

2.1.3. Why do I get HTTPClient.RetryException: Premature EOF encountered errors when I increase HTTPload?

This is a known issue with The Grinder 2 that is fixed in The Grinder 3.0b14. I have noplans to backport the fix to The Grinder 2, but see this e-mail(http://article.gmane.org/gmane.comp.java.grinder.user/893/match=premature+eof) fordetails of how to do this yourself.

2.2. Scripts

2.2.1. How do I simulate different users with The Grinder 2?

You probably want to use a String Bean.

2.2.2. How do I generate POST data with a String Bean?

Sean Kroah writes:

grinder.test0.parameter.post=<getPostData> doesn't work.

I didn't really see this in the docs anywhere so if anyone is interested. I neededdynamic post data and the HTTP plugin post parameter only took a file name. Thekey was that the file can optionally contain a reference to a String Bean method. Itried it on a fluke and it worked.

grinder.test0.parameter.url=<getLoginUrl>grinder.test0.parameter.post=getPostData.dat

getPostData.dat has one line in it:

<getPostData>

This tells The Grinder to call my String Bean method which returns my dynamicPOST content based on the test description. I'm pretty pleased with that.

2.2.3. Why do my POSTs have an extra new line?

An anonymous user reports:

The Grinder 2 appends a new line character to the end of file which contains postinformation for a test. This appears to happen only under Windows.

The Grinder

Page 160Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 161: The Grinder

This is a known issue with The Grinder 2 that is fixed in The Grinder 3.0. I have no plansto backport the fix to The Grinder 2, but see the bug report for how to do this yourself.

6.1.1.3. 3. The Grinder 3

3.1. General

3.1.1. Why does The Grinder take a while to start up?

The Grinder 3 worker process start up time is marginally slower than The Grinder 2.However, it is much slower when you add new Java libraries to your CLASSPATH.

At start up, the Jython engine processes all new Java libraries it finds. When it does thisyou'll see output similar to the following in the window you used to start The Grinder:

*sys-package-mgr*: processing new jar,'E:\src\grinder3\lib\jakarta-oro-2.0.6.jar'*sys-package-mgr*: processing new jar, 'E:\src\grinder3\lib\jython.jar'

Jython caches the result of this processing, so subsequent start up times are muchreduced. You can control the location of the cache directory, see the information onJython installation.

3.1.2. What happened to the JUnit plugin?

The Grinder 2 had a JUnit plugin which allowed JUnit test cases to be called. There is noJUnit plugin in The Grinder 3 but you can call arbitrary Java code so you should be ableto invoke your JUnit test cases directly.

If you have a lot of JUnit test cases, it would be appropriate to wrap up the stepsnecessary to invoke a test case in a Jython library. Contributions to the script gallery arewelcome.

See also this article(http://sourceforge.net/mailarchive/forum.php?thread_id=1687434&forum_id=2650) ongrinder-use.

3.1.3. The Grinder pauses before either threads start sending data, or the TCPProxy carries out a request,when interacting with webservers. Why is this?

Karol Muszynski writes:

Solution:We found that it was a problem with DNS configuration. Solution was to add the webservers host names and ip addresses to the windows hosts file.

Verification:Simply on the command line write "nslookup", then hit enter, and on the following

The Grinder

Page 161Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 162: The Grinder

line type your web server "hostname" and hit enter. If this takes a long time to get anip address, it can mean that this is your problem.

Common locations for hosts files:

Windows: "C:\winnt\system32\drivers\etc\hosts"Unix: "/etc/hosts"

3.1.4. How do I debug worker processes?

Worker processes are separate child processes of agent processes, so they are sometimes abit fiddly to debug. It is possible to run an agent and its workers in a single process. To dothis, please start the agent with:

java -Dgrinder.debug.singleprocess=true net.grinder.Grinder

This puts the worker and agent code in a single process, making it easier to take a threaddump of the worker code.

Use the minimum number of threads that cause the problem. When the hang is observed,take a thread dump and post the output to the grinder-use mail list.

3.1.5. How do I take a thread dump?

A thread dump is a diagnostic report of the state of a Java process. If you report anunresponsive grinder process to one of the mailing lists, you most likely will be asked totake a thread dump. This answer explains how to do that.

First identify the relevant process, and start it from a command line terminal. The outputwill go to the command line, so either set the terminal up so that it has plenty of lines ofscroll-back or redirect the output of the process to a file. For example, you might start anagent process with:

C:\> java net.grinder.Grinder > output.txt

If you are using a UNIX-like operating system, taking the thread dump is simple. Just useps to identify the process id, then issue a kill -3 <pid> to send the process aSIGQUIT signal. You will find the thread dump in the terminal window (or the file towhich you redirected output).

On Windows, use one of the following:

• If it is an agent or console process you are interested in (not a worker process), typeCtrl-Break in the command line terminal.

• If it is a worker process, things are a little more involved. This is because workerprocesses are children of agent processes, and Java does not pass on Ctrl-Breaksignals to child processes. You can work around this problem by running the workersand the agent in the same process, then using Ctrl-Break as above.

The Grinder

Page 162Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 163: The Grinder

Alternatively, you can run a recent version of the Sun JVM (J2SE 5 for Linux or Solaris,J2SE 6 for Windows) and use the jstack command line. The alternative to jstack forthe BEA JRockit JVM is the powerful jrcmd command.

3.2. Scripts

3.2.1. How do I record an HTTP script?

Using the Grinder 3 it is possible to automatically generate a script of your HTTPjourney. To do this you need to make use of the TCPProxy and its associated HTTPTCPProxy filters. The recommended filter to use is "-http" as the other filters are nowdeprecated. See this link for how to use the TCPProxy and the recommended filter.

3.2.2. How do I use standard Python modules in my scripts?

The Python standard library contains a number of very useful modules. For example theregular expression module re, the threading module, and the random module. Touse these modules you must install Jython.

3.2.3. How do I simulate different users with The Grinder 3?

Calum Fitzgerald writes:

This is a method we use to generate random users for tests on web applications.

We store the usernames and passwords in a text file (eg.users.txt) with the format:user,password

We then read in the file and grab the components. The reason for using a file to storethe usernames is that if you are using hundreds or throusands of usernames thenJython/Java has a limitation with arrays over 64KB.

## testRandomise.py#import randomimport string

class TestRandomise:def __init__(self, filename):self._users = []infile = open(filename, "r")

for line in infile.readlines():self._users.append(string.split((line),','))

infile.close()

def getUserInfo(self):"Pick a random (user, password) from the list."return random.choice(self._users)

The Grinder

Page 163Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 164: The Grinder

## Test script. Originally recorded by the TCPProxy.#from testRandomise import TestRandomisetre = TestRandomise("users.txt")

class TestRunner:def __call__(self):

# Get user for this run.(user, passwd) = tre.getUserInfo()

# ...

# Use the user details to log in.

tests[2002].POST('https://host:443/securityservlet',( NVPair('functionname', 'Login'),NVPair('pagename', 'Login'),NVPair('ms_emailAddress', user),NVPair('ms_password', passwd), ))

3.2.4. Is there a way to make my requests depend on previous responses?

Carlos Franco asks:

Is there a way (with Grinder 3) to make that my requestsdepends on the response?

Absolutely. This is one of the compelling reasons to use The Grinder 3 over The Grinder2.

I will make an example. Supose that when I make a requestthe server gives me one list of, for example, cars, and Iwant to edit the information of one, but I don't know itsID, so I have to look into the response and look for it.Can I do that?

I'm assuming HTTP and that the cars come back in a table such as

<td>Jaguar</td><td>1234</td><td>Bentley</td><td>0012</td><td>Rolls Royce</td><td>8323</td><td>Rover</td><td>9876</td>

You can get the text of the HTTP response body with something like:

response = myrequest.GET("/carindex.html")text = response.text

See the script API for other things you can do with the HTTPResponse object.

Now you have the choice of using Java or Python to extract the appropriate ID. Lets do itin Python. Rather than write low level string parsing, we'll use a regular expression for

The Grinder

Page 164Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 165: The Grinder

capturing interesting information. To use the standard re module, you should installJython as described in this FAQ.

Regular expressions are faster if compiled, so we'll do this up front:

# Add to top of scriptimport reexpression = re.compile(r"<td>([\w\s]*)</td>\s*<td>(\d*)</td>")

For eyes unused to magic of regular expressions, this one matches pairs of table cells, thefirst cell containing alphanumerics and white space, the second containing a number. \wmatches an alphanumeric ("word") character, \s matches a white space character, ()captures its contents in a group.

We can then use the regular expression to parse the response:

response = myrequest.GET("/carindex.html")

for car, id in expression.findall(response.text):print car, id

Of course you want to use this to select an ID and use it in a subsequent page. Lets pick arandom one and use it for a new request:

response = myrequest.GET("/carindex.html")

# Import random for this to work.car,id = random.choice(expression.findall(response.text))

request = myrequest.GET("/viewcar?id=%d" % id)

3.2.5. How do I replay a scenario, caching image files but not text files?

The Grinder itself does not cache files. It merely simulates browser behaviour. TheNot-Modified-Since headers are recorded by the TCPProxy filter, so what yourecord with the TCPProxy is the caching behaviour of the browser you are using.

If this is not what you want you could add a function to your test script which takes anHTTPRequest, decides whether it should be considered as "cached" or not, and adds aNot-Modified-Since header as appropriate.

3.2.6. When using the script distribution feature, how can my scripts refer to other distributed files?

The directory of the distributed script is prepended to the Jython system path. You can dosomething like:

import sysf = open("%s/myresources.properties" % sys.path[0])# ...

3.2.7. Why does the Jython re module raise a ValueError when I compile a regular expression?

The Grinder

Page 165Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 166: The Grinder

Each thread that you use to compile regular expressions must import the re module. Ifyou fail to do this you'll get a ValueError like:

ValueError: ('unsupported operand type', 'in')File "D:\opt\jython\jython-2.1\Lib\sre_compile.py", line 141, in

_compileFile "D:\opt\jython\jython-2.1\Lib\sre_compile.py", line 61, in

_compileFile "D:\opt\jython\jython-2.1\Lib\sre_compile.py", line 352, in _codeFile "D:\opt\jython\jython-2.1\Lib\sre_compile.py", line 368, in

compileFile "D:\opt\jython\jython-2.1\Lib\sre.py", line 134, in _compileFile "D:\opt\jython\jython-2.1\Lib\sre.py", line 90, in compileFile "helloworld.py", line 24, in matchAllFile "helloworld.py", line 39, in __call__

The best (most efficient) way to use regular expressions is to compile them in the mainpart of your script. For example:

import re

# Compile once in initialisation threadp = re.compile('a.b', re.IGNORECASE)

class TestRunner:def __call__(self):# use p

Occasionally, the regular expression must be dynamically generated by a worker thread,so this technique doesn't work. In this case, you can import re in every worker thread:

class TestRunner:def __init__(self):import re

def __call__(self):# __call__ is executed by worker threadsp = re.compile('a.b', re.IGNORECASE)# use p

Or equivalently:

from net.grinder.script.Grinder import grinder

def getRE()import rereturn re.compile('a.b', re.IGNORECASE)

class TestRunner:def __call__(self):# __call__ is executed by worker threadsp = getRE()# use p

The Grinder

Page 166Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 167: The Grinder

3.2.8. Why do I get syntax errors when editing my Jython scripts, even when the code looks correct?

Python and Jython use indentation to delimit blocks. You need to indent your scriptconsistently.

Check out these links for more information:

http://diveintopython.org/getting_to_know_python/indenting_code.htmlhttp://www.python.org/doc/current/ref/indentation.html

3.2.9. Whenever I try to run scripts I get "the script file 'grinder.py' does not exist or is not readable"?

You need to define which script should be used. If you do not define it the default scriptname is "grinder.py".

If your script is not called "grinder.py", either add a line to grinder.properties thatspecifies its name, e.g.:

grinder.script: http.py

or specify it on the command line, e.g.:

java -Dgrinder.script=http.py net.grinder.Grinder

or set it in the console. Go to the script tab, navigate to the location of your script. Selectit and then click on the "Set script to run" button.

Finally, it could simply be that the script file is unreadable. Check the file permissions ofthe script and/or attempt to open it in an editor to check if its readable.

3.3. SSL

3.3.1. If I have several suitable certificates in my keystore, how does The Grinder chose between them?

The Grinder relies on the JVM's default KeyManager implementations. This picks acertificate from the store based on SSL negotiation with the server. If there are severalsuitable certificates, the only way to control which is used is to provide your ownKeyManager.

3.3.2. setKeyStoreFile has a parameter for the key store password. What about the pass phrase that protectsthe private key in the key store?

The pass phrases for keys must be the same as the key store password. This is a restrictionof the default KeyManagers. If you don't like this, you can provide your ownKeyManager.

3.3.3. Shouldn't I need to specify a set of certificates for trusted Certificate Authorities?

The Grinder

Page 167Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 168: The Grinder

No. The Grinder does not validate certficates received from the server, so does not need aset of CA certificates.

3.3.4. Can I use the properties javax.net.ssl.keyStore, javax.net.ssl.keyStoreType, andjavax.net.ssl.keyStorePassword to specify a global keystore?

No. The Grinder does not use these properties, primarily because the JSSE does notprovide a way to access its default SSLContext.

3.4. The TCPProxy

3.4.1. Which of the three HTTP TCPProxy filters should I use?

The correct HTTP TCPProxy filter to use is:

-http

This is the latest incarnation of the HTTP TCPProxy filter and combines the best of bothold filters as well as providing some new features.

Key new features over the previous filters (which are deprecated):

- Records to a single script.- Basic Authorization is recorded.- Modular implementation based on an XSLT style sheet.- Requests are grouped together into pages, using simple rules that guess the resourcesbelonging to a page based on the resource type. Pages are instrumented as separate tests.- Name value tokens are identified in URL path parameters and query strings and mappedto script variables. Most session tokens that encoded in URLs will now be handledautomatically.

The deprecated filters are:

-httpplugin-newhttpplugin

3.4.2. What happened to the TCPSniffer?

Starting with The Grinder 3, the TCPSniffer was renamed to the TCPProxy to morecorrectly reflect its nature. The TCPProxy has also been significantly enhanced during thedevelopment of The Grinder 3.

3.4.3. My server works fine with a browser but has errors when I use the HTTP plugin?

This is probably down to one of the following:

• Bugs in your server code. Particularly, those due to the way it handles multipleconcurrent requests. Don't be disheartened, you did good; finding bugs like this is a

The Grinder

Page 168Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 169: The Grinder

key reason to use The Grinder.

• Differences between the HTTP requests that the browser uses and those that TheGrinder sends.

The HTTP plugin sends requests that will have minor differences between those that abrowser send, but which rarely affect server behaviour. Its worth knowing how toexamine the differences with the TCPProxy. First set the TCPProxy as a browser proxyand record the output to a file. Secondly alter your tests so that all requests go via theTCPProxy (the best way is to set the test script connections to direct requests via theTCPProxy acting an HTTP proxy); again record the output to a file. Now grab a coffeeand compare.

3.4.4. I'm getting java.security.InvalidKeyException: Illegal key size or default parameters

This is due to a mismatch of the SSL configuration of the server and that of the JDK youuse to run The Grinder; most likely a clash between domestic and export strength keylengths. You should probably install the Unlimited Strength Jurisdiction Policy Files(http://java.sun.com/javase/downloads/index.jsp) for your Java version from Sun.

3.4.5. What are all those funny ^[[31 characters in the TCPProxy output?

Did you use the -colour switch? If so, the TCPProxy generates escape codes whichwork on ANSI compliant terminals and look very pretty.

For those of you on Windows platforms, this doesn't work with the CMD.EXE windowunless you're using Cygwin. (Of course, if you're using Cygwin you'd use rxvt inpreference :-)).

3.4.6. How do I record a script for a server that uses SSL?

By default, the TCPProxy will use a built-in certificate to handle SSL traffic so you don'tneed to do anything extra, just record a script as normal.

Should you encounter problems with this, especially when using Internet Explorer toconnect through the TCPProxy, you will need to provide a server certificate for TheGrinder. The easiest way to provide a server certificate is to copy the testkeys file fromthe JSSE samples distribution and start the proxy using:

java net.grinder.TCPProxy -keyStore testkeys -keyStorePasswordpassphrase

You can specify your own keystore containing relevant client and root certificates. Tellthe TCPProxy to use the keystore and ssl by using the following parameters wheninvoking the TCPProxy:

-keystore (keystore) -keystorepassword (password) -keystoretype (type)

Should you encounter further problems with your SSL connection you can turn on debug

The Grinder

Page 169Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 170: The Grinder

by using the following switch:

-Djavax.net.debug=ssl

Note:This will generate verbose output.

3.4.7. How to I set different test numbers for httpscript.py and httpscript_tests.py without manually editingthe scripts?

To do this you need to pass the following parameter to the TCPProxy at invokation whenrecording your scripts:

-DHTTPPlugin.initialTest=(integer)

The setting of the initial test number allows you to set different test ranges for scripts withdifferent functions. This is useful if you are running these different scripts concurrentlyand collating the results in the same console window.

For example you could set sales.py to have an initial test number of 2000 andpurchases.py to have an initial test number of 3000 (this assumes that sales.py has lessthan 1000 tests). When you run these scripts concurrently all results in the consolewindow with a test number of 2xxx relate to sales.py and those beginning with 3xxxrelate to purchases.py.

3.5. The Console

3.5.1. Why is the test mean time for a page greater than the total mean?

The totals line only includes data from basic tests, i.e. tests that do not include other tests.It does not include the data from composite tests, i.e. tests that include other tests, such asthose those wrapping pages in HTTP scripts generated by the TCPProxy. The time takenby these page tests is typically quite long since it also includes sleep time and can easilyexceed the average time for basic tests.

Composite test lines in the Results tab have a grey backgorund to emphasise that they aredifferent.

This is done so that the totals line is meaningful, useful, and understandable. If data fromcomposite tests were included in the totals line, some of the values (e.g. average test time)would be meaningless since time would be accounted for twice, once by the basic requesttests, and once by the page test that wrapped the requests.

Data from composite tests is also not included in the totals line in the worker processoutput log files.

6.2. Common

The Grinder

Page 170Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.

Page 171: The Grinder

6.3. The Grinder 2

6.4. The Grinder 3

The Grinder

Page 171Copyright © 2007 Philip Aston, Calum Fitzgerald All rights reserved.