<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	
	xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>Rough Book &#187; Java</title>
	<atom:link href="http://vivin.net/category/nerdy-stuff/computers-nerdy-stuff/programming-and-development-computers-nerdy-stuff/java-programming-and-development-computers-nerdy-stuff/feed/" rel="self" type="application/rss+xml" />
	<link>http://vivin.net</link>
	<description>random musings of just another computer nerd</description>
	<lastBuildDate>Sun, 25 Jul 2010 03:01:03 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Maven project for Generic (n-ary) Tree in Java</title>
		<link>http://vivin.net/2010/03/02/maven-project-for-generic-n-ary-tree-in-java/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=rss</link>
		<comments>http://vivin.net/2010/03/02/maven-project-for-generic-n-ary-tree-in-java/#comments</comments>
		<pubDate>Wed, 03 Mar 2010 04:49:48 +0000</pubDate>
		<dc:creator>vivin</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Programming and Development]]></category>
		<category><![CDATA[data structures]]></category>
		<category><![CDATA[generic trees]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[k-ary trees]]></category>
		<category><![CDATA[n-ary trees]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[software engineering]]></category>
		<category><![CDATA[trees (data structure)]]></category>

		<guid isPermaLink="false">http://vivin.net/?p=1405</guid>
		<description><![CDATA[Guus was kind enough to make a maven project for the Generic Tree. He also fixed an error in my equals method for the GenericTreeNode (I intended to do Object.equals(Object obj) but was doing GenericTreeNode.equals(GenericTreeNode obj). Since it&#8217;s a maven project, you can easily create a jar out of it and add it as a [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://vivin.net/2010/01/30/generic-n-ary-tree-in-java/#comment-1461">Guus</a> was kind enough to make a maven project for the Generic Tree. He also fixed an error in my <em>equals</em> method for the GenericTreeNode (I intended to do <em>Object.equals(Object obj)</em> but was doing <em>GenericTreeNode.equals(GenericTreeNode obj)</em>. Since it&#8217;s a maven project, you can easily create a <em>jar</em> out of it and add it as a dependency to your project. You can download the project <a href="http://vivin.net/pub/generic-tree/generic-tree.zip">here</a>. Thanks Guus!</p>
<br /><a href="http://vivin.net/?p=1405#comments" title="Comments on &quot;Maven project for Generic (n-ary) Tree in Java&quot;"><img src="http://vivin.net/wp-content/plugins/feed-comments-number/image.php?1405" alt="Comments" /></a>]]></content:encoded>
			<wfw:commentRss>http://vivin.net/2010/03/02/maven-project-for-generic-n-ary-tree-in-java/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:thumbnail url="http://vivin.net/wp-content/plugins/feed-comments-number/image.php?1405" />
		<media:content url="http://vivin.net/wp-content/plugins/feed-comments-number/image.php?1405" medium="image">
			<media:title type="html">Comments</media:title>
		</media:content>
	</item>
		<item>
		<title>Generic (n-ary) Tree in Java</title>
		<link>http://vivin.net/2010/01/30/generic-n-ary-tree-in-java/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=rss</link>
		<comments>http://vivin.net/2010/01/30/generic-n-ary-tree-in-java/#comments</comments>
		<pubDate>Sat, 30 Jan 2010 22:05:13 +0000</pubDate>
		<dc:creator>vivin</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Programming and Development]]></category>
		<category><![CDATA[data structures]]></category>
		<category><![CDATA[generic trees]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[k-ary trees]]></category>
		<category><![CDATA[n-ary trees]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[software engineering]]></category>
		<category><![CDATA[trees (data structure)]]></category>

		<guid isPermaLink="false">http://vivin.net/?p=1395</guid>
		<description><![CDATA[Last week I was solving a problem at work that required the use of a Generic (n-ary) Tree. An n-ary tree is a tree where node can have between 0 and n children. There is a special case of n-ary trees where each node can have at most n nodes (k-ary tree). This implementation focuses [...]]]></description>
			<content:encoded><![CDATA[<p>Last week I was solving a problem at work that required the use of a Generic (<em>n</em>-ary) Tree. An <em>n</em>-ary tree is a tree where node can have between 0 and <em>n</em> children. There is a special case of <em>n</em>-ary trees where each node can have <em>at most</em> <em>n</em> nodes (<em>k</em>-ary tree). This implementation focuses on the most general case, where any node can have between 0 and <em>n</em> children. Java doesn&#8217;t have a Tree or Tree Node data structure. I couldn&#8217;t find any third-party implementations either (like in commons-lang). So I decided to write my own.<br />
<span id="more-1395"></span><br />
First, we need to define the node of our tree. Our node has two attributes. One is the data, and another is a List which can contain references to the children of that node:</p>
<div id="attachment_1397" class="wp-caption aligncenter" style="width: 310px;  border: 1px solid #dddddd; background-color: #f3f3f3; padding-top: 4px; margin: 10px; text-align:center; display: block; margin-right: auto; margin-left: auto;"><a href="http://vivin.net/wordpress/wp-content/uploads/2010/01/generictreestructure.png"><img class="size-medium wp-image-1397" title="Generic Tree Node Structure" src="http://vivin.net/wordpress/wp-content/uploads/2010/01/generictreestructure-300x167.png" alt="Structure of a Generic Tree Node" width="300" height="167" /></a><p style=' padding: 0 4px 5px; margin: 0;'  class="wp-caption-text">Structure of a Generic Tree Node</p></div>
<p>The code for a Generic Tree Node looks like this:</p>
<p><strong>GenericTreeNode.java</strong></p>
<pre class="brush: java">
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class GenericTreeNode&lt;T&gt; {

    public T data;
    public List&lt;GenericTreeNode&lt;T&gt;&gt; children;

    public GenericTreeNode() {
        super();
        children = new ArrayList&lt;GenericTreeNode&lt;T&gt;&gt;();
    }

    public GenericTreeNode(T data) {
        this();
        setData(data);
    }

    public List&lt;GenericTreeNode&lt;T&gt;&gt; getChildren() {
        return this.children;
    }

    public int getNumberOfChildren() {
        return getChildren().size();
    }

    public boolean hasChildren() {
        return (getNumberOfChildren() &gt; 0);
    }

    public void setChildren(List&lt;GenericTreeNode&lt;T&gt;&gt; children) {
        this.children = children;
    }

    public void addChild(GenericTreeNode&lt;T&gt; child) {
        children.add(child);
    }

    public void addChildAt(int index, GenericTreeNode&lt;T&gt; child) throws IndexOutOfBoundsException {
        children.add(index, child);
    }

    public void removeChildren() {
        this.children = new ArrayList&lt;GenericTreeNode&lt;T&gt;&gt;();
    }

    public void removeChildAt(int index) throws IndexOutOfBoundsException {
        children.remove(index);
    }

    public GenericTreeNode&lt;T&gt; getChildAt(int index) throws IndexOutOfBoundsException {
        return children.get(index);
    }

    public T getData() {
        return this.data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public String toString() {
        return getData().toString();
    }

    public boolean equals(GenericTreeNode&lt;T&gt; node) {
        return node.getData().equals(getData());
    }

    public int hashCode() {
        return getData().hashCode();
    }

    public String toStringVerbose() {
        String stringRepresentation = getData().toString() + &quot;:[&quot;;

        for (GenericTreeNode&lt;T&gt; node : getChildren()) {
            stringRepresentation += node.getData().toString() + &quot;, &quot;;
        }

        //Pattern.DOTALL causes ^ and $ to match. Otherwise it won&#039;t. It&#039;s retarded.
        Pattern pattern = Pattern.compile(&quot;, $&quot;, Pattern.DOTALL);
        Matcher matcher = pattern.matcher(stringRepresentation);

        stringRepresentation = matcher.replaceFirst(&quot;&quot;);
        stringRepresentation += &quot;]&quot;;

        return stringRepresentation;
    }
}
</pre>
<br /><a href="http://vivin.net/?p=1395#comments" title="Comments on &quot;Generic (n-ary) Tree in Java&quot;"><img src="http://vivin.net/wp-content/plugins/feed-comments-number/image.php?1395" alt="Comments" /></a>]]></content:encoded>
			<wfw:commentRss>http://vivin.net/2010/01/30/generic-n-ary-tree-in-java/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
	
		<media:thumbnail url="http://vivin.net/wordpress/wp-content/uploads/2010/01/generictreestructure-150x150.png" />
		<media:content url="http://vivin.net/wordpress/wp-content/uploads/2010/01/generictreestructure.png" medium="image">
			<media:title type="html">Generic Tree Node Structure</media:title>
			<media:description type="html">Structure of a Generic Tree Node</media:description>
			<media:thumbnail url="http://vivin.net/wordpress/wp-content/uploads/2010/01/generictreestructure-150x150.png" />
		</media:content>
		<media:content url="http://vivin.net/wp-content/plugins/feed-comments-number/image.php?1395" medium="image">
			<media:title type="html">Comments</media:title>
		</media:content>
	</item>
		<item>
		<title>bAdkOde: An Esoteric Language</title>
		<link>http://vivin.net/2009/12/13/badkode-an-esoteric-language/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=rss</link>
		<comments>http://vivin.net/2009/12/13/badkode-an-esoteric-language/#comments</comments>
		<pubDate>Mon, 14 Dec 2009 01:53:43 +0000</pubDate>
		<dc:creator>vivin</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Perl]]></category>
		<category><![CDATA[Programming and Development]]></category>
		<category><![CDATA[Projects]]></category>
		<category><![CDATA[badkode]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[esoteric languages]]></category>
		<category><![CDATA[interpreters]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[parsers]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[programming languages]]></category>
		<category><![CDATA[project]]></category>

		<guid isPermaLink="false">http://vivin.net/?p=1317</guid>
		<description><![CDATA[I&#8217;ve added another project to the projects page. It&#8217;s called bAdkOde, an interpreter for an esoteric language that I designed. The very first incarnation of bAdkOde was written in Java and I actually posted it (or made a blog entry about it) over 8 years ago. For some reason I took it down. Probably because [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve added another project to the projects page. It&#8217;s called <strong>bAdkOde</strong>, an interpreter for an <a title="Esoteric language" href="http://en.wikipedia.org/wiki/Esoteric_language" target="_blank">esoteric language</a> that I designed. The very first incarnation of <strong>bAdkOde</strong> was written in Java and I actually <a title="First post about bAdkOde" href="http://vivin.net/2001/04/07/new-interface-and-badkode/" target="_blank">posted it </a>(or made a blog entry about it) over 8 years ago. For some reason I took it down. Probably because I stopped working on it. Anyway, I redesigned the language and wrote an interpreter for it in Perl about 4 or 5 years ago. I finally got around to posting it. <a title="bAdkOde" href="http://vivin.net/projects/badkode/" target="_self">Check out</a> the project page for more details. Let me know what you think.</p>
<br /><a href="http://vivin.net/?p=1317#comments" title="Comments on &quot;bAdkOde: An Esoteric Language&quot;"><img src="http://vivin.net/wp-content/plugins/feed-comments-number/image.php?1317" alt="Comments" /></a>]]></content:encoded>
			<wfw:commentRss>http://vivin.net/2009/12/13/badkode-an-esoteric-language/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:thumbnail url="http://vivin.net/wp-content/plugins/feed-comments-number/image.php?1317" />
		<media:content url="http://vivin.net/wp-content/plugins/feed-comments-number/image.php?1317" medium="image">
			<media:title type="html">Comments</media:title>
		</media:content>
	</item>
		<item>
		<title>An update to the Grinder testing-framework</title>
		<link>http://vivin.net/2009/12/07/an-update-to-the-grinder-testing-framework/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=rss</link>
		<comments>http://vivin.net/2009/12/07/an-update-to-the-grinder-testing-framework/#comments</comments>
		<pubDate>Mon, 07 Dec 2009 18:34:59 +0000</pubDate>
		<dc:creator>vivin</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Jython]]></category>
		<category><![CDATA[Perl]]></category>
		<category><![CDATA[Programming and Development]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[grinder]]></category>
		<category><![CDATA[jython]]></category>
		<category><![CDATA[performance testing]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[the grinder]]></category>

		<guid isPermaLink="false">http://vivin.net/?p=1258</guid>
		<description><![CDATA[Mukesh alerted me to a problem with the Perl conversion script (that converts the XML produced by the Grinder recorder into a Jython file). It wasn&#8217;t parsing all the parameters in a GET request properly. I&#8217;ve fixed the bug and uploaded a new version of the script. You can download it here.]]></description>
			<content:encoded><![CDATA[<p>Mukesh <a target = "_blank" href = "http://vivin.net/2009/09/16/writing-performance-tests-in-grinder-using-a-framework/#comment-1113">alerted</a> me to a problem with the Perl conversion script (that converts the XML produced by the Grinder recorder into a Jython file). It wasn&#8217;t parsing all the parameters in a GET request properly. I&#8217;ve fixed the bug and uploaded a new version of the script. You can download it <a href='http://vivin.net/wordpress/wp-content/uploads/2009/09/xmlToJython.pl'>here</a>.</p>
<br /><a href="http://vivin.net/?p=1258#comments" title="Comments on &quot;An update to the Grinder testing-framework&quot;"><img src="http://vivin.net/wp-content/plugins/feed-comments-number/image.php?1258" alt="Comments" /></a>]]></content:encoded>
			<wfw:commentRss>http://vivin.net/2009/12/07/an-update-to-the-grinder-testing-framework/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:thumbnail url="http://vivin.net/wp-content/plugins/feed-comments-number/image.php?1258" />
		<media:content url="http://vivin.net/wp-content/plugins/feed-comments-number/image.php?1258" medium="image">
			<media:title type="html">Comments</media:title>
		</media:content>
	</item>
		<item>
		<title>JSTL, instanceof, and hasProperty</title>
		<link>http://vivin.net/2009/12/04/jstl-instanceof-and-hasproperty/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=rss</link>
		<comments>http://vivin.net/2009/12/04/jstl-instanceof-and-hasproperty/#comments</comments>
		<pubDate>Sat, 05 Dec 2009 04:12:30 +0000</pubDate>
		<dc:creator>vivin</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Programming and Development]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[custom tags]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[instanceof]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[jsp]]></category>
		<category><![CDATA[jstl]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[reflection]]></category>
		<category><![CDATA[taglibs]]></category>
		<category><![CDATA[web development]]></category>

		<guid isPermaLink="false">http://vivin.net/?p=1233</guid>
		<description><![CDATA[I&#8217;ve been doing a little bit of JSTL over the past week, especially custom tags. I&#8217;ve written custom tags in Grails before, and there you use actual Groovy code. I guess this was how custom tags used to be written (in Java), but now you can can build your own custom tags using the standard [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been doing a little bit of <a href="http://java.sun.com/products/jsp/jstl/">JSTL</a> over the past week, especially custom tags. I&#8217;ve written custom tags in Grails before, and there you use actual Groovy code. I guess this was how custom tags used to be written (in Java), but now you can can build your own custom tags using the standard tag library. The standard tag library is still pretty useful when it comes to building custom tags. Since it&#8217;s not straight Java, it forces you think really hard about your logic. You don&#8217;t want to put any business or application logic in your tag, and you want to restrict everything to view or presentation logic. A side effect of it not being Java is that if you want to do anything extremely complicated, you&#8217;re probably better off writing the tag in Java (making sure that you don&#8217;t let any business logic creep in).</p>
<p>While writing my own custom tag, I noticed that although <em>instanceof</em> is a reserved word in the JSTL EL (expression language), it is not supported as an operator. The reason I wanted to use the <em>instanceof</em> operator is that I have an attribute that could either be a <em>List</em> or a <em>Map</em> and depending on the type, I wanted to do different things.</p>
<p>Another thing I was trying to do, was to inspect the incoming object to see if it had a certain property (reflection). JSTL uses reflection so that you can access the properties of an object via dot notation, if they follow the JavaBean naming-convention. However, there was no way for me to see if an object had a certain property. To solve both these problems, I wrote my own JSTL functions.<br />
<span id="more-1233"></span><br />
The first function I wrote was one that performed the <em>instanceof</em> operation. I created a class called <em>TagUtils</em> that contained static methods that returned primitive types (like boolean, String, or Integer). <em>instanceof</em> presents a special challenge because you can&#8217;t have the second parameter be dynamic. For example, assuming that you have an object named <em>acura</em> that&#8217;s an instance of the class <em>Car</em> and a String named <em>className</em> that holds the value &#8220;<em>Car</em>&#8220;, you can&#8217;t do <em><strong>acura</strong> instanceof <strong>className</strong></em>. You have to use reflection and create a <em>Class</em> object using the <em>forName</em> static method and then check to see if <em>acura</em> is an instance by using the <em>isInstance</em> method.</p>
<p>The second function I wrote is the <em>hasProperty</em> function which uses reflection to check whether the supplied object has a particular property. To be precise, I don&#8217;t explicitly check for the existence of the object directly. Rather, I check and see if there is a getter for that property. For example, if the property is called <em>firstName</em>, then there should be a getter called <em>getFirstName()</em>.</p>
<p>The code for the functions looks like this:</p>
<pre class="brush: java">
package util.tag;

public class TagUtils {

    //Checks to see if Object &#039;o&#039; is an instance of the class in the string &quot;className&quot;
    public static boolean instanceOf(Object o, String className) {
        boolean returnValue;

        try {
            returnValue = Class.forName(className).isInstance(o);
        }

        catch(ClassNotFoundException e) {
            returnValue = false;
        }

        return returnValue;
    }

    //Checks to see if Object &#039;o&#039; has a property specified in &quot;propertyName&quot;
    public static boolean hasProperty(Object o, String propertyName) {
        boolean methodFound = false;
        int i = 0;

        Class myClass = o.getClass();
        String methodName = &quot;get&quot; + propertyName.toUpperCase().charAt(0) + propertyName.substring(1);
        Method[] methods = myClass.getMethods();

        while(i &lt; methods.length &amp;&amp; !methodFound) {
            methodFound = methods[i].getName().compareTo(methodName) == 0;
            i++;
        }

        return methodFound;
    }
}
</pre>
<p><del datetime="2009-12-12T21:35:27+00:00">For the <em>hasProperty</em> function, you can see that I used the <em>getMethod</em> method. The second argument is an array of parameter types. Since the getter accepts no parameters, we pass in an empty array (of type <em>Class</em>).</del></p>
<p>Using exceptions in business logic is really bad, because it slows down the JVM. So the rendering will also become really slow if there are a lot of exceptions being thrown. To fix this problem, I used the <em>getMethods</em> method, which returns an array of <em>Method</em> objects. I then search the array to see if the getter I want, exists.</p>
<p>After you create your functions, you have to put them in a <em>tld</em> file. I created a one called <em>tagutils.tld</em> that I put in <em>WEB-INF</em>:</p>
<pre class="brush: xml">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;taglib xmlns=&quot;http://java.sun.com/xml/ns/javaee&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
    xsi:schemaLocation=&quot;http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd&quot;
    version=&quot;2.1&quot;&gt;
    &lt;tlib-version&gt;1.0&lt;/tlib-version&gt;
    &lt;short-name&gt;function&lt;/short-name&gt;
    &lt;uri&gt;http://tagutils&lt;/uri&gt;
    &lt;function&gt;
        &lt;name&gt;instanceOf&lt;/name&gt;
        &lt;function-class&gt;util.tag.TagUtils&lt;/function-class&gt;
        &lt;function-signature&gt;boolean instanceOf(java.lang.Object, java.lang.String)&lt;/function-signature&gt;
    &lt;/function&gt;
    &lt;function&gt;
        &lt;name&gt;hasProperty&lt;/name&gt;
        &lt;function-class&gt;util.tag.TagUtils&lt;/function-class&gt;
        &lt;function-signature&gt;boolean hasProperty(java.lang.Object, java.lang.String)&lt;/function-signature&gt;
    &lt;/function&gt;
&lt;/taglib&gt;
</pre>
<p>Once you create that file, you can use your new functions in your JSP files. To test the <em>hasProperty</em> function, I created a simple class called <em>Person</em>:</p>
<pre class="brush: java">
package domain;

public class Person {
    private String firstName;
    private String lastName;

    public Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public String toString() {
        return firstName + &quot; &quot; + lastName;
    }
}
</pre>
<p>Now I put it all together:</p>
<pre class="brush: xhtml">
&lt;%@ taglib prefix=&quot;function&quot; uri=&quot;http://tagutils&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;

&lt;%@ page import=&quot;java.util.Map&quot; %&gt;
&lt;%@ page import=&quot;java.util.LinkedHashMap&quot; %&gt;
&lt;%@ page import=&quot;java.util.List&quot; %&gt;
&lt;%@ page import=&quot;java.util.ArrayList&quot; %&gt;
&lt;%@ page import=&quot;domain.Person&quot; %&gt;
&lt;%
    Map&lt;String, String&gt; myMap = new LinkedHashMap&lt;String, String&gt;();
    pageContext.setAttribute(&quot;myMap&quot;, myMap);

    List&lt;Person&gt; myList = new ArrayList&lt;Person&gt;();
    pageContext.setAttribute(&quot;myList&quot;, myList);

    pageContext.setAttribute(&quot;person&quot;, new Person(&quot;Holly&quot;, &quot;Hoo&quot;));
%&gt;

myMap is an instance of Map:
&lt;b&gt;
 &lt;c:choose&gt;
   &lt;c:when test=&quot;${function:instanceOf(myMap, &#039;java.util.Map&#039;)}&quot;&gt;true&lt;/c:when&gt;
   &lt;c:otherwise&gt;false&lt;/c:otherwise&gt;
  &lt;/c:choose&gt;
&lt;/b&gt;&lt;br/&gt;
myList is an instance of List:
&lt;b&gt;
 &lt;c:choose&gt;
  &lt;c:when test=&quot;${function:instanceOf(myList, &#039;java.util.List&#039;)}&quot;&gt;true&lt;/c:when&gt;
  &lt;c:otherwise&gt;false&lt;/c:otherwise&gt;
 &lt;/c:choose&gt;
&lt;/b&gt;&lt;br/&gt;&lt;br/&gt;

myMap is an instance of List:
&lt;b&gt;
 &lt;c:choose&gt;
  &lt;c:when test=&quot;${function:instanceOf(myMap, &#039;java.util.List&#039;)}&quot;&gt;true&lt;/c:when&gt;
  &lt;c:otherwise&gt;false&lt;/c:otherwise&gt;&lt;/c:choose&gt;
&lt;/b&gt;&lt;br/&gt;
myList is an instance of Map:
&lt;b&gt;
 &lt;c:choose&gt;
  &lt;c:when test=&quot;${function:instanceOf(myList, &#039;java.util.Map&#039;)}&quot;&gt;true&lt;/c:when&gt;
  &lt;c:otherwise&gt;false&lt;/c:otherwise&gt;
 &lt;/c:choose&gt;
&lt;/b&gt;&lt;br/&gt;&lt;br/&gt;

Person has a property called firstName:
&lt;b&gt;
 &lt;c:choose&gt;
  &lt;c:when test=&quot;${function:hasProperty(person, &#039;firstName&#039;)}&quot;&gt;true&lt;/c:when&gt;
  &lt;c:otherwise&gt;false&lt;/c:otherwise&gt;
 &lt;/c:choose&gt;
&lt;/b&gt;&lt;br/&gt;
Person has a property called lastName:
&lt;b&gt;
 &lt;c:choose&gt;
  &lt;c:when test=&quot;${function:hasProperty(person, &#039;lastName&#039;)}&quot;&gt;true&lt;/c:when&gt;
  &lt;c:otherwise&gt;false&lt;/c:otherwise&gt;
 &lt;/c:choose&gt;
&lt;/b&gt;&lt;br /&gt;
Person has a property called id:
&lt;b&gt;
 &lt;c:choose&gt;
  &lt;c:when test=&quot;${function:hasProperty(person, &#039;id&#039;)}&quot;&gt;true&lt;/c:when&gt;
  &lt;c:otherwise&gt;false&lt;/c:otherwise&gt;
 &lt;/c:choose&gt;
&lt;/b&gt;&lt;br /&gt;
</pre>
<p><em><strong>Note</strong>: I have used a scriptlet in the above example simple to demonstrate the usage of the functions. The use of scriptlets in JSP is bad practice since it probably means that you are putting your business logic in your JSP. The proper place for such code is in a Controller or a Service.</em></p>
<p>As you can see from the above example, the usage is pretty simple. If everything went well, the output should look something like this:</p>
<blockquote><p>
myMap is an instance of Map: <strong>true</strong><br />
myList is an instance of List: <strong>true</strong></p>
<p>myMap is an instance of List: <strong>false</strong><br />
myList is an instance of Map: <strong>false</strong></p>
<p>Person has a property called firstName: <strong>true</strong><br />
Person has a property called lastName: <strong>true</strong><br />
Person has a property called id: <strong>false</strong>
</p></blockquote>
<p><em><b>Note</b>: Using the JSP above, the <em>true</em> and <em>false</em> values will actually be on separate lines. I broke the code up in my example for readability. In my actual code, The whole logic expression is in one line.</em></p>
<p>One important thing to note is that when you use the <em>instanceOf</em> function, you have to provide the <strong>fully-qualified class-name</strong>. Otherwise, the method will return <em>false</em>.</p>
<h4>References</h4>
<ol>
<li><a href="http://stackoverflow.com/questions/1076315/jstl-check-if-property-doesnt-exist"> JSTL: check if property doesn&#8217;t exist (stackoverflow.com)</a></li>
<li><a href="http://mindprod.com/jgloss/instanceof.html">instanceof: Java Glossary (mindprod.com)</a></li>
<li><a href="http://java.sun.com/javase/6/docs/api/java/lang/Class.html">Class (Java Platform SE 6)</a></li>
</ol>
<br /><a href="http://vivin.net/?p=1233#comments" title="Comments on &quot;JSTL, instanceof, and hasProperty&quot;"><img src="http://vivin.net/wp-content/plugins/feed-comments-number/image.php?1233" alt="Comments" /></a>]]></content:encoded>
			<wfw:commentRss>http://vivin.net/2009/12/04/jstl-instanceof-and-hasproperty/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:thumbnail url="http://vivin.net/wp-content/plugins/feed-comments-number/image.php?1233" />
		<media:content url="http://vivin.net/wp-content/plugins/feed-comments-number/image.php?1233" medium="image">
			<media:title type="html">Comments</media:title>
		</media:content>
	</item>
		<item>
		<title>Writing Performance Tests in Grinder using a Framework</title>
		<link>http://vivin.net/2009/09/16/writing-performance-tests-in-grinder-using-a-framework/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=rss</link>
		<comments>http://vivin.net/2009/09/16/writing-performance-tests-in-grinder-using-a-framework/#comments</comments>
		<pubDate>Thu, 17 Sep 2009 02:26:47 +0000</pubDate>
		<dc:creator>vivin</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Jython]]></category>
		<category><![CDATA[Programming and Development]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[grinder]]></category>
		<category><![CDATA[jython]]></category>
		<category><![CDATA[performance testing]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[the grinder]]></category>

		<guid isPermaLink="false">http://vivin.net/?p=937</guid>
		<description><![CDATA[Before reading this tutorial (if you haven&#8217;t already), please took at look at my last two tutorials (Performance Testing using The Grinder and Anatomy of a Grinder test-script). In this tutorial I&#8217;ll talk about easily writing Grinder test-scripts using a framework I designed. As a disclaimer, I&#8217;d like to point out that I&#8217;m not a [...]]]></description>
			<content:encoded><![CDATA[<p>Before reading this tutorial (if you haven&#8217;t already), please took at look at my last two tutorials (<a href="http://vivin.net/2009/07/27/performance-testing-using-the-grinder/">Performance Testing using The Grinder</a> and <a href="http://vivin.net/2009/08/14/anatomy-of-a-grinder-testscript/">Anatomy of a Grinder test-script</a>). In this tutorial I&#8217;ll talk about easily writing Grinder test-scripts using a framework I designed. As a disclaimer, I&#8217;d like to point out that I&#8217;m not a Python programmer and therefore certain things may not be very python-esque. If that&#8217;s the case, I apologize. My personal opinion is that this framework is especially useful (of course, since I wrote it <img src='http://vivin.net/wordpress/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> ) for web applications where you have a already have a lot of test data. In that case, you can simply record all your discrete tasks once and then construct different scenarios with them. But if you feel differently and have some constructive criticism, I do look forward to hearing from you! Also, if you&#8217;d like to try out the framework I&#8217;ve got a tarball and a zip file available for download on the very last page.<br />
<span id="more-937"></span><br />
<strong><span style="text-decoration: underline;">The motivation for a framework</span></strong></p>
<p>Typically during performance-testing, you want to test different kinds of scenarios. These scenarios are made up of discrete tasks (also known as transactions), for example, consider a scenario where a user logs in, searches for a person, and then logs out. In this scenario, you have three separate tasks: logging in, searching for a person, and logging out. Using Grinder&#8217;s TCPProxy tool, you can easily record this scenario and you can also parameterize it. What happens when you want to record another scenario? Say, one that involves logging in, searching for a person, adding a new person, and logging out? Sure, you can go ahead and record that, and even parameterize it. But consider the fact that the new scenario is a super-set of the old scenario. It has three tasks in common with the first scenario. What if there was a way to create new scenarios from scratch, not by recording, but by using previously recorded tasks? This way you would only have to record information once, and after that, you can reuse it. To do this, we need to first understand how to identify tasks within a recorded Grinder test-script. Using this as our foundation, we can figure out how to pull out the tasks into discrete units, which we can later reuse.</p>
<p><strong><span style="text-decoration: underline;">The Relationship between Requests and Tasks</span></strong></p>
<p>In the previous tutorial, I went over the anatomy of a recorded Grinder test-script. There, we saw that the script defines a bunch of requests, and then has methods that correspond to each recorded page. In each method, the script executes a bunch of requests. Initially you might think that we would simply pull out these individual methods. However, that is not the case. A single task can involve more than one recorded page. For example, the act of logging into the app involves accessing the login page, then logging in, and then hitting whatever page the login process drops you into. This involves (at the very least) three pages. In fact, the relationship between requests, pages, and tasks looks something like this:</p>
<div id="attachment_942" class="wp-caption aligncenter" style="width: 563px;  border: 1px solid #dddddd; background-color: #f3f3f3; padding-top: 4px; margin: 10px; text-align:center; display: block; margin-right: auto; margin-left: auto;"><a href="http://vivin.net/wordpress/wp-content/uploads/2009/09/Relation_between_requests_and_Tasks.png"><img class="size-large wp-image-942" title="Relation_between_requests_and_Tasks" src="http://vivin.net/wordpress/wp-content/uploads/2009/09/Relation_between_requests_and_Tasks-1024x384.png" alt="The relation between requests and tasks" width="553" height="207" /></a><p style=' padding: 0 4px 5px; margin: 0;'  class="wp-caption-text">The relation between requests and tasks</p></div>
<p>Here, you can see that a set of unique requests belong to a recorded page, and then a set of recorded pages belong to a unique task. Finally, a set of tasks belong to a scenario. What we want to do is pull out the individual tasks so that we can reuse them to create different scenarios. To do this, we will still be using Grinder&#8217;s TCPProxy tool (at least to record the scenario), but our resulting script will not look like the typical Grinder test-script. It will, instead, conform to the testing framework.</p>
<br /><a href="http://vivin.net/?p=937#comments" title="Comments on &quot;Writing Performance Tests in Grinder using a Framework&quot;"><img src="http://vivin.net/wp-content/plugins/feed-comments-number/image.php?937" alt="Comments" /></a>]]></content:encoded>
			<wfw:commentRss>http://vivin.net/2009/09/16/writing-performance-tests-in-grinder-using-a-framework/feed/</wfw:commentRss>
		<slash:comments>31</slash:comments>
	
		<media:thumbnail url="http://vivin.net/wordpress/wp-content/uploads/2009/09/Relation_between_requests_and_Tasks-150x150.png" />
		<media:content url="http://vivin.net/wordpress/wp-content/uploads/2009/09/Relation_between_requests_and_Tasks.png" medium="image">
			<media:title type="html">Relation_between_requests_and_Tasks</media:title>
			<media:description type="html">The relation between requests and tasks</media:description>
			<media:thumbnail url="http://vivin.net/wordpress/wp-content/uploads/2009/09/Relation_between_requests_and_Tasks-150x150.png" />
		</media:content>
		<media:content url="http://vivin.net/wp-content/plugins/feed-comments-number/image.php?937" medium="image">
			<media:title type="html">Comments</media:title>
		</media:content>
	</item>
		<item>
		<title>Anatomy of a Grinder test-script</title>
		<link>http://vivin.net/2009/08/14/anatomy-of-a-grinder-testscript/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=rss</link>
		<comments>http://vivin.net/2009/08/14/anatomy-of-a-grinder-testscript/#comments</comments>
		<pubDate>Fri, 14 Aug 2009 21:42:23 +0000</pubDate>
		<dc:creator>vivin</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Jython]]></category>
		<category><![CDATA[Programming and Development]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[grinder]]></category>
		<category><![CDATA[jython]]></category>
		<category><![CDATA[performance testing]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[the grinder]]></category>

		<guid isPermaLink="false">http://vivin.net/wordpress/?p=412</guid>
		<description><![CDATA[This is my second post regarding Grinder. In this post I&#8217;ll go over the anatomy of a recorder Grinder test-script. If you haven&#8217;t read my previous post, please take a look at it. Otherwise this post won&#8217;t make much sense! High-level structure of a Grinder script The high-level structure of a recorded Grinder-test-script looks like [...]]]></description>
			<content:encoded><![CDATA[<p>This is my second post regarding <a target = "_blank" href = "http://grinder.sourceforge.net/">Grinder</a>. In this post I&#8217;ll go over the anatomy of a recorder Grinder test-script. If you haven&#8217;t read my <a target = "_blank" href = "http://vivin.net/2009/07/27/performance-testing-using-the-grinder/">previous post</a>, please take a look at it. Otherwise this post won&#8217;t make much sense!</p>
<p><strong><span style = "text-decoration:underline">High-level structure of a Grinder script</span></strong></p>
<p>The high-level structure of a recorded Grinder-test-script looks like this:</p>
<pre class="brush: python">
[
...
import statements
...
]

[
...
header definitions
...
]

[
...
url definitions
...
]

[
...
request and test definitions
...
]

class TestRunner:
     [
     ...
     method definitions - a method is defined for each recorded page
     ...
     ]

     def __call__(self):
     [
     ...
     calls to defined methods, which actually runs the requests
     ...
     ]

     [ utility function (I&#039;ll go over this later) ]

     [
     ...
     calls to utility function to wrap/instrument tests (I&#039;ll go over this later)
     ...
     ]
</pre>
<p><span id="more-412"></span><br />
Let&#8217;s go over each of these sections one by one:</p>
<p><strong><span style = "text-decoration:underline">Import statements</span></strong></p>
<p>If you know Java, then this section is pretty much self-explanatory. Here you import the stuff you need, to make the tests work. By default, the following libraries are imported:</p>
<pre class="brush: python">
from net.grinder.script import Test
from net.grinder.script.Grinder import grinder
from net.grinder.plugin.http import HTTPPluginControl, HTTPRequest
from HTTPClient import NVPair
</pre>
<p>The first two <span style = "font-family:courier new">import</span> statements import the <span style = "font-family:courier new">Test</span> and <span style = "font-family:courier new">grinder</span> objects, which give you access to Grinder&#8217;s testing framework, and the grinder environment. The next two <span style = "font-family:courier new">import</span> statements import some utility classes which perform HTTP Requests. The NVPair class lets you organize parameters and values for POST requests into name-value pairs.</p>
<p><strong><span style = "text-decoration:underline">Header definitions</span></strong></p>
<p>When the recorder runs, it records every request that the browser makes. After that, it goes through all the requests and generates a set of unique headers. These headers are then used to create the request objects (which we&#8217;ll look at later). The generated code looks like this:</p>
<pre class="brush: python">
connectionDefaults = HTTPPluginControl.getConnectionDefaults()
httpUtilities = HTTPPluginControl.getHTTPUtilities()

# To use a proxy server, uncomment the next line and set the host and port.
# connectionDefaults.setProxyServer(&quot;localhost&quot;, 8001)

# These definitions at the top level of the file are evaluated once,
# when the worker process is started.

connectionDefaults.defaultHeaders = \
  ( NVPair(&#039;Accept-Language&#039;, &#039;en-us,en;q=0.5&#039;),
    NVPair(&#039;Accept-Charset&#039;, &#039;ISO-8859-1,utf-8;q=0.7,*;q=0.7&#039;),
    NVPair(&#039;Accept-Encoding&#039;, &#039;gzip,deflate&#039;),
    NVPair(&#039;User-Agent&#039;, &#039;Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.11) Gecko/2009060308 Ubuntu/9.04 (jaunty) Firefox/3.0.11&#039;), )

headers0= \
  ( NVPair(&#039;Accept&#039;, &#039;text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8&#039;),
    NVPair(&#039;Referer&#039;, &#039;https://local.infusiontest.com:8443/?msg=You%27ve+been+logged+out+-+thanks+for+playing%21&amp;amp;amp;amp;amp;notification=You%27ve+been+logged+out+-+thanks+for+playing%21&#039;), )

headers1= \
  ( NVPair(&#039;Accept&#039;, &#039;text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8&#039;),
    NVPair(&#039;Referer&#039;, &#039;https://local.infusiontest.com:8443/Admin/home.jsp&#039;), )
</pre>
<p>The first statement creates an HTTPPluginConnection object, which lets you control the behavior of the connection. The next statement creates an instance of an HTTPUtilities object which provides access to various utility methods (you&#8217;ll see examples of this later). The next two commented lines let you know that you can proxy these requests through a proxy server if you wish. The two comments after that let you know that these definitions are evaluated once for each process. Essentially, all data defined at the top of a script, outside the class, are shared between threads belonging to a single worker process.</p>
<p>In the next few lines, the script actually defines the headers. First, it sets up the default headers that are used by each request (things like the User-Agent, Charset, and Encoding). After that, it goes through and defines the headers. As you can see, these headers are basically tuples of <span style = "font-family:courier new">NVPair</span> objects. One <span style = "font-family:courier new">NVPair</span> defines the <span style = "font-family:courier new">Accept</span> parameter, and the other defines the <span style = "font-family:courier new">Referer</span>. Those with an eye for detail will notice that there seems to be an extra comma at the end of the tuple definition. This isn&#8217;t a problem in Python because the parser will treat the last element as an empty element.</p>
<p><strong><span style = "text-decoration:underline">URL definitions</span></strong></p>
<p>In the next section of the script, you&#8217;ll find URL definitions, which look like this:</p>
<pre class="brush: python">
url0 = &#039;https://local.infusiontest.com:8443&#039;
</pre>
<p>In this example, there is only one URL. But let&#8217;s say that the app you&#8217;re testing hits a few different URL&#8217;s during the scenario that you&#8217;re recording. Then the script will have a definition for each unique URL that the recorder encountered. The definitions are of the form <span style = "font-family:courier new">urlN = &#8220;protocol://recorded.url.here&#8221;</span>. The combination of a URL and Header is what defines a request (more on that in the next section).</p>
<p><strong><span style = "text-decoration:underline">Request and test definitions</span></strong></p>
<p>In the next section of the script, we define the actual requests that we&#8217;re going to use for our tests:</p>
<pre class="brush: python">
# 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, &#039;POST processLogin.jsp&#039;).wrap(request101)

request102 = HTTPRequest(url=url0, headers=headers0)
request102 = Test(102, &#039;GET home.jsp&#039;).wrap(request102)

request201 = HTTPRequest(url=url0, headers=headers1)
request201 = Test(201, &#039;GET popUpTask.jsp&#039;).wrap(request201)

request301 = HTTPRequest(url=url0, headers=headers2)
request301 = Test(301, &#039;POST calendarBackend.jsp&#039;).wrap(request301)
</pre>
<p>The comments give a terse explanation of what&#8217;s happening, but allow me to elaborate. First, you create an instance of an <span style = "font-family:courier new">HTTPRequest</span> object using the previously defined URL&#8217;s and Headers. An important thing to note here is that there is a naming scheme for the requests, which is of the form <span style = "font-family:courier new">request[Page Number][Request Number]</span>. So, for example, <span style = "font-family:courier new">request105</span> means the <span style = "font-family:courier new">fifth</span> request in the <span style = "font-family:courier new">first</span> recorded page, and <span style = "font-family:courier new">request1101</span> means the <span style = "font-family:courier new">first</span> request in the <span style = "font-family:courier new">eleventh</span> recorded page (yes, there is an inherent ambiguity, because 1101 could also mean the <span style = "font-family:courier new">one hundred and first</span> request in the <span style = "font-family:courier new">first</span> recorded page. But if you have a hundred and one unique requests per page, then you have a problem).</p>
<p>The next statement is the most important one. The way Grinder records test statistics is by proxying whatever you want to test, through the <span style = "font-family:courier new">Test</span> object. What this means is that the <span style = "font-family:courier new">Test</span> object is a wrapper around the object you want to test. You can still treat the wrapped object like the original object, but under the hood, access to the methods of that object are proxied through the <span style = "font-family:courier new">Test</span> object. This way, Grinder can record statistics. The picture might help understand what&#8217;s going on:</p>
<p style = "text-align:center">
<a target = "_blank" href = "http://vivin.net/pub/grinder/Proxying_through_Test_object.png"><img title = "Proxying through Test object" alt = "Proxying through Test object" class = ""   src = "http://vivin.net/php/image.php?source=/home/vivin/www/pub/grinder/Proxying_through_Test_object.png&amp;type=png&amp;height=0.5&amp;width=0.5" /></a>
</p>
<p>On the top you can see how a regular request behaves when accessing the <span style = "font-family:courier new">GET</span> method. It&#8217;s a straight call. The bottom picture shows you what happens when you wrap your request object with a <span style = "font-family:courier new">Test</span> object. Now, the call to <span style = "font-family:courier new">GET</span> isn&#8217;t made on the actual request object. It&#8217;s proxied through the <span style = "font-family:courier new">Test</span> object, which has an attribute called <span style = "font-family:courier new">__target__</span> (which is the actual request object). Once it gets to the actual request object, the actual <span style = "font-family:courier new">GET</span> method is called. The reason Grinder does this is so that it can collect statistics on actions performed by an object.</p>
<p><strong><span style = "text-decoration:underline">Method definitions</span></strong></p>
<p>After the request objects have been defined, we come to the actual <span style = "font-family:courier new">TestRunner</span> class. This is where you define methods that correspond to each recorded page:</p>
<pre class="brush: python">
# A method for each recorded page.
  def page1(self):
    &quot;&quot;&quot;GET / (requests 101-104).&quot;&quot;&quot;
    result = request101.GET(&#039;/&#039;)

    grinder.sleep(307)

    # Expecting 302 &#039;Moved Temporarily&#039;
    request102.GET(&#039;/slices/infusion-crm.gif&#039;)

    grinder.sleep(29)

    # Expecting 302 &#039;Moved Temporarily&#039;
    request103.GET(&#039;/login/defaultLogin.jsp&#039;)
    self.token_msg = \
      httpUtilities.valueFromLocationURI(&#039;msg&#039;) # &#039;Whoa,+easy+there+tiger.+You\&#039;re+gonna+nee...&#039;

    grinder.sleep(24)
    request104.GET(&#039;/index.jsp&#039; +
      &#039;?msg=&#039; +
      self.token_msg)

    return result

  def page2(self):
    &quot;&quot;&quot;POST processLogin.jsp (requests 201-202).&quot;&quot;&quot;

    # Expecting 302 &#039;Moved Temporarily&#039;
    result = request201.POST(&#039;/login/processLogin.jsp&#039;,
      ( NVPair(&#039;username&#039;, &#039;vivin&#039;),
        NVPair(&#039;password&#039;, &#039;abAB12!@&#039;),
        NVPair(&#039;Login&#039;, &#039;Login&#039;), ),
      ( NVPair(&#039;Content-Type&#039;, &#039;application/x-www-form-urlencoded&#039;), ))

    grinder.sleep(72)
    request202.GET(&#039;/Admin/home.jsp&#039;)

    return result
</pre>
<p>In the script, you have a method for each recorded page, which are called page1, page2, and so forth. Within each page, there are one or more GET and/or POST requests made via the request objects, which simulate the transactions made by a single page. Let&#8217;s take a look at the <span style = "font-family:courier new">page1</span> method. In this method you can see that it makes three requests. The first two requests are simple requests without any parameters. The last request, however, is a GET request with a querystring. The script uses the <span style = "font-family:courier new">httpUtilities</span> variable (defined earlier, at the beginning of the script) to get the value of the <span style = "font-family:courier new">msg</span> parameter from the URI, and constructs the querystring for the request.</p>
<p>The <span style = "font-family:courier new">page2</span> method provides an example of a POST request. The values to the POST are supplied in name-value pairs using the <span style = "font-family:courier new">NVPair</span> object. Also notice the calls to <span style = "font-family:courier new">grinder.sleep()</span>. These calls simulate &#8220;think time&#8221;.</p>
<p>In both methods, you can see that the return value from the first request is assigned to a variable called <span style = "font-family:courier new">result</span>. The method then returns this variable (which contains statistics from Grinder).</p>
<p><strong><span style = "text-decoration:underline">Utility method, and calls to the utility method</span></strong></p>
<p>I&#8217;m going to go out of order here and talk about the <span style = "font-family:courier new">instrumentMethod</span> utility method before I talk about the <span style = "font-family:courier new">__call__</span> method. The <span style = "font-family:courier new">instrumentMethod</span> and the calls to that method look like this:</p>
<pre class="brush: python">
def instrumentMethod(test, method_name, c=TestRunner):
  &quot;&quot;&quot;Instrument a method with the given Test.&quot;&quot;&quot;
  unadorned = getattr(c, method_name)
  import new
  method = 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, &#039;Page 1&#039;), &#039;page1&#039;)
instrumentMethod(Test(200, &#039;Page 2&#039;), &#039;page2&#039;)
instrumentMethod(Test(300, &#039;Page 3&#039;), &#039;page3&#039;)
instrumentMethod(Test(400, &#039;Page 4&#039;), &#039;page4&#039;)
instrumentMethod(Test(500, &#039;Page 5&#039;), &#039;page5&#039;)
instrumentMethod(Test(600, &#039;Page 6&#039;), &#039;page6&#039;)
instrumentMethod(Test(700, &#039;Page 7&#039;), &#039;page7&#039;)
</pre>
<p>The <span style = "font-family:courier new">instrumentMethod</span> method is another way to proxy calls through the Test object, except, instead of proxying an object, it proxies a method. To accomplish this, <span style = "font-family:courier new">instrumentMethod</span> performs some metaprogramming. Metaprogramming lets you modify a class&#8217;s properties and methods during runtime. You can add, remove, and modify existing methods and properties. The <span style = "font-family:courier new">instrumentMethod</span> method has three arguments. The first argument is a test object, the second is the method name (as a string), and the third argument is a default argument that is set to <span style = "font-family:courier new">TestRunner</span>. First, <span style = "font-family:courier new">instrumentMethod</span> gets a reference to a <span style = "font-family:courier new">TestRunner</span> class method identified by <span style = "font-family:courier new">method_name</span> (assigned to the variable <span style = "font-family:courier new">unadorned</span>). Then it creates a new method (using <span style = "font-family:courier new">new.instancemethod</span>) which has the test object wrapped around the original method. <span style = "font-family:courier new">instrumentMethod</span> then assigns this new method back to the <span style = "font-family:courier new">TestRunner</span> class, effectively proxying all calls to the original method through the <span style = "font-family:courier new">Test</span> object.</p>
<p><strong><span style = "text-decoration:underline">__call__ method</span></strong></p>
<p>In Python any method that implements the <span style = "font-family:courier new">__call__</span> is _callable_, which basically means that this function is called when you call the class instance as a function. In the context of the this script, it just means that this is where all the testing starts off. In the <span style = "font-family:courier new">__call__</span> method you can see that there are calls to all the recorded page methods (along with think time), and this essentially runs the recorded test.</p>
<p><strong><span style = "text-decoration:underline">Conclusion</span></strong></p>
<p>Now you should have a general idea of what a recorded Grinder test-script looks like. In my next tutorial, I&#8217;ll go over writing performance tests in Grinder using a testing framework.</p>
<br /><a href="http://vivin.net/wordpress/?p=412#comments" title="Comments on &quot;Anatomy of a Grinder test-script&quot;"><img src="http://vivin.net/wp-content/plugins/feed-comments-number/image.php?412" alt="Comments" /></a>]]></content:encoded>
			<wfw:commentRss>http://vivin.net/2009/08/14/anatomy-of-a-grinder-testscript/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
	
		<media:thumbnail url="http://vivin.net/php/image.php?source=/home/vivin/www/pub/grinder/Proxying_through_Test_object.png&#38;type=png&#38;height=0.5&#38;width=0.5" />
		<media:content url="http://vivin.net/php/image.php?source=/home/vivin/www/pub/grinder/Proxying_through_Test_object.png&#38;type=png&#38;height=0.5&#38;width=0.5" medium="image">
			<media:title type="html">Proxying through Test object</media:title>
		</media:content>
		<media:content url="http://vivin.net/wp-content/plugins/feed-comments-number/image.php?412" medium="image">
			<media:title type="html">Comments</media:title>
		</media:content>
	</item>
		<item>
		<title>Performance testing using The Grinder</title>
		<link>http://vivin.net/2009/07/27/performance-testing-using-the-grinder/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=rss</link>
		<comments>http://vivin.net/2009/07/27/performance-testing-using-the-grinder/#comments</comments>
		<pubDate>Tue, 28 Jul 2009 00:34:05 +0000</pubDate>
		<dc:creator>vivin</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Jython]]></category>
		<category><![CDATA[Programming and Development]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[grinder]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[jython]]></category>
		<category><![CDATA[performance testing]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[the grinder]]></category>
		<category><![CDATA[web development]]></category>

		<guid isPermaLink="false">http://vivin.net/wordpress/?p=411</guid>
		<description><![CDATA[About a month ago at work, I was trying out a bunch of different performance-testing tools to figure out which one to use to performance-test our software. I ended up discovering a tool called The Grinder which uses Jython to build performance-testing scripts that you can then use to test your application. I even built [...]]]></description>
			<content:encoded><![CDATA[<p>About a month ago at work, I was trying out a bunch of different performance-testing tools to figure out which one to use to performance-test our software. I ended up discovering a tool called <a target = "_blank" href = "http://grinder.sourceforge.net/">The Grinder</a> which uses <a target = "_blank" href = "http://www.jython.org/">Jython</a> to build performance-testing scripts that you can then use to test your application. I even built a little framework in Jython to make the test scripts more modular and reusable. Over the next few days I&#8217;ll be publishing three articles (this one included) that talk about Grinder.</p>
<p><strong><span style = "text-decoration:underline">Introduction</span></strong></p>
<p>In this article I&#8217;ll go over installing and setting up Grinder. I am not going to go into too many details since my aim is to provide information that will enable you to have Grinder up and running quickly. If you want more information, you can look at the rather thorough <a target = "_blank" href = "http://grinder.sourceforge.net/g3/manual.html">Grinder User Guide</a>. At the end of this guide, you&#8217;ll know how to install and set up grinder, record a test, modify (and/or parameterize) it, start running it through the console, and record data from the test. <strong>Note: The instructions in this guide relate to a Linux environment. You can run Grinder in Windows; the set-up is not much different. The only differences will be in installation locations and shell scripts. If you want more information about setting up Grinder in a Windows environment, please take a look <a target = "_blank" href = "http://grinder.sourceforge.net/g3/getting-started.html#howtostart">here</a>.</strong><br />
<span id="more-411"></span><br />
<strong><span style = "text-decoration:underline">Downloading everything you need</span></strong></p>
<p>First you need to download Grinder. You can get it from <a target = "_blank" href = "http://sourceforge.net/project/platformdownload.php?group_id=18598e">here</a>. You should only need to download the binary distribution and not the source distribution (unless you&#8217;re planning on extending Grinder). Unzip the installation file to somewhere convenient. I extracted the file to <span style = "font-family:courier new">/usr/local/grinder</span>. I recommend <span style = "font-family:courier new">chown</span>&#8216;ing the installation directory to <span style = "font-family:courier new">yourusername:yourusername</span>. Make sure that your version of the JDK is greater than or equal to 1.4, otherwise Grinder won&#8217;t work! The second thing you need to install is Jython. Note that this is not absolutely necessary, but I found it useful to have around when I was trying to parameterize the test-scripts, and I needed to test something out first. I recommend <strong>not</strong> using the Jython that comes with Ubuntu. Instead, download and install Jython 2.2.1 from <a target = "_blank" href = "http://wiki.python.org/jython/DownloadInstructions">here</a> (there are issues with Jython 2.5 and Grinder). </p>
<p><strong><span style = "text-decoration:underline">Setting up Grinder</span></strong></p>
<p>For demonstration purposes, I&#8217;m going to show you how to set up Grinder with a local <span style = "font-family:courier new">grinder.properties</span> file, with a local console and a local agent running workers. To get an explanation as to what these terms mean, take a look at the picture below that I shamelessly stole from the Grinder documentation page:</p>
<p style = "text-align:center">
<img title = "Processes" alt = "Processes"    src = "http://vivin.net/pub/grinder/processes.png" /></p>
<p>As you can see, you essentially have a console that sits on top of everything. It acts like a controller, pushing out scenarios and test cases (scripts) to agents that are running on load-injection machines. Each agent is in charge of one or more workers that inject load into the system you are testing. The workers also report data back to the console. The console aggregates all the data it receives from the workers and presents it to you. This is the ideal scenario that you would use for actual performance testing, however like I said before, I&#8217;m not going to concentrate on that &#8211; I&#8217;m just going to show you how to quickly set up Grinder and run a performance test on your local machine. If you want to look into distributing scenarios and tests across different agents, you can take a look <a target = "_blank" href = "http://grinder.sourceforge.net/g3/manual.html#Script+tab">here</a>.</p>
<p>The first thing you want to do is set up a <span style = "font-family:courier new">grinder.properties</span> file. I put this file into the root directory of the checked out app (so basically, wherever you checked out your app). It&#8217;s not necessary to put it there; you can put it wherever you want. So open up your favorite text editor, and create the following file:</p>
<pre class="brush: php">
# Tell Grinder how many processes, threads and runs (iterations) we have in this scenario
grinder.processes = 1
grinder.threads = 5
grinder.runs = 10

# grinder.jvm.arguments sends arguments to the JVM. The reason I have the following value
# is to prevent an error (Jython complains about caches) from popping up when you start
# a test. Jython caches imports and so you need to let it know where it can store the imports.
# Though it&#039;s not strictly necessary, you do need it if you are performing wildcard imports
# (like java.util.*)
grinder.jvm.arguments = -Dpython.cachedir=/tmp/mycache
grinder.script = grinder.py

# Here you set up the arguments that tells the agent where the console lives.
grinder.hostID = dauntless
grinder.consoleHost = dauntless
grinder.consolePort = 6372
grinder.useConsole = true
grinder.reportToConsole.interval = 500

# sleepTime is a synonym for &amp;amp;amp;quot;think time&amp;amp;amp;quot;. You don&#039;t want the load-generator hammering away at the
# app one request after another with a uniform interval. sleepTime introduces time variations between
# different requests
grinder.initialSleepTime = 0
grinder.sleepTimeFactor = 1
grinder.sleepTimeVariation = 0.2

# These properties are used to configure the behavior of Grinder with respect to logging
grinder.logProcessStreams = true
grinder.reportTimesToConsole = true
grinder.debug.singleProcess = false
grinder.useNanoTime = false
</pre>
<p>If you want more information regarding the <span style = "font-family:courier new">grinder.properties</span> file, take a look at <a target = "_blank" href = "http://grinder.sourceforge.net/g3/manual.html#g3/properties.html">this page</a>.</p>
<p>Alright, now that you have the <span style = "font-family:courier new">grinder.properties</span> created, you also need a few shell-scripts to easily start and stop the console, agent, and TCP Proxy. You will have to change the values of <span style = "font-family:courier new">GRINDERPATH</span>, <span style = "font-family:courier new">GRINDERPROPERTIES</span>, and <span style = "font-family:courier new">JAVA_HOME</span> to values that make sense on your system.</p>
<pre class="brush: bash">
#!/bin/bash
GRINDERPATH=/usr/local/grinder/grinder-3.2
GRINDERPROPERTIES=./grinder.properties
CLASSPATH=$GRINDERPATH/lib/grinder.jar:$CLASSPATH
JAVA_HOME=/usr/local/java/6u13
PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH PATH GRINDERPROPERTIES
</pre>
<pre class="brush: bash">
#!/bin/bash
. ./setGrinderEnv.sh
java -cp $CLASSPATH net.grinder.TCPProxy -console -http &gt; grinder.py
</pre>
<pre class="brush: bash">
#!/bin/bash
. ./setGrinderEnv.sh
java -cp $CLASSPATH net.grinder.Console
</pre>
<pre class="brush: bash">
#!/bin/bash
. ./setGrinderEnv.sh
java -cp $CLASSPATH net.grinder.Grinder $GRINDERPROPERTIES
</pre>
<p><strong><span style = "text-decoration:underline">Creating a Test</span></strong></p>
<p>Before you start performance testing, you need to have a test. Grinder test scripts are written in the Jython language. You can write your tests from scratch if you wish (it can be useful if you&#8217;re trying to test things like XML-RPC), but more often than not you will be using Grinder&#8217;s proxy tool to record a usage scenario and generate a test script. The tool that Grinder comes with is called TCP Proxy. It sits between the browser and the server and records requests and responses that move between the browser and the server. By itself, it simply echoes whatever goes through it (in that sense, it makes a pretty good debugging tool), but we want it to generate Jython test scripts. <strong>Note: TCPProxy actually uses XSLT to translate the header information into a Jython script. This is pretty neat, actually because you can use XSLT to translate the code to Groovy, or even JRuby.</strong></p>
<p style = "text-align:center">
<img title = "TCPProxy" alt = "TCPProxy"    src = "http://vivin.net/pub/grinder/tcpproxy.gif" /></p>
<p>First, you need to start up TCP Proxy. Simply run <span style = "font-family:courier new">startProxy.sh</span> and you should be good to go. A little window will pop-up letting you know that the proxy is running. There&#8217;s a textbox in the window that lets you insert comments too. Once you the proxy is started up, you need to configure your browser to use the proxy at <span style = "font-family:courier new">localhost:8001</span>. Now access the app you want to test, in your browser. Log in and do whatever it is you need to do (search for a contact, add a contact, send and email, etc.). In the scenario I created, I logged in, searched for contacts, added a new contact, searched for more contacts, and then logged out. <strong>Note: If you get an alert box that complains about security certificates, you can add an exception in Firefox by going to Edit-&#62;Preferences-&#62;Advanced-&#62;Encryption Tab-&#62;View Certificates-&#62;Add Exception. Put in the URL to your server and Firefox should grab the certificate and add an exception.</strong></p>
<p>Once you&#8217;re done recording the test, press the <span style = "font-family:courier new">Stop</span> button in TCPProxy.</p>
<p><span style = "color:"><strong>WARNING: When you&#8217;re running TCPProxy, make sure that you have no other tabs open! Also make sure that you have no plugins trying to phone home (through the browser). TCPProxy intercepts every communication that your browser makes so you don&#8217;t want to pollute your results with irrelevant requests (I think it&#8217;s possible to filter the data using the domain name, but I haven&#8217;t looked into how to do it yet)</strong></span></p>
<p><strong><span style = "text-decoration:underline">Parameterizing the test</span></strong></p>
<p>There are a few tests where you would like to supply data (for example, when adding a contact). In the context of performance-testing, this is called parameterizing. Since Grinder uses Jython, it&#8217;s actually pretty easy to parameterize a test. First, let&#8217;s take a look at a TCPProxy-generated Jython-script. I&#8217;m only going to reproduce parts of the script since the entire script itself is rather large (however, if you want to look at the entire script, you can <a target = "_blank" href = "http://vivin.net/pub/grinder/grinder.py">download</a> it):</p>
<pre class="brush: python">
# The Grinder 3.2
# HTTP script recorded by TCPProxy at Jun 17, 2009 4:57:02 PM

from net.grinder.script import Test
from net.grinder.script.Grinder import grinder
from net.grinder.plugin.http import HTTPPluginControl, HTTPRequest
from HTTPClient import NVPair
connectionDefaults = HTTPPluginControl.getConnectionDefaults()
httpUtilities = HTTPPluginControl.getHTTPUtilities()

# To use a proxy server, uncomment the next line and set the host and port.
# connectionDefaults.setProxyServer(&quot;localhost&quot;, 8001)

# These definitions at the top level of the file are evaluated once,
# when the worker process is started.

connectionDefaults.defaultHeaders = \
  ( NVPair(&#039;Accept-Language&#039;, &#039;en-us,en;q=0.5&#039;),
    NVPair(&#039;Accept-Charset&#039;, &#039;ISO-8859-1,utf-8;q=0.7,*;q=0.7&#039;),
    NVPair(&#039;Accept-Encoding&#039;, &#039;gzip,deflate&#039;),
    NVPair(&#039;User-Agent&#039;, &#039;Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.11) Gecko/2009060308 Ubuntu/9.04 (jaunty) Firefox/3.0.11&#039;), )

headers0= \
  ( NVPair(&#039;Accept&#039;, &#039;text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8&#039;),
    NVPair(&#039;Referer&#039;, &#039;https://local.infusiontest.com:8443/?msg=You%27ve+been+logged+out+-+thanks+for+playing%21&amp;amp;amp;amp;notification=You%27ve+been+logged+out+-+thanks+for+playing%21&#039;), )

headers1= \
  ( NVPair(&#039;Accept&#039;, &#039;text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8&#039;),
    NVPair(&#039;Referer&#039;, &#039;https://local.infusiontest.com:8443/Admin/home.jsp&#039;), )

...
...

headers9= \
  ( NVPair(&#039;Accept&#039;, &#039;text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8&#039;),
    NVPair(&#039;Referer&#039;, &#039;https://local.infusiontest.com:8443/Admin/home.jsp?revertCalUser=true&#039;),
    NVPair(&#039;Cache-Control&#039;, &#039;no-cache&#039;), )

headers10= \
  ( NVPair(&#039;Accept&#039;, &#039;image/png,image/*;q=0.8,*/*;q=0.5&#039;),
    NVPair(&#039;Referer&#039;, &#039;https://local.infusiontest.com:8443/?msg=You%27ve+been+logged+out+-+thanks+for+playing%21&amp;amp;amp;amp;notification=You%27ve+been+logged+out+-+thanks+for+playing%21&#039;), )

url0 = &#039;https://local.infusiontest.com:8443&#039;

# 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, &#039;POST processLogin.jsp&#039;).wrap(request101)

request102 = HTTPRequest(url=url0, headers=headers0)
request102 = Test(102, &#039;GET home.jsp&#039;).wrap(request102)

request201 = HTTPRequest(url=url0, headers=headers1)
request201 = Test(201, &#039;GET popUpTask.jsp&#039;).wrap(request201)

...
...

request3004 = HTTPRequest(url=url0, headers=headers0)
request3004 = Test(3004, &#039;GET defaultLogin.jsp&#039;).wrap(request3004)

request3005 = HTTPRequest(url=url0, headers=headers0)
request3005 = Test(3005, &#039;GET index.jsp&#039;).wrap(request3005)

class TestRunner:
  &quot;&quot;&quot;A TestRunner instance is created for each worker thread.&quot;&quot;&quot;

  # A method for each recorded page.
  def page1(self):
    &quot;&quot;&quot;POST processLogin.jsp (requests 101-102).&quot;&quot;&quot;

    # Expecting 302 &#039;Moved Temporarily&#039;
    result = request101.POST(&#039;/login/processLogin.jsp&#039;,
      ( NVPair(&#039;username&#039;, &#039;myusername&#039;),
        NVPair(&#039;password&#039;, &#039;myp@55W0rd&#039;),
        NVPair(&#039;Login&#039;, &#039;Login&#039;), ),
      ( NVPair(&#039;Content-Type&#039;, &#039;application/x-www-form-urlencoded&#039;), ))

    grinder.sleep(49)
    request102.GET(&#039;/Admin/home.jsp&#039;)

    return result

  def page2(self):
    &quot;&quot;&quot;GET popUpTask.jsp (request 201).&quot;&quot;&quot;
    result = request201.GET(&#039;/files/popUpTask.jsp&#039; +
      &#039;?0.6697580414936783&#039;)

    return result

  def page3(self):
    &quot;&quot;&quot;POST calendarBackend.jsp (request 301).&quot;&quot;&quot;
    result = request301.POST(&#039;/Calendar/calendarBackend.jsp&#039;,
      ( NVPair(&#039;calType&#039;, &#039;Day&#039;),
        NVPair(&#039;userId&#039;, &#039;1&#039;),
        NVPair(&#039;calDate&#039;, &#039;17&#039;),
        NVPair(&#039;calMonth&#039;, &#039;5&#039;),
        NVPair(&#039;calYear&#039;, &#039;2009&#039;),
        NVPair(&#039;weekStartDate&#039;, &#039;-1&#039;),
        NVPair(&#039;weekStartMonth&#039;, &#039;-1&#039;),
        NVPair(&#039;weekStartYear&#039;, &#039;-1&#039;),
        NVPair(&#039;weekEndDate&#039;, &#039;-1&#039;),
        NVPair(&#039;weekEndMonth&#039;, &#039;-1&#039;),
        NVPair(&#039;weekEndYear&#039;, &#039;-1&#039;), ),
      ( NVPair(&#039;Content-Type&#039;, &#039;application/x-www-form-urlencoded; charset=UTF-8&#039;), ))

    return result

  ...
  ...

  def page18(self):
    &quot;&quot;&quot;POST processContact.jsp (requests 1801-1802).&quot;&quot;&quot;

    # Expecting 302 &#039;Moved Temporarily&#039;
    result = request1801.POST(&#039;/Contact/processContact.jsp&#039;,
      ( NVPair(&#039;addingFormId&#039;, &#039;0&#039;),
        NVPair(&#039;tabs_sel&#039;, &#039;main&#039;),
        NVPair(&#039;Contact0Id&#039;, &#039;0&#039;),
        NVPair(&#039;Contact0FirstName&#039;, &quot;Mikey&quot;),
        NVPair(&#039;Contact0LastName&#039;, &quot;Mike&quot;),
        ...
        ...
        NVPair(&#039;Contact0Website&#039;, &#039;&#039;),
        NVPair(&#039;Contact0Email&#039;, &quot;mikey.mike@mike.com&quot;),
        NVPair(&#039;email_Contact0Email&#039;, &#039;1&#039;),
        ...
        ...
        NVPair(&#039;view&#039;, &#039;add&#039;),
        NVPair(&#039;Save&#039;, &#039;Save&#039;), ),
      ( NVPair(&#039;Content-Type&#039;, &#039;application/x-www-form-urlencoded&#039;), ))
    self.token_view = \
      httpUtilities.valueFromLocationURI(&#039;view&#039;) # &#039;edit&#039;
    self.token_tabs_sel = \
      httpUtilities.valueFromLocationURI(&#039;tabs_sel&#039;) # &#039;main&#039;
    self.token_msg = \
      httpUtilities.valueFromLocationURI(&#039;msg&#039;) # &#039;Person Added&#039;

    grinder.sleep(46)
    request1802.GET(&#039;/Contact/manageContact.jsp&#039; +
      &#039;?view=&#039; +
      self.token_view +
      &#039;&amp;amp;amp;amp;tabs_sel=&#039; +
      self.token_tabs_sel +
      &#039;&amp;amp;amp;amp;msg=&#039; +
      self.token_msg)

    return result

  ...
  ...

  def __call__(self):
    &quot;&quot;&quot;This method is called for every run performed by the worker thread.&quot;&quot;&quot;
    self.page1()      # POST processLogin.jsp (requests 101-102)

    grinder.sleep(350)
    self.page2()      # GET popUpTask.jsp (request 201)

    grinder.sleep(385)
    self.page3()      # POST calendarBackend.jsp (request 301)

  ...
  ...

    grinder.sleep(407)
    self.page29()     # POST calendarBackend.jsp (request 2901)

    grinder.sleep(3661)
    self.page30()     # GET logout.jsp (requests 3001-3005)

def instrumentMethod(test, method_name, c=TestRunner):
  &quot;&quot;&quot;Instrument a method with the given Test.&quot;&quot;&quot;
  unadorned = getattr(c, method_name)
  import new
  method = 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, &#039;Page 1&#039;), &#039;page1&#039;)
instrumentMethod(Test(200, &#039;Page 2&#039;), &#039;page2&#039;)
instrumentMethod(Test(300, &#039;Page 3&#039;), &#039;page3&#039;)
...
...
instrumentMethod(Test(2900, &#039;Page 29&#039;), &#039;page29&#039;)
instrumentMethod(Test(3000, &#039;Page 30&#039;), &#039;page30&#039;)
</pre>
<p>From the above listing, you can get a general idea of the anatomy of a test script. Of course, there&#8217;s a little bit more that goes into this, so if you want a detailed description, take a look at <a target = "_blank" href = "http://grinder.sourceforge.net/g3/manual.html#Scripts">this page</a> (in my next article, I&#8217;ll go into more detail about the anatomy of a recorded script). By itself, this script is 95% useful. However, to make it exactly what we need, we need to parameterize certain areas. The first section we need to parameterize is where we add a new contact (look at the page18 method). Right now, it inserts a contact called &#8220;Mikey Mike&#8221; with the email &#8220;mikey.mike@mike.com&#8221;. That&#8217;s not particularly useful if we&#8217;re running this test a bunch of times. We want to parameterize this value. What we&#8217;re going to do is create a random first name, last name, and email address, and use those values in the request. Here&#8217;s how we do it (once again, I&#8217;m only showing the pertinent sections of the code i.e., the sections that have changed):</p>
<pre class="brush: python">
# The Grinder 3.2
# HTTP script recorded by TCPProxy at Jun 17, 2009 4:57:02 PM

# We need to import java.util.Random for random number generation
from java.util import Random

...
...

  def page18(self):
    &quot;&quot;&quot;POST processContact.jsp (requests 1801-1802).&quot;&quot;&quot;

    chars = [ &quot;b&quot;, &quot;c&quot;, &quot;d&quot;, &quot;f&quot;, &quot;g&quot;, &quot;h&quot;, &quot;j&quot;, &quot;l&quot;, &quot;m&quot;, &quot;n&quot;, &quot;p&quot;, &quot;q&quot;, &quot;r&quot;, &quot;s&quot;, &quot;t&quot;, &quot;v&quot;, &quot;w&quot;, &quot;x&quot;, &quot;y&quot;, &quot;z&quot;, &quot;th&quot;, &quot;dh&quot;, &quot;gh&quot;, &quot;ph&quot;, &quot;ch&quot;, &quot;st&quot;, &quot;sw&quot;, &quot;sp&quot;, &quot;kh&quot; ];
    vowels = [ &quot;a&quot;, &quot;e&quot;, &quot;i&quot;, &quot;o&quot;, &quot;u&quot; ];
    r = Random();

    fname = &quot;&quot;;
    lname = &quot;&quot;;

    for i in range(0, r.nextInt(3) + 2):
       fname += chars[r.nextInt(len(chars))] + vowels[r.nextInt(len(vowels))];

    for i in range(0, r.nextInt(3) + 2):
       lname += chars[r.nextInt(len(chars))] + vowels[r.nextInt(len(vowels))];

    # Expecting 302 &#039;Moved Temporarily&#039;
    result = request1801.POST(&#039;/Contact/processContact.jsp&#039;,
      ( NVPair(&#039;addingFormId&#039;, &#039;0&#039;),
        NVPair(&#039;tabs_sel&#039;, &#039;main&#039;),
        NVPair(&#039;Contact0Id&#039;, &#039;0&#039;),
        NVPair(&#039;Contact0FirstName&#039;, fname.capitalize()),
        NVPair(&#039;Contact0LastName&#039;, lname.capitalize()),
        ...
        ...
        NVPair(&#039;Contact0Website&#039;, &#039;&#039;),
        NVPair(&#039;Contact0Email&#039;, fname + &quot;.&quot; + lname + &quot;@&quot; + lname + &quot;.com&quot;),
        NVPair(&#039;email_Contact0Email&#039;, &#039;1&#039;),
        ...
        ...
        NVPair(&#039;Save&#039;, &#039;Save&#039;), ),
      ( NVPair(&#039;Content-Type&#039;, &#039;application/x-www-form-urlencoded&#039;), ))
    self.token_view = \
      httpUtilities.valueFromLocationURI(&#039;view&#039;) # &#039;edit&#039;
    self.token_tabs_sel = \
      httpUtilities.valueFromLocationURI(&#039;tabs_sel&#039;) # &#039;main&#039;
    self.token_msg = \
      httpUtilities.valueFromLocationURI(&#039;msg&#039;) # &#039;Person Added&#039;

    grinder.sleep(46)
    request1802.GET(&#039;/Contact/manageContact.jsp&#039; +
      &#039;?view=&#039; +
      self.token_view +
      &#039;&amp;amp;amp;amp;tabs_sel=&#039; +
      self.token_tabs_sel +
      &#039;&amp;amp;amp;amp;msg=&#039; +
      self.token_msg)

    return result
</pre>
<p>As you can see, we&#8217;ve now parameterized the first name and last name values that go into processContact.jsp. There is still one more thing we need to parameterize. If you go back and take a look at <span style = "font-family:courier new">grinder.properties</span>, you can see that I set <span style = "font-family:courier new">grinder.threads</span> to 5. This means that there are going to be 5 threads running. We don&#8217;t want the same user logging in more than once (our app doesn&#8217;t allow it). So what we also need to do is parameterize user logins:</p>
<pre class="brush: python">
# The Grinder 3.2
# HTTP script recorded by TCPProxy at Jun 17, 2009 4:57:02 PM

from java.util import Random
from net.grinder.script import Test
from net.grinder.script.Grinder import grinder
from net.grinder.plugin.http import HTTPPluginControl, HTTPRequest
from HTTPClient import NVPair
connectionDefaults = HTTPPluginControl.getConnectionDefaults()
httpUtilities = HTTPPluginControl.getHTTPUtilities()

# To use a proxy server, uncomment the next line and set the host and port.
# connectionDefaults.setProxyServer(&quot;localhost&quot;, 8001)

# These definitions at the top level of the file are evaluated once,
# when the worker process is started.

# Setting up an array of dictionaries (i.e., hash/map/associative-array) of username and password name-value pairs
loginCredentials = [{&quot;username&quot;:&quot;vivin&quot;, &quot;password&quot;:&quot;abAB12!@&quot;},
                    {&quot;username&quot;:&quot;jimbo&quot;, &quot;password&quot;:&quot;abAB12!@&quot;},
                    {&quot;username&quot;:&quot;hippy&quot;, &quot;password&quot;:&quot;abAB12!@&quot;},
                    {&quot;username&quot;:&quot;flippy&quot;, &quot;password&quot;:&quot;abAB12!@&quot;},
                    {&quot;username&quot;:&quot;batman&quot;, &quot;password&quot;:&quot;abAB12!@&quot;}];

...
...

class TestRunner:
  &quot;&quot;&quot;A TestRunner instance is created for each worker thread.&quot;&quot;&quot;

  # A method for each recorded page.
  def page1(self):
    &quot;&quot;&quot;POST processLogin.jsp (requests 101-102).&quot;&quot;&quot;

    loginCredential = loginCredentials[grinder.threadNumber];
    username = loginCredential[&quot;username&quot;];
    password = loginCredential[&quot;password&quot;];
    # Expecting 302 &#039;Moved Temporarily&#039;
    result = request101.POST(&#039;/login/processLogin.jsp&#039;,
      ( NVPair(&#039;username&#039;, username),
        NVPair(&#039;password&#039;, password),
        NVPair(&#039;Login&#039;, &#039;Login&#039;), ),
      ( NVPair(&#039;Content-Type&#039;, &#039;application/x-www-form-urlencoded&#039;), ))

    grinder.sleep(49)
    request102.GET(&#039;/Admin/home.jsp&#039;)

    return result
</pre>
<p>To parameterize logins, I created an array of dictionaries that store username and password combinations. Then, based on the thread number (which is zero-based and which I can access via grinder.threadNumber) I select the appropriate username and password combination, to log in.</p>
<p><strong><span style = "text-decoration:underline">Running the tests</span></strong></p>
<p>Now we can actually start running the tests and recording data! The first thing you need to do is start up your app. Then, you need to start the Grinder console. Use the <span style = "font-family:courier new">startConsole.sh</span> shell-script and start up the Grinder console. After that, use <span style = "font-family:courier new">startAgent.sh</span> to start up the agent. You should see something like this in your shell:</p>
<pre class="brush: php">
vivin@dauntless ~/Projects/crm-1.16.0.x
$ startConsole.sh &amp;amp;amp;amp;amp;
[1] 16180

vivin@dauntless ~/Projects/crm-1.16.0.x
$ startAgent.sh
6/18/09 4:43:19 PM (agent): The Grinder 3.2
6/18/09 4:43:19 PM (agent): connected to console at dauntless/127.0.1.1:6372
6/18/09 4:43:19 PM (agent): waiting for console signal
</pre>
<p>If you see this, it means that your agent started up, and was able to talk to the Grinder console. In the Grinder console, there are four icons in the top-left corner of the window. If you mouse-over the leftmost icon, you will see a tooltip that says &#8220;Start the worker processes&#8221;. Click that button. A dialog box will pop up saying that you haven&#8217;t selected a properties file and that the worker processes will run the script set in the agent&#8217;s properties file. This is fine, since you are running Grinder locally and not distributing a file. Click Ok. You should now see something like this in your shell:</p>
<pre class="brush: php">
vivin@dauntless ~/Projects/crm-1.16.0.x
$ startConsole.sh &amp;amp;amp;amp;amp;
[1] 16180

vivin@dauntless ~/Projects/crm-1.16.0.x
&amp;amp;amp;amp;amp;#36; startAgent.sh
6/18/09 4:50:12 PM (agent): The Grinder 3.2
6/18/09 4:50:12 PM (agent): connected to console at dauntless/127.0.1.1:6372
6/18/09 4:50:12 PM (agent): waiting for console signal
6/18/09 4:51:48 PM (agent): received a start message
6/18/09 4:51:48 PM (agent): Worker process command line: java &#039;-Dpython.cachedir=/tmp/mycache&#039; -classpath &#039;/usr/local/grinder/grinder-3.2/lib/grinder.jar:&#039; net.grinder.engine.process.WorkerProcessEntryPoint
6/18/09 4:51:48 PM (agent): worker dauntless-0 started
*sys-package-mgr*: processing new jar, &#039;/usr/local/grinder/grinder-3.2/lib/grinder.jar&#039;
*sys-package-mgr*: processing new jar, &#039;/usr/local/java/6u13/jre/lib/resources.jar&#039;
*sys-package-mgr*: processing new jar, &#039;/usr/local/java/6u13/jre/lib/rt.jar&#039;
*sys-package-mgr*: processing new jar, &#039;/usr/local/java/6u13/jre/lib/jsse.jar&#039;
*sys-package-mgr*: processing new jar, &#039;/usr/local/java/6u13/jre/lib/jce.jar&#039;
*sys-package-mgr*: processing new jar, &#039;/usr/local/java/6u13/jre/lib/charsets.jar&#039;
*sys-package-mgr*: processing new jar, &#039;/usr/local/java/6u13/jre/lib/ext/sunpkcs11.jar&#039;
*sys-package-mgr*: processing new jar, &#039;/usr/local/java/6u13/jre/lib/ext/sunjce_provider.jar&#039;
*sys-package-mgr*: processing new jar, &#039;/usr/local/java/6u13/jre/lib/ext/localedata.jar&#039;
*sys-package-mgr*: processing new jar, &#039;/usr/local/java/6u13/jre/lib/ext/dnsns.jar&#039;
*sys-package-mgr*: processing new jar, &#039;/usr/local/grinder/grinder-3.2/lib/jython.jar&#039;
6/18/09 4:51:53 PM (process dauntless-0): starting threads
</pre>
<p>If you check back into the Grinder console, you can see that it is grabbing data from the agent. Now just sit back and let the test run. Once the test is complete, hit the stop button and exit out of console. If you go to the directory from where you started up the agent, you will see three log files: data_&#60;hostname&#62;-N.log, error_&#60;hostname&#62;-N.log, and out_&#60;hostname&#62;-N.log. data_&#60;hostname&#62;-N.log is the file that contains all the data gathered (like mean response time, transactions per second, etc.). It is a comma-separated-values file and you can open it up in Excel or Openoffice Calc. error_&#60;hostname&#62;-N.log contains any errors encountered while running the test, and out_&#60;hostname&#62;-N.log contains logging data from the script file. You are now in a position to analyze data from the log files. <span style = "color:"><strong>WARNING: I recommend NOT using the version of Java that comes with Ubuntu (GCJ/IcedTea or whatever). Instead, use Sun&#8217;s Java6. I ran into a whole bunch of problems and wasted an entire day (you can view the thread describing the problem <a target = "_blank" href = "http://www.nabble.com/Exception-in-ServerReceiver-ts24291955.html">here</a>).</strong></span></p>
<p><strong><span style = "text-decoration:underline">Analyzing Grinder-Generated Data</span></strong></p>
<p>Grinder spits out raw data. So it&#8217;s not that user-friendly when compared to, say, WebLoad. This makes sense when you consider the fact that Grinder is geared towards developers. Grinder says &#8220;Here is the data I collected, do whatever you want with it&#8221;. If you want a quick overview of your performance data, there is a tool called <strong>GrinderAnalyzer</strong> that you can download from <a target = "_blank" href = "http://track.sourceforge.net/analyzer.html">here</a>. Usage is pretty straightfoward (check out the documentation). Extract it to your user home directory. Then run the shell script (<span style = "font-family:courier new">run.sh</span>) in the bin directory while providing it your data_&#60;hostname&#62;-N.log and out_&#60;hostname&#62;-N.log files. GrinderAnalyzer will generate some pretty graphs and some pretty tables for you to view. Your other option is to examine the data on your own and generate graphs. This, while a bit more involved, gives you the freedom to display the data in a manner you see fit.</p>
<p><strong><span style = "text-decoration:underline">Conclusion</span></strong></p>
<p>Grinder is a pretty flexible and powerful performance-testing tool. I&#8217;m hoping that after this tutorial you have enough knowledge to go out and start creating and running tests of your own. Like I mentioned before, this guide is by no meand comprehensive. It&#8217;s simply something to get you started. If you want more information, I highly recommend looking at the excellent <a target = "_blank" href = "http://grinder.sourceforge.net/g3/manual.html">Grinder Documentation</a>.</p>
<p><em><strong>Stay tuned for the next article, where I&#8217;ll be talking about the anatomy of a recorded Grinder test-script&#8230;</strong></em></p>
<br /><a href="http://vivin.net/wordpress/?p=411#comments" title="Comments on &quot;Performance testing using The Grinder&quot;"><img src="http://vivin.net/wp-content/plugins/feed-comments-number/image.php?411" alt="Comments" /></a>]]></content:encoded>
			<wfw:commentRss>http://vivin.net/2009/07/27/performance-testing-using-the-grinder/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
	
		<media:thumbnail url="http://vivin.net/pub/grinder/processes.png" />
		<media:content url="http://vivin.net/pub/grinder/processes.png" medium="image">
			<media:title type="html">Processes</media:title>
		</media:content>
		<media:content url="http://vivin.net/pub/grinder/tcpproxy.gif" medium="image">
			<media:title type="html">TCPProxy</media:title>
		</media:content>
		<media:content url="http://vivin.net/wp-content/plugins/feed-comments-number/image.php?411" medium="image">
			<media:title type="html">Comments</media:title>
		</media:content>
	</item>
		<item>
		<title>Running the JavaFX 1.1 SDK on Linux</title>
		<link>http://vivin.net/2009/04/30/running-the-javafx-11-sdk-on-linux/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=rss</link>
		<comments>http://vivin.net/2009/04/30/running-the-javafx-11-sdk-on-linux/#comments</comments>
		<pubDate>Thu, 30 Apr 2009 16:18:50 +0000</pubDate>
		<dc:creator>vivin</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Programming and Development]]></category>
		<category><![CDATA[cpio]]></category>
		<category><![CDATA[dmg]]></category>
		<category><![CDATA[dsl]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[javafx]]></category>
		<category><![CDATA[javafx script]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[mac]]></category>
		<category><![CDATA[os x]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[ria]]></category>
		<category><![CDATA[scripting]]></category>
		<category><![CDATA[sdk]]></category>

		<guid isPermaLink="false">http://vivin.net/wordpress/?p=407</guid>
		<description><![CDATA[This is an update to my instructions on running the JavaFX 1.0 SDK on Linux. Those instructions do not work on the dmg image for the 1.1 version of the SDK. Mike (thanks Mike!) posted a comment on that blog mentioning a small change that needed to be made. To get JavaFX 1.1 on Linux, [...]]]></description>
			<content:encoded><![CDATA[<p>This is an update to my instructions on <a target = "_blank" href = "http://vivin.net/2008/12/04/running-the-javafx-10-sdk-on-linux/">running the JavaFX 1.0 SDK on Linux</a>. Those instructions do not work on the dmg image for the 1.1 version of the SDK.</p>
<p>Mike (thanks Mike!) posted a comment on that blog mentioning a small change that needed to be made. To get JavaFX 1.1 on Linux, first follow the steps in the original guide. When you need to mount the dmg, you need to provide an offset. So instead of the original command, do the following:</p>
<pre class="brush: php">
vivin@dauntless ~
$; sudo mount -o loop,offset=$((1024*17)) -t hfsplus javafx_sdk-1_0-macosx-universal.dmg.out javafx
</pre>
<p>The dmg should be mounted now.</p>
<br /><a href="http://vivin.net/wordpress/?p=407#comments" title="Comments on &quot;Running the JavaFX 1.1 SDK on Linux&quot;"><img src="http://vivin.net/wp-content/plugins/feed-comments-number/image.php?407" alt="Comments" /></a>]]></content:encoded>
			<wfw:commentRss>http://vivin.net/2009/04/30/running-the-javafx-11-sdk-on-linux/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:thumbnail url="http://vivin.net/wp-content/plugins/feed-comments-number/image.php?407" />
		<media:content url="http://vivin.net/wp-content/plugins/feed-comments-number/image.php?407" medium="image">
			<media:title type="html">Comments</media:title>
		</media:content>
	</item>
		<item>
		<title>Running the JavaFX 1.0 SDK on Linux</title>
		<link>http://vivin.net/2008/12/04/running-the-javafx-10-sdk-on-linux/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=rss</link>
		<comments>http://vivin.net/2008/12/04/running-the-javafx-10-sdk-on-linux/#comments</comments>
		<pubDate>Fri, 05 Dec 2008 00:32:41 +0000</pubDate>
		<dc:creator>vivin</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Programming and Development]]></category>
		<category><![CDATA[cpio]]></category>
		<category><![CDATA[dmg]]></category>
		<category><![CDATA[dsl]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[javafx]]></category>
		<category><![CDATA[javafx script]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[mac]]></category>
		<category><![CDATA[os x]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[ria]]></category>
		<category><![CDATA[scripting]]></category>
		<category><![CDATA[sdk]]></category>

		<guid isPermaLink="false">http://vivin.net/wordpress/?p=395</guid>
		<description><![CDATA[The JavaFX 1.0 SDK was released today. I&#8217;ve played with the preview SDK, so I was pretty excited to try out the 1.0 SDK. Inexplicably, and this was the case with the preview SDK as well, Sun hasn&#8217;t released a version of the SDK for Linux. However, this wasn&#8217;t a problem because it was possible [...]]]></description>
			<content:encoded><![CDATA[<p>The JavaFX 1.0 SDK was <a target = "_blank" href = "http://news.cnet.com/8301-17939_109-10113196-2.html">released</a> today. I&#8217;ve <a target = "_blank" href = "http://vivin.net/2008/08/24/javafx-the-new-hotness/">played</a> with the preview SDK, so I was pretty excited to try out the 1.0 SDK. Inexplicably, and this was the case with the preview SDK as well, Sun hasn&#8217;t released a version of the SDK for Linux. However, this wasn&#8217;t a problem because it was <a target = "_blank" href = "http://www.weiqigao.com/blog/2008/08/05/watch_javafx_sdk_run_on_linux.html">possible</a> to run the Mac version of the Preview SDK on Linux. The preview SDK came in the form of a zip, but the 1.0 SDK comes in the form of a dmg, so I was initially stumped. But I&#8217;ve figured out how to get the Mac version of the SDK to work on Linux. It&#8217;s a little more complicated than getting the preview SDK to work, but it <em>works</em>!</p>
<p>The thing about dmg files is that you can easily mount them on Linux since they are essentially stored in the <a target = "_blank" href = "http://en.wikipedia.org/wiki/HFS_Plus">HFS Plus</a> filesystem format. So I immediately set about trying to mount it:</p>
<pre style = "border:1px solid #cccccc; padding:4px; background-color:#eeeeee">
vivin@dauntless ~
$ mkdir javafx</strong>

vivin@dauntless ~
$ sudo mount -o loop -t hfsplus javafx_sdk-1_0-macosx-universal.dmg javafx
[sudo] password for vivin:
mount: wrong fs type, bad option, bad superblock on /dev/loop0,
       missing codepage or helper program, or other error
       In some cases useful info is found in syslog - try
       dmesg | tail  or so
</pre>
<p>Hmm&#8230; ok, that wasn&#8217;t what I expected, so I tried to see what type of file it was:</p>
<pre style = "border:1px solid #cccccc; padding:4px; background-color:#eeeeee">
vivin@dauntless ~
$; file javafx_sdk-1_0-macosx-universal.dmg</strong>
javafx_sdk-1_0-macosx-universal.dmg: bzip2 compressed data, block size = 100k
</pre>
<p>Ok, so it look&#8217;s like it&#8217;s a bzipped file. All we need to do then, is bunzip it and mount it:</p>
<pre style = "border:1px solid #cccccc; padding:4px; background-color:#eeeeee">
vivin@dauntless ~
$ bunzip2 javafx_sdk-1_0-macosx-universal.dmg
bunzip2: Can't guess original name for javafx_sdk-1_0-macosx-universal.dmg -- using javafx_sdk-1_0-macosx-universal.dmg.out

bunzip2: javafx_sdk-1_0-macosx-universal.dmg: trailing garbage after EOF ignored

vivin@dauntless ~
$ sudo mount -o loop -t hfsplus javafx_sdk-1_0-macosx-universal.dmg.out javafx

vivin@dauntless ~
$ ls javafx
javafx_sdk-1_0.mpkg
</pre>
<p>Awesome! So we were able to get the dmg mounted. Now all we need to do is find were the SDK lives. After going through the dmg, I found out that the SDK is stored in a compressed (gzipped) file. You can find it at <span style = "font-family:courier new">&#60;mountpoint&#62;/javafx_sdk-1_0.mpkg/Contents/Packages/javafxsdk.pkg/Contents/Archive.pax.gz</span>. Copy this file into another working directory (or wherever you want your SDK to reside. I put mine in <span style = "font-family:courier new">/usr/local</span>):</p>
<pre style = "border:1px solid #cccccc; padding:4px; background-color:#eeeeee">
vivin@dauntless ~/working
$ cp ~/javafx/javafx_sdk-1_0.mpkg/Contents/Packages/javafxsdk.pkg/Contents/Archive.pax.gz .

vivin@dauntless ~/working
$ gunzip Archive.pax.gz

vivin@dauntless ~/working
$ file Archive.pax
Archive.pax: ASCII cpio archive (pre-SVR4 or odc)
</pre>
<p>When I gunzipped the file, I got Archive.pax, and I wasn&#8217;t sure what to do with it. So I ran <span style = "font-family:courier new">file</span> on it and discovered that it was a cpio file. Some quick Googling and man-page perusal later:</p>
<pre style = "border:1px solid #cccccc; padding:4px; background-color:#eeeeee">
vivin@dauntless ~/working
$ cpio -i &#60;Archive.pax
65687 blocks

vivin@dauntless ~/working
$ ls</strong>
Archive.pax  COPYRIGHT.html  lib          profiles     samples     src.zip                      timestamp
bin          docs            LICENSE.txt  README.html  servicetag  THIRDPARTYLICENSEREADME.txt

vivin@dauntless ~/working
$ bin/javafx

Usage: java [-options] class [args...]
           (to execute a class)
   or  java [-options] -jar jarfile [args...]
           (to execute a jar file)

where options include:
    -d32          use a 32-bit data model if available

    -d64          use a 64-bit data model if available
    -client	  to select the "client" VM
    -server	  to select the "server" VM
    -hotspot	  is a synonym for the "client" VM  [deprecated]
                  The default VM is server,
                  because you are running on a server-class machine.

    -cp &#60;class search path of directories and zip/jar files&#62;
    -classpath &#60;class search path of directories and zip/jar files&#62;
                  A : separated list of directories, JAR archives,
                  and ZIP archives to search for class files.
    -D&#60;name&#62;=&#60;value&#62;
                  set a system property
    -verbose[:class|gc|jni]
                  enable verbose output
    -version      print product version and exit
    -version:&#60;value&#62;
                  require the specified version to run
    -showversion  print product version and continue
    -jre-restrict-search | -jre-no-restrict-search
                  include/exclude user private JREs in the version search
    -? -help      print this help message
    -X            print help on non-standard options
    -ea[:&#60;packagename&#62;...|:&#60;classname&#62;]
    -enableassertions[:&#60;packagename&#62;...|:&#60;classname&#62;]
                  enable assertions
    -da[:&#60;packagename&#62;...|:&#60;classname&#62;]
    -disableassertions[:&#60;packagename&#62;...|:&#60;classname&#62;]
                  disable assertions
    -esa | -enablesystemassertions
                  enable system assertions
    -dsa | -disablesystemassertions
                  disable system assertions
    -agentlib:&#60;libname&#62;[=&#60;options&#62;]
                  load native agent library &#60;libname&#62;, e.g. -agentlib:hprof
                    see also, -agentlib:jdwp=help and -agentlib:hprof=help
    -agentpath:&#60;pathname&#62;[=&#60;options&#62;]
                  load native agent library by full pathname
    -javaagent:&#60;jarpath&#62;[=&#60;options&#62;]
                  load Java programming language agent, see java.lang.instrument
</pre>
<p>As you can see, you now have a working JavaFX 1.0 SDK on your Linux box!</p>
<br /><a href="http://vivin.net/wordpress/?p=395#comments" title="Comments on &quot;Running the JavaFX 1.0 SDK on Linux&quot;"><img src="http://vivin.net/wp-content/plugins/feed-comments-number/image.php?395" alt="Comments" /></a>]]></content:encoded>
			<wfw:commentRss>http://vivin.net/2008/12/04/running-the-javafx-10-sdk-on-linux/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
	
		<media:thumbnail url="http://vivin.net/wp-content/plugins/feed-comments-number/image.php?395" />
		<media:content url="http://vivin.net/wp-content/plugins/feed-comments-number/image.php?395" medium="image">
			<media:title type="html">Comments</media:title>
		</media:content>
	</item>
	</channel>
</rss>
