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

<channel>
	<title>Opsview Labs &#187; Development</title>
	<atom:link href="http://labs.opsview.com/tag/development/feed/" rel="self" type="application/rss+xml" />
	<link>http://labs.opsview.com</link>
	<description>Opsview&#039;s Engineering Blog</description>
	<lastBuildDate>Fri, 20 Jan 2012 09:32:54 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Internationalising Catalyst, Part 2</title>
		<link>http://labs.opsview.com/2010/12/internationalising-catalyst-part-2/</link>
		<comments>http://labs.opsview.com/2010/12/internationalising-catalyst-part-2/#comments</comments>
		<pubDate>Tue, 21 Dec 2010 09:17:41 +0000</pubDate>
		<dc:creator>tonvoon</dc:creator>
				<category><![CDATA[Catalyst]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Frameworks]]></category>

		<guid isPermaLink="false">http://labs.opsview.com/?p=699</guid>
		<description><![CDATA[
			
				
			
		We present the 2nd part to our Catalyst advent calendar entry about internationalising your Catalyst application, complete with some really neat scripts such as automatically translating your string using Google! See it here. Enjoy!
]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Flabs.opsview.com%2F2010%2F12%2Finternationalising-catalyst-part-2%2F">
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Flabs.opsview.com%2F2010%2F12%2Finternationalising-catalyst-part-2%2F&amp;style=normal&amp;b=2" height="61" width="50" />
			</a>
		</div><p><a class="lightbox" title="catalyst_logo" href="http://labs.opsview.com/wp-content/uploads/2010/12/catalyst_logo.png"><img class="alignleft size-full wp-image-696" style="margin: 0pt 10px 5px 0pt;" title="catalyst_logo" src="http://labs.opsview.com/wp-content/uploads/2010/12/catalyst_logo.png" alt="" width="103" height="146" /></a>We present the <a href="http://bit.ly/eECIOH">2nd part</a> to our <a href="http://catalystframework.org">Catalyst</a> advent calendar entry about <a href="http://bit.ly/g2SzQb">internationalising your Catalyst application</a>, complete with some really neat scripts such as automatically translating your string using Google! See it <a href="http://bit.ly/eECIOH">here</a>. Enjoy!</p>
]]></content:encoded>
			<wfw:commentRss>http://labs.opsview.com/2010/12/internationalising-catalyst-part-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Turning ideas into features: Part 2</title>
		<link>http://labs.opsview.com/2010/12/turning-ideas-into-features-part-2/</link>
		<comments>http://labs.opsview.com/2010/12/turning-ideas-into-features-part-2/#comments</comments>
		<pubDate>Fri, 03 Dec 2010 08:32:24 +0000</pubDate>
		<dc:creator>James Peel</dc:creator>
				<category><![CDATA[Opsview]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[framework]]></category>
		<category><![CDATA[roadmap]]></category>

		<guid isPermaLink="false">http://labs.opsview.com/?p=654</guid>
		<description><![CDATA[
			
				
			
		In our recent Opsview Customer Survey we asked for feedback on specific features we were considering for the product roadmap. This had a direct influence on our plans as I’m hoping to illustrate in this article. Opsview Customer Survey Results
The features below scored highest so I&#8217;ll describe how we&#8217;re planning to address them&#8230;

Reports Module: More [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Flabs.opsview.com%2F2010%2F12%2Fturning-ideas-into-features-part-2%2F">
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Flabs.opsview.com%2F2010%2F12%2Fturning-ideas-into-features-part-2%2F&amp;style=normal&amp;b=2" height="61" width="50" />
			</a>
		</div><p><a class="lightbox" title="survey_icon3" href="http://labs.opsview.com/wp-content/uploads/2010/12/survey_icon3.png"><img class="alignleft size-full wp-image-671" style="margin: 0pt 10px 5px 0pt;" title="survey_icon3" src="http://labs.opsview.com/wp-content/uploads/2010/12/survey_icon3.png" alt="" width="80" height="89" /></a>In our recent Opsview Customer Survey we asked for feedback on specific features we were considering for the product roadmap. This had a direct influence on our plans as I’m hoping to illustrate in this article. Opsview Customer Survey Results</p>
<p>The features below scored highest so I&#8217;ll describe how we&#8217;re planning to address them&#8230;<span id="more-654"></span></p>
<h3><a class="lightbox" title="featuresPlannedOpsviewEnterprise" href="http://labs.opsview.com/wp-content/uploads/2010/12/featuresPlannedOpsviewEnterprise1.png"><img class="aligncenter size-full wp-image-666" style="border: 1px solid #cccccc; margin-bottom: 8px;" title="featuresPlannedOpsviewEnterprise" src="http://labs.opsview.com/wp-content/uploads/2010/12/featuresPlannedOpsviewEnterprise1.png" alt="" width="574" height="347" /></a></h3>
<h3>Reports Module: More out of the box reports</h3>
<p>We delivered three new reports for this module during the Opsview Enterprise 3.10 release and we plan to introduce a similar number for Opsview Enterprise 3.12. We also upgraded the underlying Jasper framework and we are making improvements to documentation and management tools.</p>
<h3>Auto discovery / auto inventory</h3>
<p>We have started work on the Opsview Auto Discovery Module and we plan to release this during the first half of 2011. This is expected to include a discovery component and a set of browser based management tools to manipulate discovered hosts.</p>
<h3>Feature-rich, configurable dashboard</h3>
<p>This has been on the roadmap for a while. We have now completed our research into suitable technologies and are currently working on a proof of concept.</p>
<h3>Graphing: CSV, export</h3>
<p>We added the ability to export graphing data in CSV format in Opsview Enterprise 3.10.</p>
<h3>Improved Documentation</h3>
<p>We had four interns working with us over the summer period and part of their assignment was to help us improve our online documentation. We also improved the design and layout of the documentation site and implemented a style guide during the same period. Documentation is under constant update and improvement as part of the release cycle.</p>
<h3>Easier Installation</h3>
<p>Time was also spent over the summer period improving packaging and documentation in order to make it even easier to install Opsview.</p>
<h3>Log monitoring and event correlation</h3>
<p>Improved log monitoring and event correlation capabilities on the roadmap for next year. I’ll post an update when we start work on these features.</p>
<div id="attachment_656" class="wp-caption alignleft" style="width: 118px"><a class="lightbox" title="ipod-touch-video-player" href="http://labs.opsview.com/wp-content/uploads/2010/12/ipod-touch-video-player.jpg"><img class="size-medium wp-image-656  " style="margin: 0pt 10px 5pt 0px;" title="ipod-touch-video-player" src="http://labs.opsview.com/wp-content/uploads/2010/12/ipod-touch-video-player-300x240.jpg" alt="" width="108" height="86" /></a><p class="wp-caption-text">iPod Touch: won by Mark Maas</p></div>
<h3>Prize Draw</h3>
<p>All the customers who participated in our Enterprise Survey prize entered a prize draw to win the latest iPod Touch. The winner was Mark Maas, Unix Engineer at Binck Bank in the Netherlands.</p>
]]></content:encoded>
			<wfw:commentRss>http://labs.opsview.com/2010/12/turning-ideas-into-features-part-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Turning ideas into features: Part 1</title>
		<link>http://labs.opsview.com/2010/12/turning-ideas-into-features-part-1/</link>
		<comments>http://labs.opsview.com/2010/12/turning-ideas-into-features-part-1/#comments</comments>
		<pubDate>Thu, 02 Dec 2010 09:20:41 +0000</pubDate>
		<dc:creator>James Peel</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Opsview]]></category>
		<category><![CDATA[customer survey]]></category>
		<category><![CDATA[feedback]]></category>
		<category><![CDATA[james peel]]></category>
		<category><![CDATA[roadmap]]></category>

		<guid isPermaLink="false">http://labs.opsview.com/?p=647</guid>
		<description><![CDATA[
			
				
			
		I thought it might be interesting if I provided some insight into how we decide which features are included in each Opsview release cycle.

The long and winding roadmap
We are very lucky to have a committed user community and this is a great source of feedback and ideas. Part of my role is to work with [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Flabs.opsview.com%2F2010%2F12%2Fturning-ideas-into-features-part-1%2F">
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Flabs.opsview.com%2F2010%2F12%2Fturning-ideas-into-features-part-1%2F&amp;style=normal&amp;b=2" height="61" width="50" />
			</a>
		</div><p><a class="lightbox" title="roadmap" href="http://labs.opsview.com/wp-content/uploads/2010/12/roadmap.jpg"><img class="size-full wp-image-650 alignleft" style="margin: 0pt 10px 7px 0pt;" title="roadmap" src="http://labs.opsview.com/wp-content/uploads/2010/12/roadmap.jpg" alt="" width="180" height="135" /></a>I thought it might be interesting if I provided some insight into how we decide which features are included in each <a href="http://www.opsview.com">Opsview</a> release cycle.</p>
<p><span id="more-647"></span></p>
<h3>The long and winding roadmap</h3>
<p>We are very lucky to have a committed user community and this is a great source of feedback and ideas. Part of my role is to work with customers and prospective customers to figure out how to get the most out of Opsview in their IT environments. This is another extremely valuable source of feedback about how existing features could be developed and which new features we should be considering. One of our biggest challenges is taking all of these great ideas and figuring out which ones the product development team should work on.</p>
<p>Ideas are captured in our Atlassian JIRA system and this forms the feature backlog. For a feature to move from the backlog to the product roadmap it goes through a scoring and review process. This allows us to work out what the relative priorities are and how much effort will be involved in developing the feature. The scoring system takes into account the following factors:</p>
<ul>
<li>Opsview Enterprise customer feedback</li>
<li>Opsview Community feedback</li>
<li>Feedback from pre-sales discussions with potential customers</li>
<li>Input from the Opsview team</li>
<li>Where the feature sits in terms of our long term product strategy</li>
<li>The level of complexity involved in developing the feature</li>
</ul>
<p>The resulting score helps us to narrow down the feature backlog to a roadmap covering the next year of releases. Usually this comprises three Community release cycles with corresponding Enterprise releases. The roadmap is under constant refinement and there is always scope to insert features or change priorities. This allows us to remain flexible and responsive.</p>
<p>If a feature is sponsored it will usually take priority. Features requested by our customers receive a higher weighting however the features appearing top on the roadmap are usually those where there is a high level combined of interest from customers, pre-sales and community users.</p>
<h3>From concept to code</h3>
<p>Customer / Community (User) feedback is our primary source of feature ideas since this means we&#8217;re always addressing &#8216;real&#8217; issues.  Our challenge is to translate ideas into more concrete feature specifications and then into code. This is where the Opsview team&#8217;s creativity is involved, spotting related threads, designing solutions that will work for everyone and figuring out the tricky details.</p>
<p><strong>In Part 2: <a href="http://labs.opsview.com/2010/12/turning-ideas-into-features-part-2/">How our recent Customer Survey has influenced the Product Roadmap</a></strong></p>
]]></content:encoded>
			<wfw:commentRss>http://labs.opsview.com/2010/12/turning-ideas-into-features-part-1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Grails &amp; Hudson Part 3: Testing</title>
		<link>http://labs.opsview.com/2010/09/grails-hudson-part-3-testing/</link>
		<comments>http://labs.opsview.com/2010/09/grails-hudson-part-3-testing/#comments</comments>
		<pubDate>Fri, 10 Sep 2010 16:04:02 +0000</pubDate>
		<dc:creator>tcallway</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Frameworks]]></category>
		<category><![CDATA[Grails]]></category>
		<category><![CDATA[Hudson]]></category>
		<category><![CDATA[Unix / Linux]]></category>

		<guid isPermaLink="false">http://labs.opsview.com/?p=528</guid>
		<description><![CDATA[
			
				
			
		Since Grails incorporated the testing plugin into core it provides good unit &#38; integration testing support (via the test-app script). There are also additional plugins to support BDD tools (e.g. EasyB) and functional testing (e.g. Canoo WebTest).
One of the useful roles that Hudson fulfils is helping to manage quality. Consequently it has plugins available for [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Flabs.opsview.com%2F2010%2F09%2Fgrails-hudson-part-3-testing%2F">
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Flabs.opsview.com%2F2010%2F09%2Fgrails-hudson-part-3-testing%2F&amp;style=normal&amp;b=2" height="61" width="50" />
			</a>
		</div><p><a title="grails" href="http://labs.opsview.com/wp-content/uploads/2010/08/grails.png"><img class="alignleft" title="grails" src="http://labs.opsview.com/wp-content/uploads/2010/08/grails.png" alt="" width="176" height="53" /></a>Since Grails incorporated the testing plugin into core it provides good unit &amp; integration testing support (via the test-app script). There are also additional plugins to support BDD tools (e.g. EasyB) and functional testing (e.g. Canoo WebTest).</p>
<p><span id="more-528"></span>One of the useful roles that Hudson fulfils is helping to manage quality. Consequently it has plugins available for most of the popular testing tools (we saw an example of the Violations plugin in <a href="http://labs.opsview.com/2010/08/grails-hudson-part-1-codenarc/">part 1</a> of this series).</p>
<p>We’ll start with the standard unit &amp; integration tests, then add in test coverage and functional tests.</p>
<p><strong>Unit tests</strong>
Assuming you already have unit/integration tests in your Grails project – we need to add the targets into the Grails builder options.
<code>clean compile "test-app -unit -integration"</code></p>
<p style="text-align: center;"><img class="alignnone" src="http://leanjavaengineering.files.wordpress.com/2010/09/grails-builder-test-app.png?w=453&amp;h=152" alt="" width="453" height="152" /></p>
<p style="text-align: left;">Notice how the server port has been set to prevent port collision with the servlet container running Hudson.
<em> </em></p>
<p style="text-align: left;"><em>Tip:</em> you’ll need to set this differently on each job too.</p>
<p style="text-align: left;">Then set the post-build action:</p>
<p style="text-align: center;"><img class="alignnone" src="http://leanjavaengineering.files.wordpress.com/2010/09/post-build_junit_small.png?w=418&amp;h=86" alt="" width="418" height="86" /></p>
<p style="text-align: left;">After a couple of builds (so that there is a trend), Hudson will show:</p>
<p style="text-align: center;"><a href="http://leanjavaengineering.files.wordpress.com/2010/08/test_trend.png"><img class="alignnone" src="http://leanjavaengineering.files.wordpress.com/2010/08/test_trend.png?w=150&amp;h=136" alt="" width="150" height="136" /></a></p>
<p>With blue for tests that passed and red for failed tests.
You can also drill into the test results just as with the standard HTML junit reports.</p>
<p><strong>Test coverage</strong>
As we’re looking to use Hudson to help ensure the quality of the software project, we’ll also inspect the level of test coverage as one of our quality metrics.</p>
<p><em>Warning</em>: determining test coverage isn’t foolproof and high levels of coverage don’t guarantee good code or good tests. It is possible to achieve a high percentage with poor tests (I’m writing a separate post on this topic).</p>
<p>For this post, we’ll be using Cobertura to check the code coverage. There are other options, but we’ll be using Cobertura because:</p>
<ol>
<li>There is a <a href="http://www.grails.org/plugin/code-coverage">Grails plugin</a></li>
<li>There is a <a href="http://wiki.hudson-ci.org/display/HUDSON/Cobertura+Plugin">Hudson plugin</a> too.</li>
</ol>
<p><strong>Install the Grails code-coverage plugin</strong>
<code>grails install-plugin code-coverage</code></p>
<p>You may want to configure additional exclusions in BuildConfig.groovy, e.g. to avoid testing 3rd party code such as a plugin taglib that isn’t used. (the plugin scripts/_Events.groovy includes a default list)</p>
<p style="text-align: center;"><img class="alignnone" src="http://leanjavaengineering.files.wordpress.com/2010/09/buildconfig_cobertura_exclusions.png?w=247&amp;h=160" alt="" width="247" height="160" /></p>
<p><strong>Install the Hudson Cobertura plugin</strong>
This follows the same process that we covered in <a href="http://leanjavaengineering.wordpress.com/2010/08/31/grails-hudson-basics/">part 2</a> of this series (we’ll omit the screenshots this time):</p>
<ol>
<li>From the main Hudson dashboard, click on the “Manage Hudson” menu item</li>
<li>Click on “Manage plugins”</li>
<li>Go to the available tab, find &amp; select Cobertura, click on the install button.</li>
<li>When the plugin has downloaded and installed, you will need to restart Hudson (you can use the button provided).</li>
</ol>
<p><strong>Configure your Hudson job</strong>
There are 2 things that need to be done here:</p>
<ol>
<li>Set up the Grails build action</li>
<li>Enable the post-build action for Cobertura</li>
</ol>
<p>1. This is a case of adding “-coverage -xml” into the build targets within the test-app double quoted section.
2. Enable the Cobertura post-build processing, tell it where the XML report is and set thresholds:</p>
<p style="text-align: center;"><a href="http://leanjavaengineering.files.wordpress.com/2010/09/post-build_cobertura_config.png"><img class="aligncenter" src="http://leanjavaengineering.files.wordpress.com/2010/09/post-build_cobertura_config.png?w=300&amp;h=93" alt="" width="300" height="93" /></a></p>
<p style="text-align: left;"><strong>Leverage the information</strong></p>
<p style="text-align: left;">Watch the trends:</p>
<p style="text-align: center;"><a href="http://leanjavaengineering.files.wordpress.com/2010/09/cobertura_summary_report.png"><img class="aligncenter" src="http://leanjavaengineering.files.wordpress.com/2010/09/cobertura_summary_report.png?w=300&amp;h=266" alt="" width="300" height="266" /></a></p>
<p style="text-align: left;">and drill down to see the in-context coverage:</p>
<p style="text-align: center;"><img class="alignnone" src="http://leanjavaengineering.files.wordpress.com/2010/09/cobertura_in_context.png?w=443&amp;h=76" alt="" width="443" height="76" /></p>
<p>When you have the insight as to where you are missing coverage, then you can take the appropriate action to remedy the situation…</p>
<p><strong>Functional tests</strong>
There are a number of options for functional testing your application. We’ll cover <a href="http://webtest.canoo.com/">Canoo WebTest</a> in this post for the following reasons:</p>
<ol>
<li>WebTest is <a href="http://htmlunit.sourceforge.net/">htmlunit</a> based – portable across different environments</li>
<li>Canoo have written a <a href="http://webtestrecorder.canoo.com/">Firefox WebTest Recorder add-on</a> – allowing less technical users to create the basis of the tests</li>
<li>There is also a <a href="http://wiki.hudson-ci.org/display/HUDSON/WebTest+Presenter+Plugin">WebTest Presenter plugin</a> for Hudson.</li>
</ol>
<p>Note that as WebTest isn’t browser-based, you may encounter some issues with JavaScript on particular test scenarios.</p>
<p><strong>Install the Grails webtest plugin</strong>
<code>grails install-plugin webtest</code>
If you are using older versions of Grails, you may need to do an interactive plugin install on the Hudson server (the plugin used to download a WebTest bundle, rather than having it as a managed dependency).</p>
<p><strong>Install the Firefox WebTest Recorder add-on</strong>
You can install the WebTest Recorder in Firefox from:
<a href="https://www.canoo.com/webtestrecorder-dist/webtestrecorder.xpi">https://www.canoo.com/webtestrecorder-dist/webtestrecorder.xpi</a></p>
<p><strong>Create some web tests</strong>
<em>You can come back to this bit later</em></p>
<p>The process we’ll follow here uses the WebTest Recorder to create the initial test steps.</p>
<p>1. Run “grails create-webtest <em>name</em>” e.g.
<code>grails create-webtest HomepageLogin</code></p>
<p>This plugin script will create us the placeholder test file under test/webtest.</p>
<p>2. <code>grails run-app</code></p>
<p>3. Bring up Firefox
4. Navigate to the starting page for your test scenario.
5. Enable the WebTest Recorder Sidebar (Tools &gt; Webtest Recorder Sidebar)
6. Click on the Groovy tab.</p>
<p>This should already contain “invoke http://localhost:8080/YourGrailsApp/” (you’ll probably want to trim this down to just a relative path e.g. /YourGrailsApp/)</p>
<p>Using the Webtest Recorder Sidebar, you can click around the site and also easily add verification of content on pages (as a test isn’t a test if you don’t verify that the output is correct for the given input). This can then be placed into a method in the groovy file we created earlier (note: you may need to trim some instructions e.g. where the recorder has created different versions for the same input field).
For more information using Webtest, see the <a href="http://webtest.canoo.com/webtest/manual/manualOverview.html">manual</a>.</p>
<p style="text-align: center;"><img class="alignnone" src="http://leanjavaengineering.files.wordpress.com/2010/09/webtest_recorder.png?w=300&amp;h=241" alt="" width="300" height="241" /></p>
<p>I’d strongly advise you to run your tests locally first before checking them in – in the interests of time/space we’ll assume you’ve done that using:
<code>grails test-app -functional</code></p>
<p><strong>Install the Hudson WebTest presenter plugin</strong>
This follows the same process as the Cobertura plugin above (see <a href="http://leanjavaengineering.wordpress.com/2010/08/31/grails-hudson-basics/">part 2</a> of this series for screenshots):</p>
<ol>
<li>From the main Hudson dashboard, click on the “Manage Hudson” menu item</li>
<li>Click on “Manage plugins”</li>
<li>Go to the available tab, find &amp; select WebTest Presenter, click on the install button.</li>
<li>When the plugin has downloaded and installed, you will need to restart Hudson (you can use the button provided).</li>
</ol>
<p><strong>Configure your Hudson job</strong>
There are 2 things that need to be done here:</p>
<ol>
<li>Set up the Grails build action</li>
<li>Enable the post-build action for the WebTest Presenter</li>
</ol>
<p>1. This is a case of adding “-functional -headless” into the build targets within the test-app double quoted section (The headless option tells Webtest to not run the test monitor nor to load the test results into the browser).</p>
<p>2. The Webtest Presenter post-build action needs enabling and telling where to find the results</p>
<p style="text-align: center;"><img class="alignnone" src="http://leanjavaengineering.files.wordpress.com/2010/09/webtest_presenter_config.png?w=473&amp;h=58" alt="" width="473" height="58" /></p>
<p>After the Hudson job has executed your webtests, you’ll see that these have been included in your overall test count (and may even have improved your test coverage if you’ve followed the Cobertura section above).</p>
<p>If you click onto an individual build, the left hand menu will now have “Webtest Results”</p>
<p style="text-align: center;"><img class="alignnone" src="http://leanjavaengineering.files.wordpress.com/2010/09/job_webtest_results_menu_item.png?w=161&amp;h=305" alt="" width="161" height="305" /></p>
<p style="text-align: left;">This will give you an overview report:</p>
<p style="text-align: center;"><a href="http://leanjavaengineering.files.wordpress.com/2010/09/webtest_report.png"><img class="aligncenter" src="http://leanjavaengineering.files.wordpress.com/2010/09/webtest_report.png?w=300&amp;h=112" alt="" width="300" height="112" /></a></p>
<p style="text-align: left;">and a detailed step-by-step report for each test (including resulting pages and any errors):</p>
<p style="text-align: center;"><a href="http://leanjavaengineering.files.wordpress.com/2010/09/webtest_test_step_report.png"><img class="aligncenter" src="http://leanjavaengineering.files.wordpress.com/2010/09/webtest_test_step_report.png?w=300&amp;h=228" alt="" width="300" height="228" /></a></p>
<p style="text-align: left;">So there you go, a whistle stop tour of setting up test execution, coverage analysis and result reporting with trends.</p>
<p style="text-align: left;">Next in the series, we move into using Hudson for continuous deployment of Grails projects.</p>
<div></div>
]]></content:encoded>
			<wfw:commentRss>http://labs.opsview.com/2010/09/grails-hudson-part-3-testing/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Grails &amp; Hudson Part 1: CodeNarc</title>
		<link>http://labs.opsview.com/2010/08/grails-hudson-part-1-codenarc/</link>
		<comments>http://labs.opsview.com/2010/08/grails-hudson-part-1-codenarc/#comments</comments>
		<pubDate>Thu, 26 Aug 2010 13:25:43 +0000</pubDate>
		<dc:creator>rbramley</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Frameworks]]></category>
		<category><![CDATA[Grails]]></category>
		<category><![CDATA[Hudson]]></category>
		<category><![CDATA[Opsview]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[CodeNarc]]></category>

		<guid isPermaLink="false">http://labs.opsview.com/?p=483</guid>
		<description><![CDATA[
			
				
			
		The first part of a series of posts on Grails and Hudson leading up to a presentation at the London Groovy &#38; Grails User Group. Subsequent instalments will include testing (unit, integration, functional), test coverage, automatic war deployment and monitoring Hudson with Opsview Enterprise.

Grails has a rich plugin eco-system with over 400 hundred plugins – so it’s [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Flabs.opsview.com%2F2010%2F08%2Fgrails-hudson-part-1-codenarc%2F">
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Flabs.opsview.com%2F2010%2F08%2Fgrails-hudson-part-1-codenarc%2F&amp;style=normal&amp;b=2" height="61" width="50" />
			</a>
		</div><p><a class="lightbox" title="grails" href="http://labs.opsview.com/wp-content/uploads/2010/08/grails.png"><img class="alignleft size-full wp-image-501" style="margin-bottom: 8px; margin-right: 10px;" title="grails" src="http://labs.opsview.com/wp-content/uploads/2010/08/grails.png" alt="" width="176" height="53" /></a>The first part of a series of posts on <a href="http://en.wikipedia.org/wiki/Grails_(framework)">Grails</a> and <a href="http://en.wikipedia.org/wiki/Hudson_(software)">Hudson</a> leading up to a presentation at the London Groovy &amp; Grails User Group. Subsequent instalments will include testing (unit, integration, functional), test coverage, automatic war deployment and monitoring Hudson with <a href="https://www.opsview.com/products/opsview-enterprise">Opsview Enterprise</a>.</p>
<div><span id="more-483"></span></div>
<p>Grails has a rich plugin eco-system with over 400 hundred plugins – so it’s easy to miss something useful. If you’re serious about software craftsmanship, then using static code analysis tools should be part of your quality regime as it gives further insight into the code base (and if you insist, yes it’ll help with your Technical Debt management).</p>
<p><a title="CodeNarc" href="http://codenarc.sourceforge.net/">CodeNarc</a> provides static code analysis for Groovy and the <a title="CodeNarc plugin" href="http://www.grails.org/plugin/codenarc/">CodeNarc plugin for Grails</a> allows you to perform this analysis with the “grails codenarc” script. Behind the scenes this uses the CodeNarc ant task and settings from grails-app/conf/Config.groovy and produces an HTML report by default.</p>
<p>Until recently, if you used the codenarc target within a continuous integration server such as <a title="Hudson CI" href="http://hudson-ci.org/">Hudson</a> – then the HTML report would be generated and sit in the workspace waiting for a diligent developer to check it. You can imagine how often that happens in practice with all the other demands of a project!</p>
<p>However, I’ve now integrated the CodeNarc XML output with the <a title="Violations plugin" href="http://wiki.hudson-ci.org/display/HUDSON/Violations">Hudson Violations plugin </a>so that an overview trend line is shown against the Hudson job. Then the team quickly fixed the violations…</p>
<p><a class="lightbox" title="1" href="http://labs.opsview.com/wp-content/uploads/2010/08/1.png"><img class="aligncenter size-full wp-image-484" title="1" src="http://labs.opsview.com/wp-content/uploads/2010/08/1.png" alt="" width="300" height="169" /></a>You can also get a breakdown by priority:</p>
<p><a class="lightbox" title="2" href="http://labs.opsview.com/wp-content/uploads/2010/08/2.png"><img class="aligncenter size-full wp-image-485" title="2" src="http://labs.opsview.com/wp-content/uploads/2010/08/2.png" alt="" width="300" height="259" /></a>And in-context views of the violations so you know what to fix:</p>
<p><a class="lightbox" title="3" href="http://labs.opsview.com/wp-content/uploads/2010/08/3.png"><img class="aligncenter size-full wp-image-486" title="3" src="http://labs.opsview.com/wp-content/uploads/2010/08/3.png" alt="" width="300" height="143" /></a></p>
<p>This is how you do it…</p>
<p>Grails <strong>config.groovy</strong>
<code>codenarc {
reportName = 'target/test-reports/CodeNarcReport.xml'
reportType = 'xml'
// any further settings like maxPriority1Violations=0
}</code></p>
<h3>Hudson</h3>
<p>Set up your Grails build step – normally you’d add the ‘codenarc’ target:</p>
<p><a class="lightbox" title="4" href="http://labs.opsview.com/wp-content/uploads/2010/08/4.png"><img class="aligncenter size-full wp-image-487" title="4" src="http://labs.opsview.com/wp-content/uploads/2010/08/4.png" alt="" width="519" height="459" /></a></p>
<p><em>The <a title="CodeNarc rule configuration" href="http://codenarc.sourceforge.net/codenarc-configuring-rules.html#Configuring_Rules_Using_a_Properties_File">codenarc.properties</a> file can be used to configure specific exclusions, the location of this file can be passed in as a system property (shown above).</em></p>
<p>You need to have installed the violations plugin (Manage Hudson &gt; Manage Plugins &gt; Available and search for Violations). As this is a recent addition, I’m using a patched version of the Violations plugin, though the patch has been integrated into trunk (I’ll update when it is released). Configure violations:</p>
<p><a class="lightbox" title="5" href="http://labs.opsview.com/wp-content/uploads/2010/08/5.png"><img class="aligncenter size-full wp-image-488" title="5" src="http://labs.opsview.com/wp-content/uploads/2010/08/5.png" alt="" width="526" height="515" /></a></p>
<p><em>Note the ‘Faux project path’ – you may need to set this to get an in-context view working properly due to path (e.g. if your code is checked out to workspace/trunk)</em></p>
<p>I also had a contribution to CodeNarc accepted at the weekend to add an<em>inlineXml</em> report type – this will, with a minor tweak to the CodeNarc parser, allow the Hudson Violations plugin to give the rule description on the pop-up message.</p>
]]></content:encoded>
			<wfw:commentRss>http://labs.opsview.com/2010/08/grails-hudson-part-1-codenarc/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>GWT/GXT dashboard primer</title>
		<link>http://labs.opsview.com/2010/05/gwtgxt-dashboard-primer/</link>
		<comments>http://labs.opsview.com/2010/05/gwtgxt-dashboard-primer/#comments</comments>
		<pubDate>Wed, 19 May 2010 13:03:20 +0000</pubDate>
		<dc:creator>tcallway</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Frameworks]]></category>
		<category><![CDATA[GXT]]></category>
		<category><![CDATA[Google Web Toolkit]]></category>
		<category><![CDATA[Opsview]]></category>
		<category><![CDATA[System Management]]></category>
		<category><![CDATA[google]]></category>
		<category><![CDATA[gwt]]></category>
		<category><![CDATA[next-gen]]></category>
		<category><![CDATA[opsview enterprise]]></category>
		<category><![CDATA[research]]></category>

		<guid isPermaLink="false">http://labs.opsview.com/?p=369</guid>
		<description><![CDATA[
			
				
			
		This post is based on research we’ve undertaken to develop a pilot mash-up style, charting dashboard for our monitoring solution, Opsview Enterprise. However the concepts we discuss could be used when building a dashboard that displays information from many other enterprise solutions. The assumption we make is that the reader is familiar with Java and [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Flabs.opsview.com%2F2010%2F05%2Fgwtgxt-dashboard-primer%2F">
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Flabs.opsview.com%2F2010%2F05%2Fgwtgxt-dashboard-primer%2F&amp;style=normal&amp;b=2" height="61" width="50" />
			</a>
		</div><p>This post is based on research we’ve undertaken to develop a pilot mash-up style, charting dashboard for our monitoring solution, Opsview Enterprise. However the concepts we discuss could be used when building a dashboard that displays information from many other enterprise solutions. The assumption we make is that the reader is familiar with Java and the Google Web Toolkit (GWT). For more information about GWT and the other libraries used in this blog please see the Resources section at the end.
<span id="more-369"></span></p>
<h4>Introduction</h4>
<p>The Opsview Enterprise platform is very flexible and allows us to extract monitoring information via a REST based API. This API returns monitoring data as JSON objects or XML so it is easy to integrate with many UI frameworks. In this example we will develop a mini portal-like application that displays status for different host groups in one of its portlets.</p>
<p>The client will be using the <a href="http://www.extjs.com/products/gwt/">‘GXT’ framework</a> that is based on GWT for the user interface and some Java libraries for HTTP communication and JSON parsing.</p>
<h4>Using the REST based API to talk to Opsview Enterprise</h4>
<p>Let’s first have a look at how you can call Opsview Enterprise from a browser to collect monitoring data. In this case we can use the following URL to fetch host groups:</p>
<p><code>http://&lt;hostname&gt;/opsview/api/status/hostgroup</code></p>
<p>If we try using this URL directly from a browser it will not work, a 403 access denied will be returned even if we login with correct credentials. A couple of extra headers are needed for the authentication to work; we will look at them later on.</p>
<p>To get around this problem we must first login to Opsview Enterprise the normal way via the Opsview Enterprise UI and then hit the URL:</p>
<p><code>https://&lt;hostname&gt;/opsview/status/hostgroup</code> </p>
<p>This is basically the first URL you get to. Now you will have a session in the browser so you can test the API URL again.</p>
<p>It will return JSON looking something like this:</p>
<pre><code>{"hostgroup":{
"summary":{"handled":782,"unhandled":60,"total":842,
   "service":{"ok":558,"critical":206,"handled":747,"unknown":29,
"unhandled":58,"warning":12,"total":805},
  		   "host":{"handled":35,"unhandled":2,"down":7,"up":30,"total":37}},
"list":[
        {
            "hostgroup_id":"7",
            "hosts":{ "handled":2, "unhandled":0, "down":{"handled":1},
"up":{"handled":1}, "total":2},
"services":{"ok":{"handled":24},"critical":{"handled":21,"unhandled":3},
"handled":50,"highest":"critical","unknown":{"handled":5}, "unhandled":3,"total":53},
            "name":"ProjectX",
            "downtime":null
        },
        {
            "hostgroup_id":"13",
            "hosts":{"handled":2,"unhandled":1,"down":{"handled":2,"unhandled":1},"total":3},
            "services":{"critical":{"handled":73},"handled":86,"highest":"critical",
			"unknown":{"handled":13},"unhandled":0,"total":86},
            "name":"Internal",
            "downtime":"2"
        },
        {
            "hostgroup_id":"14",
            "hosts":{"handled":31,"unhandled":1,"down":{"handled":2,"unhandled":1},
"up":{"handled":29},"total":32},
            "services":{"ok":{"handled":534},"critical":{"handled":66,"unhandled":43},
"handled":611,"highest":"critical","unknown":{"handled":9,
"unhandled":2},"unhandled":55,"warning":{"handled":2,"unhandled":10},
"total":666},
            "name":"Hosting",
            "downtime":"2"
        }
    ]}}</code></pre>
<p>So we can see by looking at it that it is nested JSON and in this case it returned the current status for 3 host groups: ProjectX, Internal, and Hosting.</p>
<p>Because the JSON is nested we will benefit from doing some processing on it before giving it to the UI routines that in GXT which only handles flat JSON.</p>
<h4>The Architecture for the Mini-Dashboard Application</h4>
<p>The following picture shows an overview of the architecture and the different components that will be used in this solution:</p>
<p><a class="lightbox" title="DashboardAppArchitecture" href="http://labs.opsview.com/wp-content/uploads/2010/05/DashboardAppArchitecture.png"><img class="aligncenter size-full wp-image-371" title="DashboardAppArchitecture" src="http://labs.opsview.com/wp-content/uploads/2010/05/DashboardAppArchitecture.png" alt="" width="400" /></a></p>
<p>The GXT client will not call the Opsview Enterprise server directly but instead go via a proxy server. This has the benefit that the JSON response from the Opsview Enterprise server can be processed and converted into <code>Serializable</code> Java objects. In this case the JSON is turned into a Java object called <code>HostGroup</code>, which in turn contains a <code>HostStatus</code> object. The <code>HostGroup</code> object will contain enough information to be able to draw a pie chart showing the status for the different host groups.</p>
<p>The <code>HostStatus</code> object will be used when the user clicks on one of the host group pie slices and wants to drill down to see more detailed status for a particular host group. Because we send the <code>HostStatus</code> object at the same time as the <code>HostGroup</code> object we do not have to make another remote call when the user drills down to see individual host group status.</p>
<p>Another benefit of having the proxy server is that it enables us to have the Mini Dashboard client web app and the Opsview Enterprise server web app on different hosts in different domains. If we did not have the proxy server it would not be possible to access content in Opsview Enterprise as for security reasons you cannot open URL connections to a different  host to the host from where the client is downloaded. Instead, you would have to install the Dashboard web app on the same host as Opsview Enterprise is running on.</p>
<h4>The Mini-Dashboard Proxy Server</h4>
<p>Let’s start implementing the proxy server using the Apache Commons HTTP Client and the Jackson JSON parser. For the remote calls between the Mini-Dashboard client and the proxy server (i.e. AJAX based client calls) we will implement a <code>com.google.gwt.user.client.rpc. RemoteService</code> as provided by Google Web Toolkit. It will handle all marshalling of calls for us.</p>
<p>Here is a UML Diagram giving you an overview of the involved proxy server classes:
<a class="lightbox" title="ServerImplUMLDiagram" href="http://labs.opsview.com/wp-content/uploads/2010/05/ServerImplUMLDiagram.png"><img class="aligncenter size-full wp-image-384" title="ServerImplUMLDiagram" src="http://labs.opsview.com/wp-content/uploads/2010/05/ServerImplUMLDiagram.png" alt="" width="400" /></a></p>
<p>First setup the OpsviewServerProxyService interface as follows:</p>
<pre><code>/**
  *
  * The client side stub for the Opsview Proxy Service RPC service.
  * @author Martin Bergljung, Opsera Ltd.
  */
@RemoteServiceRelativePath("opsviewproxy")
public interface OpsviewServerProxyService extends RemoteService {
    public static final String SERVICE_NAME = "OpsviewServerProxyService";

    public List getHostGroups();
}</code></pre>
<p>The <code>RemoteServiceRelativePath</code> annotation associates a <code>RemoteService</code> with a relative path. This annotation will cause the client-side proxy to automatically invoke the <code>ServiceDefTarget.setServiceEntryPoint</code> method with
<code>GWT.getModuleBaseURL() + value()</code> as its argument. Subsequent calls to <code>ServiceDefTarget.setServiceEntryPoint</code> will override this default path.</p>
<p>We define one method called <code>getHostGroups</code> that will return a list of <code>HostGroup</code> objects. Create a <code>HostGroup</code> object as follows:</p>
<pre><code>/**
 * Host Group (method level javadoc omitted for simplicity).
 *  
 *  {
 *   "hostgroup_id":"14",
 *   "hosts":{"handled":31,"unhandled":1,"down":{"handled":2,"unhandled":1},
 *            "up":{"handled":29},"total":32},
 *   "services":{"ok":{"handled":534},"critical":{"handled":66,"unhandled":43},
 *              "handled":611,"highest":"critical","unknown":{"handled":9,"unhandled":2},
 *              "unhandled":55,"warning":{"handled":2,"unhandled":10},"total":666},
 *   "name":"Internal",
 *   "downtime":"2"
 *   }
 * 
 * @author Martin Bergljung, Opsera Ltd.
 */
public class HostGroup implements Serializable {
    private int m_downtime;
    private String m_hostgroupId;
    private HostStatus m_hostStatus;
    private String m_name;
    private String m_services; // not used

    public HostGroup() {}

    //Getters, Setters, equals, hashCode, and toString have intentionally been left out to save space 

}
</code></pre>
<p>Next step is to create an asynchronous version of the interface as AJAX calls are asynchronous by nature.  This is the actual interface that the client will come in contact with:</p>
<pre><code>/**
 * The async counterpart of <code>OpsviewServerProxyService</code>.
 */
public interface OpsviewServerProxyServiceAsync {
    public void getHostGroups(AsyncCallback
&gt; callback);
}       </code></pre>
<p>The extra element here is the callback parameter that the client will have to implement. Now let’s move on to the actual implementation of this interface. Create the following class that implements the <code>getHostGroups</code> method:</p>
<pre><code>/**
 * The server side implementation of the Opsview Proxy Server RPC service.
*/
public class OpsviewServerProxyServiceImpl extends RemoteServiceServlet
implements OpsviewServerProxyService {
   public static final String OPSVIEW_API_BASE_URI =
            "https:///opsview/api";
   public static final String OPSVIEW_API_HOSTGROUP_STATUS_URI =
            OPSVIEW_API_BASE_URI + "/status/hostgroup";

   public List getHostGroups() {
        String json = callOpsview(OPSVIEW_API_HOSTGROUP_STATUS_URI);

        if (json != null &amp;&amp; json.length() &gt; 0) {
            return readHostGroupsJSON(json);
        }

        return new ArrayList();
   }
</code></pre>
<p>Here you need to update the hostname in the OPSVIEW_API_BASE_URI constant to reflect your installation. Also, if you are not using SSL then change to http.</p>
<p>Then add the <code>readHostGroupsJSON</code> method that takes JSON returned from Opsview Enterprise and turns it into a list of <code>HostGroup</code> objects:</p>
<pre><code>    private List readHostGroupsJSON(String json) {
        List hostGroups = new ArrayList();

        try {
            ObjectMapper mapper = new ObjectMapper(); // can reuse, share globally
            JsonNode rootNode = mapper.readValue(json, JsonNode.class);
            JsonNode hostGroupNode = rootNode.path("hostgroup");
            JsonNode listNode = hostGroupNode.path("list");

            for (JsonNode hostGroup : listNode) {
                HostGroup hg = new HostGroup();
                hg.setName(hostGroup.path("name").getTextValue());
                hg.setDowntime(hostGroup.path("downtime").getIntValue());
                String hostGroupId = hostGroup.path("hostgroup_id").getTextValue();
                hg.setHostGroupId(hostGroupId);
                hg.setServices("0"); // not used at the moment

                JsonNode hostStatus = hostGroup.path("hosts");
                HostStatus hs = new HostStatus();
                hs.setHostGroupId(hostGroupId);
                hs.setHandled(hostStatus.path("handled").getIntValue());
                hs.setUnhandled(hostStatus.path("unhandled").getIntValue());
                hs.setTotal(hostStatus.path("total").getIntValue()); // only one needed now

                JsonNode down = hostStatus.path("down");
                JsonNode up = hostStatus.path("up");
                hs.setDownHandled(down.path("handled").getIntValue());
                hs.setDownUnhandled(down.path("unhandled").getIntValue());
                hs.setUpHandled(up.path("handled").getIntValue());
                hs.setUpUnhandled(up.path("unhandled").getIntValue());

                hg.setHosts(hs);
                hostGroups.add(hg);
            }
        } catch (IOException e) {
            System.err.println("Fatal JSON Parsing error: " + e.getMessage());
            e.printStackTrace();
        }

        return hostGroups;
    }</code></pre>
<p>What we do here is use Jackson JSON parser (org.codehaus.jackson) to first lookup the hostgroup node in the JSON response and then with this node we go on to lookup the list node. When we have the list node we can step through all the host groups and setup corresponding HostGroup objects. The HostStatus object is also setup by looking up the hosts node for each hostgroup node.</p>
<p>Here is a snippet of the JSON structure that we are talking about:</p>
<pre><code>{"hostgroup":{
...
"list":[
        {
            "hostgroup_id":"7",
            "hosts":{
</code></pre>
<p>Finally we also need to implement the callOpsview method that will use Apache Commons HTTPClient to call Opsview Enterprise. Here is how that is done:</p>
<pre><code>private String callOpsview(String url) {
        String username = ;
        String password =
;

        HttpClient client = new HttpClient();
	// Apache is adding basic authentication in Opsera’s installation so
// the following 2 lines are necessary
        Credentials defaultcreds = new UsernamePasswordCredentials(username, password);
        client.getState().setCredentials(AuthScope.ANY, defaultcreds);
        GetMethod getMethod = new GetMethod(url);
        getMethod.setRequestHeader("X-Username", username);
        getMethod.setRequestHeader("X-Password", password);
        getMethod.setRequestHeader("Accept", "application/json");

        try {
            int statusCode = client.executeMethod(getMethod);
            if (statusCode == HttpStatus.SC_OK) {
                String contents = getMethod.getResponseBodyAsString();
                System.out.println(contents);
                return contents;
            } else {
                System.err.println("Got Error " + statusCode +
                        " when calling Opsview API: " +url);
            }
        } catch (Exception e) {
            System.err.println("Fatal transport error: " + e.getMessage());
            e.printStackTrace();
        } finally {
            // Release the connection.
            getMethod.releaseConnection();
        }

        return "";
    }</code></pre>
<p>When you implement this the username and password need to be specified according to the user account in your Opsview Enterprise installation. Here we can also see that we are setting the following headers:</p>
<ul>
<li><strong>X-Username</strong> – Username for Opsview user account</li>
<li><strong>X-Password</strong> – Password for Opsview user account</li>
<li><strong>Accept</strong> – indicates that we will accept and handle JSON responses (can also be set to accept XML)</li>
</ul>
<p>The Opsview Enterprise proxy server implementation also needs to be registered to a URL. We do this in the GWT module definition file as follows for testing purposes:</p>
<pre><code>  &lt;!— Setup the Opsview Server Proxy Service for hosted mode testing --&gt;
    &lt;servlet path="/opsviewproxy" class= 
			"com.<your package path>.server.OpsviewServerProxyServiceImpl" /&gt;</code></pre>
<p>And as follows for the real web application, open up web.xml and add:</p>
<pre><code>      &lt;servlet&gt;
      &lt;servlet-name&gt;opsviewServerProxyServiceServlet&lt;/servlet-name&gt;
      &lt;servlet-class&gt;com.&lt;your package&gt;.server.OpsviewServerProxyServiceImpl&lt;/servlet-class&gt;
    &lt;/servlet&gt;

    &lt;servlet-mapping&gt;
      &lt;servlet-name&gt;opsviewServerProxyServiceServlet&lt;/servlet-name&gt;
      &lt;url-pattern>/dashboardApp/opsviewproxy&lt;/url-pattern&gt;
    &lt;/servlet-mapping&gt;</code></pre>
<h4>The Mini-Dashboard Client</h4>
<p>The client is implemented as Google Web Toolkit application. So first create the main entry point / class for the application:</p>
<pre><code>public class DashboardApp implements EntryPoint {

    public void onModuleLoad() {
        OpsviewServerProxyServiceAsync service = (OpsviewServerProxyServiceAsync)
                GWT.create(OpsviewServerProxyService.class);
        Registry.register(OpsviewServerProxyService.SERVICE_NAME, service);

        LayoutContainer container = new LayoutContainer();
        final BorderLayout layout = new BorderLayout();
        container.setLayout(layout);
        container.setWidth(1024);
        container.setHeight(768);
        container.add(createMainMenuAndToolbarPanel(), createMainMenuAndToolbarLayoutData());
        container.add(new Dashboard(2), createDashboardLayoutData());
        container.add(createBottomStatusBar(), createBottomStatusBarLayoutData());

        RootPanel.get().add(container);
    }</code></pre>
<p>This creates the stub that handles communication with the Opsview Enterprise proxy server and registers this stub object locally. Then the layout for the GXT application is setup. Now create the individual panels and layout data:</p>
<pre><code>    private ContentPanel createMainMenuAndToolbarPanel() {
        ContentPanel mainMenuAndToolbarPanel = new ContentPanel();
        mainMenuAndToolbarPanel.setTopComponent(new MainMenu());
        mainMenuAndToolbarPanel.setHeading("Opsview Dashboard...");
        return mainMenuAndToolbarPanel;
    }

    private ContentPanel createBottomStatusBar() {
        ContentPanel panel = new ContentPanel();
        ToolBar toolbar = new ToolBar();
        panel.setHeaderVisible(false);
        Label opsviewVersion = new Label();
        opsviewVersion.setStyleName("Arial");
        opsviewVersion.setText("Opsview Dashboard Example");
        Label loggedInInfo = new Label();
        loggedInInfo.setText("Logged in as Opsview Admin");
        toolbar.add(opsviewVersion);
        toolbar.add(new FillToolItem());
        toolbar.add(loggedInInfo);
        toolbar.setHeight(25);
        panel.add(toolbar);
        return panel;
    }

    private BorderLayoutData createMainMenuAndToolbarLayoutData() {
        BorderLayoutData breadcrumbsLayoutData =
new BorderLayoutData(Style.LayoutRegion.NORTH, 50);
        breadcrumbsLayoutData.setHideCollapseTool(true);
        breadcrumbsLayoutData.setMargins(new Margins(5, 5, 0, 5));
        return breadcrumbsLayoutData;
    }

    private BorderLayoutData createDashboardLayoutData() {
        BorderLayoutData data = new BorderLayoutData(Style.LayoutRegion.CENTER);
        data.setMargins(new Margins(0, 5, 0, 5));
        return data;
    }

    private BorderLayoutData createBottomStatusBarLayoutData() {
        BorderLayoutData breadcrumbsLayoutData =
new BorderLayoutData(Style.LayoutRegion.SOUTH, 25);
        breadcrumbsLayoutData.setHideCollapseTool(true);
        breadcrumbsLayoutData.setMargins(new Margins(0, 5, 0, 5));
        return breadcrumbsLayoutData;
    }</code></pre>
<p>We are also going to need a menu and you can implement it if you like as it is not going to be used in this example. Here is a starting point:</p>
<pre><code>public class MainMenu extends ToolBar {
   public MainMenu() {....
   }
}</code></pre>
<p>Last thing we need for the client is the actual Dashboard class that will call the Opsview Enterprise proxy and draw the pie chart with host group status:</p>
<pre><code>public class Dashboard extends Portal {
   public static final String OFC_FLASH_LOCATION = "dashboardApp/chart/open-flash-chart.swf";

   private Chart m_hostGroupsChart;
   private List m_currentHostGroups;

   public Dashboard(int columns) {
        super(columns);
        setBorders(true);
        setStyleAttribute("backgroundColor", "white");
        setColumnWidth(0, .50);
        setColumnWidth(1, .50);

        add(createHostGroupsChartPortlet(), 1);
        loadHostGroupsChartPortlet();
   }</code></pre>
<p>Here we start off by implementing the Dashboard as a GXT Portal. We add one Portlet that will show the Host Group status pie chart. Then we call a method that will load the pie chart.</p>
<p>Next implement the method that creates the Host Group chart portlet as follows:</p>
<pre><code>   private Portlet createHostGroupsChartPortlet() {
        Portlet portlet = new Portlet();
        portlet.setHeading("Status - Host Groups Hierarchy");
        portlet.setLayout(new FitLayout());
        portlet.setHeight(400);

        String url = !isExplorer() ? "../../" : "";
        url += OFC_FLASH_LOCATION;
        m_hostGroupsChart = new Chart(url);
        m_hostGroupsChart.setBorders(true);

        //portlet.setIcon(ICONS.hostgroup());
        portlet.setTopComponent(createChartPortletToolBar());
        portlet.add(m_hostGroupsChart);

        configurePortlet(portlet);

        return portlet;
    }</code></pre>
<p>First we create the portlet and set its layout and heading etc. We then create a chart component and add it to the portlet. The chart component uses Flash to draw the chart so we need to supply the chart component with the URL for the Flash component. We also create a little toolbar for the portlet to show that you could implement configuration of what you want to see this way:</p>
<pre><code>    private ToolBar createChartPortletToolBar() {
        ToolBar toolbar = new ToolBar();

        Button searchButton = new Button("Config");
        //searchButton.setIcon(ICONS.properties());
        searchButton.setBorders(true);
        searchButton.setToolTip("Click here to configure the host group status chart");

        toolbar.setBorders(true);
        toolbar.setHeight(25);
        toolbar.add(new FillToolItem());
        toolbar.add(searchButton);

        return toolbar;
    }</code></pre>
<p>The loading of the Chart Portlet is done as follows by calling the getHostGroups method of the Opsview Enterprise proxy server stub and implementing the onSuccess and onFailure methods:</p>
<pre><code>    private void loadHostGroupsChartPortlet() {
        // Get the Opsview Server Proxy Service from the Registry
        final OpsviewServerProxyServiceAsync opsviewServerProxyService =
		(OpsviewServerProxyServiceAsync)
                Registry.get(OpsviewServerProxyService.SERVICE_NAME);

        // Make sure we got the service
        if (opsviewServerProxyService == null) {
            showInfoMsg("Opsview Server Proxy service could not be detected!");
        } else {
            opsviewServerProxyService.getHostGroups(new AsyncCallback
&gt;() {
                public void onSuccess(List hostGroups) {
                    m_currentHostGroups = hostGroups;

                    ChartModel cm = new ChartModel("Hosts by Host Group",
                            "font-size: 14px; font-family: Verdana; text-align: center;");
                    cm.setBackgroundColour("#fffff5");

                    PieChart pie = new PieChart();
                    pie.addChartListener(listener);
                    pie.setAlpha(0.5f);
                    pie.setTooltip("#label#
#percent#");
                    pie.setColours("#ff0000", "#00aa00", "#0000ff", "#ff9900", "#ff00ff");

                    for (HostGroup hostGroup : hostGroups) {
                        int totalHosts = hostGroup.getHosts().getTotal();
                        String name = hostGroup.getName();
                        pie.addSlices(new PieChart.Slice(totalHosts, name + " (" +
totalHosts + ")", name));
                    }

                    cm.addChartConfig(pie);

                    m_hostGroupsChart.setChartModel(cm);
                }

                public void onFailure(Throwable throwable) {
			throw new RuntimeException(throwable);
                    showInfoMsg("Opsview Server Proxy
getHostGroups call failed: " + throwable.getMessage());
                }
            });
        }
    }</code></pre>
<p>When the Opsview Enterprise server proxy responds we store the current host groups in a global variable so we can access it later on when the user clicks on one of the slices. We then do not have to call the server proxy as we have the HostStatus for the clicked on host group slice. We then create the PieChart and add a slice for each host group that was returned. A listener is also set for the pie chart and it will load the host status pie chart. The listener implementation looks like this:</p>
<pre><code>    private ChartListener listener = new ChartListener() {
        public void chartClick(ChartEvent ce) {
            PieChart.Slice hostGroup = (PieChart.Slice) ce.getDataType();
            loadHostStatusChartPortlet(hostGroup.getText());
            Info.display("Chart Clicked", "You selected {0}.", "" + ce.getValue() +
", " + hostGroup.getLabel());
        }
    };</code></pre>
<p>The Host Status Chart is loaded like this:</p>
<pre><code>    private void loadHostStatusChartPortlet(String hostGroupName) {
        ChartModel cm = new ChartModel("Host Status for Host Group " + hostGroupName,
                "font-size: 14px; font-family: Verdana; text-align: center;");
        cm.setBackgroundColour("#fffff5");

        PieChart pie = new PieChart();
        pie.addChartListener(listener);
        pie.setAlpha(0.5f);
        pie.setTooltip("#label#
#percent#");
        pie.setColours("#ff0000", "#00aa00", "#0000ff", "#ff9900", "#ff00ff");

        for (HostGroup hostGroup : m_currentHostGroups) {
            if (hostGroupName.equals(hostGroup.getName())) {
                HostStatus hostStatus = hostGroup.getHosts();
                pie.addSlices(new PieChart.Slice(hostStatus.getDownUnhandled(),
"Down - unhandled (" + hostStatus.getDownUnhandled() + ")", "Down - unhandled"));
                pie.addSlices(new PieChart.Slice(hostStatus.getUpHandled(),
"Up - handled (" + hostStatus.getUpHandled() + ")", "Up - handled"));
                pie.addSlices(new PieChart.Slice(hostStatus.getDownHandled(),
"Down - handled (" + hostStatus.getDownHandled() + ")", "Down - handled"));
                pie.addSlices(new PieChart.Slice(hostStatus.getUpUnhandled(),
"Up - unhandled (" + hostStatus.getUpUnhandled() + ")", "Up - unhandled"));
            }
        }

        cm.addChartConfig(pie);

        m_hostGroupsChart.setChartModel(cm);
    }</code></pre>
<p>When the Host Status chart is loaded it is not possible to go back to the Host Groups chart. There is no menu item for that or any implemented code to do that. If you like go ahead and implement this solution by yourself.</p>
<p>The little toolbar in the Chart Portlet is implemented like this:</p>
<pre><code>    private void configurePortlet(final ContentPanel panel) {
        panel.setCollapsible(true);
        panel.setAnimCollapse(false);
        panel.getHeader().addTool(new ToolButton("x-tool-gear"));
        panel.getHeader().addTool(
                new ToolButton("x-tool-close", new SelectionListener() {
                    @Override
                    public void componentSelected(IconButtonEvent ce) {
                        panel.removeFromParent();
                    }
                }));
    }</code></pre>
<p>The last couple of methods are helpers:</p>
<pre><code>    public static boolean isExplorer() {
        String test = Window.Location.getPath();
        if (test.indexOf("pages") != -1) {
            return false;
        }
        return true;
    }

    public static void showInfoMsg(String msg) {
        MessageBox box = new MessageBox();
        box.setButtons(MessageBox.OK);
        box.setIcon(MessageBox.INFO);
        box.setTitle("Information");
        box.setMessage(msg);
        box.show();
    }
}</code></pre>
<h4>Running the Mini-Dashboard Client</h4>
<p>To run this application after installing it under for example Tomcat we use a URL like this:</p>
<p><code>http://localhost:8080/opsviewui/dashboardApp.html</code></p>
<p>When we run this we should see a Host Groups Chart looking something like this:</p>
<p><a class="lightbox" title="HostGroupsPieChart" href="http://labs.opsview.com/wp-content/uploads/2010/05/HostGroupsPieChart.png"><img class="aligncenter size-full wp-image-372" title="HostGroupsPieChart" src="http://labs.opsview.com/wp-content/uploads/2010/05/HostGroupsPieChart.png" alt="" width="400" /></a></p>
<p>Clicking on one of the Host Groups will show Host status as follows:</p>
<p><a class="lightbox" title="HostStatusPieChart" href="http://labs.opsview.com/wp-content/uploads/2010/05/HostStatusPieChart.png"><img class="aligncenter size-full wp-image-373" title="HostStatusPieChart" src="http://labs.opsview.com/wp-content/uploads/2010/05/HostStatusPieChart.png" alt="" width="400" /></a></p>
<h4>Resources</h4>
<p>The Java libraries that were used in this example can be found here:</p>
<ul>
<li>GWT &#8211; <a href="http://code.google.com/webtoolkit/">http://code.google.com/webtoolkit/</a></li>
<li>GXT &#8211; <a href="http://www.extjs.com/products/gwt/">http://www.extjs.com/products/gwt/</a></li>
<li>Apache Commons HTTP Client – <a href="http://www.extjs.com/products/gwt/">http://hc.apache.org/httpclient-3.x/</a></li>
<li>Jackson JSON Parser &#8211; <a href="http://jackson.codehaus.org/">http://jackson.codehaus.org/</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://labs.opsview.com/2010/05/gwtgxt-dashboard-primer/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

