<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:clearspace="http://www.jivesoftware.com/xmlns/clearspace/rss" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>Elastic Path Grep Community : All Content - Technical Blog</title>
    <link>http://grep.elasticpath.com/community/techblog</link>
    <description>All Content in Technical Blog</description>
    <language>en</language>
    <pubDate>Fri, 09 Jul 2010 23:27:59 GMT</pubDate>
    <generator>Clearspace 2.5.7 (http://jivesoftware.com/products/clearspace/)</generator>
    <dc:date>2010-07-09T23:27:59Z</dc:date>
    <dc:language>en</dc:language>
    <item>
      <title>Troubleshooting load-related issues with JMeter</title>
      <link>http://grep.elasticpath.com/community/techblog/blog/2010/07/09/troubleshooting-load-related-issues-with-jmeter</link>
      <description>&lt;!-- [DocumentBodyStart:ea93fcfa-bf62-4727-b98d-a53c5d697faf] --&gt;&lt;div class='jive-rendered-content'&gt;&lt;p&gt;Recently, I needed to reproduce a problem that was only occurring when the storefront was under heavy load. As a developer, I cannot easily generate realistic load in my environment and I don't have access to a live production system, so I used JMeter to help me out. JMeter is a Java application for performing load tests on web applications. In this post, I'll show how to use JMeter to quickly set up a simple performance test against the Elastic Path Commerce storefront.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;I wanted to see how long it would take to perform a search in the storefront for Canon products. In this case, I wanted JMeter to fire the following query a few hundred times using one or more threads: &lt;a class="jive-link-external-small" href="http://demo.elasticpath.com:8080/storefront/search.ep?categoryId=&amp;amp;keyWords=canon&amp;amp;submit=search"&gt;http://demo.elasticpath.com:8080/storefront/search.ep?categoryId=&amp;amp;keyWords=canon&amp;amp;submit=search&lt;/a&gt;&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;First, I cre&lt;span style="font-size: 10pt;"&gt;a&lt;/span&gt;ted a thread group under the test plan tree node. (Right-click the test plan and choose &lt;strong&gt;Add-&amp;gt;Thread Group&lt;/strong&gt;.) If you want to simulate more than one user hitting the same search at the same time, you can set the number of threads in the thread group configuration.&lt;/p&gt;&lt;p&gt;&lt;a href="http://grep.elasticpath.com/servlet/JiveServlet/showImage/38-1112-1520/img1_1.png"&gt;&lt;img alt="img1_1.png" class="jive-image-thumbnail jive-image" src="http://grep.elasticpath.com/servlet/JiveServlet/downloadImage/38-1112-1520/img1_1.png" width="620"/&gt;&lt;/a&gt;&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Next, I added a loop controller to my thread group. (Right-click the thread group and click &lt;strong&gt;Add-&amp;gt;Logic Controller-&amp;gt;Loop Controller&lt;/strong&gt;.) Here, I defined how many times the loop will run. In my case, I checked the Infinite checkbox.&lt;/p&gt;&lt;p&gt;&lt;a href="http://grep.elasticpath.com/servlet/JiveServlet/showImage/38-1112-1519/img2.png"&gt;&lt;img alt="img2.png" class="jive-image-thumbnail jive-image" src="http://grep.elasticpath.com/servlet/JiveServlet/downloadImage/38-1112-1519/img2.png" width="620"/&gt;&lt;/a&gt;&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Then, I added an HTTP request to the loop controller. (Right-clicking the loop controller and click &lt;strong&gt;Add-&amp;gt;Sampler-&amp;gt;HTTP Request&lt;/strong&gt;.) I set the Server Name, Server Port, URL Path, chose GET in the dropdown and set three parameters in the request URL.&lt;/p&gt;&lt;p&gt;&lt;a href="http://grep.elasticpath.com/servlet/JiveServlet/showImage/38-1112-1522/img3_1.png"&gt;&lt;img alt="img3_1.png" class="jive-image-thumbnail jive-image" src="http://grep.elasticpath.com/servlet/JiveServlet/downloadImage/38-1112-1522/img3_1.png" width="620"/&gt;&lt;/a&gt;&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;At this point, I started the test by clicking &lt;strong&gt;Run-&amp;gt;Start&lt;/strong&gt;. JMeter starts to execute the HTTP request I defined in the loop controller. Since I set the loop count to &lt;em&gt;Forever&lt;/em&gt;, it will continue to execute until I stop it.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Now, it would be a good idea to attach some reports to debug the execution. I tried a few reports and found that the View Results Tree presented all the information I needed. It shows the raw request sent by JMeter and the response data the server is providing. You can examine the server response to make sure the HTTP request you configured is doing what it is expected to do.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;To add a View Results Tree report to your test plan, right-click your HTTP request test, and choose &lt;strong&gt;Add-&amp;gt;Listener-&amp;gt;View Results Tree&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;You can finally see the results of each call on the report.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;a href="http://grep.elasticpath.com/servlet/JiveServlet/showImage/38-1112-1523/img4.png"&gt;&lt;img alt="img4.png" class="jive-image-thumbnail jive-image" src="http://grep.elasticpath.com/servlet/JiveServlet/downloadImage/38-1112-1523/img4.png" width="620"/&gt;&lt;/a&gt;&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;You can build more complex tests, with several simultaneous requests over different URLs. For example, you can configure JMeter to first simulate a user logging in to your application (and hold session information), then a few requests to simulate the user navigating through the web application and then repeat this test a few hundred times using as many threads as you want (or your machine can handle).&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Using JMeter, I was able to simulate a sufficently heavy load to reproduce the problem I wanted to reproduce and this post should help you do the same thing if you face a similar problem. For more details on JMeter, go to: &lt;a class="jive-link-external-small" href="http://jakarta.apache.org/jmeter/"&gt;http://jakarta.apache.org/jmeter/&lt;/a&gt;&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Hope you enjoy!&lt;/p&gt;&lt;/div&gt;&lt;!-- [DocumentBodyEnd:ea93fcfa-bf62-4727-b98d-a53c5d697faf] --&gt;</description>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">performance</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">storefront</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">testing</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">load</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">jmeter</category>
      <pubDate>Fri, 09 Jul 2010 23:27:59 GMT</pubDate>
      <author>developer.forums@elasticpath.com</author>
      <guid>http://grep.elasticpath.com/community/techblog/blog/2010/07/09/troubleshooting-load-related-issues-with-jmeter</guid>
      <dc:date>2010-07-09T23:27:59Z</dc:date>
      <clearspace:dateToText>3 weeks, 2 hours ago</clearspace:dateToText>
    </item>
    <item>
      <title>Unique Cross-Environment Order Numbers</title>
      <link>http://grep.elasticpath.com/community/techblog/blog/2010/06/24/unique-cross-environment-order-numbers</link>
      <description>&lt;!-- [DocumentBodyStart:d53736ab-f277-4359-b762-04a39d567e2c] --&gt;&lt;div class='jive-rendered-content'&gt;&lt;h4&gt;&lt;span style="font-weight: normal; font-size: 13px;"&gt;&lt;span style="font-size: 10pt;"&gt;One of the pain points that can arise during a development project is the payment gateway's support of recycled order numbers. Live production gateways will track all historical order numbers and generate a duplicate order number error if an existing order number is reused. This is fine in production but troublesome when the payment gateway test servers also behave in this manner. Most payment gateway providers I have worked with in the past offer duplicate checks within an hour or two on their test servers, but beyond that, order numbers can be recycled which is a good thing for development purposes. Some providers, however, appear to extend this duplicate check to a day, week, or may even provide no support for order number reuse.&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;&lt;p&gt;&lt;span style="font-size: 10pt;"&gt;&lt;br/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 10pt;"&gt;If you use the database population scripts that come with Elastic Path to facilitate schema/data changes, this limitation can frustrate your development team, who will most likely end up manually tracking used order numbers and making frequent updates to &lt;/span&gt;&lt;span style="font-family: 'courier new', courier;"&gt;&lt;span style="font-size: 10pt;"&gt;TORDERNUMBERGENERATOR&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: 10pt;"&gt;.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 10pt;"&gt;&lt;br/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 10pt;"&gt;If your payment gateway provider doesn't support order number reuse or at least not to the frequency you desire, what can you do?&lt;/span&gt;&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;h3&gt;Some Alternatives&lt;/h3&gt;&lt;p&gt;&lt;span style="font-size: 10pt;"&gt;The simplest approach would be to externalize the initial order number to the env.config and assign everyone a unique order number block. The problem with this is that each environment would have to keep track of the last order number used and update the env.config with the next number in the block with every new database build.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 10pt;"&gt;&lt;br/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 10pt;"&gt;Another approach would be to externalize the initial order number (or perhaps even the order number incrementer) to a central database. This database would have a simple mapping of environment to next order number and each environment would have a sufficient block assigned to last the duration of the project. The problem with this approach is that many teams/environments are distributed and you may not be able to set up a central database which can be accessed by all environments.&lt;/span&gt;&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;h3&gt;A Better Solution&lt;/h3&gt;&lt;p&gt;&lt;span style="font-size: 10pt;"&gt;A better &lt;/span&gt;solution (though not 100% foolproof) is to construct the initial order number during the database build process using a unique element of the environment as well as a random element to get around frequent rebuilds on the same environment. To do this, we can create a custom Ant task that takes a fixed component (derived from either a fixed prefix or the environment's IP address) and appends a randomly generated number. If the random number is sufficiently large, the likelihood of duplicate hits will be minimal.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;h3&gt;Creating the Ant Task&lt;/h3&gt;&lt;p&gt;&lt;span style="font-size: 10pt;"&gt;The following is an example of a custom Ant task that accepts min and max parameters for the random number component and a prefix. Regardless of whether a fixed prefix or an environment's IP address was provided, the task will use the hash code of the fixed value for the resulting order number in order to work with the out of the box numerical order number incrementer.&lt;/span&gt;&lt;/p&gt;&lt;!--[CodeBlockStart:e4a0014f-67c1-4e73-9f2b-42e47bfadbb3]--&gt;&lt;pre class="jive-pre"&gt;&lt;code class="jive-code jive-java"&gt;&lt;font color="navy"&gt;&lt;b&gt;public&lt;/b&gt;&lt;/font&gt; &lt;font color="navy"&gt;&lt;b&gt;class&lt;/b&gt;&lt;/font&gt; OrderNumberGeneratorTask  &lt;font color="navy"&gt;&lt;b&gt;extends&lt;/b&gt;&lt;/font&gt; Task &lt;font color="navy"&gt;{&lt;/font&gt;
    &lt;font color="navy"&gt;&lt;b&gt;private&lt;/b&gt;&lt;/font&gt; String min = &lt;font color="navy"&gt;&lt;b&gt;null&lt;/b&gt;&lt;/font&gt;;
    &lt;font color="navy"&gt;&lt;b&gt;private&lt;/b&gt;&lt;/font&gt; String max = &lt;font color="navy"&gt;&lt;b&gt;null&lt;/b&gt;&lt;/font&gt;;
    &lt;font color="navy"&gt;&lt;b&gt;private&lt;/b&gt;&lt;/font&gt; String property = &lt;font color="navy"&gt;&lt;b&gt;null&lt;/b&gt;&lt;/font&gt;;
    &lt;font color="navy"&gt;&lt;b&gt;private&lt;/b&gt;&lt;/font&gt; String prefix = &lt;font color="navy"&gt;&lt;b&gt;null&lt;/b&gt;&lt;/font&gt;;
    &lt;font color="navy"&gt;&lt;b&gt;private&lt;/b&gt;&lt;/font&gt; Random random = &lt;font color="navy"&gt;&lt;b&gt;new&lt;/b&gt;&lt;/font&gt; Random(System.currentTimeMillis());
&amp;nbsp;
    @Override
    &lt;font color="navy"&gt;&lt;b&gt;public&lt;/b&gt;&lt;/font&gt; &lt;font color="navy"&gt;&lt;b&gt;void&lt;/b&gt;&lt;/font&gt; execute() &lt;font color="navy"&gt;&lt;b&gt;throws&lt;/b&gt;&lt;/font&gt; BuildException &lt;font color="navy"&gt;{&lt;/font&gt;
         String localPrefix = &lt;font color="navy"&gt;&lt;b&gt;null&lt;/b&gt;&lt;/font&gt;;
        &lt;font color="navy"&gt;&lt;b&gt;if&lt;/b&gt;&lt;/font&gt; (min == &lt;font color="navy"&gt;&lt;b&gt;null&lt;/b&gt;&lt;/font&gt; || min.equals(&lt;font color="red"&gt;""&lt;/font&gt;))
            &lt;font color="navy"&gt;&lt;b&gt;throw&lt;/b&gt;&lt;/font&gt; &lt;font color="navy"&gt;&lt;b&gt;new&lt;/b&gt;&lt;/font&gt; BuildException(&lt;font color="red"&gt;"Min property is missing."&lt;/font&gt;);
&amp;nbsp;
        &lt;font color="navy"&gt;&lt;b&gt;if&lt;/b&gt;&lt;/font&gt; (max == &lt;font color="navy"&gt;&lt;b&gt;null&lt;/b&gt;&lt;/font&gt; || max.equals(&lt;font color="red"&gt;""&lt;/font&gt;))
            &lt;font color="navy"&gt;&lt;b&gt;throw&lt;/b&gt;&lt;/font&gt; &lt;font color="navy"&gt;&lt;b&gt;new&lt;/b&gt;&lt;/font&gt; BuildException(&lt;font color="red"&gt;"Max property is missing."&lt;/font&gt;);
&amp;nbsp;
        &lt;font color="navy"&gt;&lt;b&gt;int&lt;/b&gt;&lt;/font&gt; minInt = Integer.parseInt(min);
        &lt;font color="navy"&gt;&lt;b&gt;int&lt;/b&gt;&lt;/font&gt; maxInt = Integer.parseInt(max);
&amp;nbsp;
        &lt;font color="navy"&gt;&lt;b&gt;if&lt;/b&gt;&lt;/font&gt; (minInt &amp;gt; maxInt)
            &lt;font color="navy"&gt;&lt;b&gt;throw&lt;/b&gt;&lt;/font&gt; &lt;font color="navy"&gt;&lt;b&gt;new&lt;/b&gt;&lt;/font&gt; BuildException(&lt;font color="red"&gt;"Min is bigger than max."&lt;/font&gt;);
&amp;nbsp;
        localPrefix = prefix;
        &lt;font color="navy"&gt;&lt;b&gt;if&lt;/b&gt;&lt;/font&gt; (localPrefix == &lt;font color="navy"&gt;&lt;b&gt;null&lt;/b&gt;&lt;/font&gt;) &lt;font color="navy"&gt;{&lt;/font&gt;
             &lt;font color="navy"&gt;&lt;b&gt;try&lt;/b&gt;&lt;/font&gt; &lt;font color="navy"&gt;{&lt;/font&gt;
                  localPrefix = InetAddress.getLocalHost().getHostAddress();
               &lt;font color="navy"&gt;}&lt;/font&gt; &lt;font color="navy"&gt;&lt;b&gt;catch&lt;/b&gt;&lt;/font&gt; (UnknownHostException e) &lt;font color="navy"&gt;{&lt;/font&gt;
                    &lt;font color="navy"&gt;&lt;b&gt;throw&lt;/b&gt;&lt;/font&gt; &lt;font color="navy"&gt;&lt;b&gt;new&lt;/b&gt;&lt;/font&gt; BuildException(&lt;font color="red"&gt;"Unable to get local host address."&lt;/font&gt;, e);
               &lt;font color="navy"&gt;}&lt;/font&gt;
        &lt;font color="navy"&gt;}&lt;/font&gt;
        &lt;font color="navy"&gt;&lt;b&gt;int&lt;/b&gt;&lt;/font&gt; randomInt = calculateRandom(minInt, maxInt);
&amp;nbsp;
        getProject().setNewProperty(property, String.valueOf(localPrefix.hashCode()) + String.valueOf(randomInt));
    &lt;font color="navy"&gt;}&lt;/font&gt;
&amp;nbsp;
    &lt;font color="navy"&gt;&lt;b&gt;protected&lt;/b&gt;&lt;/font&gt; &lt;font color="navy"&gt;&lt;b&gt;int&lt;/b&gt;&lt;/font&gt; calculateRandom(&lt;font color="navy"&gt;&lt;b&gt;final&lt;/b&gt;&lt;/font&gt; &lt;font color="navy"&gt;&lt;b&gt;int&lt;/b&gt;&lt;/font&gt; minInt, &lt;font color="navy"&gt;&lt;b&gt;final&lt;/b&gt;&lt;/font&gt; &lt;font color="navy"&gt;&lt;b&gt;int&lt;/b&gt;&lt;/font&gt; maxInt) &lt;font color="navy"&gt;{&lt;/font&gt;
        &lt;font color="navy"&gt;&lt;b&gt;return&lt;/b&gt;&lt;/font&gt; minInt + random.nextInt(maxInt - minInt + 1);
    &lt;font color="navy"&gt;}&lt;/font&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;!--[CodeBlockEnd:e4a0014f-67c1-4e73-9f2b-42e47bfadbb3]--&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;h3&gt;Updating the Build Process&lt;/h3&gt;&lt;p&gt;&lt;span style="font-size: 10pt;"&gt;To use this task, we need to package the compiled class in a jar and add it to the Maven repository along with an entry in the &lt;/span&gt;&lt;span style="font-family: 'courier new', courier;"&gt;&lt;span style="font-size: 10pt;"&gt;ant/setup/pom.xm&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: 10pt;"&gt;l so that it gets included in the ant setup script. Once the class is available on the Ant class path, we add &lt;/span&gt;the following task definition and task invocation &lt;span&gt;to the &lt;/span&gt;&lt;span style="font-family: 'courier new', courier;"&gt;&lt;span style="font-family: 'courier new', courier;"&gt;ant/default.xml&lt;/span&gt;&lt;/span&gt;&lt;span&gt; setup file. This will create the order number and store it in the &lt;/span&gt;&lt;strong&gt;ep.initial.order.number&lt;/strong&gt;&lt;span&gt; variable.&lt;/span&gt;&lt;/p&gt;&lt;!--[CodeBlockStart:fbe2dde7-fb34-4ceb-809f-015e544ac09f]--&gt;&lt;pre class="jive-pre"&gt;&lt;code class="jive-code jive-xml"&gt;&lt;span class="jive-xml-tag"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;/span&gt;

&lt;span class="jive-xml-tag"&gt;&amp;lt;project name="ant_default" xmlns:artifact="antlib:org.apache.maven.artifact.ant" xmlns:contrib="antlib:net.sf.antcontrib"&amp;gt;&lt;/span&gt;

  &lt;span class="jive-xml-tag"&gt;&amp;lt;taskdef name="ordernumbergenerator" classname="com.elasticpath.antextension.OrderNumberGeneratorTask"&amp;gt;&lt;/span&gt;&lt;span class="jive-xml-tag"&gt;&amp;lt;/taskdef&amp;gt;&lt;/span&gt;
... 
  &lt;span class="jive-xml-tag"&gt;&amp;lt;ordernumbergenerator min="1" max="1000000" property="ep.initial.order.number"&amp;gt;&lt;/span&gt;&lt;span class="jive-xml-tag"&gt;&amp;lt;/ordernumbergenerator&amp;gt;&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;&lt;!--[CodeBlockEnd:fbe2dde7-fb34-4ceb-809f-015e544ac09f]--&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 10pt;"&gt;Now we can use this variable in the &lt;/span&gt;&lt;span style="font-family: 'courier new', courier;"&gt;&lt;span style="font-size: 10pt;"&gt;database/src/base-insert.xml.vm&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: 10pt;"&gt; file as the initial order number of the &lt;/span&gt;&lt;span style="font-family: 'courier new', courier;"&gt;&lt;span style="font-size: 10pt;"&gt;TORDERNUMBERGENERATOR &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: 10pt;"&gt;table.&lt;/span&gt;&lt;/p&gt;&lt;!--[CodeBlockStart:5d8f0cc4-a612-43f6-87ea-acf6db8b68ee]--&gt;&lt;pre class="jive-pre"&gt;&lt;code class="jive-code jive-xml"&gt;     &lt;span class="jive-xml-tag"&gt;&amp;lt;Tordernumbergenerator Uidpk="1" NextOrderNumber="${ep_initial_order_number}" /&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;!--[CodeBlockEnd:5d8f0cc4-a612-43f6-87ea-acf6db8b68ee]--&gt;&lt;/div&gt;&lt;!-- [DocumentBodyEnd:d53736ab-f277-4359-b762-04a39d567e2c] --&gt;</description>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">order</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">order_number</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">unique</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">payment_gateway</category>
      <pubDate>Mon, 07 Jun 2010 23:39:12 GMT</pubDate>
      <author>anthony.mcaffee@elasticpath.com</author>
      <guid>http://grep.elasticpath.com/community/techblog/blog/2010/06/24/unique-cross-environment-order-numbers</guid>
      <dc:date>2010-06-07T23:39:12Z</dc:date>
      <clearspace:dateToText>1 month, 3 weeks ago</clearspace:dateToText>
    </item>
    <item>
      <title>Customizing PMD Rules for use in Elastic Path code</title>
      <link>http://grep.elasticpath.com/community/techblog/blog/2010/06/24/customizing-pmd-rules-for-use-in-elastic-path-code</link>
      <description>&lt;!-- [DocumentBodyStart:7927d174-1691-4850-b37b-62a42d7316f5] --&gt;&lt;div class='jive-rendered-content'&gt;&lt;h1&gt;&lt;span style="font-weight: normal; font-size: 13px;"&gt;PMD contains a useful set of default rules for enforcing best practices in Java code, but in some circumstances it may be useful to create additional PMD rules for best practices that are specific to your company's internal coding standards, a particular Java framework (such as Spring, OpenJPA, DROOLS, etc), or specifics of the Elastic Path code-base. Capturing these best-practices in PMD rules is often more effective than simply posting a list of standards on a wiki, which can easily be ignored or forgotten. Adding custom PMD rules is relatively simple, but not immediately obvious. In this blog post, I will outline the steps for adding custom PMD rules to your Elastic Path project and provide a reference for PMD attributes which do not appear to be documented anywhere online.&lt;/span&gt;&lt;/h1&gt;&lt;h1&gt;Defining an XPath Expression for the Rule&lt;/h1&gt;&lt;p&gt;There are two ways to create custom PMD rules:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Write a rule using Java&lt;/li&gt;&lt;li&gt;Write an XPath expression&lt;/li&gt;&lt;/ul&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;XPath expressions are much quicker to create, and are flexible enough to identify most conditions. For the purposes of this blog post, I will only focus on XPath expressions. For details about creating rules using Java classes, read &lt;a class="jive-link-external-small" href="http://pmd.sourceforge.net/howtowritearule.html"&gt;PMD - How to Write a Rule&lt;/a&gt;.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;The best way to get started is to write a simple class or interface that violates the rule that you chose.  For example, &lt;a class="jive-link-external-small" href="http://forum.springsource.org/showthread.php?t=42900"&gt;best practices state that Spring setters should be public on the class but not exposed on the interface&lt;/a&gt;. So I will create a rule to identify Spring setters that are exposed on an interface. Here is my simple example:&lt;/p&gt;&lt;!--[CodeBlockStart:c1321ed0-b838-480d-b3ff-457d08f6534e]--&gt;&lt;pre class="jive-pre"&gt;&lt;code class="jive-code jive-java"&gt;&lt;font color="navy"&gt;&lt;b&gt;public&lt;/b&gt;&lt;/font&gt; &lt;font color="navy"&gt;&lt;b&gt;interface&lt;/b&gt;&lt;/font&gt; CustomerAuthenticationService &lt;font color="navy"&gt;{&lt;/font&gt;
    &lt;font color="navy"&gt;&lt;b&gt;void&lt;/b&gt;&lt;/font&gt; setElasticPath(&lt;font color="navy"&gt;&lt;b&gt;final&lt;/b&gt;&lt;/font&gt; ElasticPath elasticPath);
    &lt;font color="navy"&gt;&lt;b&gt;void&lt;/b&gt;&lt;/font&gt; setStoreProductService(&lt;font color="navy"&gt;&lt;b&gt;final&lt;/b&gt;&lt;/font&gt; StoreProductService storeProductService);
&lt;font color="navy"&gt;}&lt;/font&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;!--[CodeBlockEnd:c1321ed0-b838-480d-b3ff-457d08f6534e]--&gt;&lt;p&gt;First, launch the PMD Rule Designer. This can be run from the command line by executing &lt;span style="font-family: 'courier new',courier;"&gt;designer.bat&lt;/span&gt; (or &lt;span style="font-family: 'courier new',courier;"&gt;designer.sh&lt;/span&gt; in Linux) from &lt;span style="font-family: 'courier new',courier;"&gt;&amp;lt;PMD_ROOT&amp;gt;/bin&lt;/span&gt;.  Alternatively, this tool can be launched from Eclipse (if PMD for Eclipse is installed) by opening &lt;strong&gt;Window --&amp;gt; Preferences&lt;/strong&gt;, then &lt;strong&gt;PMD --&amp;gt; Rules Configuration&lt;/strong&gt;, and clicking the &lt;strong&gt;Rule Designer&lt;/strong&gt; button. Paste your example code into the "Source Code" text box, and then click the &lt;strong&gt;Go&lt;/strong&gt; button below the "XPath Query" text box. The designer should then show an Abstract Syntax Tree as shown in the following screenshot.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;a href="http://grep.elasticpath.com/servlet/JiveServlet/showImage/38-1106-1494/PMD+Rule+Designer+1.png"&gt;&lt;img alt="PMD Rule Designer 1.png" class="jive-image-thumbnail jive-image" src="http://grep.elasticpath.com/servlet/JiveServlet/downloadImage/38-1106-1494/PMD+Rule+Designer+1.png" width="620"/&gt;&lt;/a&gt;&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;We can use the Abstract Syntax Tree (AST) to help us create an XPath expression to identify the problem code. Each node in the AST represents an element of the &lt;a class="jive-link-external-small" href="http://java.sun.com/docs/books/jls/second_edition/html/syntax.doc.html"&gt;Java Syntactical Grammar&lt;/a&gt; representing the example code we specified.  The XPath expression should navigate through the grammar hierarchy and select elements that violate your chosen rule. PMD also supports the use of XPath attributes (in square brackets) to further filter specific grammar elements based on name, access modifiers, class types, level of nesting, existence of comments, etc. Unfortunately, these attributes are not listed in the Rule Designer or documented anywhere on the PMD site. At the end of this article, I've listed some of the more useful PMD attributes (pulled from existing PMD rules).&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;If your XPath syntax skills are a bit rusty, &lt;a class="jive-link-external-small" href="http://msdn.microsoft.com/en-us/library/ms256471.aspx"&gt;Microsoft has an excellent tutorial&lt;/a&gt; available. There is also a simple &lt;a class="jive-link-external-small" href="http://pmd.sourceforge.net/xpathruletutorial.html"&gt;XPath Rule Tutorial&lt;/a&gt; available on the PMD site. You may also find it useful to review the XPath expressions for the default PMD rules in  &lt;span style="font-family: 'courier new',courier;"&gt;&amp;lt;PROJECT_ROOT&amp;gt;/ant/target/pmd/pmd-elasticpath-rules.xml&lt;/span&gt; to get a better feel for available XPath grammar nodes and attributes.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Here is the XPath expression that I came up with for this rule:&lt;/p&gt;&lt;!--[CodeBlockStart:a45557bf-f456-4d4b-b406-ec0fa7ee5f09]--&gt;&lt;pre class="jive-pre"&gt;&lt;code class="jive-code"&gt;//ClassOrInterfaceDeclaration[@Interface='true']/ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/MethodDeclaration/MethodDeclarator[starts-with(@Image, 'set') and (ends-with(@Image, 'Service') or ends-with(@Image, 'ElasticPath') or ends-with(@Image, 'Helper') or ends-with(@Image, 'Utility') or ends-with(@Image, 'Engine'))&lt;/code&gt;&lt;/pre&gt;&lt;!--[CodeBlockEnd:a45557bf-f456-4d4b-b406-ec0fa7ee5f09]--&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;To test the XPath expression in the Rule Designer, simply paste the expression into the "XPath Query" field, and click &lt;strong&gt;Go&lt;/strong&gt; again.  We know our XPath expression works because the field to the right of the Abstract Syntax Tree indicates the position of two matches from our example source code (one for each setter method). Notice that the XPath expression closely matches the node heirarchy in the Abstract Syntax Tree.&lt;/p&gt;&lt;h1&gt;Adding the Rule to the Ruleset File&lt;/h1&gt;&lt;p&gt;Now that we have created the XPath expression for our rule, we need to add it to our project's ruleset file. Open &lt;span style="font-family: 'courier new',courier;"&gt;&amp;lt;PROJECT_ROOT&amp;gt;/ant/target/pmd/pmd-elasticpath-rules.xml&lt;/span&gt; in a text editor and add the following immediately before the terminating &lt;span style="font-family: 'courier new',courier;"&gt;&amp;lt;/ruleset&amp;gt;&lt;/span&gt; tag:&lt;/p&gt;&lt;!--[CodeBlockStart:3115b71e-2d70-49a4-9c1b-daf20fd5f604]--&gt;&lt;pre class="jive-pre"&gt;&lt;code class="jive-code"&gt;&amp;lt;rule name="SpringInjectedSetterInInterface" 
      message="Spring-injected setters should not be declared in interface." 
      class="net.sourceforge.pmd.rules.XPathRule" dfa="false" externalInfoUrl="" typeResolution="true"&amp;gt;
    &amp;lt;description&amp;gt;Spring-injected setters should not be declared in interface.&amp;lt;/description&amp;gt;
    &amp;lt;priority&amp;gt;3&amp;lt;/priority&amp;gt;
    &amp;lt;properties&amp;gt;
        &amp;lt;property name="xpath"&amp;gt;
            &amp;lt;value&amp;gt;&amp;lt;![CDATA[//ClassOrInterfaceDeclaration[@Interface='true']/ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/MethodDeclaration/MethodDeclarator[starts-with(@Image, 'set') and (ends-with(@Image, 'Service') or ends-with(@Image, 'ElasticPath') or ends-with(@Image, 'Helper') or ends-with(@Image, 'Utility') or ends-with(@Image, 'Engine'))]]]&amp;gt;&amp;lt;/value&amp;gt;
        &amp;lt;/property&amp;gt;
    &amp;lt;/properties&amp;gt;
    &amp;lt;example&amp;gt;&amp;lt;![CDATA[
public interface CustomerAuthenticationService {
     void setElasticPath(final ElasticPath elasticPath);
     void setStoreProductService(final StoreProductService storeProductService);
}
    ]]&amp;gt;&amp;lt;/example&amp;gt;
&amp;lt;/rule&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;!--[CodeBlockEnd:3115b71e-2d70-49a4-9c1b-daf20fd5f604]--&gt;&lt;p&gt;The rule tag contains the following attributes:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-family: 'courier new',courier;"&gt;name&lt;/span&gt;: The rule name which will be used for suppression annotations and for identifying the rule in build-stats.&lt;/li&gt;&lt;li&gt;&lt;span style="font-family: 'courier new',courier;"&gt;message&lt;/span&gt;: The friendly rule description which will be displayed when the rule is violated.&lt;/li&gt;&lt;li&gt;&lt;span style="font-family: 'courier new',courier;"&gt;class&lt;/span&gt;: For XPath-based rules, this should always be set to "net.sourceforge.pmd.rules.XPathRule"&lt;/li&gt;&lt;li&gt;&lt;span style="font-family: 'courier new',courier;"&gt;dfa&lt;/span&gt;: Data Flow Analysis - this should always be set to "false" for XPath rules.&lt;br/&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family: 'courier new',courier;"&gt;externalInfoUrl&lt;/span&gt;: If there is a web page that provides additional information about the violation, provide the link here.&lt;br/&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family: 'courier new',courier;"&gt;typeResolution:This &lt;/span&gt;should always be set to "false" for XPath rules.&lt;br/&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;The rule tag also contains the following inner tags:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-family: 'courier new',courier;"&gt;description&lt;/span&gt;: This should be the same as the rule tag message attribute value.&lt;br/&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family: 'courier new',courier;"&gt;priority&lt;/span&gt;: An integer value in the range 1 to 5: &lt;ul&gt;&lt;li&gt;1 = Error High - &lt;strong&gt;Change absolutely required.&lt;/strong&gt; Behavior is critically broken/buggy.&lt;/li&gt;&lt;li&gt;2 = Error - &lt;strong&gt;Change highly recommended.&lt;/strong&gt; Behavior is quite likely to be broken/buggy.&lt;/li&gt;&lt;li&gt;3 = Warning High - &lt;strong&gt;Change recommended.&lt;/strong&gt; Behavior is confusing, perhaps buggy, and/or against standards/best practices.&lt;/li&gt;&lt;li&gt;4 = Warning - &lt;strong&gt;Change optional.&lt;/strong&gt; Behavior is not likely to be buggy, but more just flies in the face of standards/style/good taste.&lt;/li&gt;&lt;li&gt;5 = Information - &lt;strong&gt;Change highly optional.&lt;/strong&gt; Nice to have, such as a consistent naming policy for package/class/fields.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family: 'courier new',courier;"&gt;properties&lt;/span&gt;: This should contain a single &lt;span style="font-family: 'courier new',courier;"&gt;&amp;lt;property&amp;gt;&lt;/span&gt; tag with &lt;span style="font-family: 'courier new',courier;"&gt;name="xpath&lt;/span&gt;", containing a &lt;span style="font-family: 'courier new',courier;"&gt;&amp;lt;value&amp;gt;&lt;/span&gt; tag with the XPath expression.&lt;br/&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family: 'courier new',courier;"&gt;example&lt;/span&gt;: A simple example of the rule being violated.  It is often best to simply use the class or interface that you created for the Rule Designer when designing your XPath expression.&lt;br/&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h1&gt;Making the Rule Available to Ant and PMD for Eclipse&lt;/h1&gt;&lt;p&gt;Once the ruleset file has been updated, we can immediately run PMD from the command line by running &lt;span style="font-family: 'courier new',courier;"&gt;ant pmd-all&lt;/span&gt; from the project root, or &lt;span style="font-family: 'courier new',courier;"&gt;ant pmd&lt;/span&gt; from a specific project folder.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;To detect the new rule from Eclipse using the PMD-Eclipse plugin, follow these steps:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;From the root of the EP source directory, run &lt;span style="font-family: 'courier new',courier;"&gt;ant eclipse-setup-all&lt;/span&gt;.&lt;br/&gt;&lt;/li&gt;&lt;li&gt; Refresh all projects in Eclipse. &lt;/li&gt;&lt;li&gt; Right-click on the project and click &lt;strong&gt;PMD-&amp;gt;Check Code&lt;/strong&gt; with PMD. &lt;/li&gt;&lt;li&gt; Wait for "Review Code" task to complete.&lt;/li&gt;&lt;/ol&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;The new rule violations should now appear in your "Problems" view:&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;a href="http://grep.elasticpath.com/servlet/JiveServlet/showImage/38-1106-1508/PMD_Problems_1.png"&gt;&lt;img alt="PMD_Problems_1.png" class="jive-image" src="http://grep.elasticpath.com/servlet/JiveServlet/downloadImage/38-1106-1508/PMD_Problems_1.png"/&gt;&lt;/a&gt;&lt;/p&gt;&lt;h1&gt;&lt;span style="font-weight: normal; font-size: 13px;"&gt;Custom PMD rules are an effective way to automatically enforce coding standards and best practices for all developers in a project. Since rule checking is automatic, developers don't need to know all of the standards by heart; they can learn the rules by breaking them and getting immediate feedback. This helps to train developers to create code that is more consistent and readable by all members of the team. There are even circumstances where PMD can identify bugs that wouldn't get caught by the Java compiler.&lt;/span&gt;&lt;/h1&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;For example, a team I worked on was responsible for continuous improvement on a project that involved extensive use of integration tests to validate core functionality. Most of the integration classes extended an abstract class that provided helper methods and a constructor for setting up the initial database. Unfortunately, some of the test classes defined their own constructors that would re-initialize the database, even though the abstract class had already done the initialization. Although this didn't cause any actual problems for the tests, it significantly slowed down execution of the tests. A PMD rule was written to identify these circumstances. By creating this rule, we were able to not only validate that all of the test classes were fixed, but we were also able to ensure that future developers didn't make the same mistake.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Try to identify common pitfalls for your own team's developers, and create custom PMD rules to flag such patterns for future developers. The following attributes are grouped based on which node type they are available for. This is not an exhaustive list, but it represents the more commonly-used attributes.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;ul&gt;&lt;li&gt; All  &lt;ul&gt;&lt;li&gt; @Image (string) - Class/method/variable name &lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt; ClassOrInterfaceDeclaration  &lt;ul&gt;&lt;li&gt; @Interface (boolean) - Is an interface &lt;/li&gt;&lt;li&gt; @Class (boolean) - Is a class &lt;/li&gt;&lt;li&gt; @Nested (boolean) - Is a nested class (or interface) &lt;/li&gt;&lt;li&gt; @Abstract (boolean) - Is an abstract class &lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt; Block  &lt;ul&gt;&lt;li&gt; @ContainsComment (boolean) - Contains a comment &lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt; Initializer  &lt;ul&gt;&lt;li&gt; @Static (boolean) - Is declared as static &lt;/li&gt;&lt;li&gt; @Final (boolean) - Is declared as final &lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt; IfStatement  &lt;ul&gt;&lt;li&gt; @Else (boolean) - Is else portion of an if statement &lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt; SwitchStatement  &lt;ul&gt;&lt;li&gt; @Default (boolean) - Is default section of a switch statement &lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt; AllocationExpression  &lt;ul&gt;&lt;li&gt; @ArgumentCount (int) - Number of arguments in expression &lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt; FieldDeclaration / ConstructorDeclaration / MethodDeclaration  &lt;ul&gt;&lt;li&gt; @Public(boolean) - Is declared as public &lt;/li&gt;&lt;li&gt; @Protected (boolean) - Is declared as protected &lt;/li&gt;&lt;li&gt; @Private (boolean) - Is declared as private &lt;/li&gt;&lt;li&gt; @Static (boolean) - Is declared as static &lt;/li&gt;&lt;li&gt; @Synchronized (boolean) - Is declared as synchronized &lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt; PrimaryExpression  &lt;ul&gt;&lt;li&gt; @ArrayDereference (boolean)&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;!-- [DocumentBodyEnd:7927d174-1691-4850-b37b-62a42d7316f5] --&gt;</description>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">best_practices</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">pmd</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">coding_standards</category>
      <pubDate>Thu, 03 Jun 2010 15:36:44 GMT</pubDate>
      <author>developer.forums@elasticpath.com</author>
      <guid>http://grep.elasticpath.com/community/techblog/blog/2010/06/24/customizing-pmd-rules-for-use-in-elastic-path-code</guid>
      <dc:date>2010-06-03T15:36:44Z</dc:date>
      <clearspace:dateToText>1 month, 3 weeks ago</clearspace:dateToText>
    </item>
    <item>
      <title>Automated Selenium Testing with Maven</title>
      <link>http://grep.elasticpath.com/community/techblog/blog/2010/06/16/automated-selenium-testing-with-maven</link>
      <description>&lt;!-- [DocumentBodyStart:11959977-cb78-42e0-a158-fd9de44e9cd1] --&gt;&lt;div class='jive-rendered-content'&gt;&lt;p&gt;&lt;em&gt;(Or: How to Copy and Paste XML Your Way to Greatness)&lt;/em&gt;&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;We have a truckload of Selenium tests that poke and probe away at the user interface of our project here at EP. We realized it would be awesome if this testing could just automagically happen and let us know the results. So we took a look at our Maven build and realized the steps we wanted to automate were:&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Check out our source code, build the latest set of artifacts and deploy them to our internal Maven repository.&lt;/li&gt;&lt;li&gt;Stop the web application running on our QA server environment.&lt;/li&gt;&lt;li&gt;Recreate our test MySQL database with our default schema, apply any change sets needed to bring it to the latest version and load our QA data.&lt;/li&gt;&lt;li&gt;Update the test data to reflect our QA server environment.&lt;/li&gt;&lt;li&gt;Deploy our demo assets to a known location.&lt;/li&gt;&lt;li&gt;&lt;span style="color: #000000;"&gt;Start our web application and wait for it to fully start.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="color: #000000;"&gt;Launch Selenium and run our tests against our application on Firefox on our Linux Hudson slave.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="color: #000000;"&gt;Record and announce the results of the tests.&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;In this blog post, I'm going to cover how we automated the last three steps.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;The first thing we needed to do was find a tool that could automate configuring a web app container and deploying our WAR files. This tool would need to support multiple web containers without having to write custom scripts for each one. After doing some research, we decided to go with the fairly capable &lt;a class="jive-link-external-small" href="http://cargo.codehaus.org/Home"&gt;Cargo&lt;/a&gt; project. It has a fairly mature Maven plugin and good support for our preferred container, Tomcat, and it's come a &lt;em&gt;long&lt;/em&gt; way since its inception. On our side, all we'd need to do is write a simple Groovy script to wait until our web application is in a ready-to-test state.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Our particular application really benefits from the new &lt;a class="jive-link-external-small" href="http://code.google.com/p/selenium/wiki/GettingStarted"&gt;WebDriver&lt;/a&gt; features in Selenium 2.0, and while we'd love to take advantage of the &lt;a class="jive-link-external-small" href="http://mojo.codehaus.org/selenium-maven-plugin/"&gt;Selenium Plugin for Maven&lt;/a&gt;, it's tied to Selenium 1.0, so we can't fully use it yet. We can however use it to bootstrap an X server on our Hudson build slave.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Our Selenium tests are written using &lt;a class="jive-link-external-small" href="http://testng.org/doc/documentation-main.html"&gt;TestNG&lt;/a&gt; and stored in their own Maven artifact. We can easily bind the Maven Failsafe Plugin to run during our &lt;strong&gt;integration-test&lt;/strong&gt; phase, but we'd like to publish the output to our internal reporting site, so we can take a look at the last results. Luckily, we can use the Maven Surefire Report plugin to generate our site. Lastly, we'll get Hudson to announce the results of our test. (The page on the &lt;a class="jive-link-external-small" href="http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#Lifecycle_Reference"&gt;Maven build lifecycle&lt;/a&gt; is invaluable when figuring out when to run different plugins.)&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;So, let's get started!&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 14pt;"&gt;Properties&lt;/span&gt;&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;To make Cargo and Selenium more useful, we pushed a handful of settings into a parent POM. This lets us override values and use the same artifacts for our own local development testing.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;!--[CodeBlockStart:4b8f6f4a-c937-4e5c-a82d-d48d00bef146]--&gt;&lt;pre class="jive-pre"&gt;&lt;code class="jive-code jive-xml"&gt;&lt;span class="jive-xml-tag"&gt;&amp;lt;properties&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;cargo.jvmargs&amp;gt;&lt;/span&gt;-XX:MaxPermSize=512M -Xmx1024m&lt;span class="jive-xml-tag"&gt;&amp;lt;/cargo.jvmargs&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;cargo.servlet.port&amp;gt;&lt;/span&gt;8080&lt;span class="jive-xml-tag"&gt;&amp;lt;/cargo.servlet.port&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;cargo.tomcat.ajp.port&amp;gt;&lt;/span&gt;8009&lt;span class="jive-xml-tag"&gt;&amp;lt;/cargo.tomcat.ajp.port&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;cargo.rmi.port&amp;gt;&lt;/span&gt;1099&lt;span class="jive-xml-tag"&gt;&amp;lt;/cargo.rmi.port&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;cargo.tomcat.shutdown.port&amp;gt;&lt;/span&gt;8205&lt;span class="jive-xml-tag"&gt;&amp;lt;/cargo.tomcat.shutdown.port&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;cargo.wait&amp;gt;&lt;/span&gt;true&lt;span class="jive-xml-tag"&gt;&amp;lt;/cargo.wait&amp;gt;&lt;/span&gt;

    &lt;span class="jive-xml-tag"&gt;&amp;lt;demo.hostname&amp;gt;&lt;/span&gt;10.9.8.7&lt;span class="jive-xml-tag"&gt;&amp;lt;/demo.hostname&amp;gt;&lt;/span&gt;

    &lt;span class="jive-xml-tag"&gt;&amp;lt;searchserver.context&amp;gt;&lt;/span&gt;searchserver&lt;span class="jive-xml-tag"&gt;&amp;lt;/searchserver.context&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;ourapp.context&amp;gt;&lt;/span&gt;ourapp&lt;span class="jive-xml-tag"&gt;&amp;lt;/ourapp.context&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;cmserver.context&amp;gt;&lt;/span&gt;cmserver&lt;span class="jive-xml-tag"&gt;&amp;lt;/cmserver.context&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;storefront.context&amp;gt;&lt;/span&gt;storefront&lt;span class="jive-xml-tag"&gt;&amp;lt;/storefront.context&amp;gt;&lt;/span&gt;

    &lt;span class="jive-xml-comment"&gt;&amp;lt;!-- Absolute --&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;assets.directory&amp;gt;&lt;/span&gt;${user.home}/ep-assets&lt;span class="jive-xml-tag"&gt;&amp;lt;/assets.directory&amp;gt;&lt;/span&gt;

    &lt;span class="jive-xml-tag"&gt;&amp;lt;jdbc.driver&amp;gt;&lt;/span&gt;com.mysql.jdbc.Driver&lt;span class="jive-xml-tag"&gt;&amp;lt;/jdbc.driver&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;jdbc.host&amp;gt;&lt;/span&gt;10.9.8.7:3306&lt;span class="jive-xml-tag"&gt;&amp;lt;/jdbc.host&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;jdbc.db&amp;gt;&lt;/span&gt;OURAPP_DB&lt;span class="jive-xml-tag"&gt;&amp;lt;/jdbc.db&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;jdbc.username&amp;gt;&lt;/span&gt;atwill&lt;span class="jive-xml-tag"&gt;&amp;lt;/jdbc.username&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;jdbc.password&amp;gt;&lt;/span&gt;grep&lt;span class="jive-xml-tag"&gt;&amp;lt;/jdbc.password&amp;gt;&lt;/span&gt;
&lt;span class="jive-xml-tag"&gt;&amp;lt;/properties&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;!--[CodeBlockEnd:4b8f6f4a-c937-4e5c-a82d-d48d00bef146]--&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Okay, that's more than a handful. but let's go over them really quickly. The &lt;strong&gt;cargo.* &lt;/strong&gt;properties are there to let us easily have multiple Tomcats running on the same IP, the &lt;strong&gt;*.context&lt;/strong&gt; properties tell Cargo where to deploy our various WAR files. The &lt;strong&gt;cargo.wait&lt;/strong&gt; property tells Cargo to pause after it starts the application server. If set to &lt;strong&gt;false&lt;/strong&gt;, Cargo will proceed along the Maven build lifecycle.  The &lt;strong&gt;assets.directory&lt;/strong&gt; is used by steps 4 and 5.  The &lt;strong&gt;demo.hostname&lt;/strong&gt; is the public hostname (or IP in our case) for our deployment (you'll see why we do this as we move along). The &lt;strong&gt;jdbc.* &lt;/strong&gt;properties describe our JDBC DataSource. We've broken the information up so that we can use it in steps 3, 4 and 6. As you can imagine, it's pretty easy to override these properties in your local settings.xml and use Cargo to start a local app server for you - lucky you!&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 14pt;"&gt;Containing the Excitement&lt;/span&gt;&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Our first goal is to start a container, deploy our artifacts and wait for them to all start. For our situation, we felt it was best to have a separate &lt;strong&gt;container&lt;/strong&gt; Maven artifact (with &lt;strong&gt;pom&lt;/strong&gt; packaging) dedicated to steps 5 and 6. So, first we'll declare the WAR and JAR files that we depend on for the container. We included the JDBC driver and the JAI JARs for our Storefront too. We've already declared the versions for all these artifacts in a parent POM.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;!--[CodeBlockStart:ff8c3b8d-0c01-487f-898b-ef1b21f29188]--&gt;&lt;pre class="jive-pre"&gt;&lt;code class="jive-code jive-xml"&gt;....            
           &lt;span class="jive-xml-tag"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt; 
                &lt;span class="jive-xml-comment"&gt;&amp;lt;!-- WARs to be deployed by Cargo (also add them in cargo below) --&amp;gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.elasticpath.mdm&lt;span class="jive-xml-tag"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;ourapp-war&lt;span class="jive-xml-tag"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;type&amp;gt;&lt;/span&gt;war&lt;span class="jive-xml-tag"&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

                &lt;span class="jive-xml-tag"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.elasticpath.mdm&lt;span class="jive-xml-tag"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;searchserver&lt;span class="jive-xml-tag"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;type&amp;gt;&lt;/span&gt;war&lt;span class="jive-xml-tag"&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

                &lt;span class="jive-xml-tag"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.elasticpath.mdm&lt;span class="jive-xml-tag"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;cmserver&lt;span class="jive-xml-tag"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;type&amp;gt;&lt;/span&gt;war&lt;span class="jive-xml-tag"&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

                &lt;span class="jive-xml-tag"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.elasticpath.mdm&lt;span class="jive-xml-tag"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;storefront&lt;span class="jive-xml-tag"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;type&amp;gt;&lt;/span&gt;war&lt;span class="jive-xml-tag"&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

                &amp;lt;!-- Dependencies provided to Cargo --&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;mysql&lt;span class="jive-xml-tag"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;mysql-connector-java&lt;span class="jive-xml-tag"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;provided&lt;span class="jive-xml-tag"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

                &lt;span class="jive-xml-tag"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;javax.media&lt;span class="jive-xml-tag"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;jai-core&lt;span class="jive-xml-tag"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;provided&lt;span class="jive-xml-tag"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

                &lt;span class="jive-xml-tag"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.sun.media&lt;span class="jive-xml-tag"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;jai-codec&lt;span class="jive-xml-tag"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;provided&lt;span class="jive-xml-tag"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;!--[CodeBlockEnd:ff8c3b8d-0c01-487f-898b-ef1b21f29188]--&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Next up, we'll start Cargo in the &lt;strong&gt;pre-integration-test&lt;/strong&gt; phase and stop it when we hit the &lt;strong&gt;post-integration-test&lt;/strong&gt; phase. If you're copying and pasting this XML, you'll want to paste the next three sections in sequence into your POM, but we'll cover them individually.&lt;/p&gt;&lt;!--[CodeBlockStart:5baf7a04-7b8c-4ad2-820f-29723fe0755c]--&gt;&lt;pre class="jive-pre"&gt;&lt;code class="jive-code jive-xml"&gt;&lt;span class="jive-xml-tag"&gt;&amp;lt;build&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;plugins&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
            &lt;span class="jive-xml-tag"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.codehaus.cargo&lt;span class="jive-xml-tag"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="jive-xml-tag"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;cargo-maven2-plugin&lt;span class="jive-xml-tag"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="jive-xml-tag"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.0.1-beta-2&lt;span class="jive-xml-tag"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
            &lt;span class="jive-xml-tag"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;start-container&lt;span class="jive-xml-tag"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;pre-integration-test&lt;span class="jive-xml-tag"&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                            &lt;span class="jive-xml-tag"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;start&lt;span class="jive-xml-tag"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;stop-container&lt;span class="jive-xml-tag"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;post-integration-test&lt;span class="jive-xml-tag"&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                            &lt;span class="jive-xml-tag"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;stop&lt;span class="jive-xml-tag"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
            &lt;span class="jive-xml-tag"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;

            &lt;span class="jive-xml-tag"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;container&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
                            &lt;span class="jive-xml-tag"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;mysql&lt;span class="jive-xml-tag"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                            &lt;span class="jive-xml-tag"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;mysql-connector-java&lt;span class="jive-xml-tag"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

                        &lt;span class="jive-xml-tag"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
                            &lt;span class="jive-xml-tag"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;javax.media&lt;span class="jive-xml-tag"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                            &lt;span class="jive-xml-tag"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;jai-core&lt;span class="jive-xml-tag"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

                        &lt;span class="jive-xml-tag"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
                            &lt;span class="jive-xml-tag"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.sun.media&lt;span class="jive-xml-tag"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                            &lt;span class="jive-xml-tag"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;jai-codec&lt;span class="jive-xml-tag"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;

                    &lt;span class="jive-xml-tag"&gt;&amp;lt;containerId&amp;gt;&lt;/span&gt;tomcat5x&lt;span class="jive-xml-tag"&gt;&amp;lt;/containerId&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;zipUrlInstaller&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;file:${project.basedir}/apache-tomcat-5.5.29.zip&lt;span class="jive-xml-tag"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;installDir&amp;gt;&lt;/span&gt;${installDir}&lt;span class="jive-xml-tag"&gt;&amp;lt;/installDir&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;/zipUrlInstaller&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;/container&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;!--[CodeBlockEnd:5baf7a04-7b8c-4ad2-820f-29723fe0755c]--&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;In the previous example, we bind the plugin to the lifecycle and tell Cargo to include the JDBC driver and JAI JARs into the classpath. (We won't get native acceleration for JAI, but that's fine for our purposes.) You'll probably notice the tricky thing we did for the &lt;strong&gt;zipUrlInstaller&lt;/strong&gt;. To remove a dependency from our startup, we've checked in a copy of Tomcat into our project. Let's take a look at the Cargo configuration:&lt;/p&gt;&lt;!--[CodeBlockStart:04da4106-cb16-492f-95ae-6309a50146e4]--&gt;&lt;pre class="jive-pre"&gt;&lt;code class="jive-code jive-xml"&gt;&lt;span class="jive-xml-tag"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;home&amp;gt;&lt;/span&gt;${project.build.directory}/tomcat5x/container&lt;span class="jive-xml-tag"&gt;&amp;lt;/home&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;properties&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;cargo.jvmargs&amp;gt;&lt;/span&gt;${cargo.jvmargs}&lt;span class="jive-xml-tag"&gt;&amp;lt;/cargo.jvmargs&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;cargo.logging&amp;gt;&lt;/span&gt;high&lt;span class="jive-xml-tag"&gt;&amp;lt;/cargo.logging&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;cargo.servlet.port&amp;gt;&lt;/span&gt;${cargo.servlet.port}&lt;span class="jive-xml-tag"&gt;&amp;lt;/cargo.servlet.port&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;cargo.tomcat.ajp.port&amp;gt;&lt;/span&gt;${cargo.tomcat.ajp.port}&lt;span class="jive-xml-tag"&gt;&amp;lt;/cargo.tomcat.ajp.port&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;cargo.rmi.port&amp;gt;&lt;/span&gt;${cargo.rmi.port}&lt;span class="jive-xml-tag"&gt;&amp;lt;/cargo.rmi.port&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;cargo.tomcat.shutdown.port&amp;gt;&lt;/span&gt;${cargo.tomcat.shutdown.port}&lt;span class="jive-xml-tag"&gt;&amp;lt;/cargo.tomcat.shutdown.port&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-comment"&gt;&amp;lt;!--
            Ampersands must be escaped such that they show up as "&amp;amp;" in
            the resulting XML file, so we use "&amp;amp;amp;" here.
        --&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;cargo.datasource.datasource&amp;gt;&lt;/span&gt;
            cargo.datasource.url=jdbc:mysql://${jdbc.host}/${jdbc.db}?AutoReconnect=true&amp;amp;amp;amp;useUnicode=true&amp;amp;amp;amp;characterEncoding=utf-8|
            cargo.datasource.driver=${jdbc.driver}|
            cargo.datasource.username=${jdbc.username}|
            cargo.datasource.password=${jdbc.password}|
            cargo.datasource.type=javax.sql.DataSource|
            cargo.datasource.jndi=jdbc/epjndi
        &lt;span class="jive-xml-tag"&gt;&amp;lt;/cargo.datasource.datasource&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;/properties&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;!--[CodeBlockEnd:04da4106-cb16-492f-95ae-6309a50146e4]--&gt;&lt;p&gt;Here we're passing in the &lt;strong&gt;cargo.* &lt;/strong&gt;properties from our parent POM and we're asking Cargo to create a DataSource for us with the &lt;strong&gt;jdbc.*&lt;/strong&gt; properties and add it to the JNDI tree.  For our situation, we're always using MySQL, so we can make a handful of assumptions in the URL.  Now that we've picked a container and configured it, we'll tell Cargo which artifacts we'd like to have deployed:&lt;/p&gt;&lt;!--[CodeBlockStart:47058c46-4240-48b6-b28a-e931bf72adb4]--&gt;&lt;pre class="jive-pre"&gt;&lt;code class="jive-code jive-xml"&gt;            &lt;span class="jive-xml-tag"&gt;&amp;lt;deployables&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-comment"&gt;&amp;lt;!-- Applications to Deploy --&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;deployable&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.elasticpath.mdm&lt;span class="jive-xml-tag"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;ourapp-war&lt;span class="jive-xml-tag"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;type&amp;gt;&lt;/span&gt;war&lt;span class="jive-xml-tag"&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;properties&amp;gt;&lt;/span&gt;
                            &lt;span class="jive-xml-tag"&gt;&amp;lt;context&amp;gt;&lt;/span&gt;/${ourapp.context}&lt;span class="jive-xml-tag"&gt;&amp;lt;/context&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;/properties&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;pingURL&amp;gt;&lt;/span&gt;http://${demo.hostname}:${cargo.servlet.port}/${ourapp.context}
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;/pingURL&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;pingTimeout&amp;gt;&lt;/span&gt;60000&lt;span class="jive-xml-tag"&gt;&amp;lt;/pingTimeout&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;/deployable&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;deployable&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.elasticpath.mdm&lt;span class="jive-xml-tag"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;searchserver&lt;span class="jive-xml-tag"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;type&amp;gt;&lt;/span&gt;war&lt;span class="jive-xml-tag"&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;properties&amp;gt;&lt;/span&gt;
                            &lt;span class="jive-xml-tag"&gt;&amp;lt;context&amp;gt;&lt;/span&gt;/${searchserver.context}&lt;span class="jive-xml-tag"&gt;&amp;lt;/context&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;/properties&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;pingURL&amp;gt;&lt;/span&gt;http://${demo.hostname}:${cargo.servlet.port}/${searchserver.context}/product/select?q=*:*
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;/pingURL&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;pingTimeout&amp;gt;&lt;/span&gt;60000&lt;span class="jive-xml-tag"&gt;&amp;lt;/pingTimeout&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;/deployable&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;deployable&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.elasticpath.mdm&lt;span class="jive-xml-tag"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;cmserver&lt;span class="jive-xml-tag"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;type&amp;gt;&lt;/span&gt;war&lt;span class="jive-xml-tag"&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;properties&amp;gt;&lt;/span&gt;
                            &lt;span class="jive-xml-tag"&gt;&amp;lt;context&amp;gt;&lt;/span&gt;/${cmserver.context}&lt;span class="jive-xml-tag"&gt;&amp;lt;/context&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;/properties&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;pingURL&amp;gt;&lt;/span&gt;http://${demo.hostname}:${cargo.servlet.port}/${cmserver.context}/
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;/pingURL&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;pingTimeout&amp;gt;&lt;/span&gt;60000&lt;span class="jive-xml-tag"&gt;&amp;lt;/pingTimeout&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;/deployable&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;deployable&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.elasticpath.mdm&lt;span class="jive-xml-tag"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;storefront&lt;span class="jive-xml-tag"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;type&amp;gt;&lt;/span&gt;war&lt;span class="jive-xml-tag"&gt;&amp;lt;/type&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;properties&amp;gt;&lt;/span&gt;
                            &lt;span class="jive-xml-tag"&gt;&amp;lt;context&amp;gt;&lt;/span&gt;/${storefront.context}&lt;span class="jive-xml-tag"&gt;&amp;lt;/context&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;/properties&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;pingURL&amp;gt;&lt;/span&gt;http://${demo.hostname}:${cargo.servlet.port}/${storefront.context}/
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;/pingURL&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;pingTimeout&amp;gt;&lt;/span&gt;60000&lt;span class="jive-xml-tag"&gt;&amp;lt;/pingTimeout&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;/deployable&amp;gt;&lt;/span&gt;
            &lt;span class="jive-xml-tag"&gt;&amp;lt;/deployables&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;span class="jive-xml-tag"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;!--[CodeBlockEnd:47058c46-4240-48b6-b28a-e931bf72adb4]--&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;This is pretty much textbook Cargo; you'll see our use of &lt;strong&gt;demo.hostname&lt;/strong&gt; above in &lt;strong&gt;PingURL&lt;/strong&gt;. Cargo will fetch the artifacts and deploy them into Tomcat waiting for the specified URL to not return an&lt;strong&gt; &lt;/strong&gt;error, waiting up to &lt;strong&gt;pingTimeout&lt;/strong&gt; milliseconds. Since we're just a demo application, the &lt;strong&gt;PingURL&lt;/strong&gt; for the searchserver does a query for products. The problem here is that, while the searchserver may be available, the search indexes might not be built yet. So, we'll need something to check that the searchserver is not only started, but that its indexes are populated.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;For this, we put together a simple Groovy script that waits until the searchserver returns products when queried. We put this in &lt;strong&gt;src/main/script/waitforstartup.groovy&lt;/strong&gt; and it looks a little something like this:&lt;/p&gt;&lt;!--[CodeBlockStart:740edb22-c585-49b6-a2ee-139c3497e015]--&gt;&lt;pre class="jive-pre"&gt;&lt;code class="jive-code"&gt;import java.net.URL
import groovy.xml.StreamingMarkupBuilder


 def url = project.properties['searchserverUrl'];
 def timeout = project.properties['timeout'].toLong();

 /* For standalone testing, use something like:
  * 
  *     def url = "http://localhost:8080/searchserver/product/select?q=*:*"
  *     def timeout = 120*1000;
  */

def ready = false;
def text = "";
def first = true;

def begin = System.currentTimeMillis()

while (!ready) {
        
        if (System.currentTimeMillis() &amp;gt; (begin+timeout)) {
                // provided by gmaven
                fail("Timeout of "+timeout+"ms reached waiting for "+url+" to return at least one search result.")
        }
        
        if (!first) {
                Thread.sleep(10*1000);
        } else {
                first = false;
        }
        
        URL searchServer;
        try     {
                searchServer = new URL(url);
                text = searchServer.getText();
        } catch (e) {
                print "Server not yet ready, sleeping for 10 seconds. ("+e+")\n"
                continue;
        }
        
        if (text.size() == 0) {
                print "Not yet ready, sleeping for 10 seconds. (Empty HTTP response received)\n"
                continue;
        }
        
        try {
                def root = new XmlSlurper().parseText(text)
                
                if (numFound(root)!=0) {
                        ready = true;
                } else {
                        print "No elements returned in search result, sleeping for 10 seconds.\n"
                        continue;
                }
        } catch (e) {
                print "Trouble parsing XML result, sleeping for 10 seconds. ("+e+")\n"
                continue;
        }
}

def numFound(root) {
        return root.result.@numFound;
}
&lt;/code&gt;&lt;/pre&gt;&lt;!--[CodeBlockEnd:740edb22-c585-49b6-a2ee-139c3497e015]--&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;XmlSlurper was pretty neat (as used in &lt;strong&gt;numFound&lt;/strong&gt;). Running it is pretty trivial with the GMaven plugin. You'll notice how we can easily pass in properties to the Groovy script via &lt;strong&gt;project.properties.&lt;/strong&gt;&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;!--[CodeBlockStart:fb646955-95c5-491d-b28e-a3f3b4331bfa]--&gt;&lt;pre class="jive-pre"&gt;&lt;code class="jive-code jive-xml"&gt;&lt;span class="jive-xml-tag"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt; 
    &lt;span class="jive-xml-tag"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.codehaus.groovy.maven&lt;span class="jive-xml-tag"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;gmaven-plugin&lt;span class="jive-xml-tag"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;properties&amp;gt;&lt;/span&gt;
            &lt;span class="jive-xml-tag"&gt;&amp;lt;timeout&amp;gt;&lt;/span&gt;300000&lt;span class="jive-xml-tag"&gt;&amp;lt;/timeout&amp;gt;&lt;/span&gt;
            &lt;span class="jive-xml-tag"&gt;&amp;lt;searchserverUrl&amp;gt;&lt;/span&gt;http://${demo.hostname}:${cargo.servlet.port}/${searchserver.context}/product/select?q=*:*&lt;span class="jive-xml-tag"&gt;&amp;lt;/searchserverUrl&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;/properties&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;source&amp;gt;&lt;/span&gt;${basedir}/src/main/script/waitforstartup.groovy&lt;span class="jive-xml-tag"&gt;&amp;lt;/source&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;span class="jive-xml-tag"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;!--[CodeBlockEnd:fb646955-95c5-491d-b28e-a3f3b4331bfa]--&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;The &lt;strong&gt;source&lt;/strong&gt; element is relative to the current working directory, not your project's base directory, so it's a good idea to put in &lt;strong&gt;${basedir}&lt;/strong&gt; to prevent surprises later on.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Running &lt;strong&gt;mvn integration-test&lt;/strong&gt; should scroll for a while as Maven downloads all required dependencies and eventually the container will start. If we run &lt;strong&gt;gmaven:execute&lt;/strong&gt;, our Groovy script will run and happily poll our searchserver until it's ready!&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;This alone is a great set up for running builds of our project both on my development box and for our QA server!&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;In Hudson, I have one Job which spawns both targets in parallel.  The &lt;strong&gt;integration-test&lt;/strong&gt; job never returns (until it's killed in step 2), but once the &lt;strong&gt;gmaven:execute&lt;/strong&gt; target returns, it runs the Selenium tests.  Let's talk about those now...&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 14pt;"&gt;Prepare for Take Off&lt;/span&gt;&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Our second goal is to launch Selenium, run our tests and shut Selenium down.  It made sense for us to have a separate &lt;strong&gt;selenium-tests&lt;/strong&gt; artifact for that purpose, let's start by taking a look at the properties we decided to set:&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;!--[CodeBlockStart:2a7d031a-4514-4f56-90a0-94604f6029d7]--&gt;&lt;pre class="jive-pre"&gt;&lt;code class="jive-code jive-xml"&gt;&lt;span class="jive-xml-tag"&gt;&amp;lt;properties&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;selenium.version&amp;gt;&lt;/span&gt;2.0a4&lt;span class="jive-xml-tag"&gt;&amp;lt;/selenium.version&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;selenium.host&amp;gt;&lt;/span&gt;127.0.0.1&lt;span class="jive-xml-tag"&gt;&amp;lt;/selenium.host&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;selenium.port&amp;gt;&lt;/span&gt;14444&lt;span class="jive-xml-tag"&gt;&amp;lt;/selenium.port&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;selenium.DISPLAY&amp;gt;&lt;/span&gt;:17&lt;span class="jive-xml-tag"&gt;&amp;lt;/selenium.DISPLAY&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;selenium.background&amp;gt;&lt;/span&gt;true&lt;span class="jive-xml-tag"&gt;&amp;lt;/selenium.background&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;xvfb.option&amp;gt;&lt;/span&gt;-ac&lt;span class="jive-xml-tag"&gt;&amp;lt;/xvfb.option&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;seleniumUrl&amp;gt;&lt;/span&gt;http://${selenium.host}:${selenium.port}/wd/hub&lt;span class="jive-xml-tag"&gt;&amp;lt;/seleniumUrl&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;appUrl&amp;gt;&lt;/span&gt;http://${demo.hostname}:${cargo.servlet.port}/${ourapp.context}&lt;span class="jive-xml-tag"&gt;&amp;lt;/appUrl&amp;gt;&lt;/span&gt;
&lt;span class="jive-xml-tag"&gt;&amp;lt;/properties&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;!--[CodeBlockEnd:2a7d031a-4514-4f56-90a0-94604f6029d7]--&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;We're making use of the Maven Selenium Plugin to start up &lt;a class="jive-link-external-small" href="http://en.wikipedia.org/wiki/Xvfb"&gt;Xvfb&lt;/a&gt; on display :17, and we disable access control to Xvfb to work around any xauth issues we may run into.  Here you see another use of &lt;strong&gt;demo.hostname&lt;/strong&gt;, passing it to Selenium as the application under test.  The &lt;strong&gt;selenium.background&lt;/strong&gt; property allows us to run Selenium on the command line and keep the server running with &lt;strong&gt;-Dselenium.background=false&lt;/strong&gt;, this running these tests during development makes testing a lot quicker!&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Next we'll specify the dependencies we need to make this all happen:&lt;/p&gt;&lt;!--[CodeBlockStart:9374a69e-f8bc-4df7-8de7-cdc5b98ae2e7]--&gt;&lt;pre class="jive-pre"&gt;&lt;code class="jive-code jive-xml"&gt;&lt;span class="jive-xml-tag"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.seleniumhq.selenium&lt;span class="jive-xml-tag"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;selenium&lt;span class="jive-xml-tag"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${selenium.version}&lt;span class="jive-xml-tag"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

    &lt;span class="jive-xml-tag"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.testng&lt;span class="jive-xml-tag"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;testng&lt;span class="jive-xml-tag"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;5.11&lt;span class="jive-xml-tag"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;test&lt;span class="jive-xml-tag"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;classifier&amp;gt;&lt;/span&gt;jdk15&lt;span class="jive-xml-tag"&gt;&amp;lt;/classifier&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

    &lt;span class="jive-xml-tag"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.seleniumhq.selenium&lt;span class="jive-xml-tag"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;selenium-server&lt;span class="jive-xml-tag"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${selenium.version}&lt;span class="jive-xml-tag"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;span class="jive-xml-tag"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;   
&lt;span class="jive-xml-tag"&gt;&amp;lt;repositories&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;repository&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;selenium-repository&lt;span class="jive-xml-tag"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;http://selenium.googlecode.com/svn/repository/&lt;span class="jive-xml-tag"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;/repository&amp;gt;&lt;/span&gt;
&lt;span class="jive-xml-tag"&gt;&amp;lt;/repositories&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;!--[CodeBlockEnd:9374a69e-f8bc-4df7-8de7-cdc5b98ae2e7]--&gt;&lt;p&gt;No real surprises here, although I'm adding an extra repository just to pull down Selenium 2.0a4. You could easily mirror it locally in your own company-wide repository.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Now we need to start and stop Selenium.  Normally we'd use the Maven Selenium Plugin, but it's pinned to the older generation of Selenium. Instead of trying to be overly clever, we just ask Ant to start and stop Selenium during the &lt;strong&gt;pre-integration-test&lt;/strong&gt; and &lt;strong&gt;post-integration-test&lt;/strong&gt; phases, we pass in a DISPLAY variable which is ignored on non-Unix operating systems.&lt;/p&gt;&lt;!--[CodeBlockStart:a066ca01-fde7-442a-a98f-d980e189c272]--&gt;&lt;pre class="jive-pre"&gt;&lt;code class="jive-code jive-xml"&gt;&lt;span class="jive-xml-tag"&gt;&amp;lt;build&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;plugins&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
            &lt;span class="jive-xml-comment"&gt;&amp;lt;!--
                Use Ant to start and stop the selenium server - once the selenium
                plugin handles 2.0 we can get rid of this.
            --&amp;gt;&lt;/span&gt;
            &lt;span class="jive-xml-tag"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.maven.plugins&lt;span class="jive-xml-tag"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="jive-xml-tag"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;maven-antrun-plugin&lt;span class="jive-xml-tag"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="jive-xml-tag"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;start-selenium&lt;span class="jive-xml-tag"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;pre-integration-test&lt;span class="jive-xml-tag"&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;tasks&amp;gt;&lt;/span&gt;
                            &lt;span class="jive-xml-tag"&gt;&amp;lt;echo taskname="start-selenium"
                                message="Starting Selenium Server v${selenium.version} on ${selenium.port} offering display ${selenium.DISPLAY}" /&amp;gt;&lt;/span&gt;
                            &lt;span class="jive-xml-tag"&gt;&amp;lt;java taskname="start-selenium"
                                jar="lib/selenium-server-standalone-${selenium.version}.jar"
                                fork="true" spawn="${selenium.background}"&amp;gt;&lt;/span&gt;
                                &lt;span class="jive-xml-tag"&gt;&amp;lt;env key="DISPLAY" value="${selenium.DISPLAY}" /&amp;gt;&lt;/span&gt;
                                &lt;span class="jive-xml-tag"&gt;&amp;lt;arg line="-timeout 30 -debug -browserSideLog -port ${selenium.port}" /&amp;gt;&lt;/span&gt;
                            &lt;span class="jive-xml-tag"&gt;&amp;lt;/java&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;/tasks&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;run&lt;span class="jive-xml-tag"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;stop-selenium&lt;span class="jive-xml-tag"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;post-integration-test&lt;span class="jive-xml-tag"&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;tasks&amp;gt;&lt;/span&gt;
                            &lt;span class="jive-xml-tag"&gt;&amp;lt;echo message="Stopping Selenium Server" /&amp;gt;&lt;/span&gt;
                            &lt;span class="jive-xml-tag"&gt;&amp;lt;get taskname="stop-selenium"
                                src="http://${selenium.host}:${selenium.port}/selenium-server/driver/?cmd=shutDown"
                                dest="${project.build.directory}/selenium-shutdown.txt"
                                ignoreerrors="true" /&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;/tasks&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;run&lt;span class="jive-xml-tag"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
            &lt;span class="jive-xml-tag"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;!--[CodeBlockEnd:a066ca01-fde7-442a-a98f-d980e189c272]--&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Okay, so we've configured Selenium to start and stop, now lets ask it to run our tests. We created an extremely simple TestNG suite in &lt;strong&gt;src/test/it/testng.xml:&lt;/strong&gt;&lt;/p&gt;&lt;!--[CodeBlockStart:1eaebf6f-80dd-4891-a4d8-a88f8da4d16a]--&gt;&lt;pre class="jive-pre"&gt;&lt;code class="jive-code jive-xml"&gt;&lt;span class="jive-xml-tag"&gt;&amp;lt;suite name="integration-tests"&amp;gt;&lt;/span&gt;
     &lt;span class="jive-xml-tag"&gt;&amp;lt;test name="Everybody Everybody"&amp;gt;&lt;/span&gt;
          &lt;span class="jive-xml-tag"&gt;&amp;lt;packages&amp;gt;&lt;/span&gt;
               &lt;span class="jive-xml-tag"&gt;&amp;lt;package name="com.elasticpath.mdm.tests.it" /&amp;gt;&lt;/span&gt;
          &lt;span class="jive-xml-tag"&gt;&amp;lt;/packages&amp;gt;&lt;/span&gt;
     &lt;span class="jive-xml-tag"&gt;&amp;lt;/test&amp;gt;&lt;/span&gt;
&lt;span class="jive-xml-tag"&gt;&amp;lt;/suite&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;!--[CodeBlockEnd:1eaebf6f-80dd-4891-a4d8-a88f8da4d16a]--&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;By convention, the Maven Failsafe Plugin will run &lt;strong&gt;src/test/java/&amp;lt;above packages&amp;gt;/&lt;/strong&gt;&lt;strong&gt;*IT.java &lt;/strong&gt;tests when called with &lt;strong&gt;integration-test &lt;/strong&gt;and TestNG will look for methods annotated with &lt;strong&gt;@Test&lt;/strong&gt;.  So back in the POM, we say:&lt;/p&gt;&lt;!--[CodeBlockStart:9637e9cd-cbc7-4c3b-88de-bd088e02d9a4]--&gt;&lt;pre class="jive-pre"&gt;&lt;code class="jive-code jive-xml"&gt;    &lt;span class="jive-xml-tag"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-comment"&gt;&amp;lt;!--
            Run tests specified in the testng.xml file for integration-tests
        --&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.maven.plugins&lt;span class="jive-xml-tag"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;maven-failsafe-plugin&lt;span class="jive-xml-tag"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;2.5&lt;span class="jive-xml-tag"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;

        &lt;span class="jive-xml-tag"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
            &lt;span class="jive-xml-tag"&gt;&amp;lt;suiteXmlFiles&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;suiteXmlFile&amp;gt;&lt;/span&gt;src/test/it/testng.xml&lt;span class="jive-xml-tag"&gt;&amp;lt;/suiteXmlFile&amp;gt;&lt;/span&gt;
            &lt;span class="jive-xml-tag"&gt;&amp;lt;/suiteXmlFiles&amp;gt;&lt;/span&gt;
            &lt;span class="jive-xml-tag"&gt;&amp;lt;systemPropertyVariables&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;seleniumUrl&amp;gt;&lt;/span&gt;${seleniumUrl}&lt;span class="jive-xml-tag"&gt;&amp;lt;/seleniumUrl&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;appUrl&amp;gt;&lt;/span&gt;${appUrl}&lt;span class="jive-xml-tag"&gt;&amp;lt;/appUrl&amp;gt;&lt;/span&gt;
            &lt;span class="jive-xml-tag"&gt;&amp;lt;/systemPropertyVariables&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;

        &lt;span class="jive-xml-tag"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
            &lt;span class="jive-xml-tag"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;integration-test&lt;span class="jive-xml-tag"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;integration-test&lt;span class="jive-xml-tag"&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;integration-test&lt;span class="jive-xml-tag"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
            &lt;span class="jive-xml-tag"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
&lt;span class="jive-xml-tag"&gt;&amp;lt;/plugins&amp;gt;&lt;/span&gt;
 
&lt;/code&gt;&lt;/pre&gt;&lt;!--[CodeBlockEnd:9637e9cd-cbc7-4c3b-88de-bd088e02d9a4]--&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;We bind it to the &lt;strong&gt;integration-test&lt;/strong&gt; phase so it's run after Selenium starts. You'll notice that we pass &lt;strong&gt;seleniumUrl&lt;/strong&gt; and &lt;strong&gt;appUrl&lt;/strong&gt; into the TestNG tests. We pass those as parameters into a &lt;strong&gt;@BeforeSuite&lt;/strong&gt; method; we'll touch on that in a moment.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Last bit, we're going to launch Xvfb, but only if we're on a UNIXy operating system, so we'll wrap it in a profile stanza:&lt;/p&gt;&lt;!--[CodeBlockStart:b02baaf9-0365-4a66-8ff6-5b49d6c59fba]--&gt;&lt;pre class="jive-pre"&gt;&lt;code class="jive-code jive-xml"&gt;&lt;span class="jive-xml-tag"&gt;&amp;lt;profiles&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;profile&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;xvfb-started&lt;span class="jive-xml-tag"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;activation&amp;gt;&lt;/span&gt;
            &lt;span class="jive-xml-tag"&gt;&amp;lt;os&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;family&amp;gt;&lt;/span&gt;unix&lt;span class="jive-xml-tag"&gt;&amp;lt;/family&amp;gt;&lt;/span&gt;
            &lt;span class="jive-xml-tag"&gt;&amp;lt;/os&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;/activation&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;build&amp;gt;&lt;/span&gt;
            &lt;span class="jive-xml-tag"&gt;&amp;lt;plugins&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-comment"&gt;&amp;lt;!--
                        Before running the Selenium server, start a Xvfb to display
                        browsers into.
                    --&amp;gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.codehaus.mojo&lt;span class="jive-xml-tag"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;selenium-maven-plugin&lt;span class="jive-xml-tag"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
                            &lt;span class="jive-xml-tag"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;xvfb&lt;span class="jive-xml-tag"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
                            &amp;lt;!--
                                We want to start Xvfb *before* pre-integration-tests, but the
                                only phase before pre-integration-test is package, so we put it
                                there.
                            --&amp;gt;&lt;/span&gt;
                            &lt;span class="jive-xml-tag"&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;package&lt;span class="jive-xml-tag"&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
                            &lt;span class="jive-xml-tag"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                                &lt;span class="jive-xml-tag"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;xvfb&lt;span class="jive-xml-tag"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
                            &lt;span class="jive-xml-tag"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
                            &lt;span class="jive-xml-tag"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
                                &lt;span class="jive-xml-tag"&gt;&amp;lt;display&amp;gt;&lt;/span&gt;${selenium.DISPLAY}&lt;span class="jive-xml-tag"&gt;&amp;lt;/display&amp;gt;&lt;/span&gt;
                                &lt;span class="jive-xml-tag"&gt;&amp;lt;options&amp;gt;&lt;/span&gt;
                                    &lt;span class="jive-xml-tag"&gt;&amp;lt;option&amp;gt;&lt;/span&gt;${xvfb.option}&lt;span class="jive-xml-tag"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
                                &lt;span class="jive-xml-tag"&gt;&amp;lt;/options&amp;gt;&lt;/span&gt;
                            &lt;span class="jive-xml-tag"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
            &lt;span class="jive-xml-tag"&gt;&amp;lt;/plugins&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;/build&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;/profile&amp;gt;&lt;/span&gt;
&lt;span class="jive-xml-tag"&gt;&amp;lt;/profiles&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;!--[CodeBlockEnd:b02baaf9-0365-4a66-8ff6-5b49d6c59fba]--&gt;&lt;p&gt;Oh the hack.  Ironically, Maven does not have a way to specify that a particular execution id &lt;em&gt;depends&lt;/em&gt; on another&lt;em&gt; &lt;/em&gt;execution id, so we had to bind launching Xvfb to the &lt;strong&gt;package&lt;/strong&gt; phase. Maybe in Maven 3.0?&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Getting into the actual code, earlier I mentioned the &lt;strong&gt;@BeforeSuite &lt;/strong&gt;attribute, in that method we'll configure Selenium for our purposes:&lt;/p&gt;&lt;!--[CodeBlockStart:0a428549-e648-49d6-af85-e82781961817]--&gt;&lt;pre class="jive-pre"&gt;&lt;code class="jive-code jive-java"&gt;.....
&amp;nbsp;
        &lt;font color="darkgreen"&gt;/*
         * When running TestNG from within Eclipse, the plugin passes this value in to parameters it cannot substitute.
         */&lt;/font&gt;
        &lt;font color="navy"&gt;&lt;b&gt;private&lt;/b&gt;&lt;/font&gt; &lt;font color="navy"&gt;&lt;b&gt;static&lt;/b&gt;&lt;/font&gt; &lt;font color="navy"&gt;&lt;b&gt;final&lt;/b&gt;&lt;/font&gt; String PARAMETER_NOT_FOUND = &lt;font color="red"&gt;"not-found"&lt;/font&gt;;
.....
        &lt;font color="darkgreen"&gt;/*
         * Passing in these properties will allow you to use the specified values if the parameters are not found by TestNG.
         */&lt;/font&gt;
        &lt;font color="navy"&gt;&lt;b&gt;public&lt;/b&gt;&lt;/font&gt; &lt;font color="navy"&gt;&lt;b&gt;static&lt;/b&gt;&lt;/font&gt; &lt;font color="navy"&gt;&lt;b&gt;final&lt;/b&gt;&lt;/font&gt; String SELENIUM_URL_PROPERTY = &lt;font color="red"&gt;"selenium.url"&lt;/font&gt;;
&amp;nbsp;
        &lt;font color="navy"&gt;&lt;b&gt;public&lt;/b&gt;&lt;/font&gt; &lt;font color="navy"&gt;&lt;b&gt;static&lt;/b&gt;&lt;/font&gt; &lt;font color="navy"&gt;&lt;b&gt;final&lt;/b&gt;&lt;/font&gt; String APPLICATION_URL_PROPERTY = &lt;font color="red"&gt;"app.url"&lt;/font&gt;;
&amp;nbsp;
        &lt;font color="navy"&gt;&lt;b&gt;private&lt;/b&gt;&lt;/font&gt; &lt;font color="navy"&gt;&lt;b&gt;static&lt;/b&gt;&lt;/font&gt; WebDriver driver;
&amp;nbsp;
        &lt;font color="navy"&gt;&lt;b&gt;private&lt;/b&gt;&lt;/font&gt; &lt;font color="navy"&gt;&lt;b&gt;static&lt;/b&gt;&lt;/font&gt; WebDriverBackedSelenium selenium;
&amp;nbsp;
        &lt;font color="navy"&gt;&lt;b&gt;private&lt;/b&gt;&lt;/font&gt; &lt;font color="navy"&gt;&lt;b&gt;static&lt;/b&gt;&lt;/font&gt; String sessionString;
        &lt;font color="navy"&gt;&lt;b&gt;private&lt;/b&gt;&lt;/font&gt; &lt;font color="navy"&gt;&lt;b&gt;static&lt;/b&gt;&lt;/font&gt; String appUrl;
.....
        @BeforeSuite
        @Parameters( &lt;font color="navy"&gt;{&lt;/font&gt; &lt;font color="red"&gt;"seleniumUrl"&lt;/font&gt;, &lt;font color="red"&gt;"appUrl"&lt;/font&gt; &lt;font color="navy"&gt;}&lt;/font&gt;)
        &lt;font color="navy"&gt;&lt;b&gt;public&lt;/b&gt;&lt;/font&gt; &lt;font color="navy"&gt;&lt;b&gt;void&lt;/b&gt;&lt;/font&gt; startBrowser(String seleniumUrl, String appUrl) &lt;font color="navy"&gt;&lt;b&gt;throws&lt;/b&gt;&lt;/font&gt; Exception &lt;font color="navy"&gt;{&lt;/font&gt;
                
                &lt;font color="navy"&gt;&lt;b&gt;if&lt;/b&gt;&lt;/font&gt; (PARAMETER_NOT_FOUND.equals(seleniumUrl)) &lt;font color="navy"&gt;{&lt;/font&gt;
                        seleniumUrl = System.getProperty(SELENIUM_URL_PROPERTY);
                &lt;font color="navy"&gt;}&lt;/font&gt;
                &lt;font color="navy"&gt;&lt;b&gt;if&lt;/b&gt;&lt;/font&gt; (PARAMETER_NOT_FOUND.equals(appUrl)) &lt;font color="navy"&gt;{&lt;/font&gt;
                        appUrl = System.getProperty(APPLICATION_URL_PROPERTY);
                &lt;font color="navy"&gt;}&lt;/font&gt;
&amp;nbsp;
                driver = &lt;font color="navy"&gt;&lt;b&gt;new&lt;/b&gt;&lt;/font&gt; RemoteWebDriver(&lt;font color="navy"&gt;&lt;b&gt;new&lt;/b&gt;&lt;/font&gt; URL(seleniumUrl), DesiredCapabilities.firefox());
&amp;nbsp;
                &lt;font color="darkgreen"&gt;// This will cause all find-element operations to keep trying for up to 10 seconds automatically.&lt;/font&gt;
                driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
&amp;nbsp;
                selenium = &lt;font color="navy"&gt;&lt;b&gt;new&lt;/b&gt;&lt;/font&gt; WebDriverBackedSelenium(driver, appUrl);
&amp;nbsp;
                &lt;font color="darkgreen"&gt;/*
                 * Generate a unique string so if we update a field to a new value, we know it's going to be a new value (and can compare that it's so).
                 */&lt;/font&gt;
                sessionString = System.currentTimeMillis() + &lt;font color="red"&gt;""&lt;/font&gt;;
&amp;nbsp;
                selenium.open(appUrl);
&amp;nbsp;
        &lt;font color="navy"&gt;}&lt;/font&gt;
....
        @AfterSuite
        &lt;font color="navy"&gt;&lt;b&gt;public&lt;/b&gt;&lt;/font&gt; &lt;font color="navy"&gt;&lt;b&gt;void&lt;/b&gt;&lt;/font&gt; stopBrowser() &lt;font color="navy"&gt;{&lt;/font&gt;
                driver.close();
&amp;nbsp;
        &lt;font color="navy"&gt;}&lt;/font&gt;
.....
 
&lt;/code&gt;&lt;/pre&gt;&lt;!--[CodeBlockEnd:0a428549-e648-49d6-af85-e82781961817]--&gt;&lt;p&gt;This method is called before our test suite runs.  We do some extra work to allow developers to run TestNG with &lt;strong&gt;-Dselenium.url&lt;/strong&gt; and &lt;strong&gt;-Dapp.url&lt;/strong&gt; if running using the TestNG plugin for Eclipse to pass in parameters.  We also configure WebDriver to automatically wait 10 seconds for events to occur.  Don't forget to tell people writing tests that they don't need to do this again in their code!&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;This configuration gives us a handful of options, developers can boot up a Selenium server by issuing "&lt;strong&gt;mvn -Dselenium.background=false integration-test&lt;/strong&gt;", then run tests either in Eclipse or by calling surefire directly with "&lt;strong&gt;mvn surefire:integration-test&lt;/strong&gt;".&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 14pt;"&gt;Reporting the News&lt;/span&gt;&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;To recap, we can start a container, deploy our application, wait for it to start, then launch Selenium, run some tests and shut Selenium down when our tests have finished.  Our final goal is to report an announce the results of our test. Generating reports is pretty easy, we'll just add the following to our POM:&lt;/p&gt;&lt;!--[CodeBlockStart:f970f51a-2cb4-4bf5-bfa2-92f6d421ec4a]--&gt;&lt;pre class="jive-pre"&gt;&lt;code class="jive-code jive-xml"&gt;&lt;span class="jive-xml-tag"&gt;&amp;lt;reporting&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;plugins&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
            &lt;span class="jive-xml-tag"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.maven.plugins&lt;span class="jive-xml-tag"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="jive-xml-tag"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;maven-surefire-report-plugin&lt;span class="jive-xml-tag"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="jive-xml-tag"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;2.5&lt;span class="jive-xml-tag"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
            &lt;span class="jive-xml-tag"&gt;&amp;lt;reportSets&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;reportSet&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;integration-tests&lt;span class="jive-xml-tag"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;reports&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;report&amp;gt;&lt;/span&gt;report-only&lt;span class="jive-xml-tag"&gt;&amp;lt;/report&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;/reports&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;outputName&amp;gt;&lt;/span&gt;failsafe-report&lt;span class="jive-xml-tag"&gt;&amp;lt;/outputName&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;reportsDirectories&amp;gt;&lt;/span&gt;
                            &lt;span class="jive-xml-tag"&gt;&amp;lt;reportsDirectory&amp;gt;&lt;/span&gt;${project.build.directory}/failsafe-reports&lt;span class="jive-xml-tag"&gt;&amp;lt;/reportsDirectory&amp;gt;&lt;/span&gt;
                        &lt;span class="jive-xml-tag"&gt;&amp;lt;/reportsDirectories&amp;gt;&lt;/span&gt;
                    &lt;span class="jive-xml-tag"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
                &lt;span class="jive-xml-tag"&gt;&amp;lt;/reportSet&amp;gt;&lt;/span&gt;
            &lt;span class="jive-xml-tag"&gt;&amp;lt;/reportSets&amp;gt;&lt;/span&gt;
        &lt;span class="jive-xml-tag"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
    &lt;span class="jive-xml-tag"&gt;&amp;lt;/plugins&amp;gt;&lt;/span&gt;
&lt;span class="jive-xml-tag"&gt;&amp;lt;/reporting&amp;gt;&lt;/span&gt;
 
&lt;/code&gt;&lt;/pre&gt;&lt;!--[CodeBlockEnd:f970f51a-2cb4-4bf5-bfa2-92f6d421ec4a]--&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Wow, that was easy.  In Hudson, we configure the &lt;strong&gt;selenium-tests&lt;/strong&gt; project to run the following Maven Goals:&lt;strong&gt; integration-test site-deploy failsafe:verify&lt;/strong&gt;. This asks Maven to perform the tests, deploy the site and then fail if the tests failed.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;I've also configured Hudson to send an email out if this build fails, that email contains the URL directly to the Selenium Failsafe report!&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Now we have an automated deployment mechanism, Selenium tests and reporting.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Awesome!&lt;/p&gt;&lt;/div&gt;&lt;!-- [DocumentBodyEnd:11959977-cb78-42e0-a158-fd9de44e9cd1] --&gt;</description>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">testing</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">maven</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">hudson</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">selenium</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">tomcat</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">testng</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">cargo</category>
      <pubDate>Wed, 16 Jun 2010 21:34:03 GMT</pubDate>
      <author>james.atwill@elasticpath.com</author>
      <guid>http://grep.elasticpath.com/community/techblog/blog/2010/06/16/automated-selenium-testing-with-maven</guid>
      <dc:date>2010-06-16T21:34:03Z</dc:date>
      <clearspace:dateToText>1 month, 2 weeks ago</clearspace:dateToText>
    </item>
    <item>
      <title>How to Skip Over Dynamic Proxy Classes when Debugging in Eclipse</title>
      <link>http://grep.elasticpath.com/community/techblog/blog/2010/06/16/how-to-skip-over-dynamic-proxy-classes-when-debugging-in-eclipse</link>
      <description>&lt;!-- [DocumentBodyStart:75298ee6-9d9f-44d5-98d8-c30899ad7f22] --&gt;&lt;div class='jive-rendered-content'&gt;&lt;p&gt;Short screencast illustrating how to enable and use the Step Filtering feature of Eclipse:&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;object height="350" width="425"&gt;&lt;param name="allowfullscreen" value="true"/&gt;&lt;param name="allowscriptaccess" value="always"/&gt;&lt;param name="movie" value="http://www.vimeo.com/moogaloop.swf?clip_id=12598448&amp;amp;server=www.vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1"/&gt;&lt;param name="wmode" value="transparent"/&gt;&lt;embed allowfullscreen="true" allowscriptaccess="always" height="350" src="http://www.vimeo.com/moogaloop.swf?clip_id=12598448&amp;amp;server=www.vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1" type="application/x-shockwave-flash" width="425" wmode="transparent"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Here are the step filters that should be enabled to skip over Spring's dynamic proxy code:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;$Proxy*&lt;/li&gt;&lt;li&gt;java.*&lt;/li&gt;&lt;li&gt;javax.*&lt;/li&gt;&lt;li&gt;org.apache.*&lt;/li&gt;&lt;li&gt;org.springframework.*&lt;/li&gt;&lt;li&gt;sun.*&lt;/li&gt;&lt;li&gt;java.lang.ClassLoader&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;!-- [DocumentBodyEnd:75298ee6-9d9f-44d5-98d8-c30899ad7f22] --&gt;</description>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">spring</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">eclipse</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">debugging</category>
      <pubDate>Wed, 16 Jun 2010 15:52:04 GMT</pubDate>
      <author>developer.forums@elasticpath.com</author>
      <guid>http://grep.elasticpath.com/community/techblog/blog/2010/06/16/how-to-skip-over-dynamic-proxy-classes-when-debugging-in-eclipse</guid>
      <dc:date>2010-06-16T15:52:04Z</dc:date>
      <clearspace:dateToText>1 month, 2 weeks ago</clearspace:dateToText>
    </item>
    <item>
      <title>Patch-Fragment Commerce Manager - Part 1 - Visualizing plugin dependencies</title>
      <link>http://grep.elasticpath.com/community/techblog/blog/2010/06/09/patch-fragment-commerce-manager--part-1--visualizing-plugin-dependencies</link>
      <description>&lt;!-- [DocumentBodyStart:6413689f-bbf9-4c5d-9551-30e3b1db0d91] --&gt;&lt;div class='jive-rendered-content'&gt;&lt;p&gt;Anyone who has had the pleasure of customizing the Commerce Manager knows that it is a complicated piece of software with a lot of moving parts.  &lt;img height="16px" src="http://grep.elasticpath.com/images/emoticons/happy.gif" width="16px"/&gt;&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;However, with the release of 6.2.1 combined with the new binary-based architecture and a number of clients going through an upgrade process, it has become even more complex with the introduction of patch fragments and a new approach to customizations.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;For this blog post, I wanted to demonstrate a handy Eclipse plug-in that lets you visualize the dependencies between the various plug-ins within the Commerce Manager.  This is really useful for quickly discovering which plug-ins/bundles are dependent on each other.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;The plug-in is called "Plug-In Dependency Graph Plugin". You can download it from &lt;a class="jive-link-external-small" href="http://testdrivenguy.blogspot.com/2009/05/eclipse-plug-in-dependency-graph.html"&gt;http://testdrivenguy.blogspot.com/2009/05/eclipse-plug-in-dependency-graph.html&lt;/a&gt;. Then just unpack it into your Eclipse &lt;span style="font-family: courier new,courier;"&gt;plugins&lt;/span&gt; folder and restart Eclipse.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Open the "Graph Plug-in Dependencies" view, click the search icon in the top right corner, and type in the name of the plugin you want to check out. In this example, I am interested in the our customized &lt;span style="font-family: courier new,courier;"&gt;admin.configuration &lt;/span&gt;plugin.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;a href="http://grep.elasticpath.com/servlet/JiveServlet/showImage/38-1108-1495/admin-config-callees.png"&gt;&lt;img alt="admin-config-callees.png" class="jive-image-thumbnail jive-image" src="http://grep.elasticpath.com/servlet/JiveServlet/downloadImage/38-1108-1495/admin-config-callees.png" width="620"/&gt;&lt;/a&gt;&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;You can see that there are a lot of dependencies between all the plugins, but by highlighting the &lt;span style="font-family: courier new,courier;"&gt;admin.configuration&lt;/span&gt; plugin I can quickly see which plugins are directly referenced by it.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Alternatively, if you want to see who is directly referencing your plugin, you can show the callers:&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;a href="http://grep.elasticpath.com/servlet/JiveServlet/showImage/38-1108-1496/admin-config-callers.png"&gt;&lt;img alt="admin-config-callers.png" class="jive-image-thumbnail jive-image" src="http://grep.elasticpath.com/servlet/JiveServlet/downloadImage/38-1108-1496/admin-config-callers.png" width="620"/&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;In this case, not too interesting.  Let's take a look at &lt;span style="font-family: courier new,courier;"&gt;com.elasticpath.cmclient.core&lt;/span&gt;:&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;a href="http://grep.elasticpath.com/servlet/JiveServlet/showImage/38-1108-1497/cmclient-core-callers.png"&gt;&lt;img alt="cmclient-core-callers.png" class="jive-image-thumbnail jive-image" src="http://grep.elasticpath.com/servlet/JiveServlet/downloadImage/38-1108-1497/cmclient-core-callers.png" width="620"/&gt;&lt;/a&gt;&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;A little more interesting. This plugin can help you quickly track down configuration issues in your plug-in manifest files when developing using the new patch fragment approach.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;In future posts, I will provide more technical details as to our approach in implementing the patch fragment architecture including technical challenges encountered and our solutions to these issues.&lt;/p&gt;&lt;/div&gt;&lt;!-- [DocumentBodyEnd:6413689f-bbf9-4c5d-9551-30e3b1db0d91] --&gt;</description>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">upgrade</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">customization</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">cm_client</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">cmclient</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">6.2</category>
      <pubDate>Wed, 09 Jun 2010 20:14:07 GMT</pubDate>
      <author>developer.forums@elasticpath.com</author>
      <guid>http://grep.elasticpath.com/community/techblog/blog/2010/06/09/patch-fragment-commerce-manager--part-1--visualizing-plugin-dependencies</guid>
      <dc:date>2010-06-09T20:14:07Z</dc:date>
      <clearspace:dateToText>1 month, 3 weeks ago</clearspace:dateToText>
    </item>
    <item>
      <title>Loading Elastic Path Content Using Widgets</title>
      <link>http://grep.elasticpath.com/community/techblog/blog/2010/06/04/loading-elastic-path-content-using-widgets</link>
      <description>&lt;!-- [DocumentBodyStart:f342cf25-088a-4901-a497-2e0a223123c5] --&gt;&lt;div class='jive-rendered-content'&gt;&lt;p&gt;Out of the box, Elastic Path is configured to serve pages for the storefront. Some customers, however, need more flexibility. For example, we have a customer with content in a Content Management System (CMS) that needed to include links to EP content. The approach taken was to make Elastic Path content available as widgets which could be loaded remotely using JavaScript. This approach has the limitation that if the end user has JavaScript turned off then they will not be able to access the shopping functionality. The jQuery JavaScript library was used.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;h1&gt;Product Browsing&lt;/h1&gt;&lt;p&gt;To enable product browsing, the ShoppingItemConfigController was customized to support two request parameters: type and  productWidgetId. When the type parameter was set to "widget", the  ShoppingItemConfigController returned the widget product view. This  product view was customised to fit in the small space allowed on the  page and a custom template was created for this. The productWidgetId parameter was used to identify the widget so that  multiple widgets could exist on one page.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;The host page from the CMS included a div at the required location for the product widget:&lt;/p&gt;&lt;!--[CodeBlockStart:1d9b4bf1-3d49-448c-b927-e9f0d7a7ceb9]--&gt;&lt;pre class="jive-pre"&gt;&lt;code class="jive-code jive-xml"&gt;&lt;span class="jive-xml-tag"&gt;&amp;lt;div class="blockElement" id="product-widget-1"&amp;gt;&lt;/span&gt;&lt;span class="jive-xml-tag"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;!--[CodeBlockEnd:1d9b4bf1-3d49-448c-b927-e9f0d7a7ceb9]--&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;A script element on the page included the following code:&lt;/p&gt;&lt;!--[CodeBlockStart:168b707b-c6d3-4969-b01a-fbe097a5e058]--&gt;&lt;pre class="jive-pre"&gt;&lt;code class="jive-code jive-xml"&gt;$(document).ready(function() {
    $('#product-widget-1').load('storefront/category1/productCode.html?type=widget&amp;amp;productWidgetId=product-widget-1');
}

&lt;/code&gt;&lt;/pre&gt;&lt;!--[CodeBlockEnd:168b707b-c6d3-4969-b01a-fbe097a5e058]--&gt;&lt;p&gt;This code executes after the page loads but before all sub-content (e.g. images)  is loaded, sending an asynchronous request to EP for the product content and loading it in  the div with the id "product-widget-1".&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;h1&gt;Add to Cart&lt;/h1&gt;&lt;p&gt;To add an item to the cart, an asynchronous call to DWR was made using the widget id to identify the SKU and quantity to add.&lt;/p&gt;&lt;!--[CodeBlockStart:ac89134d-ada2-4666-ad53-3c48ec469a2b]--&gt;&lt;pre class="jive-pre"&gt;&lt;code class="jive-code jive-xml"&gt;&lt;span class="jive-xml-tag"&gt;&amp;lt;form id="skuSelectForm" name="skuSelectForm" method="post" onsubmit="return addToCartSubmit('#$productWidgetId'); "&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;!--[CodeBlockEnd:ac89134d-ada2-4666-ad53-3c48ec469a2b]--&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;!--[CodeBlockStart:60d9d148-3586-4ade-8041-9a91aa38393e]--&gt;&lt;pre class="jive-pre"&gt;&lt;code class="jive-code jive-xml"&gt;function addToCartSubmit(widgetId) {      
    shoppingCartAjaxController.addSkuToCart(jQuery(widgetId + ' 
        .skuCodeParameter').val(), jQuery(widgetId +'-quantitySelect').val(), 
        function(data) {
            refreshShoppingCartSummary();
       
        });
   return false;
}
&lt;/code&gt;&lt;/pre&gt;&lt;!--[CodeBlockEnd:60d9d148-3586-4ade-8041-9a91aa38393e]--&gt;&lt;p&gt;&lt;span style="font-family: courier new,courier;"&gt;&lt;br/&gt;&lt;/span&gt;&lt;/p&gt;&lt;h1&gt;Shopping Cart Summary&lt;/h1&gt;&lt;p&gt;When the &lt;span style="font-family: courier new,courier;"&gt;addSkuToCart&lt;/span&gt; request returns, the &lt;span style="font-family: courier new,courier;"&gt;refreshShoppingCartSummary()&lt;/span&gt; method is called. This is defined in a JavaScript file included in the hosted page. It makes another asynchronous call in order to determine the number of items in the cart. (Note: for greater network efficiency, the number of items in the cart could be returned from &lt;span style="font-family: courier new,courier;"&gt;addSkuToCart&lt;/span&gt;.)&lt;/p&gt;&lt;!--[CodeBlockStart:c2b3cf60-ad91-4c8e-8308-170050aa4db8]--&gt;&lt;pre class="jive-pre"&gt;&lt;code class="jive-code jive-xml"&gt;function refreshShoppingCartSummary() {
    shoppingCartAjaxController.getCartItemCount(function(data) { 
        jQuery('#cartSummaryText').text(data); 
    });            
};
&lt;/code&gt;&lt;/pre&gt;&lt;!--[CodeBlockEnd:c2b3cf60-ad91-4c8e-8308-170050aa4db8]--&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;h1&gt;Shopping Cart Summary Popup&lt;/h1&gt;&lt;p&gt;The jQuery &lt;span style="font-family: courier new,courier;"&gt;hover&lt;/span&gt; method was used  to detect when the mouse was over the icon and display a popup showing the contents of the cart. A DWR call was made to retrieve the cart data. To reduce the number of round trips, this call was designed to return all required information for the cart and its items. JavaScript was used to build the DOM tree for this data. (Note that, in production, a JS client side templating engine should be considered for this role.)&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;The next issue was making sure the popup remained open when the mouse moved to the checkout button. This was done using JavaScript timeouts; when the hover out function was called, the timeout would start. When a hover in function was called for the cart icon or for the popup, the timeout would be cancelled. If the timeout fired without being cancelled, the popup would be closed.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;!--[CodeBlockStart:b6935610-ae3f-43d9-bbe7-3ada6d246e97]--&gt;&lt;pre class="jive-pre"&gt;&lt;code class="jive-code jive-xml"&gt;    $('#shoppingCartMenu').hover(function() {
        shoppingCartAjaxController.getCartSummary(function(cart) {
            var popupHtml = '&lt;span class="jive-xml-tag"&gt;&amp;lt;div class="cartTop"&amp;gt;&lt;/span&gt;';           
            popupHtml += '&lt;span class="jive-xml-tag"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;';
            popupHtml += '&lt;span class="jive-xml-tag"&gt;&amp;lt;div class="cartBottom transparent"&amp;gt;&lt;/span&gt;';
            popupHtml +=    '&lt;span class="jive-xml-tag"&gt;&amp;lt;div class="yourCart"&amp;gt;&lt;/span&gt;';
            popupHtml +=        '&lt;span class="jive-xml-tag"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;YOUR CART&lt;span class="jive-xml-tag"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;';
            popupHtml +=    '&lt;span class="jive-xml-tag"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;'

            for (var i=0; i&lt;span class="jive-xml-tag"&gt;&amp;lt;cart.shoppingItems.length; i++) {
                popupHtml += '&amp;lt;div class="product"&amp;gt;&lt;/span&gt;';
                shoppingItem = cart.shoppingItems[i];
                popupHtml +=    '&lt;span class="jive-xml-tag"&gt;&amp;lt;div class="items"&amp;gt;&lt;/span&gt;';
                popupHtml +=        '&lt;span class="jive-xml-tag"&gt;&amp;lt;div class="itemsImg"&amp;gt;&lt;/span&gt;';
                popupHtml +=            '&lt;span class="jive-xml-tag"&gt;&amp;lt;img width="35" height="41" border="0" alt="' + shoppingItem.localizedProductName;
                popupHtml += '" src="/storefront/renderImage.image?imageName=' + shoppingItem.imageFileName +'&amp;amp;width=35&amp;amp;height=41" id="productImage"&amp;gt;&lt;/span&gt;';
                popupHtml +=        '&lt;span class="jive-xml-tag"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;';
                popupHtml +=        '&lt;span class="jive-xml-tag"&gt;&amp;lt;div class="itemsText"&amp;gt;&lt;/span&gt;';
                popupHtml +=            '&lt;span class="jive-xml-tag"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;&lt;span class="jive-xml-tag"&gt;&amp;lt;strong&amp;gt;&lt;/span&gt;' + shoppingItem.localizedProductName + '&lt;span class="jive-xml-tag"&gt;&amp;lt;/strong&amp;gt;&lt;/span&gt;&lt;span class="jive-xml-tag"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;';
                popupHtml += '&lt;span class="jive-xml-tag"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;';
                for (var j=0; j&lt;span class="jive-xml-tag"&gt;&amp;lt;shoppingItem.localizedSkuOptionDisplayNames.length; j++) {
                    if (j != 0) popupHtml += ',';
                    popupHtml += shoppingItem.localizedSkuOptionDisplayNames[j];
                }
                popupHtml += '&amp;lt;/p&amp;gt;&lt;/span&gt;';
                popupHtml +=        '&lt;span class="jive-xml-tag"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;';                                               
                popupHtml +=    '&lt;span class="jive-xml-tag"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;';   
                popupHtml +=    '&lt;span class="jive-xml-tag"&gt;&amp;lt;div class="priceSubtotal"&amp;gt;&lt;/span&gt;';
                popupHtml +=        '&lt;span class="jive-xml-tag"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Qty: ' + shoppingItem.quantity +'&lt;span class="jive-xml-tag"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;';
                popupHtml +=    '&lt;span class="jive-xml-tag"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;';
                popupHtml +=    '&lt;span class="jive-xml-tag"&gt;&amp;lt;div class="price"&amp;gt;&lt;/span&gt;';
                popupHtml +=        '&lt;span class="jive-xml-tag"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;&lt;span class="jive-xml-tag"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;' + getCents(shoppingItem.total) + '&lt;span class="jive-xml-tag"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class="jive-xml-tag"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;' + getDollars(shoppingItem.total) + '&lt;span class="jive-xml-tag"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;&lt;span class="jive-xml-tag"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;$&lt;span class="jive-xml-tag"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class="jive-xml-tag"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;';
                popupHtml +=    '&lt;span class="jive-xml-tag"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;';
                popupHtml += '&lt;span class="jive-xml-tag"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;';
            }
            popupHtml += '&lt;span class="jive-xml-tag"&gt;&amp;lt;div class="subtotal"&amp;gt;&lt;/span&gt;';
            popupHtml +=    '&lt;span class="jive-xml-tag"&gt;&amp;lt;div class="items"&amp;gt;&lt;/span&gt;';
            popupHtml +=        '&lt;span class="jive-xml-tag"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;ITEMS IN CART: ' + cart.numItems + '&lt;span class="jive-xml-tag"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;';
            popupHtml +=    '&lt;span class="jive-xml-tag"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;';   
            popupHtml +=    '&lt;span class="jive-xml-tag"&gt;&amp;lt;div class="priceSubtotal"&amp;gt;&lt;/span&gt;';
            popupHtml +=        '&lt;span class="jive-xml-tag"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;SUBTOTAL:&lt;span class="jive-xml-tag"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;';
            popupHtml +=    '&lt;span class="jive-xml-tag"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;';
            popupHtml +=    '&lt;span class="jive-xml-tag"&gt;&amp;lt;div class="price"&amp;gt;&lt;/span&gt;';

            popupHtml +=        '&lt;span class="jive-xml-tag"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;&lt;span class="jive-xml-tag"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;' + getCents(cart.subTotal) + '&lt;span class="jive-xml-tag"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class="jive-xml-tag"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;' + getDollars(cart.subTotal) + '&lt;span class="jive-xml-tag"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;&lt;span class="jive-xml-tag"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;$&lt;span class="jive-xml-tag"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class="jive-xml-tag"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;';
            popupHtml +=    '&lt;span class="jive-xml-tag"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;';                                               
            popupHtml += '&lt;span class="jive-xml-tag"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;';                                   
            popupHtml += '&lt;span class="jive-xml-tag"&gt;&amp;lt;div class="checkout"&amp;gt;&lt;/span&gt;';
            popupHtml +=    '&lt;span class="jive-xml-tag"&gt;&amp;lt;a href="/storefront/check-out.ep" title=""&amp;gt;&lt;/span&gt;&lt;span class="jive-xml-tag"&gt;&amp;lt;img src="images/btn_checkout.gif" alt="" /&amp;gt;&lt;/span&gt;&lt;span class="jive-xml-tag"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;';
            popupHtml += '&lt;span class="jive-xml-tag"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;';
            popupHtml += '&lt;span class="jive-xml-tag"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;';
            $('#shoppingCartPopup').html(popupHtml);
            $('#shoppingCartPopup').css('display', 'block');
        });
    }, function() {
        shoppingCartPopupClose = setTimeout(&lt;span class="jive-xml-quote"&gt;closeShoppingCartPopup();&lt;/span&gt;, 500);
    });
   
    $('#shoppingCartPopup').hover(function() {
        clearTimeout(shoppingCartPopupClose);
    }, function() {
        shoppingCartPopupClose = setTimeout(&lt;span class="jive-xml-quote"&gt;closeShoppingCartPopup();&lt;/span&gt;, 500);
    })
&lt;/code&gt;&lt;/pre&gt;&lt;!--[CodeBlockEnd:b6935610-ae3f-43d9-bbe7-3ada6d246e97]--&gt;&lt;/div&gt;&lt;!-- [DocumentBodyEnd:f342cf25-088a-4901-a497-2e0a223123c5] --&gt;</description>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">storefront</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">ajax</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">dwr</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">widget</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">jquery</category>
      <pubDate>Tue, 25 May 2010 16:05:56 GMT</pubDate>
      <author>developer.forums@elasticpath.com</author>
      <guid>http://grep.elasticpath.com/community/techblog/blog/2010/06/04/loading-elastic-path-content-using-widgets</guid>
      <dc:date>2010-05-25T16:05:56Z</dc:date>
      <clearspace:dateToText>2 months, 6 days ago</clearspace:dateToText>
    </item>
    <item>
      <title>Tracking Checkout Errors in Google Analytics</title>
      <link>http://grep.elasticpath.com/community/techblog/blog/2010/05/27/tracking-checkout-errors-in-google-analytics</link>
      <description>&lt;!-- [DocumentBodyStart:2d238afe-ee47-40f0-8a29-faba913970d4] --&gt;&lt;div class='jive-rendered-content'&gt;&lt;p&gt;Question: what errors are your customers seeing during checkout? Standard checkout optimization has us creating funnels in our analytics packages, identifying the choke points, and A/B testing variations of poorly performing pages. But how do we decide what changes to make to the page? Where are shoppers having problems? What if we've moved to a &lt;a class="jive-link-blog-small" href="http://grep.elasticpath.com/community/techblog/blog/2010/03/09/single-vs-multi-page-checkout"&gt;single page checkout&lt;/a&gt; – how do we identify problem areas?&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;h1&gt;Capturing the Information&lt;/h1&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;One way to help answer these questions is to track the validation errors that customers see by using Google Analytics' event tracking utilities. Event tracking allows us to store arbitrary information in a hierarchical format within Google Analytics, so that we can examine our data in typical Google Analytics fashion. Using a relatively simple snippet of code in our Elastic Path velocity templates, we can store our form validation errors:&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;!--[CodeBlockStart:fa62a285-d613-4051-9708-bc9f40ef7e6e]--&gt;&lt;pre class="jive-pre"&gt;&lt;code class="jive-code"&gt;
#set($errorsObj = $springMacroRequestContext.getErrors("expressCheckoutFormBean"))
#if ($errorsObj.errorCount &amp;gt; 0)

     &amp;lt;script type="text/javascript"&amp;gt;
          #if($errorsObj.globalErrorCount &amp;gt; 0)
               #foreach($error in $errorsObj.globalErrors)
                    _gaq.push(["_trackEvent", "Checkout Error", "$error.code", "global"]);
               #end
          #end


          #if($errorsObj.fieldErrorCount &amp;gt; 0)
               #foreach($error in $errorsObj.fieldErrors)
                    _gaq.push(["_trackEvent", "Checkout Error", "$error.code", "$error.field"]);
               #end
          #end
     &amp;lt;/script&amp;gt;
#end

&lt;/code&gt;&lt;/pre&gt;&lt;!--[CodeBlockEnd:fa62a285-d613-4051-9708-bc9f40ef7e6e]--&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;You'll need to substitute the name of your own form bean for the call to &lt;span style="font-family: 'courier new',courier;"&gt;getErrors()&lt;/span&gt;. If you have a multi-page checkout process, you may want to use a different event tracking label for each page, rather than just &lt;span style="font-family: 'courier new',courier;"&gt;Checkout Error&lt;/span&gt;. Note also that this code uses the newer asynchronous format of the Google Analytics tracker – if you are using the old format, call the &lt;span style="font-family: 'courier new',courier;"&gt;_trackEvent&lt;/span&gt; function of your GA tracker object.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;h1&gt;Analyzing the Data&lt;/h1&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Now that we have the event tracking in place, what can we learn from the data? If we navigate in Google Analytics to &lt;span style="font-family: 'courier new',courier;"&gt;Content → Event Tracking → Categories → Checkout Error&lt;/span&gt;, we can see exactly which errors customers experienced (one thing to keep in mind: every error will be counted, so one page view can result in multiple errors being recorded). More interesting though is the &lt;span style="font-family: 'courier new',courier;"&gt;Ecommerce&lt;/span&gt; tab, which shows us in the &lt;span style="font-family: 'courier new',courier;"&gt;Transactions &lt;/span&gt;column whether or not customers eventually checked out:&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;a href="http://grep.elasticpath.com/servlet/JiveServlet/showImage/38-1103-1488/blog_image_2.jpg"&gt;&lt;img alt="blog_image_2.jpg" class="jive-image-thumbnail jive-image" src="http://grep.elasticpath.com/servlet/JiveServlet/downloadImage/38-1103-1488/blog_image_2.jpg" width="620"/&gt;&lt;/a&gt;&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;This is over a 10 day time period. Not surprisingly, missing required fields are the most common error encountered. If we drill down into &lt;span style="font-family: 'courier new',courier;"&gt;errors.required&lt;/span&gt; we can see exactly which required fields are tripping visitors up:&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;a href="http://grep.elasticpath.com/servlet/JiveServlet/showImage/38-1103-1489/blog_image_3.jpg"&gt;&lt;img alt="blog_image_3.jpg" class="jive-image-thumbnail jive-image" src="http://grep.elasticpath.com/servlet/JiveServlet/downloadImage/38-1103-1489/blog_image_3.jpg" width="620"/&gt;&lt;/a&gt;&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;The big winner is CVV code! Fortunately only 6 of 128 people failed to eventually checkout, and it's possible that not all of those 6 were legitimate shoppers. Still, we may be able to make the checkout process smoother if we &lt;a class="jive-link-external-small" href="http://www.getelastic.com/cvv2-explanation/"&gt;explain what CVV is and where to find it&lt;/a&gt;. A surprising number of people seem to have left off their credit card number as well. Potentially customers are simply missing the credit card section altogether. Now we have some ideas for A/B tests we can run.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Other problem fields seem to be email and phone number. Not too shocking; people are wary about giving these out and were perhaps hoping they weren't actually required. Maybe we should do a better job of explaining why we need this information.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Looking back at the first &lt;span style="font-family: 'courier new',courier;"&gt;Checkout Error&lt;/span&gt; screen, the big conversion drop-offs seem to be in places we'd expect: rejected credit card authorizations, incorrect address data, asking the customer to phone in their order, and no inventory. Hmm, what's this &lt;span style="font-family: 'courier new',courier;"&gt;errors.whitespace&lt;/span&gt;?&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;a href="http://grep.elasticpath.com/servlet/JiveServlet/showImage/38-1103-1490/blog_image_4.jpg"&gt;&lt;img alt="blog_image_4.jpg" class="jive-image-thumbnail jive-image" src="http://grep.elasticpath.com/servlet/JiveServlet/downloadImage/38-1103-1490/blog_image_4.jpg" width="620"/&gt;&lt;/a&gt;&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;The validation rule is "no leading or trailing whitespace allowed", so someone probably had a space after their email address, couldn't figure out the error, and gave up. Stripping the whitespace prior to validation makes more sense and might have saved the order. From a net revenue standpoint this improvement isn't high on the list, but fixing it is 10 minutes of our time, and we can check to see if this rule applies to any other fields.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;There is a great deal more information to delve into here, and over time we will build up a history that will allow us to spot new problems as they crop up. By adding a short and simple snippet of code, we've gained greater insight into our customers' behavior, and hopefully discovered some ways to increase our conversion rate and create a smoother checkout process.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;em&gt;David Minor works for &lt;/em&gt;&lt;a class="jive-link-external-small" href="http://www.teamestrogen.com"&gt;&lt;em&gt;women's sportswear retailer Team Estrogen&lt;/em&gt;&lt;/a&gt;&lt;em&gt;, an Elastic Path customer. &lt;/em&gt;&lt;/p&gt;&lt;/div&gt;&lt;!-- [DocumentBodyEnd:2d238afe-ee47-40f0-8a29-faba913970d4] --&gt;</description>
      <pubDate>Thu, 27 May 2010 23:03:55 GMT</pubDate>
      <author>developer.forums@elasticpath.com</author>
      <guid>http://grep.elasticpath.com/community/techblog/blog/2010/05/27/tracking-checkout-errors-in-google-analytics</guid>
      <dc:date>2010-05-27T23:03:55Z</dc:date>
      <clearspace:dateToText>2 months, 4 days ago</clearspace:dateToText>
    </item>
    <item>
      <title>Eclipse plugins and the OSGI console...</title>
      <link>http://grep.elasticpath.com/community/techblog/blog/2010/05/27/eclipse-plugins-and-the-osgi-console</link>
      <description>&lt;!-- [DocumentBodyStart:1ac85c68-33ac-427a-a1b6-14d2fed56522] --&gt;&lt;div class='jive-rendered-content'&gt;&lt;p&gt;As you may know, Eclipse RCP (which the CM Client is implemented on) is itself built upon the OSGI and runs in the Equinox OSGI container. That means there's the full power of the OSGI framework at your disposal.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;One of the interesting features that Equinox provides is a console that allows you to poke around at the insides of a running OSGI application.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;To see the console simply add the command line flag &lt;span style="font-family: courier new,courier;"&gt;-console&lt;/span&gt; when launching an Eclipse application. The following is an example of accessing the console running Eclipse itself on my Mac, but you could do exactly the same when running the &lt;span style="font-family: courier new,courier;"&gt;Commerce Manager.exe&lt;/span&gt; on Windows.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;div class="jive-quote"&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;ep-wl-0594:eclipse35 ivanjensen$ &lt;strong&gt;./eclipse/Eclipse.app/Contents/MacOS/eclipse -console&lt;/strong&gt;&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;osgi&amp;gt; &lt;/strong&gt;&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;/div&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;You can see the &lt;span style="font-family: courier new,courier;"&gt;osgi&amp;gt;&lt;/span&gt; prompt just there. There's lots of options in there that can be very useful in debugging issues. You can see all the available options by simply typing &lt;span style="font-family: courier new,courier;"&gt;help&lt;/span&gt; at the osgi prompt:&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;div class="jive-quote"&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;osgi&amp;gt; help &lt;/p&gt;&lt;p&gt;---Controlling the OSGi framework---&lt;/p&gt;&lt;p&gt;        launch - start the OSGi Framework&lt;/p&gt;&lt;p&gt;        shutdown - shutdown the OSGi Framework&lt;/p&gt;&lt;p&gt;        close - shutdown and exit&lt;/p&gt;&lt;p&gt;        exit - exit immediately (System.exit)&lt;/p&gt;&lt;p&gt;        init - uninstall all bundles&lt;/p&gt;&lt;p&gt;        setprop &amp;lt;key&amp;gt;=&amp;lt;value&amp;gt; - set the OSGi property&lt;/p&gt;&lt;p&gt;---Controlling Bundles---&lt;/p&gt;&lt;p&gt;        install - install and optionally start bundle from the given URL&lt;/p&gt;&lt;p&gt;        uninstall - uninstall the specified bundle(s)&lt;/p&gt;&lt;p&gt;        start - start the specified bundle(s)&lt;/p&gt;&lt;p&gt;        stop - stop the specified bundle(s)&lt;/p&gt;&lt;p&gt;        refresh - refresh the packages of the specified bundles&lt;/p&gt;&lt;p&gt;        update - update the specified bundle(s)&lt;/p&gt;&lt;p&gt;---Displaying Status---&lt;/p&gt;&lt;p&gt;        status [-s [&amp;lt;comma separated list of bundle states&amp;gt;]  [&amp;lt;segment of bsn&amp;gt;]] - display installed bundles and registered services&lt;/p&gt;&lt;p&gt;        ss [-s [&amp;lt;comma separated list of bundle states&amp;gt;]  [&amp;lt;segment of bsn&amp;gt;]] - display installed bundles (short status)&lt;/p&gt;&lt;p&gt;        services [filter] - display registered service details&lt;/p&gt;&lt;p&gt;        packages [&amp;lt;pkgname&amp;gt;|&amp;lt;id&amp;gt;|&amp;lt;location&amp;gt;] - display imported/exported package details&lt;/p&gt;&lt;p&gt;        bundles [-s [&amp;lt;comma separated list of bundle states&amp;gt;]  [&amp;lt;segment of bsn&amp;gt;]] - display details for all installed bundles&lt;/p&gt;&lt;p&gt;        bundle (&amp;lt;id&amp;gt;|&amp;lt;location&amp;gt;) - display details for the specified bundle(s)&lt;/p&gt;&lt;p&gt;        headers (&amp;lt;id&amp;gt;|&amp;lt;location&amp;gt;) - print bundle headers&lt;/p&gt;&lt;p&gt;        log (&amp;lt;id&amp;gt;|&amp;lt;location&amp;gt;) - display log entries&lt;/p&gt;&lt;p&gt;---Extras---&lt;/p&gt;&lt;p&gt;        exec &amp;lt;command&amp;gt; - execute a command in a separate process and wait&lt;/p&gt;&lt;p&gt;        fork &amp;lt;command&amp;gt; - execute a command in a separate process&lt;/p&gt;&lt;p&gt;        gc - perform a garbage collection&lt;/p&gt;&lt;p&gt;        getprop  [ name ] - displays the system properties with the given name, or all of them.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;...output abbreviated...&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;osgi&amp;gt; &lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;/div&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;The command I use most frequently to get a general overview of which bundles are available and their status is &lt;span style="font-family: courier new,courier;"&gt;ss&lt;/span&gt;:&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;div class="jive-quote"&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;osgi&amp;gt; ss      &lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Framework is launched.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;id      State       Bundle&lt;/p&gt;&lt;p&gt;0       ACTIVE      org.eclipse.osgi_3.5.0.v20090520&lt;/p&gt;&lt;p&gt;1       ACTIVE      org.eclipse.equinox.simpleconfigurator_1.0.100.v20090520-1905&lt;/p&gt;&lt;p&gt;2       &amp;lt;&amp;lt;LAZY&amp;gt;&amp;gt;    com.ibm.icu_4.0.1.v20090415&lt;/p&gt;&lt;p&gt;3       RESOLVED    com.jcraft.jsch_0.1.41.v200903070017&lt;/p&gt;&lt;p&gt;4       &amp;lt;&amp;lt;LAZY&amp;gt;&amp;gt;    com.sun.jna_3.1.0&lt;/p&gt;&lt;p&gt;5       RESOLVED    java_cup.runtime_0.10.0.v200803061811&lt;/p&gt;&lt;p&gt;6       RESOLVED    javax.activation_1.1.0.v200905021805&lt;/p&gt;&lt;p&gt;7       RESOLVED    javax.mail_1.4.0.v200905040518&lt;/p&gt;&lt;p&gt;8       RESOLVED    javax.servlet_2.5.0.v200806031605&lt;/p&gt;&lt;p&gt;9       RESOLVED    javax.servlet.jsp_2.0.0.v200806031607&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;...output abbreviated...&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;osgi&amp;gt;&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;/div&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Here you can see some of the bundles that run in my installation of eclipse. Each bundle is given a unique number that can be used with some of the other console commands. You can also see the bundle's state and the bundle's symbolic name. For more information on the bundle states &lt;a class="jive-link-external-small" href="http://en.wikipedia.org/wiki/OSGi#Life-Cycle"&gt;check out this Wikipedia article&lt;/a&gt;.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;There's plenty more good stuff down in the console, including being able to start, stop and update bundles in a running system.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;So why not take some time and get to know your OSGI console and add another string to your RCP bow?&lt;/p&gt;&lt;/div&gt;&lt;!-- [DocumentBodyEnd:1ac85c68-33ac-427a-a1b6-14d2fed56522] --&gt;</description>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">cm_client</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">rcp</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">cmclient</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">osgi</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">equinox</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">osgi_console</category>
      <pubDate>Thu, 27 May 2010 21:27:32 GMT</pubDate>
      <author>developer.forums@elasticpath.com</author>
      <guid>http://grep.elasticpath.com/community/techblog/blog/2010/05/27/eclipse-plugins-and-the-osgi-console</guid>
      <dc:date>2010-05-27T21:27:32Z</dc:date>
      <clearspace:dateToText>2 months, 4 days ago</clearspace:dateToText>
    </item>
    <item>
      <title>Caching and Load Balancing with Apache HTTP Server</title>
      <link>http://grep.elasticpath.com/community/techblog/blog/2010/05/04/caching-and-load-balancing-with-apache-http-server</link>
      <description>&lt;!-- [DocumentBodyStart:8b5bfd7d-6681-476c-bc01-2aef63dbce98] --&gt;&lt;div class='jive-rendered-content'&gt;&lt;p&gt;Apache HTTP Server is a very effective tool for caching static content and, if configured properly, can improve performance of your Elastic Path deployment by up to 30%! Furthermore, Apache does a great job of load balancing a cluster of storefront nodes, giving you even more throughput and scalability, without resorting to expensive hardware load balancers. Obviously, Apache will never perform like a hardware load balancer, but it is a little more affordable (read: free). So really, what more can you ask for from an HTTP server?&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;In this post, we'll look at using Apache to load balance our storefront servers. We'll also look at enabling caching of static content at the Apache level, removing a lot of network and CPU load from our application servers and giving a faster load time to browsers. Before we begin, make sure you have the following:&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Apache HTTP Server 2.2.10+ with either JBoss 4.2+ or Tomcat 5.5+ (using Apache with WebLogic is more complicated and requires the use of a specific Oracle-WebLogic Apache plug-in.)&lt;/li&gt;&lt;li&gt;Apache has been built with the following modules: mod_proxy, mod_proxy_ajp, mod_proxy_balancer, mod_cache, mod_disk_cache.&lt;/li&gt;&lt;/ul&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;h1&gt;&lt;span style="font-size: 18pt;"&gt;Configuring a Proxy and Static Content Cache&lt;/span&gt;&lt;/h1&gt;&lt;p&gt;Let's start by creating a proxy server and caching static content at the Apache level. This is relatively easy to set up, but important to understand before moving on to load balancing. We'll assume Apache is the front-most facing component to the user's browser. The architecture will look something like the following diagram.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;a href="http://grep.elasticpath.com/servlet/JiveServlet/showImage/38-1100-1477/ApacheSimple.jpg"&gt;&lt;img alt="ApacheSimple.jpg" class="jive-image" src="http://grep.elasticpath.com/servlet/JiveServlet/downloadImage/38-1100-1477/ApacheSimple.jpg"/&gt;&lt;/a&gt;&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Let's examine a request working it's way through this architecture. A typical first request from a shopper's browser, such as viewing a product page, will flow through Apache (bypassing all caches since they're empty) and arrive at the application server. The application server will gather and serve the necessary HTML and subsequent embedded objects (images, js, css, etc). These objects will pass back through Apache and to the user's browser. The key process here, however, is that as these static objects pass back through Apache, Apache will cache them based on their cache control headers.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;When a request comes in for the same product page (or any request for the same set of static HTML objects), Apache will serve the static objects straight back to the user's browser from its cache. Only the dynamic HTML and other dynamic content will come from the app server. Although a second load of the same page on the same user's browser will already be cached at the user's browser level, it will be very useful for new sessions that have an empty browser cache.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Unfortunately, there are a couple issues we need to think about before we can implement this setup, such as:&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;How do we communicate between Apache and the app server?&lt;/li&gt;&lt;li&gt;What protocol do we use between Apache and the app server, HTTP or AJP?&lt;/li&gt;&lt;li&gt;How do we support Acegi security, which is required by the storefront application servers?&lt;/li&gt;&lt;/ul&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Don't worry! We did a fair amount of performance testing to answer these questions, and came up with the following diagram.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;a href="http://grep.elasticpath.com/servlet/JiveServlet/showImage/38-1100-1478/ApacheProtocols.jpg"&gt;&lt;img alt="ApacheProtocols.jpg" class="jive-image" src="http://grep.elasticpath.com/servlet/JiveServlet/downloadImage/38-1100-1478/ApacheProtocols.jpg"/&gt;&lt;/a&gt;&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;The key here, is that a) we're using AJP between Apache and the app server, a fast binary protocol, and b) we're using two separate AJP connectors on the app server, one non-secure for HTTP traffic and one considered "secure" for HTTPS traffic. This allows Acegi to know that a request is "secure" so that it will not try to redirect endlessly to a secure port (a typical problem we see). I'm putting "secure" in quotes because it's really no different than the insecure channel (it's not encrypted). It simply has additional header information stating it's a secure channel.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;In order to implement this, there are a number of items to configure such as Apache's mod_proxy and mod_cache, as well as any cache control configuration that needs to be done on the application server.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;h4&gt;mod_proxy&lt;/h4&gt;&lt;p&gt;We need to allow requests that come in to Apache to pass through to the application server and then return to the user. This is done using Apache's mod_proxy module. The full mod_proxy documentation is here: &lt;a class="jive-link-external-small" href="http://httpd.apache.org/docs/2.2/mod/mod_proxy.html"&gt;http://httpd.apache.org/docs/2.2/mod/mod_proxy.html&lt;/a&gt;. It's a recommended read. We'll also be using the mod_proxy_ajp module for AJP support.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;The first step is to enable the two AJP connectors on the application server, in server.xml (or jboss-server.xml):&lt;/p&gt;&lt;table border="1" cellpadding="3" cellspacing="0" style="width: 100%; border: 1px solid #000000; background: none repeat scroll 0% 0% #fef5d6;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;pre&gt;&amp;lt;Connector enableLookups="false" port="8009" protocol="AJP/1.3"/&amp;gt;&lt;br/&gt;&amp;lt;Connector enableLookups="false" port="8010" protocol="AJP/1.3" scheme="https" secure="true"/&amp;gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Note the secure parameters for port 8010. This fools Acegi into thinking that anything coming over this port with AJP is a secure connection and it will not redirect it.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;The second step is to ensure Acegi knows it may receive connections over port 80 and its secure mapped port is then 443 (the typical HTTP and HTTPS ports). To do this, we edit the storefront web app's &lt;span style="font-family: 'courier new',courier;"&gt;WEB-INF/conf/spring/security/acegi.xml&lt;/span&gt; file and add an additional port mapping to the portMapper bean as follows:&lt;/p&gt;&lt;table border="1" cellpadding="3" cellspacing="0" style="width: 100%; border: 1px solid #000000; background: none repeat scroll 0% 0% #fef5d6;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;pre&gt;    &amp;lt;!-- port # are specified in default.xml --&amp;gt;&lt;br/&gt;    &amp;lt;bean id="portMapper" class="org.acegisecurity.util.PortMapperImpl"&amp;gt;&lt;br/&gt;        &amp;lt;property name="portMappings"&amp;gt;&lt;br/&gt;            &amp;lt;map&amp;gt;&lt;br/&gt;                &lt;strong&gt;&amp;lt;entry key="80"&amp;gt;&amp;lt;value&amp;gt;443&amp;lt;/value&amp;gt;&amp;lt;/entry&amp;gt;&lt;/strong&gt;&lt;br/&gt;                &amp;lt;entry key="8080"&amp;gt;&amp;lt;value&amp;gt;8443&amp;lt;/value&amp;gt;&amp;lt;/entry&amp;gt;&lt;br/&gt;            &amp;lt;/map&amp;gt;&lt;br/&gt;        &amp;lt;/property&amp;gt;&lt;br/&gt;    &amp;lt;/bean&amp;gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;div&gt; &lt;/div&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;In the third and final step, we want to configure the HTTP and HTTPS virtual hosts on Apache to listen to ports 80 and 443.&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: monospace;"&gt;&lt;span style="border-collapse: collapse; white-space: pre;"&gt;&lt;br/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;table border="1" cellpadding="3" cellspacing="0" style="width: 100%; border: 1px solid #000000; background: none repeat scroll 0% 0% #fef5d6;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;pre&gt;&lt;pre&gt;LoadModule proxy_ajp_module modules/mod_proxy_ajp.so&lt;br/&gt;&lt;br/&gt;&amp;lt;VirtualHost 10.10.90.54:80&amp;gt;&lt;br/&gt;        ServerName 10.10.90.54&lt;br/&gt;        ProxyPreserveHost On&lt;br/&gt;        ProxyPass /storefront ajp://10.10.90.54:8009/storefront keepalive=On&lt;br/&gt;&amp;lt;/VirtualHost&amp;gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;VirtualHost 10.10.90.54:443&amp;gt;&lt;br/&gt;        ServerName 10.10.90.54&lt;br/&gt;        # Enable/Disable SSL for this virtual host if you want to terminate SSL here&lt;br/&gt;        ProxyPreserveHost On&lt;br/&gt;        ProxyPass /storefront ajp://10.10.90.54:8010/storefront keepalive=On&lt;br/&gt;&amp;lt;/VirtualHost&amp;gt;&lt;/pre&gt;
&lt;br/&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;pre&gt;&lt;/pre&gt;&lt;p&gt;There's a lot going on here, so let's have a look at the HTTPS:443 virtual host as it's the more complex one here:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Clearly, one would want to configure the virtual hosts to listen on the specific machine's port.&lt;/li&gt;&lt;li&gt;Within here is where we would do any SSL termination before passing the request over AJP to the app server.&lt;/li&gt;&lt;li&gt;"&lt;span style="font-family: courier new,courier;"&gt;ProxyPreserveHost On&lt;/span&gt;" ensures the &lt;span style="font-family: courier new,courier;"&gt;Host&lt;/span&gt; header is maintained as it's passed to the app server. This is required for Elastic Path 6.1 and later to be able to handle multi-store requests.&lt;/li&gt;&lt;li&gt;The &lt;span style="font-family: courier new,courier;"&gt;ProxyPass&lt;/span&gt; directive is the key here. This tells Apache to pass any requests coming in matching&lt;span style="font-family: courier new,courier;"&gt; /storefront&lt;/span&gt; to the app server's AJP connector under &lt;span style="font-family: courier new,courier;"&gt;/storefront&lt;/span&gt;.&lt;/li&gt;&lt;li&gt;There are a large number of options for this directive, including maintaining &lt;span style="font-family: courier new,courier;"&gt;keepalive&lt;/span&gt;, as we've done here.&lt;/li&gt;&lt;li&gt;Note that the storefront server doesn't have to be the localhost. We'll see this later when we being load balancing.&lt;/li&gt;&lt;/ol&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;At this point, after rebooting, you should be able to hit Apache on port 80 and pull up your storefront.&lt;/p&gt;&lt;h4&gt;mod_cache&lt;/h4&gt;&lt;p&gt;Next, we want to cache any static content we can on the Apache side. To do this, we'll use mod_cache, or more specifically mod_disk_cache. There is also mod_mem_cache, which is a memory based cache, but we've actually found better performance results with mod_disk_cache, plus the persistence of all cache files is a plus.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Adding the &lt;span style="font-family: courier new,courier;"&gt;httpd.conf&lt;/span&gt; directives for a disk cache is fairly straightforward. Let's try the following:&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;table border="1" cellpadding="3" cellspacing="0" style="width: 100%; border: 1px solid #000000; background: none repeat scroll 0% 0% #fef5d6;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;pre&gt;&lt;pre&gt;CacheEnable disk /storefront/&lt;br/&gt;CacheRoot /var/www/cache&lt;br/&gt;CacheDirLevels 5&lt;br/&gt;CacheDirLength 2&lt;br/&gt;CacheIgnoreHeaders Set-Cookie&lt;/pre&gt;
&lt;br/&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Looking at the lines in detail:&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Enable the disk cache on the URL &lt;span style="font-family: courier new,courier;"&gt;/storefront/&lt;/span&gt;.&lt;/li&gt;&lt;li&gt;Specify the cache location on the local disk, in this case &lt;span style="font-family: courier new,courier;"&gt;/var/www/cache&lt;/span&gt;. You'll want to make sure the Apache user can write to that directory.&lt;/li&gt;&lt;li&gt;The number of directory levels in the cache tree structure.&lt;/li&gt;&lt;li&gt;The number of characters for each directory.&lt;/li&gt;&lt;li&gt;Finally, we specify which headers we DO NOT want to cache. This is essential. If we don't set this for cookies, we will end up getting someone else's session!&lt;/li&gt;&lt;/ol&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;At this point, after rebooting Apache, we will begin to cache any static objects with cache control headers. In order to expand on what is (or isn't cached), let's move on to the next section.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;h4&gt;Cache Control Header Config&lt;/h4&gt;&lt;p&gt;Finally, we want the application server, or more specifically, the deployed applications, to tell Apache if there's anything to cache. This is typically done by using cache control headers, such as &lt;span style="font-family: 'courier new',courier;"&gt;max-age&lt;/span&gt;.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;For the storefront web application, you can use the Caching Control Filter to add the &lt;tt&gt;max-age&lt;/tt&gt; cache control header to requests for specific types of content (based on URL patterns). The Caching Control Filter configuration is in the storefront's &lt;tt&gt;conf/spring/web/filter-config.xml&lt;/tt&gt; file, in the &lt;tt&gt;cachingControlFilter&lt;/tt&gt; bean definition. The &lt;tt&gt;cachingControlEntries&lt;/tt&gt; list contains bean definitions that represent the URL patterns to test and &lt;tt&gt;max-age&lt;/tt&gt; value to set.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;The following is an example of caching all &lt;span style="font-family: courier new,courier;"&gt;/renderImage.image&lt;/span&gt; dynamic image calls, all &lt;span style="font-family: courier new,courier;"&gt;/template-resources/&lt;/span&gt; calls (css, js, etc) and any dynamic content assets under &lt;span style="font-family: courier new,courier;"&gt;/content/&lt;/span&gt;:&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;table border="1" cellpadding="3" cellspacing="0" style="width: 100%; border: 1px solid #000000; background: none repeat scroll 0% 0% #fef5d6;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;pre&gt;&amp;lt;bean id=&lt;span class="code-quote"&gt;"cachingControlFilter"&lt;/span&gt;&lt;/pre&gt;&lt;pre class="code-java"&gt;     class=&lt;span class="code-quote"&gt;"com.elasticpath.commons.filter.impl.CachingControlFilter"&lt;/span&gt;&amp;gt;&lt;br/&gt;     &amp;lt;property name=&lt;span class="code-quote"&gt;"cachingControlEntries"&lt;/span&gt;&amp;gt;&lt;br/&gt;          &amp;lt;list&amp;gt;&lt;br/&gt;               &amp;lt;bean class=&lt;span class="code-quote"&gt;"com.elasticpath.commons.filter.impl.CachingControlFilter$CachingControlEntry"&lt;/span&gt;&amp;gt;&lt;br/&gt;                    &amp;lt;property name=&lt;span class="code-quote"&gt;"urlPattern"&lt;/span&gt;&amp;gt;&lt;br/&gt;                         &amp;lt;value&amp;gt;^.*renderImage\.image.*$&amp;lt;/value&amp;gt;&lt;br/&gt;                    &amp;lt;/property&amp;gt;&lt;br/&gt;                    &amp;lt;property name=&lt;span class="code-quote"&gt;"maxAge"&lt;/span&gt;&amp;gt;&lt;br/&gt;                         &amp;lt;value&amp;gt;86400&amp;lt;/value&amp;gt;&lt;br/&gt;                    &amp;lt;/property&amp;gt;&lt;br/&gt;               &amp;lt;/bean&amp;gt;&lt;br/&gt;               &amp;lt;bean class=&lt;span class="code-quote"&gt;"com.elasticpath.commons.filter.impl.CachingControlFilter$CachingControlEntry"&lt;/span&gt;&amp;gt;&lt;br/&gt;                    &amp;lt;property name=&lt;span class="code-quote"&gt;"urlPattern"&lt;/span&gt;&amp;gt;&lt;br/&gt;                         &amp;lt;value&amp;gt;^.*template-resources.*$&amp;lt;/value&amp;gt;&lt;br/&gt;                    &amp;lt;/property&amp;gt;&lt;br/&gt;                    &amp;lt;property name=&lt;span class="code-quote"&gt;"maxAge"&lt;/span&gt;&amp;gt;&lt;br/&gt;                         &amp;lt;value&amp;gt;86400&amp;lt;/value&amp;gt;&lt;br/&gt;                    &amp;lt;/property&amp;gt;&lt;br/&gt;               &amp;lt;/bean&amp;gt;&lt;br/&gt;               &amp;lt;bean class=&lt;span class="code-quote"&gt;"com.elasticpath.commons.filter.impl.CachingControlFilter$CachingControlEntry"&lt;/span&gt;&amp;gt;&lt;br/&gt;                          &amp;lt;property name=&lt;span class="code-quote"&gt;"urlPattern"&lt;/span&gt;&amp;gt;&lt;br/&gt;                                  &amp;lt;value&amp;gt;^.*content.*$&amp;lt;/value&amp;gt;&lt;br/&gt;                    &amp;lt;/property&amp;gt;&lt;br/&gt;                    &amp;lt;property name=&lt;span class="code-quote"&gt;"maxAge"&lt;/span&gt;&amp;gt;&lt;br/&gt;                         &amp;lt;value&amp;gt;86400&amp;lt;/value&amp;gt;&lt;br/&gt;                    &amp;lt;/property&amp;gt;&lt;br/&gt;               &amp;lt;/bean&amp;gt;&lt;br/&gt;          &amp;lt;/list&amp;gt;&lt;br/&gt;     &amp;lt;/property&amp;gt;&lt;br/&gt;&amp;lt;/bean&amp;gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Now, after restarting Apache, you should have a fully functioning Apache proxy with proper caching of static content.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;h1&gt;&lt;span style="font-size: 18pt;"&gt;Configuring Load Balancing&lt;/span&gt;&lt;/h1&gt;&lt;p&gt;Load balancing is an easy extension once our proxy is set up. Essentially, with load balancing, instead of passing the request through to the same machine each time, we pass it to a cluster of machines (two or more) based on a certain algorithm. I recommend reading the complete Apache documentation on mod_proxy_balancer, which is the module we'll use to enable load balancing. It can be found here: &lt;a class="jive-link-external-small" href="http://httpd.apache.org/docs/2.2/mod/mod_proxy_balancer.html"&gt;http://httpd.apache.org/docs/2.2/mod/mod_proxy_balancer.html&lt;/a&gt;&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Let's first lay out the Apache configuration, adding to our existing &lt;span style="font-family: courier new,courier;"&gt;VirtualHost&lt;/span&gt; entries.&lt;/p&gt;&lt;table border="1" cellpadding="3" cellspacing="0" style="width: 100%; border: 1px solid #000000; background: none repeat scroll 0% 0% #fef5d6;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;pre&gt;&amp;lt;VirtualHost 10.10.90.54:80&amp;gt;&lt;br/&gt;&lt;br/&gt;        ServerName 10.10.90.54&lt;br/&gt;&lt;br/&gt;        # ProxyPreserveHost On&lt;br/&gt;        RequestHeader set Host mars.elasticpath.net&lt;br/&gt;&lt;br/&gt;        &amp;lt;Proxy balancer://tomcatservers&amp;gt;&lt;br/&gt;                BalancerMember ajp://localhost:9009 route=node1 loadfactor=90&lt;br/&gt;                BalancerMember ajp://10.10.90.51:9009 route=node2 loadfactor=100&lt;br/&gt;                BalancerMember ajp://10.10.90.52:9009 route=node3 loadfactor=100&lt;br/&gt;                BalancerMember ajp://10.10.90.53:9009 route=node4 loadfactor=100&lt;br/&gt;        &amp;lt;/Proxy&amp;gt;&lt;br/&gt;&lt;br/&gt;        ProxyPass /storefront balancer://tomcatservers/storefront stickysession=JSESSIONID nofailover=Off&lt;br/&gt;        ProxyPass /server-status !&lt;br/&gt;&lt;br/&gt;&amp;lt;/VirtualHost&amp;gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;VirtualHost 10.10.90.54:443&amp;gt;&lt;br/&gt;&lt;br/&gt;        ServerName 10.10.90.54&lt;br/&gt;&lt;br/&gt;        LogLevel warn&lt;br/&gt;        #CustomLog logs/ssl_request_log "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"&lt;br/&gt;        LogFormat "%h %l %u %t \"%r\" %&amp;gt;s %b" common&lt;br/&gt;        CustomLog logs/ssl_access_log common&lt;br/&gt;        ErrorLog logs/ssl_error_log&lt;br/&gt;&lt;br/&gt;        #   SSL Engine Switch:&lt;br/&gt;        #   Enable/Disable SSL for this virtual host.&lt;br/&gt;        SSLEngine on&lt;br/&gt;        SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL&lt;br/&gt;        SSLCertificateFile "/usr/local/apache2/conf/server.crt"&lt;br/&gt;        SSLCertificateKeyFile "/usr/local/apache2/conf/server.key"&lt;br/&gt;&lt;br/&gt;        #DocumentRoot    "/var/www/html/one"&lt;br/&gt;&lt;br/&gt;        # ProxyPreserveHost On &lt;br/&gt;        RequestHeader set HOST mars.elasticpath.net&lt;br/&gt;&lt;br/&gt;        SetEnvIf User-Agent ".*MSIE.*" nokeepalive ssl-unclean-shutdown downgrade-1.0 force-response-1.0&lt;br/&gt;&lt;br/&gt;        &amp;lt;Proxy balancer://tomcatservers-ssl&amp;gt;&lt;br/&gt;                BalancerMember ajp://localhost:9010 route=node1 loadfactor=90&lt;br/&gt;                BalancerMember ajp://10.10.90.51:9010 route=node2 loadfactor=100&lt;br/&gt;                BalancerMember ajp://10.10.90.52:9010 route=node3 loadfactor=100&lt;br/&gt;                BalancerMember ajp://10.10.90.53:9010 route=node4 loadfactor=100&lt;br/&gt;        &amp;lt;/Proxy&amp;gt;&lt;br/&gt;&lt;br/&gt;        ProxyPass /storefront balancer://tomcatservers-ssl/storefront stickysession=JSESSIONID nofailover=off&lt;br/&gt;&lt;br/&gt;&amp;lt;/VirtualHost&amp;gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;We've seen the &lt;span style="font-family: courier new,courier;"&gt;VirtualHost&lt;/span&gt; entries before, so let's just look at the &lt;span style="font-family: courier new,courier;"&gt;Proxy balancer&lt;/span&gt; configuration in detail. We'll zoom in on this below for the insecure, port 80 connector:&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;table border="1" cellpadding="3" cellspacing="0" style="width: 100%; border: 1px solid #000000; background: none repeat scroll 0% 0% #fef5d6;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;pre&gt;...&lt;br/&gt;        &amp;lt;Proxy balancer://tomcatservers&amp;gt;&lt;br/&gt;                BalancerMember ajp://localhost:9009 route=node1 loadfactor=80&lt;br/&gt;                BalancerMember ajp://10.10.90.51:9009 route=node2 loadfactor=100&lt;br/&gt;                BalancerMember ajp://10.10.90.52:9009 route=node3 loadfactor=100&lt;br/&gt;                BalancerMember ajp://10.10.90.53:9009 route=node4 loadfactor=100&lt;br/&gt;        &amp;lt;/Proxy&amp;gt;&lt;br/&gt;&lt;br/&gt;        ProxyPass /storefront balancer://tomcatservers/storefront stickysession=JSESSIONID nofailover=Off&lt;br/&gt;        ProxyPass /server-status !&lt;br/&gt;...&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Within the &lt;span style="font-family: courier new,courier;"&gt;Proxy balancer&lt;/span&gt; directive, we've named our balancer &lt;span style="font-family: 'courier new',courier;"&gt;&amp;lt;Proxy balancer://tomcatservers&amp;gt;&lt;/span&gt; and defined our load balancer members, for example &lt;span style="font-family: 'courier new',courier;"&gt;BalancerMember ajp://10.10.90.51:9009 route=node2 loadfactor=100&lt;/span&gt;. In this case, we have 4 storefronts included, 1 being on the localhost (the same machine as the Apache install). We've opened up AJP port 9009 on these machines and configured all to have an even load factor, except the first node, which has slightly lower factor to give breathing room for Apache on the same machine.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;The next directive, &lt;span style="font-family: 'courier new',courier;"&gt;ProxyPass /storefront balancer://tomcatservers/storefront&lt;/span&gt;, we specify our &lt;span style="font-family: courier new,courier;"&gt;ProxyPass&lt;/span&gt; to allow requests to &lt;span style="font-family: courier new,courier;"&gt;/storefront*&lt;/span&gt; to pass to the balancer's &lt;span style="font-family: courier new,courier;"&gt;/storefront*&lt;/span&gt;.  Note that we're also specifying the cookie name we want to keep stuck to each storefront node, so subsequent requests return to the same node. In our case, this is the &lt;span style="font-family: courier new,courier;"&gt;JSESSIONID&lt;/span&gt;.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;The last key setting in here is the &lt;span style="font-family: 'courier new',courier;"&gt;route=nodeN&lt;/span&gt; on each &lt;span style="font-family: courier new,courier;"&gt;BalancerMember&lt;/span&gt;. This is the name you configure for a node's &lt;span style="font-family: courier new,courier;"&gt;jvmRoute&lt;/span&gt; within the app's &lt;span style="font-family: courier new,courier;"&gt;server.xml&lt;/span&gt;. This allows Apache and the application server to identify which requests will go to which node. Without this setting (and/or the &lt;span style="font-family: 'courier new',courier;"&gt;stickysession&lt;/span&gt; setting), the user's session may bounce between storefront nodes. This will cause strange behavior, like gettin bounced back to the homepage.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;To set the &lt;span style="font-family: courier new,courier;"&gt;jvmRoute&lt;/span&gt; within the &lt;span style="font-family: courier new,courier;"&gt;server.xml&lt;/span&gt;, look for a commented-out line like the following:&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;table border="1" cellpadding="3" cellspacing="0" style="width: 100%; border: 1px solid #000000; background: none repeat scroll 0% 0% #fef5d6;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;pre&gt;&amp;lt;!-- You should set jvmRoute to support load-balancing via AJP ie :&lt;br/&gt; &amp;lt;Engine name="Catalina" defaultHost="localhost" jvmRoute="node1"&amp;gt;         &lt;br/&gt;--&amp;gt;   &lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Uncomment this and change &lt;span style="font-family: 'courier new',courier;"&gt;jvmRoute=""&lt;/span&gt; to be the same as your &lt;span style="font-family: courier new,courier;"&gt;BalancerMember&lt;/span&gt; entry (or vice versa). The same configuration as above is done for the secure connectors, which, in this case are on port 9010.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;After rebooting Apache, you should be getting load balanced to a specific node in the cluster and stay on that node for subsequent requests. Your HTML assets will also be getting cached at the Apache layer as they pass through the proxy.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Now you can cache and load balance storefront servers with Apache HTTP Server. Go ahead and try it. Once you're set up, I would recommend tailing your Apache and app server access logs to watch your requests pass through Apache and your app server and ensure they're using sticky sessions correctly. Increasing the access log level on Apache and the app server to output cookie names/values is handy if you need to debug any sticky session config issues.&lt;/p&gt;&lt;p style="min-height: 8pt; height: 8pt; padding: 0px;"&gt;&amp;nbsp;&lt;/p&gt;&lt;h2&gt;&lt;span style="font-size: 18pt;"&gt;Some Final Considerations&lt;/span&gt;&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;span style="font-weight: normal;"&gt;There are some known issues around keep-alive and some older versions of Apache HTTP and Tomcat where the AJP connections between the two will not get released, causing the connection pool to fill and not allow new requests.&lt;/span&gt;&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;Consider using Apache's htcacheclean, which runs as a daemon or a one-time job, to control the size of your Apache cache on the disk. If your website has a small, finite number of cacheable HTML objects, this typically isn't a huge issue. On the other hand, if you have many GBs of assets and want to keep your cache to, say, 500 MB, htcacheclean is your tool. See the documentation for full details: &lt;a class="jive-link-external-small" href="http://httpd.apache.org/docs/2.2/programs/htcacheclean.html"&gt;http://httpd.apache.org/docs/2.2/programs/htcacheclean.html&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Test, test, test. Make sure you do proper functional testing on a staging environment to ensure there are no strange redirects or odd behavior after putting another layer between your ecommerce site and the user. And just as importantly, proper performance testing will ensure there are no capacity issues between Apache and the app server. This will allow you to fine tune your connection pools for maximum performance, both on the Apache side and the app server side.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;!-- [DocumentBodyEnd:8b5bfd7d-6681-476c-bc01-2aef63dbce98] --&gt;</description>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">clustering</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">apache</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">proxy</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">cache</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">load</category>
      <category domain="http://grep.elasticpath.com/tags?containerType=14&amp;container=2089">balancing</category>
      <pubDate>Fri, 30 Apr 2010 20:41:13 GMT</pubDate>
      <author>developer.forums@elasticpath.com</author>
      <guid>http://grep.elasticpath.com/community/techblog/blog/2010/05/04/caching-and-load-balancing-with-apache-http-server</guid>
      <dc:date>2010-04-30T20:41:13Z</dc:date>
      <clearspace:dateToText>3 months, 1 day ago</clearspace:dateToText>
    </item>
  </channel>
</rss>

