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

<channel>
	<title>Lean Java Engineering</title>
	<atom:link href="http://leanjavaengineering.wordpress.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://leanjavaengineering.wordpress.com</link>
	<description>A journey through architecture, processes &#38; testing</description>
	<lastBuildDate>Mon, 17 Jun 2013 20:29:26 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='leanjavaengineering.wordpress.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://s2.wp.com/i/buttonw-com.png</url>
		<title>Lean Java Engineering</title>
		<link>http://leanjavaengineering.wordpress.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://leanjavaengineering.wordpress.com/osd.xml" title="Lean Java Engineering" />
	<atom:link rel='hub' href='http://leanjavaengineering.wordpress.com/?pushpress=hub'/>
		<item>
		<title>Groovy as a GoogleCode API client</title>
		<link>http://leanjavaengineering.wordpress.com/2013/06/17/googlecode-api-client/</link>
		<comments>http://leanjavaengineering.wordpress.com/2013/06/17/googlecode-api-client/#comments</comments>
		<pubDate>Mon, 17 Jun 2013 20:29:22 +0000</pubDate>
		<dc:creator>rbramley</dc:creator>
				<category><![CDATA[Scripts]]></category>
		<category><![CDATA[googlecode]]></category>
		<category><![CDATA[groovy]]></category>
		<category><![CDATA[groovy grape]]></category>
		<category><![CDATA[GroovyMag]]></category>

		<guid isPermaLink="false">http://leanjavaengineering.wordpress.com/?p=596</guid>
		<description><![CDATA[This article gives an introduction to working with XML / HTTP APIs from Groovy in the context of a real world scenario using the GoogleCode API. This article first appeared in the March 2013 issue of GroovyMag. Since the script &#8230; <a href="http://leanjavaengineering.wordpress.com/2013/06/17/googlecode-api-client/">Continue reading <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=leanjavaengineering.wordpress.com&#038;blog=15402167&#038;post=596&#038;subd=leanjavaengineering&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p>This article gives an introduction to working with XML / HTTP APIs from Groovy in the context of a real world scenario using the GoogleCode API.</p>
<blockquote><p>This article first appeared in the <a href="http://www.groovymag.com/main.issues.description/id=55/">March 2013</a> issue of GroovyMag. Since the script was originally written Google deprecated the <a href="http://code.google.com/p/support/wiki/IssueTrackerAPI">Issue Tracker API</a> and scheduled it for closure on the 14th June 2013. So whilst the script is now for interest only, the principles are still valid for other purposes.</p></blockquote>
<p><span id="more-596"></span></p>
<p>A short while back I had a requirement to rename a Google Code project &#8211; it was due to a typo in the project name rather than a &#8216;cease &amp; desist&#8217; notice. Once my colleague had assigned me owner-level permissions I discovered that even the advanced administration options don&#8217;t permit you to change the project name. However, being Google, the issues have an API and being a dab hand with Groovy it was short work to migrate the issues to a new project (sans-typo).</p>
<p>This was early on in the project lifecycle, so the main challenge wasn&#8217;t migrating the code (there are several blog posts with instructions on that depending on your flavour of source code control) &#8211; rather it was the multitude of issues and comments from a requirements gathering workshop.</p>
<p>The final script is available from GitHub as a gist: <a href="https://gist.github.com/rbramley/5073413" rel="nofollow">https://gist.github.com/rbramley/5073413</a></p>
<h2>API &amp; toolset</h2>
<p>The Issue Tracker API is documented at <a href="http://code.google.com/p/support/wiki/IssueTrackerAPI" rel="nofollow">http://code.google.com/p/support/wiki/IssueTrackerAPI</a>  </p>
<p>It is a RESTful API using Atom feeds/entries &#8211; therefore the tools we&#8217;ll use are:</p>
<ul>
<li>Apache HttpComponents HttpClient 4.x (replaced commons-httpclient 3.x)</li>
<li>XmlSlurper &#8211; for parsing Atom Feeds that we GET</li>
<li>MarkupBuilder &#8211; for creating Atom entries to POST</li>
</ul>
<h3>Dependencies</h3>
<p><em>HttpClient</em> and its dependencies can be obtained using the Groovy GRAPE <code>@Grab</code> annotations shown in Listing 1. GRAPE (GRoovy Advanced Packaging Engine &#8211; <a href="http://groovy.codehaus.org/Grape" rel="nofollow">http://groovy.codehaus.org/Grape</a>) uses Apache Ivy for dependency resolution and was introduced in Groovy 1.6.</p>
<p><code>@Grab(group='commons-logging', module='commons-logging', version='1.1.1')<br />
@Grab(group='commons-codec', module='commons-codec', version='1.4')<br />
@Grab(group='org.apache.httpcomponents', module='httpclient', version='4.1.2')</code><br />
<em>Listing 1</em>: Grab annotations</p>
<h2>How does the script work?</h2>
<p>To better understand the context of the examples it is useful to quickly recap what the script does &#8211; the process is as follows:</p>
<ol>
<li>Form POST credentials
<li>If not 403 forbidden, extract auth token (for use in Authorization header on all subsequent requests)
<li>GET the issues list from the source project
<li>Parse the response body Atom XML (declaring the issues namespace of <a href="http://schemas.google.com/projecthosting/issues/2009" rel="nofollow">http://schemas.google.com/projecthosting/issues/2009</a>)
<li>For each entry in the feed:
<ol>
<li>Extract the issue ID
<li>Convert the entry to the required form
<li>POST the Atom entry to the target project issue creation URL
<li>If not 400 bad request, GET the list of comments by issue ID from the source project
<li>Parse the response body Atom XML (declaring the issues namespace as before)
<li>For each entry in the feed:
<ol>
<li>Convert the entry to the required form
<li>POST the Atom entry to the target project comment creation URL for the current issue
  </ol>
</ol>
</ol>
<h2>HTTP interaction basics</h2>
<p>To work with the API requires the use of two verbs, GET and POST. The examples rely on the definition of <code>HttpClient httpclient = new DefaultHttpClient()</code></p>
<h3>GET</h3>
<p><code>  get = new HttpGet(issuesCommentsListUrl)<br />
  get.setHeader('Authorization', "GoogleLogin auth=${authToken}")<br />
  response = httpclient.execute(get)</p>
<p>  println "${response.getStatusLine().getStatusCode()} - ${response.getStatusLine().getReasonPhrase()}"<br />
  commentsAtom = EntityUtils.toString(response.entity)</code><br />
<em>Listing 2</em>: HttpClient GET method</p>
<p>The commentary for Listing 2 falls into two parts: making the request and handling the response. <br />
For the request we have to construct an <em>HttpGet</em> object with the target URL, add the authorization token to the header and then instruct the <em>HttpClient</em> to execute our request.<br />
In terms of handling the response, we require it as a String for XML parsing, so the entity has to be obtained and converted to a String.</p>
<p>Whilst this is fairly straightforward, it doesn’t beat the simplicity of the Groovy augmented <code>getText</code> method on the <em>java.net.URL</em> class (e.g. <code>def response = new URL(url).getText()</code>) – in this case <em>HttpClient</em> is used for consistency and for the ability to set headers.</p>
<h3>Login form POST</h3>
<p>The first POST operation requires a form post to login, and as shown in Listing 3 the form parameters are constructed using NameValuePair and added to a list. This list is used to construct the URL encoded form entity set on the <em>HttpPost</em> object. </p>
<p><code>// set up login parameters<br />
NameValuePair accountType = new BasicNameValuePair('accountType', 'GOOGLE')<br />
NameValuePair email = new BasicNameValuePair('Email', emailAddress)<br />
NameValuePair passwd = new BasicNameValuePair('Passwd', password)<br />
NameValuePair service = new BasicNameValuePair('service', 'code')<br />
NameValuePair source = new BasicNameValuePair('source', sourceScript)</p>
<p>List params = new ArrayList(5)<br />
params.addAll([accountType, email, passwd, service, source])</p>
<p>HttpPost post = new HttpPost(googleLoginUrl)<br />
post.setEntity(new UrlEncodedFormEntity(params))</p>
<p>HttpResponse response = httpclient.execute(post)</code><br />
<em>Listing 3</em>: Form POST</p>
<p>If the response status code isn’t a 403 Forbidden, the resulting authorization token is extracted from the body of the response.</p>
<h3>POSTing XML</h3>
<p>As can be seen in Listing 4, XML payload POST operations are simpler as we are sending the XML using a <em>StringEntity</em>. In this case we also have to set the <code>Content-type</code> header to <code>application/atom+xml</code>.</p>
<p><code>  // post the issue<br />
  post = new HttpPost(issuePostUrl)<br />
  post.setHeader('Content-type', 'application/atom+xml')<br />
  post.setHeader('Authorization', "GoogleLogin auth=${authToken}")<br />
  post.setEntity(new StringEntity(issueCreationXml))<br />
  response = httpclient.execute(post)</code><br />
<em>Listing 4</em>: String body POST</p>
<h2>Groovy XML processing</h2>
<p>Groovy has some very useful helper classes for working with XML – the following sections will cover <em>XmlSlurper</em> and <em>MarkupBuilder</em> as used by the script. GPath, a powerful set of functions for querying nested data structures such as an XML document, is beyond the scope of this article but is worth further investigation if you need to perform more advanced XML handling than is required for the mapping exercise in this script.</p>
<h3>Consumption with XmlSlurper</h3>
<p>The Groovy XML Slurper (<a href="http://groovy.codehaus.org/Reading+XML+using+Groovy&#8217;s+XmlSlurper" rel="nofollow">http://groovy.codehaus.org/Reading+XML+using+Groovy&#8217;s+XmlSlurper</a>) can parse XML to an object tree and is namespace aware. Listing 5 shows an overview of the important fields in the Atom feed that are used in Listings 6 and 7. As this script was designed for issue migration, we parse the XML into nested objects (Listing 6), which are used to generate XML for recreating the issue in the target project (Listing 7).</p>
<p><code>&lt;feed xmlns='http://www.w3.org/2005/Atom' ...&gt;<br />
&lt;entry&gt;<br />
&nbsp;&nbsp;&lt;id&gt;<br />
&nbsp;&nbsp;&lt;title&gt;<br />
&nbsp;&nbsp;&lt;content type='html'&gt;<br />
&nbsp;&nbsp;&lt;author&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;name&gt;<br />
&nbsp;&nbsp;&lt;/author&gt;<br />
&nbsp;&nbsp;&lt;issues:id&gt;<br />
&nbsp;&nbsp;&lt;issues:label&gt;Type-Defect&lt;/issues:label&gt;<br />
&nbsp;&nbsp;&lt;issues:label&gt;Priority-Medium&lt;/issues:label&gt;<br />
&nbsp;&nbsp;&lt;issues:owner&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;issues:username&gt;<br />
&nbsp;&nbsp;&lt;/issues:owner&gt;<br />
&nbsp;&nbsp;&lt;issues:state&gt;<br />
&nbsp;&nbsp;&lt;issues:status&gt;<br />
&lt;/entry&gt;</code><br />
<em>Listing 5</em>: Atom entry fields</p>
<p><code>// Parse and process the atom feed<br />
feed = new XmlSlurper().parseText(atom).declareNamespace([issues:issuesXmlns])<br />
feed.entry.each { entry -&gt;<br />
&nbsp;&nbsp;issueId = entry.'issues:id'<br />
&nbsp;&nbsp;println "Issue ${issueId} - ${entry.title}"</p>
<p>&nbsp;&nbsp;issueCreationXml = buildIssue(entry, issuesXmlns, atomXmlns)</code><br />
<em>Listing 6</em>: XmlSlurper</p>
<h3>Production with MarkupBuilder</h3>
<p><em>MarkupBuilder</em> is a helper class for creating XML or HTML markup using the closure-based builder syntax. Unlike <em>XmlSlurper</em> <em>groovy.xml.MarkupBuilder</em> isn’t on the standard Groovy classpath so will need to be imported. Listing 7 shows the creation of an Atom entry with the Google Data issues API extensions.</p>
<p>For fine-grained control over specific elements you can use the <em>MarkupBuilderHelper</em> class (<a href="http://groovy.codehaus.org/api/groovy/xml/MarkupBuilderHelper.html" rel="nofollow">http://groovy.codehaus.org/api/groovy/xml/MarkupBuilderHelper.html</a>) that is accessed as <code>mkp</code> and enables direct insertion of data escaped or unescaped e.g. <code>b { mkp.yield( '3 &lt; 5&#039; ) }</code> which results in the output <b>3 &lt; 5</b>.</p>
<p><code>/** Build an atom feed for an issue */<br />
def buildIssue(entry, issuesXmlns, atomXmlns) {<br />
&nbsp;&nbsp;def writer = new StringWriter()<br />
&nbsp;&nbsp;def xml = new MarkupBuilder(writer)<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;xml.'atom:entry'('xmlns:atom':atomXmlns,'xmlns:issues':issuesXmlns) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;'atom:title'(entry.title)<br />
&nbsp;&nbsp;&nbsp;&nbsp;'atom:content'(type:'html', entry.content)<br />
&nbsp;&nbsp;&nbsp;&nbsp;'atom:author' {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'atom:name'(entry.author.name)<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;entry.'issues:label'.each {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'issues:label'(it)<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;'issues:owner' {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'issues:username'(entry.'issues:owner'.'issues:username')<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;'issues:state'(entry.'issues:state')<br />
&nbsp;&nbsp;&nbsp;&nbsp;'issues:status'(entry.'issues:status')<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;return writer.toString()<br />
}</code><br />
<em>Listing 7</em>: MarkupBuilder</p>
<h3>Groovy User Guide references</h3>
<p><a href="http://groovy.codehaus.org/GPath" rel="nofollow">http://groovy.codehaus.org/GPath</a></p>
<p><a href="http://groovy.codehaus.org/Processing+XML" rel="nofollow">http://groovy.codehaus.org/Processing+XML</a></p>
<p><a href="http://groovy.codehaus.org/Reading+XML+using+Groovy%27s+XmlSlurper" rel="nofollow">http://groovy.codehaus.org/Reading+XML+using+Groovy%27s+XmlSlurper</a></p>
<p><a href="http://groovy.codehaus.org/Creating+XML+using+Groovy%27s+MarkupBuilder" rel="nofollow">http://groovy.codehaus.org/Creating+XML+using+Groovy%27s+MarkupBuilder</a></p>
<h1>Executing the script</h1>
<p>If you have the need to migrate your issues then the necessary steps are:</p>
<ol>
<li>Create your new target project</li>
<li>Add the members from the old project (I did this manually) &#8211; this is critical to prevent creation errors</li>
<li>Grab the IssueMigrator.groovy script from GitHub gist</li>
<li>Change the project names, credentials and script source (lines 32-36)</li>
<li>If you have more than 50 issues you&#8217;ll need to increase the max-results parameter on line 50</li>
<li>Understand that the issues/comments will be created as the account running the script (and it has no warranty) &#8211; the original owners will be retained</li>
<li>Run the script (either cross your fingers or run against a guinea pig project first!)</li>
<li>Sit back and enjoy your new project</li>
</ol>
<p>You may see some 400 &#8216;bad request&#8217; responses being output from issue comments where it thinks there have been no updates. This can be ignored as the issue was created in the end state, rather than being created in the start state and then rolled forwards.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/leanjavaengineering.wordpress.com/596/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/leanjavaengineering.wordpress.com/596/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=leanjavaengineering.wordpress.com&#038;blog=15402167&#038;post=596&#038;subd=leanjavaengineering&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://leanjavaengineering.wordpress.com/2013/06/17/googlecode-api-client/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://2.gravatar.com/avatar/ba996199c3dd2c7095b45f3a645c52ef?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">leanjavaengineering</media:title>
		</media:content>
	</item>
		<item>
		<title>Monitoring elasticsearch</title>
		<link>http://leanjavaengineering.wordpress.com/2012/08/30/monitoring-elasticsearch/</link>
		<comments>http://leanjavaengineering.wordpress.com/2012/08/30/monitoring-elasticsearch/#comments</comments>
		<pubDate>Thu, 30 Aug 2012 20:07:34 +0000</pubDate>
		<dc:creator>rbramley</dc:creator>
				<category><![CDATA[How-to]]></category>
		<category><![CDATA[Monitoring]]></category>
		<category><![CDATA[elasticsearch]]></category>
		<category><![CDATA[Opsview]]></category>

		<guid isPermaLink="false">http://leanjavaengineering.wordpress.com/?p=590</guid>
		<description><![CDATA[elasticsearch is an open source distributed RESTful search engine built on top of Apache Lucene. Like any service or component in your architecture, you&#8217;ll want to monitor it to ensure that it&#8217;s available and gather performance data to help with &#8230; <a href="http://leanjavaengineering.wordpress.com/2012/08/30/monitoring-elasticsearch/">Continue reading <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=leanjavaengineering.wordpress.com&#038;blog=15402167&#038;post=590&#038;subd=leanjavaengineering&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p><a href="http://www.elasticsearch.org"><strong>elasticsearch</strong></a> is an open source distributed RESTful search engine built on top of <a href="http://lucene.apache.org/">Apache Lucene</a>.<br />
Like any service or component in your architecture, you&#8217;ll want to monitor it to ensure that it&#8217;s available and gather performance data to help with tuning.</p>
<p>In this brief post, we&#8217;ll look at how we can monitor elasticsearch using <a href="http://www.opsview.com">Opsview</a>, which is built on Nagios and thus has access to a wide range of plugins, yet provides a more approachable user interface for configuring service checks.</p>
<p><!-- more --></p>
<h3>Opsview configuration</h3>
<p>The rest of the article assumes that you&#8217;ve got Opsview (or the <a href="http://www.opsview.com/technology/downloads/opsview-core/opsview-core-virtual-appliance">Opsview VMWare appliance</a>) installed &amp; have completed the <a href="http://docs.opsview.com/doku.php?id=opsview-community:quickstart">Quick Start</a>.</p>
<h4>elasticsearch-specific Plugin</h4>
<p>We&#8217;ll install the plugin from <a href="https://github.com/rbramley/Opsview-elasticsearch">https://github.com/rbramley/Opsview-elasticsearch</a> into <em>/usr/local/nagios/libexec/</em></p>
<p>The <em>check_elasticsearch</em> plugin is developed using Perl, so that it can be contributed back to Opsview. It requires the CPAN JSON module (<em>sudo cpan -i JSON</em>).</p>
<p>The plugin includes usage instructions, <em>check_elasticsearch -h</em> which can also be viewed in Opsview by selecting the &#8216;<em>Show Plugin Help</em>&#8216; link beneath the <strong>Plugin</strong> drop down.</p>
<h4>Service check setup</h4>
<p>Figure 1 gives an overview of service check configurations.<br />
<div id="attachment_591" class="wp-caption alignnone" style="width: 510px"><a href="http://leanjavaengineering.files.wordpress.com/2012/08/es_check_defs.png"><img src="http://leanjavaengineering.files.wordpress.com/2012/08/es_check_defs.png?w=500&#038;h=161" alt="" title="es_check_defs" width="500" height="161" class="size-full wp-image-591" /></a><p class="wp-caption-text">Figure 1 &#8211; Check definitions overview</p></div></p>
<h4>The checks in action</h4>
<p>The check results shown in Figure 2 are visible by navigating through the host group hierarchy.<br />
<div id="attachment_592" class="wp-caption alignnone" style="width: 510px"><a href="http://leanjavaengineering.files.wordpress.com/2012/08/es_check_results.png"><img src="http://leanjavaengineering.files.wordpress.com/2012/08/es_check_results.png?w=500&#038;h=100" alt="" title="es_check_results" width="500" height="100" class="size-full wp-image-592" /></a><p class="wp-caption-text">Figure 2 &#8211; service check results</p></div></p>
<p>Note: They&#8217;re showing as <em>warning</em> because the checks were run against a standalone instance rather than a cluster.</p>
<h4>Summary</h4>
<p>The current checks are based on the <a href="http://www.elasticsearch.org/guide/reference/api/admin-cluster-health.html">Cluster Health API</a>, the intention is to add stats/status checks too that will take threshold criteria and output performance data. The code for the check is on GitHub at <a href="https://github.com/rbramley/Opsview-elasticsearch">https://github.com/rbramley/Opsview-elasticsearch</a> so feel free to fork &amp; send pull requests.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/leanjavaengineering.wordpress.com/590/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/leanjavaengineering.wordpress.com/590/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=leanjavaengineering.wordpress.com&#038;blog=15402167&#038;post=590&#038;subd=leanjavaengineering&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://leanjavaengineering.wordpress.com/2012/08/30/monitoring-elasticsearch/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://2.gravatar.com/avatar/ba996199c3dd2c7095b45f3a645c52ef?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">leanjavaengineering</media:title>
		</media:content>

		<media:content url="http://leanjavaengineering.files.wordpress.com/2012/08/es_check_defs.png" medium="image">
			<media:title type="html">es_check_defs</media:title>
		</media:content>

		<media:content url="http://leanjavaengineering.files.wordpress.com/2012/08/es_check_results.png" medium="image">
			<media:title type="html">es_check_results</media:title>
		</media:content>
	</item>
		<item>
		<title>Monitoring OpenStack Swift with Opsview</title>
		<link>http://leanjavaengineering.wordpress.com/2012/08/24/opsview-openstack-swift/</link>
		<comments>http://leanjavaengineering.wordpress.com/2012/08/24/opsview-openstack-swift/#comments</comments>
		<pubDate>Fri, 24 Aug 2012 20:30:31 +0000</pubDate>
		<dc:creator>rbramley</dc:creator>
				<category><![CDATA[How-to]]></category>
		<category><![CDATA[Monitoring]]></category>
		<category><![CDATA[OpenStack]]></category>
		<category><![CDATA[Opsview]]></category>

		<guid isPermaLink="false">http://leanjavaengineering.wordpress.com/?p=580</guid>
		<description><![CDATA[This is a quick how-to for Opsview users who need to monitor an OpenStack (Essex) Swift installation. As a starting point we&#8217;ll perform a &#8216;front door&#8217; check as this should work no matter what Swift implementation you are using. Note &#8230; <a href="http://leanjavaengineering.wordpress.com/2012/08/24/opsview-openstack-swift/">Continue reading <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=leanjavaengineering.wordpress.com&#038;blog=15402167&#038;post=580&#038;subd=leanjavaengineering&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p>This is a quick how-to for <a href="http://www.opsview.com">Opsview</a> users who need to monitor an <a href="http://swift.openstack.org">OpenStack (Essex) Swift</a> installation. As a starting point we&#8217;ll perform a &#8216;front door&#8217; check as this should work no matter what Swift implementation you are using.<br />
<span id="more-580"></span><br />
Note that I&#8217;m using Authentication version 2.0 with <a href="http://docs.openstack.org/developer/keystone/">Keystone</a> rather than <em>tempauth</em>.</p>
<p>The following assume you already have both Opsview Server and OpenStack Object Storage (Swift) configured and running.</p>
<h2>Pre-requisite set up tasks</h2>
<p>The machine running the check needs to have a Swift client and an Opsview Agent. If you plan to monitor from a Swift proxy server you should only need the latter; if you plan to execute the checks from your Opsview server, then you&#8217;ll need the former.</p>
<h3>Installing the Swift client</h3>
<p>This relies on having Python installed &#8211; grab the source from:<br />
<a href="https://github.com/openstack/python-swiftclient.git">https://github.com/openstack/python-swiftclient.git</a><br />
<a href="https://github.com/openstack/python-keystoneclient.git">https://github.com/openstack/python-keystoneclient.git</a><br />
<code>sudo python setup.py install</code> (for both)</p>
<h3>Installing the Opsview Agent on Ubuntu</h3>
<p><code>echo "deb <a href="http://downloads.opsview.com/opsview-core/latest/apt" rel="nofollow">http://downloads.opsview.com/opsview-core/latest/apt</a> precise main" &gt; /etc/apt/sources.list.d/opsview-core.list<br />
apt-get update<br />
apt-get install opsview-agent<br />
</code></p>
<h3>Obtaining the check</h3>
<p>The plugin details are here:<br />
<a href="http://exchange.nagios.org/directory/Plugins/Clustering-and-High-2DAvailability/check_swift/details">http://exchange.nagios.org/directory/Plugins/Clustering-and-High-2DAvailability/check_swift/details</a></p>
<p>The direct download link:<br />
<a href="http://exchange.nagios.org/components/com_mtree/attachment.php?link_id=3589&amp;cf_id=30">http://exchange.nagios.org/components/com_mtree/attachment.php?link_id=3589&amp;cf_id=30</a></p>
<p>Place this into <em>/usr/local/nagios/libexec</em> (on the Opsview Agent) and make the file executable (<em>chmod +x check_swift</em>) and owned by nagios (<em>chmod nagios:nagios check_swift</em>).</p>
<h4>Environment variables</h4>
<p>If you don&#8217;t already have the <em>ST_AUTH</em> (tenant), <em>ST_USER</em> and <em>ST_KEY</em> environment variables set, then you may want to modify the check accordingly (as per the following diff):<br />
<code>diff check_swift.orig check_swift<br />
52c52<br />
&lt; export OS_AUTH_URL=$OPTARG<br />
---<br />
&gt; export ST_AUTH=$OPTARG<br />
55c55<br />
&lt; export OS_USERNAME=$OPTARG<br />
---<br />
&gt; export ST_USER=$OPTARG<br />
58c58<br />
&lt; export OS_PASSWORD=$OPTARG<br />
---<br />
&gt; export ST_KEY=$OPTARG<br />
</code></p>
<h3>Setting up NRPE</h3>
<p>Whilst you could run the check from your Opsview server (if you install the Swift client), it is&#8217;d more likely it would be executed on an Opsview Agent via NRPE.<br />
Therefore we need to add a service check command to the NRPE configuration on the Opsview Agent:<br />
<code>echo "command[check_swift]=/usr/local/nagios/libexec/check_swift \$ARG1\$" &gt;&gt; /usr/local/nagios/etc/nrpe_local/override.cfg<br />
/etc/init.d/opsview-agent restart<br />
</code></p>
<h2>Configuring the check</h2>
<p>On your Opsview server you&#8217;ll need to define a new Service Check, Figure 1 shows a completed example.</p>
<div id="attachment_585" class="wp-caption alignnone" style="width: 510px"><a href="http://leanjavaengineering.files.wordpress.com/2012/08/check_definition.png"><img src="http://leanjavaengineering.files.wordpress.com/2012/08/check_definition.png?w=500&#038;h=27" alt="" title="check_definition" width="500" height="27" class="size-full wp-image-585" /></a><p class="wp-caption-text">Figure 1 &#8211; Service check definition</p></div>
<p>If you like to copy &amp; paste, the arguments to check_nrpe are:<br />
<code>-H $HOSTADDRESS$ -c check_swift -a '-V 2 -U admin:admin -A <a href="http://127.0.0.1:5000/v2.0/" rel="nofollow">http://127.0.0.1:5000/v2.0/</a> -K secrete -c container'</code></p>
<p>Substitute the values as applicable or override them at the host-level.</p>
<h2>The check in action</h2>
<p>Figure 2 shows the host-level view of the service check (<em>note that it appears to be ignoring the -c container value</em>).<br />
<div id="attachment_586" class="wp-caption alignnone" style="width: 510px"><a href="http://leanjavaengineering.files.wordpress.com/2012/08/check_results.png"><img src="http://leanjavaengineering.files.wordpress.com/2012/08/check_results.png?w=500&#038;h=76" alt="" title="check_results" width="500" height="76" class="size-full wp-image-586" /></a><p class="wp-caption-text">Figure 2 &#8211; Service check results</p></div></p>
<p>There you go, quick and easy basic monitoring of OpenStack Swift.</p>
<h2>Troubleshooting</h2>
<p>If it doesn&#8217;t work, first check you can perform an upload using the swift client: <code>swift -V 2 -U admin:admin -A <a href="http://127.0.0.1:5000/v2.0/" rel="nofollow">http://127.0.0.1:5000/v2.0/</a> -K secrete upload container check_swift</code></p>
<p>The second test is to verify the Nagios plugin returns a zero exit code:<br />
<code>check_swift -V 2 -U admin:admin -A <a href="http://127.0.0.1:5000/v2.0/" rel="nofollow">http://127.0.0.1:5000/v2.0/</a> -K secrete -c container<br />
echo $?</code><br />
Note that the plugin redirects output to <em>/dev/null</em> so you may need to tweak a copy of the script so that you can see the swift error messages if this step fails.</p>
<p>Lastly test NRPE:<br />
<code>check_nrpe -H localhost -c check_swift -a '-V 2 -U admin:admin -A <a href="http://127.0.0.1:5000/v2.0/" rel="nofollow">http://127.0.0.1:5000/v2.0/</a> -K secrete -c container -s 128'</code></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/leanjavaengineering.wordpress.com/580/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/leanjavaengineering.wordpress.com/580/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=leanjavaengineering.wordpress.com&#038;blog=15402167&#038;post=580&#038;subd=leanjavaengineering&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://leanjavaengineering.wordpress.com/2012/08/24/opsview-openstack-swift/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://2.gravatar.com/avatar/ba996199c3dd2c7095b45f3a645c52ef?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">leanjavaengineering</media:title>
		</media:content>

		<media:content url="http://leanjavaengineering.files.wordpress.com/2012/08/check_definition.png" medium="image">
			<media:title type="html">check_definition</media:title>
		</media:content>

		<media:content url="http://leanjavaengineering.files.wordpress.com/2012/08/check_results.png" medium="image">
			<media:title type="html">check_results</media:title>
		</media:content>
	</item>
		<item>
		<title>Updating the Grails Solr Plugin</title>
		<link>http://leanjavaengineering.wordpress.com/2012/06/28/updating-grails-solr-plugin/</link>
		<comments>http://leanjavaengineering.wordpress.com/2012/06/28/updating-grails-solr-plugin/#comments</comments>
		<pubDate>Thu, 28 Jun 2012 19:30:48 +0000</pubDate>
		<dc:creator>rbramley</dc:creator>
				<category><![CDATA[Notes]]></category>
		<category><![CDATA[Search]]></category>
		<category><![CDATA[grails]]></category>
		<category><![CDATA[solr]]></category>

		<guid isPermaLink="false">http://leanjavaengineering.wordpress.com/?p=534</guid>
		<description><![CDATA[A few months back I was working on a Grails &#38; Solr project &#8211; so it was a prime opportunity to answer my call to action (see the Solr plugin section of &#8216;Using Lucene in Grails&#8217;) and upgrade the Solr &#8230; <a href="http://leanjavaengineering.wordpress.com/2012/06/28/updating-grails-solr-plugin/">Continue reading <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=leanjavaengineering.wordpress.com&#038;blog=15402167&#038;post=534&#038;subd=leanjavaengineering&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p>A few months back I was working on a Grails &amp; Solr project &#8211; so it was a prime opportunity to answer my <em>call to action</em> (see the <em>Solr plugin</em> section of <a href="http://leanjavaengineering.wordpress.com/2012/02/24/using-lucene-in-grails/">&#8216;Using Lucene in Grails&#8217;</a>) and upgrade the Solr plugin to 3.5.0.<br />
This was done in two phases, the first was to update the SolrJ client as required by the project and then once the project was completed to update the bundled Solr server with the aim of contributing the update back.</p>
<p><strong>TLDR:</strong> Grails Solr Plugin using <del>3.5.0</del> 3.6.0 available from <a href="https://github.com/rbramley/grails-solr-plugin">https://github.com/rbramley/grails-solr-plugin</a> pending <a href="https://github.com/mbrevoort/grails-solr-plugin/pull/2">https://github.com/mbrevoort/grails-solr-plugin/pull/2</a></p>
<p><span id="more-534"></span></p>
<h2>Phase one: SolrJ</h2>
<p>I started by upgrading the plugin from Grails 1.3.4 to 2.0.0 &#8211; other than <em>application.properties</em>, this affected the <em>DataSource.groovy</em> file with the switch from <em>hsqldb</em> to <em>h2</em>.</p>
<p>The next step was to remove the bundled SolrJ jar + dependencies from lib and move to using Ivy dependency management by adding a BuildConfig.groovy file with the following dependency for SolrJ:<br />
<code>compile ('org.apache.solr:solr-solrj:3.5.0') {}</code></p>
<p>Upon running the tests I hit a few failures, I fixed up a unit test assertion in <em>SolrUtilTests.groovy</em> by coercing a return value to a String and changing the expected value from a GStringImpl to a String. The integration tests weren&#8217;t passing as the Start Solr script wasn&#8217;t working &#8211; it wasn&#8217;t on the critical path for the project so that was deferred to phase 2.</p>
<h2>Phase two: Bundled Solr</h2>
<h3>1. Failing integration tests</h3>
<p>The starting point of phase 2 was to get the integration tests working so that changes going forward were from a <em>known good</em> starting point.</p>
<p>As mentioned beforehand, the Start Solr (<code>grails start-solr</code>) script wasn&#8217;t working so that was the first port of call. The failure was caused by a missing log directory from ~/.grails/<em>&lt;project name&gt;</em>/solr-home which was easily rectified with an addition to the script:<br />
<code><br />
def solrLogDir = "${solrHome}/logs"<br />
if(! new File(solrLogDir)?.exists()) {<br />
&nbsp;&nbsp;Ant.mkdir(dir: "${solrLogDir}")<br />
}<br />
</code></p>
<p>Alright, so now the integration tests can run, but they&#8217;re still working against Solr 1.4 &#8211; time to get on with the version upgrade.</p>
<h3>2. Upgrading solr-local to 3.5.0</h3>
<p>I followed the existing plugin pattern of an embedded war file and jars for speed &#8211; though it might be nicer to move this to dependencies managed from the start script.</p>
<p>This was simply a case of unpacking Solr 3.5.0 over the top and merging / fixing up the configuration and schema.</p>
<h3>3. Spatial</h3>
<p>This error message: <code>Expected identifier at pos 87<br />
str='sum(hsin(0.6935790722757577,-1.8323287385596856,latitude_rad_d,longitude_rad_d,3963.205))'</code> required a change to use the Solr 3.1+ <a href="http://wiki.apache.org/solr/FunctionQuery#hsin.2C_ghhsin_-_Haversine_Formula"><em>hsin</em> syntax</a>.</p>
<h3>4. Assertion errors</h3>
<p>In two cases the expected field value in the <em>DomainMethodTests</em> was returning an array rather than the SolrId &#8211; the quick fix was to use the first element.</p>
<p>Tests passing = upgrade completed so it was committed and pushed to <a href="https://github.com/rbramley/grails-solr-plugin">https://github.com/rbramley/grails-solr-plugin</a> before sending a pull request.</p>
<h2>Usage</h2>
<p>If you take it from my GitHub repo, you can either download the packaged plugin or grab the full source and <a href="http://grails.org/doc/latest/ref/Command%20Line/package-plugin.html">package the plugin</a>; the plugin zip can then be <a href="http://grails.org/doc/latest/ref/Command%20Line/install-plugin.html">installed</a> into your local project (this would need doing on each environment e.g. CI).<br />
Alternatively you can use a local plugin repo (see the <a href="http://grails.org/doc/latest/guide/plugins.html#repositories">Plugins chapter of the Grails user guide</a>) or lobby for the pull request to be merged in and the updated plugin to be released!</p>
<h2>Summary</h2>
<p>The upgrade was completed and a pull request (<a href="https://github.com/mbrevoort/grails-solr-plugin/pull/2">https://github.com/mbrevoort/grails-solr-plugin/pull/2</a>) sent upstream to Mike Brevoort, but sadly he hasn&#8217;t had the time to merge this in and meanwhile Solr 3.6.0 was released&#8230; So I&#8217;ve upgraded it again &#8211; all the tests pass but be aware that I haven&#8217;t had the chance to test it more extensively. </p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/leanjavaengineering.wordpress.com/534/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/leanjavaengineering.wordpress.com/534/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=leanjavaengineering.wordpress.com&#038;blog=15402167&#038;post=534&#038;subd=leanjavaengineering&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://leanjavaengineering.wordpress.com/2012/06/28/updating-grails-solr-plugin/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://2.gravatar.com/avatar/ba996199c3dd2c7095b45f3a645c52ef?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">leanjavaengineering</media:title>
		</media:content>
	</item>
		<item>
		<title>Using Mahout Recommenders in Grails</title>
		<link>http://leanjavaengineering.wordpress.com/2012/06/20/grails-mahout-recommenders/</link>
		<comments>http://leanjavaengineering.wordpress.com/2012/06/20/grails-mahout-recommenders/#comments</comments>
		<pubDate>Wed, 20 Jun 2012 20:10:25 +0000</pubDate>
		<dc:creator>rbramley</dc:creator>
				<category><![CDATA[How-to]]></category>
		<category><![CDATA[grails]]></category>
		<category><![CDATA[GroovyMag]]></category>
		<category><![CDATA[mahout]]></category>

		<guid isPermaLink="false">http://leanjavaengineering.wordpress.com/?p=549</guid>
		<description><![CDATA[Apache Mahout is a scalable machine learning framework that can be used to create intelligent applications. In this article we&#8217;ll see how Mahout can be used to create personalised recommendations within a Grails application. This article originally appeared in the &#8230; <a href="http://leanjavaengineering.wordpress.com/2012/06/20/grails-mahout-recommenders/">Continue reading <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=leanjavaengineering.wordpress.com&#038;blog=15402167&#038;post=549&#038;subd=leanjavaengineering&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p>Apache Mahout is a scalable machine learning framework that can be used to create intelligent applications. In this article we&#8217;ll see how Mahout can be used to create personalised recommendations within a Grails application.</p>
<blockquote><p>This article originally appeared in the <a href="http://www.groovymag.com/main.issues.description/id=41/">February 2012 edition of GroovyMag</a>.</p></blockquote>
<p><span id="more-549"></span></p>
<h1>About Mahout</h1>
<p>Mahout started off as a sub-project of Apache Lucene and the name is a Hindi word referring to an elephant driver; portions of Mahout are built on top of Hadoop which was named after a stuffed toy elephant owned by the son of Doug Cutting who started that project. </p>
<p>Hadoop was extracted from the Nutch crawler Lucene sub-project and provides a scalable data processing framework using Map-Reduce on top of a distributed file system (HDFS). The use of Hadoop is beyond the scope of this introductory article.</p>
<p>Mahout has three primary areas of machine learning functionality: classification, clustering and recommendations.</p>
<p><i>Classification</i> can be used to determine how similar an item is to previously encountered items or patterns and whether it belongs to the same category. </p>
<p>For instance, Mahout supports Naive Bayes classification with the &#8216;hello world&#8217; sample training a classifier and then classifying posts from 20 newsgroups (a common reference data set for machine learning research).</p>
<p><i>Clustering</i> groups items together based on their similarity &#8211; e.g. this can be observed in Google search results.</p>
<p>We&#8217;re going to focus on <i>recommendations</i>, sometimes referred to as collaborative filtering. Amazon is a well-known example of providing suggested &quot;other people also bought&quot; products.</p>
<p>For further information on Mahout and clustering or classification I suggest you read Mahout in Action (see what I did there?).</p>
<h2>Recommendations</h2>
<p>Recommendations provide a discovery mechanism to introduce users to new items that may be of interest to them; it is usually associated with cross-selling tactics (e.g. &#8216;Also bought&#8217;) on retail e-commerce sites.</p>
<p>Mahout provides a set of components to enable the construction of customised recommendation engines. </p>
<p>Firstly there is the <code>Recommender</code> that will produce recommendations based on a <code>DataModel</code>.</p>
<p>A user-based recommender will look for similar preferences or behavior patterns between users (<code>UserSimilarity</code>), and then group these into neighbourhoods (<code>UserNeighborhood</code>) e.g. the nearest 10 users to the specified user. The chosen algorithm will then select the recommendations of new items from within the neighbourhood.</p>
<p>An item-based recommender works on similarity between items (<code>ItemSimilarity</code>).</p>
<p>The <code>DataModel</code> has a very simple representation of the <code>Preference</code> data to work with: a long user-id, a long item-id and a float preference value. This is limited to a tuple to reduce memory consumption and therefore increase scalability. DataModel implementations are available for files, MySQL, generic JDBC and Postgres.</p>
<p><a href="https://cwiki.apache.org/MAHOUT/recommender-documentation.html">https://cwiki.apache.org/MAHOUT/recommender-documentation.html</a> has lots more useful information and further links.</p>
<h1>Grails recommendations</h1>
<p>Lim Chee Kin has been hard at work and recently released a Grails Mahout-Recommender plugin in December 2011. The plugin is intended to let you evaluate different recommenders without needing to write any code; once you have selected a recommender then it can be enabled using configuration.</p>
<p>We&#8217;ll start by looking at the sample packaged <i>libimseti</i> application based on code from chapter 5 of Mahout in Action which uses 17+ million profile ratings from a Czech dating site to recommend compatible user profiles.</p>
<h2>Libimseti</h2>
<h3>Getting set up</h3>
<p>I created a clean Grails 2.0.0 application and installed the plugin (version 0.5.1 at time of writing) using<br />
the commands in Listing 1. The plugin adds some new scripts, including the ability to install a sample application that we can run using the <code>grails install-libimseti-sample-app</code> command.</p>
<p><code>grails create-app GroovyMagMahout<br />
cd GroovyMagMahout<br />
grails install-plugin mahout-recommender</code></p>
<p><strong>Listing 1:</strong> application creation and plugin installation</p>
<h3>Sample data</h3>
<p>The <i>Libimseti </i>sample data is not redistributable, so you&#8217;ll need to download the zip from <a href="http://www.occamslab.com/petricek/data/libimseti-complete.zip">http://www.occamslab.com/petricek/data/libimseti-complete.zip</a> and then extract the .dat files to <i>grails-app/conf</i></p>
<h3>Configuration</h3>
<p>There are two more things we need to do before we can run the Grails application, firstly we need to adjust the plugin configuration in <i>Config.groovy</i> to add the settings from Listing 2 and we also need to give Grails some more memory for the hungry algorithm to prevent an &quot;OutOfMemoryError: GC overhead limit exceeded&quot; which is achieved by Listing 3.</p>
<p><code>mahout.recommender.hasPreference = false<br />
mahout.recommender.data.file = 'ratings.dat'<br />
</code></p>
<p><strong>Listing 2:</strong> Additional plugin configuration</p>
<p><code>export GRAILS_OPTS=&quot;-XX:MaxPermSize=256m -Xmx1024M -server&quot;</code><br />
<strong>Listing 3:</strong> Increasing the memory allocated to Grails</p>
<h3>Libimseti sample usage</h3>
<p>When we execute <i>grails run-app</i> and browse to <a href="http://localhost:8080/GroovyMagMahout">http://localhost:8080/GroovyMagMahout</a> we will see that as per Figure 1 we have a single recommender controller listed.</p>
<div id="attachment_551" class="wp-caption alignnone" style="width: 510px"><a href="http://leanjavaengineering.files.wordpress.com/2012/06/figure1.png"><img src="http://leanjavaengineering.files.wordpress.com/2012/06/figure1.png?w=500&#038;h=162" alt="" title="figure1" width="500" height="162" class="size-full wp-image-551" /></a><p class="wp-caption-text">Figure 1: Application home page</p></div>
<p>Selecting the controller will bring us to the settings form shown in Figure 2.</p>
<div id="attachment_552" class="wp-caption alignnone" style="width: 510px"><a href="http://leanjavaengineering.files.wordpress.com/2012/06/figure2.png"><img src="http://leanjavaengineering.files.wordpress.com/2012/06/figure2.png?w=500&#038;h=419" alt="" title="figure2" width="500" height="419" class="size-full wp-image-552" /></a><p class="wp-caption-text">Figure 2: Recommender settings</p></div>
<p>Enter user ID &#8217;133&#8242;, submit the form and after some time (the algorithm is tuned for better matching rather than performance) you&#8217;ll see the recommendations shown in Figure 3 where a higher score means a better match.</p>
<div id="attachment_553" class="wp-caption alignnone" style="width: 310px"><a href="http://leanjavaengineering.files.wordpress.com/2012/06/figure3.png"><img src="http://leanjavaengineering.files.wordpress.com/2012/06/figure3.png?w=300&#038;h=102" alt="" title="figure3" width="300" height="102" class="size-medium wp-image-553" /></a><p class="wp-caption-text">Figure 3: Recommendations</p></div>
<h2>What next?</h2>
<p>The <i>libimseti</i> sample application is good to prove that the theory works on a reasonably sized dataset, but not many of us are going to want to curate our data to match an input file. More realistically we&#8217;ll have an application that has associations between users and items or allows users to rate items. </p>
<p>We&#8217;ll build a simple Grails implementation &#8211; the source code is available on GitHub from <a href="https://github.com/rbramley/GroovyMagMahout">https://github.com/rbramley/GroovyMagMahout</a>
</p>
<h3>The data model</h3>
<p>As a simplification for this exercise we will use a single <i>preference</i> table, this will represent the link (many-to-many join) table between a user and an item as illustrated in ERD notation in Figure 4.</p>
<div id="attachment_554" class="wp-caption alignnone" style="width: 510px"><a href="http://leanjavaengineering.files.wordpress.com/2012/06/figure4.png"><img src="http://leanjavaengineering.files.wordpress.com/2012/06/figure4.png?w=500&#038;h=114" alt="" title="figure4" width="500" height="114" class="size-full wp-image-554" /></a><p class="wp-caption-text">Figure 4: Sample ERD</p></div>
<p>This table will have a composite primary key comprising of the user and items IDs and then a value rating the strength of the preference. For the preference we&#8217;ll use a 1 to 5 range as this may be represented by a rating widget (such as that provided by the Grails Rich UI plugin).</p>
<p>We&#8217;ll create a domain class (using <code>grails create-domain-class com.rbramley.mahout.Preference</code>) and specify it as per Listing 4. Note that we&#8217;ve used a composite key to satisfy Mahout&#8217;s needs, but this exposes a view minor Grails issues with the generated default controller and views (<code>grails generate-all com.rbramley.mahout.Preference</code>) not being composite-key aware (this shouldn&#8217;t affect you unless you want to edit/delete preference records).</p>
<p><code><br />
package com.rbramley.mahout<br />
&nbsp;<br />
import org.apache.commons.lang.builder.HashCodeBuilder<br />
&nbsp;<br />
class Preference implements Serializable {<br />
&nbsp;&nbsp;&nbsp; long userId<br />
&nbsp;&nbsp;&nbsp; long itemId<br />
&nbsp;&nbsp;&nbsp; float prefValue<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp; static constraints = {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; userId()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; itemId()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; prefValue range: 0.0f..5.0f<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp; boolean equals(other) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(!(other instanceof Preference)) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return false<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; other.userId == userId &amp;&amp; other.itemId == itemId<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp; int hashCode() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def builder = new HashCodeBuilder()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; builder.append userId<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; builder.append itemId<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; builder.toHashCode()<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp; static mapping = {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; id composite: ['userId', 'itemId']<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;version false<br />
&nbsp;&nbsp;&nbsp; }<br />
}<br />
</code></p>
<p><strong>Listing 4:</strong> Domain class</p>
<p>We&#8217;ll use MySQL for the database, as that Mahout DataModel provider implementation is supported by the Grails plugin (Mahout also has Postgres and generic JDBC implementations).</p>
<p>Firstly we need to create the target schema in MySQL (Listing 5).</p>
<p><code><br />
mysql -u root -p<br />
mysql&gt; create database recommender;<br />
mysql&gt; grant all on recommender.* to recommender@localhost identified by 'mahoutdemo';<br />
</code></p>
<p><strong>Listing 5:</strong> MySQL commands</p>
<p>With that done we can uncomment the <code>runtime mysql-connector-java</code> dependency in <i>BuildConfig.groovy</i> and then configure <i>DataSource.groovy</i> accordingly (Listing 6).</p>
<p><code><br />
&nbsp;&nbsp;&nbsp; development {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dataSource {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; driverClassName = &quot;com.mysql.jdbc.Driver&quot;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dbCreate = &quot;create-drop&quot; // one of 'create', 'create-drop', 'update', 'validate'<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; url = &quot;jdbc:mysql://localhost:3306/recommender&quot;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; username = &quot;recommender&quot;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; password = &quot;mahoutdemo&quot;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
</code></p>
<p>Listing 6:</strong> Development data source configuration</p>
<h3>Reconfiguring the plugin</h3>
<p>We now need to reconfigure <i>Config.groovy</i> to instruct the plugin which recommender and similarity algorithms to use and where to obtain the data from, this is achieved using the settings in Listing 7.</p>
<p><code><br />
mahout.recommender.mode = 'config'&nbsp; // 'input', 'config' or 'class'<br />
mahout.recommender.hasPreference = true<br />
mahout.recommender.selected = 1 // user-based<br />
mahout.recommender.similarity = 'PearsonCorrelation'<br />
mahout.recommender.withWeighting = false<br />
mahout.recommender.neighborhood = 2<br />
mahout.recommender.data.model = 'mysql'<br />
mahout.recommender.preference.table = 'preference'<br />
mahout.recommender.preference.valueColumn = 'pref_value'<br />
</code></p>
<p><strong>Listing 7:</strong> Plugin configuration</p>
<h3>Providing data</h3>
<p>We can now run the application, select the new <i>com.rbramley.mahout.PreferenceController</i> and enter some values for our data.</p>
<p>If you enter the data set shown in Figure 5, then when you use the recommendations controller to obtain recommendations for user ID 1, you should get the recommendations of 104 and 106 as shown in Figure 6.</p>
<div id="attachment_555" class="wp-caption alignnone" style="width: 507px"><a href="http://leanjavaengineering.files.wordpress.com/2012/06/figure5.png"><img src="http://leanjavaengineering.files.wordpress.com/2012/06/figure5.png?w=500" alt="" title="figure5"   class="size-full wp-image-555" /></a><p class="wp-caption-text">Figure 5: Sample data values</p></div>
<div id="attachment_556" class="wp-caption alignnone" style="width: 310px"><a href="http://leanjavaengineering.files.wordpress.com/2012/06/figure6.png"><img src="http://leanjavaengineering.files.wordpress.com/2012/06/figure6.png?w=300&#038;h=98" alt="" title="figure6" width="300" height="98" class="size-medium wp-image-556" /></a><p class="wp-caption-text">Figure 6: Recommendations for User Id 1</p></div>
<p>Alternatively there is a SQL script within the project on GitHub that can be run to seed the preferences table with similar data (based on listing 2.1 from Mahout in Action).</p>
<h1>Evaluating recommenders</h1>
<p>The Grails plugin features a built in recommender evaluator based on average difference, in our case we can access it at <a href="http://localhost:8080/GroovyMagMahout/recommender/evaluator">http://localhost:8080/GroovyMagMahout/recommender/evaluator</a> and click on the &#8216;Run Evaluator&#8217; link, the sample output is shown in Figure 7. </p>
<p>A lower difference is better &#8211; so you may want to experiment with changing the <code>mahout.recommender.similarity</code> property that we set in Listing 7, valid values are &#8216;PearsonCorrelation&#8217;, &#8216;EuclideanDistance&#8217;, &#8216;LogLikelihood&#8217; or &#8216;TanimotoCoefficient&#8217;.</p>
<div id="attachment_557" class="wp-caption alignnone" style="width: 510px"><a href="http://leanjavaengineering.files.wordpress.com/2012/06/figure7.png"><img src="http://leanjavaengineering.files.wordpress.com/2012/06/figure7.png?w=500&#038;h=470" alt="" title="figure7" width="500" height="470" class="size-full wp-image-557" /></a><p class="wp-caption-text">Figure 7: Recommender evaluation</p></div>
<p>Likewise you may want to modify other properties such as applying weighting or adjusting the size of the neighbourhood &#8211; in any case please refer to the configuration section of the plugin manual at <a href="http://limcheekin.github.com/mahout-recommender/docs/manual/guide/configuration.html">http://limcheekin.github.com/mahout-recommender/docs/manual/guide/configuration.html</a></p>
<h1>Summary</h1>
<p>This article has introduced Apache Mahout, an open source scalable machine learning framework, and shown how you can utilise it to provide personal recommendations within a Grails application. We&#8217;ve seen custom recommendations for the <i>libimseti</i> sample data files and recommendations based on user similarity on top of a Grails domain class. In practice these recommenders would ideally be invoked asynchronously particularly for large data sets, this could be achieved using AJAX techniques.</p>
<p>Have fun integrating stylised recommendations into your application, but remember it&#8217;s good to allow the users to give feedback on the relevancy of the recommendations!</p>
<h1>References / further reading</h1>
<p>The following provide valuable sources of information:</p>
<ul>
<li><a href="http://mahout.apache.org/">http://mahout.apache.org</a></li>
<li><a href="https://cwiki.apache.org/MAHOUT/recommender-documentation.html">https://cwiki.apache.org/MAHOUT/recommender-documentation.html</a></li>
<li><a href="http://grails.org/plugin/mahout-recommender">http://grails.org/plugin/mahout-recommender</a></li>
<li><a href="http://limcheekin.github.com/mahout-recommender/docs/manual/guide/single.html">http://limcheekin.github.com/mahout-recommender/docs/manual/guide/single.html</a></li>
<li><a href="http://www.manning.com/owen/">http://www.manning.com/owen/</a> &#8211; Mahout in Action</li>
</ul>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/leanjavaengineering.wordpress.com/549/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/leanjavaengineering.wordpress.com/549/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=leanjavaengineering.wordpress.com&#038;blog=15402167&#038;post=549&#038;subd=leanjavaengineering&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://leanjavaengineering.wordpress.com/2012/06/20/grails-mahout-recommenders/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://2.gravatar.com/avatar/ba996199c3dd2c7095b45f3a645c52ef?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">leanjavaengineering</media:title>
		</media:content>

		<media:content url="http://leanjavaengineering.files.wordpress.com/2012/06/figure1.png" medium="image">
			<media:title type="html">figure1</media:title>
		</media:content>

		<media:content url="http://leanjavaengineering.files.wordpress.com/2012/06/figure2.png" medium="image">
			<media:title type="html">figure2</media:title>
		</media:content>

		<media:content url="http://leanjavaengineering.files.wordpress.com/2012/06/figure3.png?w=300" medium="image">
			<media:title type="html">figure3</media:title>
		</media:content>

		<media:content url="http://leanjavaengineering.files.wordpress.com/2012/06/figure4.png" medium="image">
			<media:title type="html">figure4</media:title>
		</media:content>

		<media:content url="http://leanjavaengineering.files.wordpress.com/2012/06/figure5.png" medium="image">
			<media:title type="html">figure5</media:title>
		</media:content>

		<media:content url="http://leanjavaengineering.files.wordpress.com/2012/06/figure6.png?w=300" medium="image">
			<media:title type="html">figure6</media:title>
		</media:content>

		<media:content url="http://leanjavaengineering.files.wordpress.com/2012/06/figure7.png" medium="image">
			<media:title type="html">figure7</media:title>
		</media:content>
	</item>
		<item>
		<title>Quick tip: Writing requirement statements</title>
		<link>http://leanjavaengineering.wordpress.com/2012/06/18/requirements-tips/</link>
		<comments>http://leanjavaengineering.wordpress.com/2012/06/18/requirements-tips/#comments</comments>
		<pubDate>Mon, 18 Jun 2012 19:45:41 +0000</pubDate>
		<dc:creator>rbramley</dc:creator>
				<category><![CDATA[Process]]></category>

		<guid isPermaLink="false">http://leanjavaengineering.wordpress.com/?p=543</guid>
		<description><![CDATA[Structure The &#8216;As a &#60;user type&#62;, I want to &#60;action to be performed&#62;, so that &#60;business benefit&#62;&#8216; user story structure encourages good requirements but can often be abused! If you&#8217;re tasked with writing user stories or requirements, then I suggest &#8230; <a href="http://leanjavaengineering.wordpress.com/2012/06/18/requirements-tips/">Continue reading <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=leanjavaengineering.wordpress.com&#038;blog=15402167&#038;post=543&#038;subd=leanjavaengineering&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<h3>Structure</h3>
<p>The &#8216;<strong>As a</strong> <em>&lt;user type&gt;</em>, <strong>I want to</strong> <em>&lt;action to be performed&gt;</em>, <strong>so that</strong> <em>&lt;business benefit&gt;</em>&#8216; user story structure encourages good requirements but can often be abused!</p>
<p>If you&#8217;re tasked with writing user stories or requirements, then I suggest that you read the Open Unified Process documentation guidance on &#8216;<a href="http://epf.eclipse.org/wikis/openup/core.tech.common.extend_supp/guidances/guidelines/writing_good_requirements_48248536.html?nodeId=9286e852">Writing good requirements</a>&#8216;:</p>
<ul>
<li>Define one requirement at a time.</li>
<li>Avoid conjunctions (and, or) that make multiple requirements.</li>
<li>Avoid let-out clauses or words that imply options or exceptions (unless, except, if necessary, but).</li>
<li>Use simple, direct sentences.</li>
<li>Use a limited (500-word) vocabulary, especially if your audience is international.</li>
<li>Identify the type of user who needs each requirement.</li>
<li>Focus on stating what result you will provide for that type of user.</li>
<li>Define verifiable criteria.</li>
</ul>
<p>(see the OpenUP wiki link for the introduction and examples)</p>
<h3>Numbering</h3>
<p>Or rather: <em><strong>numbering</strong> schemes with respect to grouping and ordering</em></p>
<p><strong>For traceability it is critical for a requirement to have a unique identifier</strong>. No arguments on that front, but the challenge comes when people get to choose their own numbering scheme for requirements.</p>
<p><em>e.g.</em></p>
<ol>
<li>First requirement</li>
<li>Second requirement</li>
<li>Third requirement</li>
</ol>
<p>All good so far you might think &#8211; it&#8217;s exactly the way they be numbered if I&#8217;d entered them in an issue tracker.<br />
Well let&#8217;s suppose that at the first pass that the list is in a spreadsheet, was collated by functional area and contains over 50 items.</p>
<p>It can easily go awry on subsequent iterations (e.g. after review cycles, prioritisation meetings etc.) when people haven&#8217;t written good requirements (see above) or you have epics that need splitting. At this point a system would append a row which is given a sequentially generated primary key, whereas with a spreadsheet the natural inclination is to insert a row into the table which then introduces the numbering dilemma.</p>
<p>There are 3 common behaviours:</p>
<ul>
<li>Generations of people trained in using numbered headings in documents will typically go with the <em>nesting instinct</em> e.g. 2.1.3 </li>
<li>Information workers who&#8217;ve dealt with numbering schemes such as Dewey Decimal might have used non-contiguous numbering in the first place e.g. start each new functional area at the next 100 &#8211; which may prevent or just defer the issue</li>
<li>Data modellers may opt for the foreign key approach for splits and use other columns for grouping/sorting.</li>
</ul>
<p>I&#8217;d encourage the latter approach, append to the table then re-sort later and don&#8217;t get hung up on having beautifully ordered reference numbers. After all you&#8217;re not doing <a href="http://www.agilemodeling.com/essays/examiningBRUF.htm">Big Requirements Up Front</a> are you?</p>
<h3>Final thought</h3>
<blockquote><p>&#8220;Adding power makes you faster on the straights. Subtracting weight makes you faster everywhere&#8221; &#8211; Colin Chapman</p></blockquote>
<p>Think about the implications of this philosophy when writing and prioritising requirements!</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/leanjavaengineering.wordpress.com/543/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/leanjavaengineering.wordpress.com/543/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=leanjavaengineering.wordpress.com&#038;blog=15402167&#038;post=543&#038;subd=leanjavaengineering&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://leanjavaengineering.wordpress.com/2012/06/18/requirements-tips/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://2.gravatar.com/avatar/ba996199c3dd2c7095b45f3a645c52ef?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">leanjavaengineering</media:title>
		</media:content>
	</item>
		<item>
		<title>Monitoring MarkLogic</title>
		<link>http://leanjavaengineering.wordpress.com/2012/06/06/opsview-marklogic/</link>
		<comments>http://leanjavaengineering.wordpress.com/2012/06/06/opsview-marklogic/#comments</comments>
		<pubDate>Wed, 06 Jun 2012 21:07:39 +0000</pubDate>
		<dc:creator>rbramley</dc:creator>
				<category><![CDATA[How-to]]></category>
		<category><![CDATA[Monitoring]]></category>
		<category><![CDATA[Opsview]]></category>

		<guid isPermaLink="false">http://leanjavaengineering.wordpress.com/?p=536</guid>
		<description><![CDATA[This is a quick how-to post for Opsview users who have a need to monitor MarkLogic. The good news is that MarkLogic have released a Nagios plugin. The reference manual is available from http://developer.marklogic.com/pubs/5.0/books/monitoring.pdf (see chapter 3 for Nagios specifics) &#8230; <a href="http://leanjavaengineering.wordpress.com/2012/06/06/opsview-marklogic/">Continue reading <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=leanjavaengineering.wordpress.com&#038;blog=15402167&#038;post=536&#038;subd=leanjavaengineering&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p>This is a quick how-to post for <a href="http://www.opsview.com">Opsview</a> users who have a need to monitor MarkLogic.<br />
<span id="more-536"></span></p>
<p>The good news is that MarkLogic have released a <a href="http://developer.marklogic.com/products/nagios">Nagios plugin</a>.</p>
<p>The reference manual is available from <a href="http://developer.marklogic.com/pubs/5.0/books/monitoring.pdf">http://developer.marklogic.com/pubs/5.0/books/monitoring.pdf</a> (see chapter 3 for Nagios specifics) and the plugin itself from <a href="http://developer.marklogic.com/download/binaries/nagios/MarkLogic-Nagios-Plugin-1.0-1.tar">http://developer.marklogic.com/download/binaries/nagios/MarkLogic-Nagios-Plugin-1.0-1.tar</a></p>
<h2>Installing the plugin</h2>
<p>You&#8217;ll only need to follow the first 3 steps of section 3.4 of the <em>Monitoring MarkLogic Guide</em>, in essence:</p>
<ol>
<li>Unzip the plugin tar</li>
<li>Copy/move check_marklogic.pl to <code>/usr/local/nagios/libexec</code></li>
<li><code>chmod +x check_marklogic.pl</code></li>
</ol>
<h2>Setting up a service check</h2>
<p>Once the plugin has been installed we can define service checks, Figure 1 shows two simple examples (using a clean install of MarkLogic plus the Hadoop Connector &#8211; maybe a topic for another blog post?).</p>
<div id="attachment_537" class="wp-caption alignnone" style="width: 510px"><a href="https://leanjavaengineering.files.wordpress.com/2012/06/opsview_marklogic_check_definitions.png"><img src="https://leanjavaengineering.files.wordpress.com/2012/06/opsview_marklogic_check_definitions.png?w=500&#038;h=90" alt="" title="Opsview_MarkLogic_check_definitions" width="500" height="90" class="size-full wp-image-537" /></a><p class="wp-caption-text">Figure 1 &#8211; Simple MarkLogic check definitions</p></div>
<p>You would normally want to set up finer-grained service checks with thresholds &#8211; consequently the plugin accepts additional arguments (but doesn&#8217;t support the <em>de facto</em> help argument) to specify keys (<code>-k</code>), warning (<code>-w</code>) and critical (<code>-c</code>) thresholds. Note that the thresholds can use an operator (<code>-op</code>) argument. These arguments are fully detailed in section <em>3.5.2.3</em> of the <em>Monitoring MarkLogic Guide</em>. </p>
<h2>Checks in action</h2>
<p>Figure 2 shows the host-level view of the service checks, with the detail of the performance data behind them shown in Figures 3 and 4.</p>
<div id="attachment_538" class="wp-caption alignnone" style="width: 510px"><a href="https://leanjavaengineering.files.wordpress.com/2012/06/opsview_marklogic_checks.png"><img src="https://leanjavaengineering.files.wordpress.com/2012/06/opsview_marklogic_checks.png?w=500&#038;h=88" alt="" title="Opsview_MarkLogic_checks" width="500" height="88" class="size-full wp-image-538" /></a><p class="wp-caption-text">Figure 2 &#8211; MarkLogic service check summaries</p></div>
<div id="attachment_539" class="wp-caption alignnone" style="width: 510px"><a href="https://leanjavaengineering.files.wordpress.com/2012/06/opsview_marklogic_document_counts_check_results.png"><img src="https://leanjavaengineering.files.wordpress.com/2012/06/opsview_marklogic_document_counts_check_results.png?w=500&#038;h=67" alt="" title="Opsview_MarkLogic_document_counts_check_results" width="500" height="67" class="size-full wp-image-539" /></a><p class="wp-caption-text">Figure 3 &#8211; Document count service check detailed results</p></div>
<div id="attachment_540" class="wp-caption alignnone" style="width: 510px"><a href="https://leanjavaengineering.files.wordpress.com/2012/06/opsview_marklogic_status_check_results.png"><img src="https://leanjavaengineering.files.wordpress.com/2012/06/opsview_marklogic_status_check_results.png?w=500&#038;h=171" alt="" title="Opsview_MarkLogic_status_check_results" width="500" height="171" class="size-full wp-image-540" /></a><p class="wp-caption-text">Figure 4 &#8211; Status service check detailed results</p></div>
<p>There you go, quick and easy basic monitoring of MarkLogic.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/leanjavaengineering.wordpress.com/536/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/leanjavaengineering.wordpress.com/536/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=leanjavaengineering.wordpress.com&#038;blog=15402167&#038;post=536&#038;subd=leanjavaengineering&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://leanjavaengineering.wordpress.com/2012/06/06/opsview-marklogic/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://2.gravatar.com/avatar/ba996199c3dd2c7095b45f3a645c52ef?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">leanjavaengineering</media:title>
		</media:content>

		<media:content url="https://leanjavaengineering.files.wordpress.com/2012/06/opsview_marklogic_check_definitions.png" medium="image">
			<media:title type="html">Opsview_MarkLogic_check_definitions</media:title>
		</media:content>

		<media:content url="https://leanjavaengineering.files.wordpress.com/2012/06/opsview_marklogic_checks.png" medium="image">
			<media:title type="html">Opsview_MarkLogic_checks</media:title>
		</media:content>

		<media:content url="https://leanjavaengineering.files.wordpress.com/2012/06/opsview_marklogic_document_counts_check_results.png" medium="image">
			<media:title type="html">Opsview_MarkLogic_document_counts_check_results</media:title>
		</media:content>

		<media:content url="https://leanjavaengineering.files.wordpress.com/2012/06/opsview_marklogic_status_check_results.png" medium="image">
			<media:title type="html">Opsview_MarkLogic_status_check_results</media:title>
		</media:content>
	</item>
		<item>
		<title>Using Lucene in Grails</title>
		<link>http://leanjavaengineering.wordpress.com/2012/02/24/using-lucene-in-grails/</link>
		<comments>http://leanjavaengineering.wordpress.com/2012/02/24/using-lucene-in-grails/#comments</comments>
		<pubDate>Fri, 24 Feb 2012 16:51:50 +0000</pubDate>
		<dc:creator>rbramley</dc:creator>
				<category><![CDATA[How-to]]></category>
		<category><![CDATA[Search]]></category>
		<category><![CDATA[compass]]></category>
		<category><![CDATA[elasticsearch]]></category>
		<category><![CDATA[grails]]></category>
		<category><![CDATA[GroovyMag]]></category>
		<category><![CDATA[lucene]]></category>
		<category><![CDATA[solr]]></category>

		<guid isPermaLink="false">http://leanjavaengineering.wordpress.com/?p=518</guid>
		<description><![CDATA[Apache Lucene is the leading open source search engine and is used in many businesses, projects and products. Lucene has sub-projects which provide additional functionality such as the Nutch web crawler and the Solr search service. This article gives an &#8230; <a href="http://leanjavaengineering.wordpress.com/2012/02/24/using-lucene-in-grails/">Continue reading <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=leanjavaengineering.wordpress.com&#038;blog=15402167&#038;post=518&#038;subd=leanjavaengineering&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p>Apache Lucene is the leading open source search engine and is used in many businesses, projects and products. Lucene has sub-projects which provide additional functionality such as the Nutch web crawler and the Solr search service. This article gives an introduction to Lucene, a tutorial on three Grails Lucene plugins and a comparison between them.</p>
<blockquote><p>This article originally appeared in the <a href="http://www.groovymag.com/main.issues.description/id=37/">September 2011 edition of GroovyMag</a>.</p></blockquote>
<p><span id="more-518"></span></p>
<h1>Lucene</h1>
<p>Apache Lucene Core provides a Java-based indexing and search implementation. In essence, the index is made up of documents that are comprised of fields. </p>
<h2>Indexing</h2>
<p>When you index a document, the fields are processed through a tokenizer to break it into terms. Figure 1 shows the effect a whitespace tokenizer would have on splitting &#8220;Mary had a little lamb&#8221; into the tokens: Mary, had, a, little, lamb.</p>
<div id="attachment_521" class="wp-caption alignnone" style="width: 310px"><a href="http://leanjavaengineering.files.wordpress.com/2012/02/figure1.png"><img src="http://leanjavaengineering.files.wordpress.com/2012/02/figure1.png?w=300&#038;h=74" alt="" title="Figure 1: Whitespace tokenization" width="300" height="74" class="size-medium wp-image-521" /></a><p class="wp-caption-text">Figure 1: Whitespace tokenization</p></div>
<p>The terms may undergo further analysis through TokenFilter classes. Figure 2 shows a stop words filter removing common terms that would skew relevancy.</p>
<div id="attachment_522" class="wp-caption alignnone" style="width: 475px"><a href="http://leanjavaengineering.files.wordpress.com/2012/02/figure2.png"><img src="http://leanjavaengineering.files.wordpress.com/2012/02/figure2.png?w=500" alt="" title="Figure 2: Stopwords"   class="size-full wp-image-522" /></a><p class="wp-caption-text">Figure 2: Stopwords</p></div>
<p>Figure 3 shows the PorterStemFilter applying the (English) Porter stemming algorithm to reduce tokens to their word stems so that they are equated. This TokenFilter needs to work on lower case input &#8211; so the LowerCaseFilter / Tokenizer should be used before the stemming.</p>
<div id="attachment_523" class="wp-caption alignnone" style="width: 364px"><a href="http://leanjavaengineering.files.wordpress.com/2012/02/figure3.png"><img src="http://leanjavaengineering.files.wordpress.com/2012/02/figure3.png?w=500" alt="" title="Figure 3: Word stemming"   class="size-full wp-image-523" /></a><p class="wp-caption-text">Figure 3: Word stemming</p></div>
<p>Lastly, the terms are then mapped to their documents as shown in Figure 4.</p>
<div id="attachment_524" class="wp-caption alignnone" style="width: 428px"><a href="http://leanjavaengineering.files.wordpress.com/2012/02/figure4.png"><img src="http://leanjavaengineering.files.wordpress.com/2012/02/figure4.png?w=500" alt="" title="Figure 4: Term index"   class="size-full wp-image-524" /></a><p class="wp-caption-text">Figure 4: Term index</p></div>
<h2>Index updates</h2>
<p>Updates to index documents are handled as a delete operation followed by an add operation. Over time the index segments can become fragmented &#8211; this can be cured by running an optimization operation to pack the index.</p>
<h2>Querying</h2>
<p>The queries in Lucene need to pass through the same analyzers as were used during indexing &#8211; otherwise identical terms might not match.<br />
A single word query (<code>TermQuery</code>) requires a lookup in the term index to return the matching documents.</p>
<p> e.g. querying the term index shown in Figure 4 for &#8216;consignment&#8217; would return documents 1, 4 and 7.</p>
<p>A two word query (<code>BooleanQuery</code>) requires Lucene to perform two lookups in the term index then filter the resulting documents on either an AND or an OR basis (from an explicit or default operator). e.g. querying the Figure 4 term index for &#8216;consign AND ship&#8217; would return document 4, whereas &#8216;consign OR ship&#8217; would return documents 1, 4 and 7.</p>
<p>A phrase query is denoted by double quotes (e.g. &#8220;new york&#8221;) and matches documents containing a particular sequence of terms. This relies on positional information in the index. Phrase slop, or proximity, is specified using the ~ operator and an integer to specify how close the terms in the phrase need to be together. e.g. &#8220;big banana&#8221;~5 would match documents containing &#8220;big banana&#8221;, &#8220;big green banana&#8221; and &#8220;big straight yellow banana&#8221;</p>
<p>By default results are returned in relevancy order, and the score is calculated by a formula (if you&#8217;re really interested it is in the JavaDoc for the Similarity class &#8211; <a href="http://lucene.apache.org/java/3_3_0/api/core/org/apache/lucene/search/Similarity.html">http://lucene.apache.org/java/3_3_0/api/core/org/apache/lucene/search/Similarity.html</a>). The score can be influenced by boosting terms. e.g. the query &#8216;subject:lucene OR author:bramley^2&#8242; would boost the score contribution of the author field two-fold for documents whose author fields contained &#8216;bramley&#8217;.</p>
<h2>Tools</h2>
<p>Before we get onto the practical application it is worth mentioning that Luke, the Lucene Index Toolbox, is an invaluable tool for allowing inspection, searching and browsing of Lucene indices.</p>
<p>It is available from <a href="http://code.google.com/p/luke/">http://code.google.com/p/luke/</a> &#8211; but be aware that you need to use the right version to match the version of Lucene that created your indices. This has been made easier and more obvious as the version numbers of Luke now correspond directly with Lucene versions (e.g. Luke 3.3.0 is for Lucene 3.3.0, however Luke 0.9.9.1 is based on Lucene 2.9.1).</p>
<h1>Implementing in Grails</h1>
<p>Rather than a Twitter-style application, we&#8217;ll build a simple To-Do list application with one domain class to represent the to-do Item, shown in Listing 1, which will use a generated controller and views (you may choose to enable scaffolding instead).</p>
<p>We&#8217;ll use this along with the Figure 5 test data in 3 similar applications to showcase the different Lucene-related Grails plugins. All the sample applications are available on GitHub under <a href="https://github.com/rbramley/GroovyMagLucene">https://github.com/rbramley/GroovyMagLucene</a></p>
<p><code><br />
package com.rbramley.todo<br />
&nbsp;<br />
class Item {<br />
&nbsp;&nbsp;&nbsp;&nbsp;Date dateCreated<br />
&nbsp;&nbsp;&nbsp;&nbsp;String subject<br />
&nbsp;&nbsp;&nbsp;&nbsp;Date startDate<br />
&nbsp;&nbsp;&nbsp;&nbsp;Date dueDate<br />
&nbsp;&nbsp;&nbsp;&nbsp;String status<br />
&nbsp;&nbsp;&nbsp;&nbsp;String priority<br />
&nbsp;&nbsp;&nbsp;&nbsp;boolean completed = false<br />
&nbsp;&nbsp;&nbsp;&nbsp;int percentComplete = 0<br />
&nbsp;&nbsp;&nbsp;&nbsp;String body<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;static constraints = {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;subject(blank:false, nullable:false)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;startDate()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dueDate()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;status(inList:["Not Started","In Progress","Completed","Waiting on someone else","Deferred"])<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;priority(inList:["Low","Normal","High"])<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;completed()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;percentComplete(range:0..100)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;body(nullable:true, maxSize:1000)<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
</code><br />
<em>Listing 1: the Item domain class</em></p>
<div id="attachment_525" class="wp-caption alignnone" style="width: 238px"><a href="http://leanjavaengineering.files.wordpress.com/2012/02/figure5.png"><img src="http://leanjavaengineering.files.wordpress.com/2012/02/figure5.png?w=228&#038;h=300" alt="" title="Figure 5: Test data" width="228" height="300" class="size-medium wp-image-525" /></a><p class="wp-caption-text">Figure 5: Test data</p></div>
<h2>Searchable plugin</h2>
<p>The Searchable plugin makes use of Compass::GPS to index Hibernate domain objects using GORM/Hibernate lifecycle events. The plugin also provides a default search controller and view. The aim, which lives up to Marc Palmer&#8217;s recommendations, is to make full text searching of your domain objects as simple as possible.</p>
<p>The first step is to install the plugin using <code>grails install-plugin searchable</code>.</p>
<p>Once we&#8217;ve created the domain class (<code>grails create-domain-class com.rbramley.todo.Item</code>) and populated it with the code from Listing 1, we then add a static searchable property to the domain class: <code>static searchable = [except: 'dateCreated']</code></p>
<p>Running the application, select the com.rbramley.todo.ItemController link from the home page, and enter the test data from Figure 5.</p>
<p>Return to the home page and then select the grails.plugin.searchable.SearchableController link.</p>
<p>Enter the query &#8216;timesheet&#8217; in the search box then click the &#8216;Search&#8217; button &#8211; this will give you the results as shown in Figure 6.</p>
<div id="attachment_526" class="wp-caption alignnone" style="width: 310px"><a href="http://leanjavaengineering.files.wordpress.com/2012/02/figure6.png"><img src="http://leanjavaengineering.files.wordpress.com/2012/02/figure6.png?w=300&#038;h=94" alt="" title="Figure 6: Searchable results" width="300" height="94" class="size-medium wp-image-526" /></a><p class="wp-caption-text">Figure 6: Searchable results</p></div>
<p>There you have it &#8211; quick and easy full text search on your domain model. Whilst the style may not match with our application, at least it gives us something to work from.</p>
<p>Note that the plugin stores the indices under ~/.grails/projects/<em>&lt;project-name&gt;</em>/searchable-index/<em>&lt;environment&gt;</em></p>
<h2>Solr plugin</h2>
<p>Version 0.2 of the plugin was released in January 2010, is compatible with Grails 1.1+ and bundles an old 1.4 release of Solr and SolrJ. It would be nice to see a new version of this plugin with a current Solr release and updated for the newer dependency resolution mechanisms &#8211; if you&#8217;ve got time to help out you can fork it on GitHub at <a href="http://github.com/mbrevoort/grails-solr-plugin">http://github.com/mbrevoort/grails-solr-plugin</a></p>
<p>Once you&#8217;ve installed the plugin using &#8216;<code>grails install-plugin solr</code>&#8216;, the &#8216;<code>grails start-solr</code>&#8216; script will start up a default Solr instance using Jetty. This is located in &#8216;<code>solr-home</code>&#8216; under the project working directory (based on the Grails build settings) using the example Solr schema and configuration. Whilst this example configuration is fine for evaluation, I (and Lucid Imagination) would strongly recommend you don&#8217;t use this configuration in production. For instance, you&#8217;ll need to modify this if you want to use the dismax handler (at which point you&#8217;re probably better off with a separate installation and version controlled configuration).</p>
<p>The plugin makes good use of convention (leveraging Solr dynamic fields) and meta-programming to add methods to domain classes.</p>
<h3>How does it do auto-indexing?</h3>
<p>If you check out the plugin <code>doWithApplicationContext</code> closure, you will see that a listener is registered for the post- insert/update/delete events.</p>
<h3>What about search?</h3>
<p>This is handled by the SolrService which uses the SolrJ client and constructs a simple query (here it would be nice to leverage dismax). However the plugin author (Mike Brevoort) has also helpfully provided the ability to construct your own advanced query and supply that to the SolrService.</p>
<p>Now we&#8217;ve uncovered how it works out of the box &#8211; it&#8217;s time to write some code so we can take it for a test drive.</p>
<p>The plugin can be installed by <code>grails install-plugin solr</code>.</p>
<p>Again we&#8217;ll use the domain class from Listing 1, but this time adding the two properties, <code>static enableSolrSearch = true</code> and <code>static solrAutoIndex = true</code></p>
<p>Once the domain class is completed we can generate the controllers &amp; views:</p>
<p><code>grails generate-all com.rbramley.todo.Item</code></p>
<p>We&#8217;ll add a search box to the main.gsp layout. This is shown in Listing 2 and styling has been left as an exercise for the reader (note you can supply an image for the search button). This submits to the search controller (created using <code>grails create-controller com.rbramley.todo.Search</code> &#8211; and populated by Listing 3) with the search.gsp displaying the results (Listing 4 shows a fragment).</p>
<p><code><br />
&lt;div&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;strong&gt;Quick search&lt;/strong&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;g:form url='[controller: "search", action: "search"]' id="searchForm" name="searchForm" method="get"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;input type="text"  name="query" value="${query ?: 'Keywords'}" onClick="javascript:if (this.value=='Keywords') { this.value='' }"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;input type="image" src="${resource(dir:'images',file:'go_quick_search.png')}"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/g:form&gt;<br />
&lt;/div&gt;<br />
</code><br />
<em>Listing 2: main.gsp search box</em></p>
<p><code><br />
package com.rbramley.todo<br />
&nbsp;<br />
class SearchController {<br />
&nbsp;&nbsp;&nbsp;&nbsp;def solrService<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;def search = {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def res = solrService.search("${params.query}")<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[query:params.query, total:res.total, searchResults:res.resultList]<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
</code><br />
<em>Listing 3: Search controller code</em></p>
<p><code><br />
&lt;g:if test="${haveResults}"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;g:each var="result" in="${searchResults}"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;g:set var="className" value="${ClassUtils.getShortName(result.doctype_s)}" /&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;li&gt;&lt;solr:resultLink result="${result}"&gt;${className}: ${result.id}&lt;/solr:resultLink&gt;&lt;/li&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;${result.subject?.encodeAsHTML()}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/g:each&gt;<br />
&lt;/g:if&gt;<br />
</code><br />
<em>Listing 4: Results page fragment</em></p>
<p>Before you start the Grails application, you need to start Solr using <code>grails start-solr</code>. </p>
<p>Once Solr and then Grails have started, we can create an item using the Figure 5 test data, then search for it using &#8216;timesheet&#8217;.</p>
<h3>Why are there no results?</h3>
<p>The plugin doc states that it will search against the default Solr field (which by default is called &#8216;text&#8217;). </p>
<p>Trying again: subject_s:timesheet also returns no results&#8230;</p>
<p>So let&#8217;s try a phrase query: subject_s:&#8221;Complete timesheet&#8221; as shown in Figure 7 &#8211; this one works because we&#8217;ve supplied the whole string to match, this is due to<br />
<strong>&#8220;The StrField type is not analyzed, but indexed/stored verbatim.&#8221;</strong></p>
<div id="attachment_527" class="wp-caption alignnone" style="width: 433px"><a href="http://leanjavaengineering.files.wordpress.com/2012/02/figure7.png"><img src="http://leanjavaengineering.files.wordpress.com/2012/02/figure7.png?w=500" alt="" title="Figure 7: Solr with fully specified query"   class="size-full wp-image-527" /></a><p class="wp-caption-text">Figure 7: Solr with fully specified query</p></div>
<p>Figure 8 shows the result of inspecting the subject field using Solr&#8217;s Luke request handler (<a href="http://localhost:8983/solr/admin/luke">http://localhost:8983/solr/admin/luke</a>). </p>
<div id="attachment_528" class="wp-caption alignnone" style="width: 345px"><a href="http://leanjavaengineering.files.wordpress.com/2012/02/figure8.png"><img src="http://leanjavaengineering.files.wordpress.com/2012/02/figure8.png?w=500" alt="" title="Figure 8: Solr Luke analysis of subject field"   class="size-full wp-image-528" /></a><p class="wp-caption-text">Figure 8: Solr Luke analysis of subject field</p></div>
<p>So to get the desired results we&#8217;ll need to do some Solr configuration, and we have the following options:</p>
<ol>
<li>Map the fields to text type so that they are analyzed (can also be forced to text through annotations) and use copy field directives to copy them to the default text field</li>
<li>Configure a dismax handler (beyond the scope of this article) to have more flexible control over the fields that are queried by default</li>
</ol>
<p>If we change the Solr schema, we&#8217;ll need to do a full re-index &#8211; or in our case with a test application in development mode, we can just delete the index files (typically in ~/.grails/<em>&lt;grails-version&gt;</em>/projects/<em>GroovyMagSolr</em>/solr-home/solr/data).</p>
<p>First we&#8217;ll modify Item.groovy to add <code>import org.grails.solr.Solr</code> and then add the Solr annotation to the String subject field e.g. <code>@Solr(asText=true)</code> &#8211; this will cause the field to be indexed as subject_t. We&#8217;d now be able to search for subject_t:timesheet and get a match &#8211; but this still doesn&#8217;t meet our usability requirements as it requires knowledge of the underlying document fields. We could use an explicit <code>&lt;copyField source="subject_t" dest="text"/&gt;</code> in the Solr schema.xml, however if you inspect the schema.xml you will see that the first 3000 characters of all &#8216;_t&#8217; fields are copied to the &#8216;text&#8217; field.</p>
<p>Now a search for &#8216;timesheet&#8217; gives the results shown in Figure 9.</p>
<div id="attachment_530" class="wp-caption alignnone" style="width: 287px"><a href="http://leanjavaengineering.files.wordpress.com/2012/02/figure9.png"><img src="http://leanjavaengineering.files.wordpress.com/2012/02/figure9.png?w=500" alt="" title="Figure 9: Solr simple query result"   class="size-full wp-image-530" /></a><p class="wp-caption-text">Figure 9: Solr simple query result</p></div>
<h2>ElasticSearch plugin</h2>
<p>ElasticSearch (<a href="http://www.elasticsearch.org/">http://www.elasticsearch.org/</a>) is a new Lucene-based distributed, RESTful search engine. It was created by Shay Banon, who created Compass (used by the Searchable Plugin) and has worked on data grid technologies. </p>
<p>Version 0.2 of the Grails plugin uses ElasticSearch 0.15.2 &#8211; you can start with an embedded instance in development mode which stores the indices in the project source directory under &#8216;data&#8217;.</p>
<p>The first step (within a clean application) is to install the plugin:</p>
<p> <code>grails install-plugin elasticsearch</code></p>
<p>Then create the domain class (<code>grails create-domain-class com.rbramley.todo.Item</code>) and fill in using the Listing 1 domain class code with the addition of Listing 5.</p>
<p><code><br />
&nbsp;&nbsp;&nbsp;&nbsp;static searchable = {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;except = 'dateCreated'<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
</code><br />
<em>Listing 5: ElasticSearch mapping</em></p>
<p>Once that is done and the controller and views generated (<code>grails generate-all com.rbramley.todo.Item</code>), we can then create our Search controller (<code>grails create-controller com.rbramley.todo.Search</code>) and complete it using Listing 6.</p>
<p><code><br />
package com.rbramley.todo<br />
&nbsp;<br />
class SearchController {<br />
&nbsp;&nbsp;&nbsp;&nbsp;def elasticSearchService<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;def search = {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def res = elasticSearchService.search("${params.query}")<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[query:params.query, total:res.total, searchResults:res.searchResults]<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
</code><br />
<em>Listing 6: ElasticSearch controller</em></p>
<p>Let&#8217;s re-use the main.gsp from the GroovyMagSolr application (Listing 2) and we&#8217;ll adapt the search.gsp fragment from Listing 4 to get Listing 7.</p>
<p><code><br />
&lt;g:if test="${haveResults}"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;g:each var="result" in="${searchResults}"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;g:set var="className" value="${ClassUtils.getShortName(result.class)}" /&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;g:set var="link" value="${createLink(controller: className[0].toLowerCase() + className[1..-1], action: 'show', id: result.id)}" /&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;li&gt;&lt;a href="${link}"&gt;${className}: ${result.id}&lt;/a&gt;&lt;/li&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;${result.subject?.encodeAsHTML()}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/g:each&gt;<br />
&lt;/g:if&gt;<br />
</code><br />
<em>Listing 7: Search results page for ElasticSearch</em></p>
<p>Having started up the application and entered the test data as shown in Figure 5, I hit a problem in that the database record was created but the index record wasn&#8217;t and the console was filling up with repeated log entries until I stopped the application:</p>
<p><code><br />
2011-08-20 22:33:51,630 [elasticsearch[index]-pool-2-thread-1] ERROR index.Index<br />
RequestQueue  - Failed bulk item: NullPointerException[null]<br />
2011-08-20 22:33:51,633 [elasticsearch[index]-pool-2-thread-2] ERROR index.Index<br />
RequestQueue  - Failed bulk item: NullPointerException[null]<br />
</code></p>
<p>When I looked at the IndexRequestQueue source, the JavaDoc says &#8220;If indexing fails, all failed objects are retried. Still no support for max number of retries (todo)&#8221;.</p>
<p>The NullPointerException itself is ElasticSearch bug 795 &#8211; but the underlying cause seems to be that the JSON document didn&#8217;t meet the expectations of ElasticSearch for that type!</p>
<p>I got the plugin working by changing the domain class searchable mapping to <code>only = 'subject'</code> &#8211; then the results look identical to Figure 9.</p>
<p>In the time available I didn&#8217;t manage to track down the initial issue (I also tried with an external ElasticSearch instance and upgrading the ElasticSearch dependencies to 0.16). I&#8217;ll be in communication with the plugin authors&#8230;</p>
<h1>Plugin comparison</h1>
<p>So how do they compare? Well this article has given a basic introduction to their usage for English text and hasn&#8217;t demonstrated advanced features or the distributed capabilities of the latter two.</p>
<p>The criteria for comparison is partially inspired by Marc Palmer&#8217;s views on plugins &#8211; a distilled form is &#8220;make it work, make it simple, make it magic&#8221;; we&#8217;ll also add scalability to this list.</p>
<h2>Searchable</h2>
<p>This is a well established plugin and works very well out of the box with simple domain classes and even relationships. It is simple to use, works as expected and provides a basic search page and controller which includes some administrative actions such as the ability to re-index all searchable domain classes.</p>
<p>I&#8217;ve occasionally encountered older versions throwing exceptions on start-up that could be rectified by removing the indices and restarting the application.</p>
<p>On the downside, due to the embedded nature of the indices, it is only really suitable for single-instance applications. </p>
<h2>Solr</h2>
<p>This plugin requires some configuration to get the best out of it and it could benefit from some attention to update it. However, it does have reasonable documentation and some powerful features such as faceted-search, spatial search and taglibs for facets and result links.</p>
<p>Also it should be possible to index domain classes that use Mongo through the use of the metaClass added <code>indexSolr()</code> method.</p>
<h2>ElasticSearch</h2>
<p>This plugin has great promise &#8211; although I encountered some issues relating to the ElasticSearch expectation of the index document structure, I should mention that the plugin documentation currently contains the warning <strong>&#8220;you should only use this plugin for testing&#8221;</strong>.</p>
<p>The main attraction of ElasticSearch is the real time search with a distributed and scalable nature.</p>
<p>As per the Solr plugin, it should also be possible to index Mongo-mapped domain classes using a metaClass added <code>index()</code> method.</p>
<h1>References</h1>
<p>For further reading the DZone Refcardz provide good overviews:</p>
<ul>
<li><a href="http://refcardz.dzone.com/refcardz/lucene">http://refcardz.dzone.com/refcardz/lucene</a>
<li><a href="http://refcardz.dzone.com/refcardz/solr-essentials">http://refcardz.dzone.com/refcardz/solr-essentials</a>
</ul>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/leanjavaengineering.wordpress.com/518/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/leanjavaengineering.wordpress.com/518/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=leanjavaengineering.wordpress.com&#038;blog=15402167&#038;post=518&#038;subd=leanjavaengineering&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://leanjavaengineering.wordpress.com/2012/02/24/using-lucene-in-grails/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://2.gravatar.com/avatar/ba996199c3dd2c7095b45f3a645c52ef?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">leanjavaengineering</media:title>
		</media:content>

		<media:content url="http://leanjavaengineering.files.wordpress.com/2012/02/figure1.png?w=300" medium="image">
			<media:title type="html">Figure 1: Whitespace tokenization</media:title>
		</media:content>

		<media:content url="http://leanjavaengineering.files.wordpress.com/2012/02/figure2.png" medium="image">
			<media:title type="html">Figure 2: Stopwords</media:title>
		</media:content>

		<media:content url="http://leanjavaengineering.files.wordpress.com/2012/02/figure3.png" medium="image">
			<media:title type="html">Figure 3: Word stemming</media:title>
		</media:content>

		<media:content url="http://leanjavaengineering.files.wordpress.com/2012/02/figure4.png" medium="image">
			<media:title type="html">Figure 4: Term index</media:title>
		</media:content>

		<media:content url="http://leanjavaengineering.files.wordpress.com/2012/02/figure5.png?w=228" medium="image">
			<media:title type="html">Figure 5: Test data</media:title>
		</media:content>

		<media:content url="http://leanjavaengineering.files.wordpress.com/2012/02/figure6.png?w=300" medium="image">
			<media:title type="html">Figure 6: Searchable results</media:title>
		</media:content>

		<media:content url="http://leanjavaengineering.files.wordpress.com/2012/02/figure7.png" medium="image">
			<media:title type="html">Figure 7: Solr with fully specified query</media:title>
		</media:content>

		<media:content url="http://leanjavaengineering.files.wordpress.com/2012/02/figure8.png" medium="image">
			<media:title type="html">Figure 8: Solr Luke analysis of subject field</media:title>
		</media:content>

		<media:content url="http://leanjavaengineering.files.wordpress.com/2012/02/figure9.png" medium="image">
			<media:title type="html">Figure 9: Solr simple query result</media:title>
		</media:content>
	</item>
		<item>
		<title>C.R.A.P. metrics for Grails</title>
		<link>http://leanjavaengineering.wordpress.com/2012/01/26/c-r-a-p-metrics-for-grails/</link>
		<comments>http://leanjavaengineering.wordpress.com/2012/01/26/c-r-a-p-metrics-for-grails/#comments</comments>
		<pubDate>Thu, 26 Jan 2012 23:59:23 +0000</pubDate>
		<dc:creator>rbramley</dc:creator>
				<category><![CDATA[Code quality]]></category>
		<category><![CDATA[How-to]]></category>
		<category><![CDATA[gmetrics]]></category>
		<category><![CDATA[grails]]></category>

		<guid isPermaLink="false">http://leanjavaengineering.wordpress.com/?p=506</guid>
		<description><![CDATA[This is a quick how-to post on getting Change Risk Anti-Patterns statistics for your Grails code. Firstly thanks to Jeff Winkler for bringing the new GMetrics v0.5 CrapMetric to my attention and thanks to Chris Mair for his great work &#8230; <a href="http://leanjavaengineering.wordpress.com/2012/01/26/c-r-a-p-metrics-for-grails/">Continue reading <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=leanjavaengineering.wordpress.com&#038;blog=15402167&#038;post=506&#038;subd=leanjavaengineering&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p>This is a quick how-to post on getting Change Risk Anti-Patterns statistics for your Grails code.</p>
<p>Firstly thanks to <a href="http://twitter.com/#!/winkler1">Jeff Winkler</a> for bringing the new <a href="http://gmetrics.sourceforge.net/">GMetrics</a> v0.5 <a href="http://gmetrics.sourceforge.net/gmetrics-CrapMetric.html">CrapMetric</a> to my attention and thanks to Chris Mair for his great work on GMetrics (and Codenarc).</p>
<p><!-- more --></p>
<h2>What is Change Risk Anti-Patterns?</h2>
<p>It is a method to analyze and predict the amount of effort, pain, and time required to maintain an existing body of code. Given a Java method m, C.R.A.P. for m is calculated as follows:<br />
<code>C.R.A.P.(m) = comp(m)^2 * (1 – cov(m)/100)^3 + comp(m)</code><br />
Where <em>comp(m)</em> is the cyclomatic complexity of method <em>m</em>, and <em>cov(m)</em> is the test code coverage provided by automated tests.<br />
For more background information see this <a href="http://googletesting.blogspot.com/2011/02/this-code-is-crap.html">blog post</a> describing the C.R.A.P. metric.</p>
<h2>Setting up Grails</h2>
<p>This is very fresh so you&#8217;ll need to do a little bit of tinkering until I can get the following changes incorporated into the <a href="http://grails.org/plugin/gmetrics">GMetrics plugin</a>!<br />
Note you&#8217;ll also need to have the Code Coverage plugin installed (refer to <a href="http://leanjavaengineering.wordpress.com/2010/09/09/grails-testing-in-hudson/">&#8220;Grails &amp; Hudson Part 3: Testing&#8221;</a> for more details on that).</p>
<h3>Install the GMetrics plugin</h3>
<p>This is the straightforward bit:<br />
<code>grails install-plugin gmetrics</code></p>
<h4>The hackery</h4>
<p>With GMetrics plugin version 0.3.1 you&#8217;ll need to edit the installed plugin <em>dependencies.groovy</em> (e.g.  <em>~/.grails/1.3.7/projects/foo/plugins/gmetrics-0.3.1/dependencies.groovy</em>) to use gmetrics 0.5 instead of 0.3.<br />
Line 27 should be changed to:<br />
<code><br />
provided('org.gmetrics:GMetrics:0.5') {<br />
        }<br />
</code></p>
<p>And you&#8217;ll want to modify <em>scripts/gmetrics.groovy</em> to use <em>metricSetFile</em> on the Ant task otherwise it will just use the default metrics which doesn&#8217;t include C.R.A.P.<br />
e.g. line 44 should become:<br />
<code><br />
ant.gmetrics(metricSetFile:metricSetFilename)<br />
</code></p>
<h3>Define a metric set</h3>
<p>We now need to tell GMetrics which metrics to run &#8211; out of laziness I saved this in the root of the Grails project as <em>test.gmetrics</em>:<br />
<code><br />
import org.gmetrics.metric.cyclomatic.CyclomaticComplexityMetric</p>
<p>final COBERTURA_FILE = 'file:target/test-reports/cobertura/coverage.xml'</p>
<p>&nbsp;&nbsp;metricset {<br />
&nbsp;&nbsp;&nbsp;&nbsp;def cyclomaticComplexityMetric = metric(CyclomaticComplexityMetric)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;def coberturaMetric = CoberturaLineCoverage {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;coberturaFile = COBERTURA_FILE<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;functions = ['total']<br />
&nbsp;&nbsp;&nbsp;&nbsp;}</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;CRAP {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;functions = ['total']<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;coverageMetric = coberturaMetric<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;complexityMetric = cyclomaticComplexityMetric<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}<br />
</code></p>
<h3>Configure the plugin</h3>
<p>If you want the HTML report, then your Config.groovy will need a single addition to specify the metric set:<br />
<code><br />
gmetrics.metricSetfilename = 'file:test.gmetrics'<br />
</code></p>
<p>If you want an XML report you&#8217;ll also need:<br />
<code><br />
gmetrics.outputFile = 'target/test-reports/GMetricsReport.xml'<br />
gmetrics.reportType = 'org.gmetrics.report.XmlReportWriter'<br />
</code></p>
<h3>Let&#8217;s go</h3>
<p>Now it&#8217;s all configured, we just need to ensure that we have coverage data before we run the CrapMetric.</p>
<p>e.g.<br />
<code>grails test-app -unit -coverage -xml</code></p>
<p>We can now run:<br />
<code>grails gmetrics</code></p>
<p>The report will be generated and the plugin output will state where it has been written to. The CRAPpy threshold has been defined as 30 &#8211; so the report will help to highlight the areas of code that you need to concentrate on first.</p>
<p>Remember that over complex code with full test coverage can still be classed as CRAPpy.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/leanjavaengineering.wordpress.com/506/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/leanjavaengineering.wordpress.com/506/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=leanjavaengineering.wordpress.com&#038;blog=15402167&#038;post=506&#038;subd=leanjavaengineering&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://leanjavaengineering.wordpress.com/2012/01/26/c-r-a-p-metrics-for-grails/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://2.gravatar.com/avatar/ba996199c3dd2c7095b45f3a645c52ef?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">leanjavaengineering</media:title>
		</media:content>
	</item>
		<item>
		<title>Monitoring Apache Solr</title>
		<link>http://leanjavaengineering.wordpress.com/2011/12/07/monitoring-apache-solr/</link>
		<comments>http://leanjavaengineering.wordpress.com/2011/12/07/monitoring-apache-solr/#comments</comments>
		<pubDate>Wed, 07 Dec 2011 19:30:17 +0000</pubDate>
		<dc:creator>rbramley</dc:creator>
				<category><![CDATA[How-to]]></category>
		<category><![CDATA[Monitoring]]></category>
		<category><![CDATA[Search]]></category>
		<category><![CDATA[JMX]]></category>
		<category><![CDATA[Opsview]]></category>
		<category><![CDATA[solr]]></category>

		<guid isPermaLink="false">http://leanjavaengineering.wordpress.com/?p=461</guid>
		<description><![CDATA[Apache Solr is an open source enterprise search service from the Lucene project. Solr is written in Java and runs as a standalone full-text search server within a servlet container such as Tomcat. Like any service or component in your &#8230; <a href="http://leanjavaengineering.wordpress.com/2011/12/07/monitoring-apache-solr/">Continue reading <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=leanjavaengineering.wordpress.com&#038;blog=15402167&#038;post=461&#038;subd=leanjavaengineering&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p><a href="http://lucene.apache.org/solr/">Apache Solr</a> is an open source enterprise search service from the Lucene project. Solr is written in Java and runs as a standalone full-text search server within a servlet container such as Tomcat.<br />
Like any service or component in your architecture, you&#8217;ll want to monitor it to ensure that it&#8217;s available and gather performance data to help with tuning.</p>
<p>In this post, we&#8217;ll look at how we can monitor Solr, what performance metrics we might want to gather and how we can easily achieve this with <a href="http://www.opsview.com">Opsview</a>.</p>
<p>We&#8217;ll use Opsview as it is built on Nagios and thus has access to a wide range of plugins, yet provides a more approachable user interface for configuring service checks.</p>
<p><!-- more --></p>
<h3>A check list for service checks</h3>
<p>Solr is built on Lucene so follows the same layout, an index contains documents that are comprised of fields. As part of the search service value add over Lucene, Solr provides a number of useful ways of obtaining health status / monitoring metrics:</p>
<ol>
<li>Health-check status using the <em>/admin/ping</em> handler</li>
<li>The admin statistics page <em>/admin/stats.jsp</em> (XML styled with XSL)</li>
<li><a href="http://wiki.apache.org/solr/SolrJmx">JMX MBeans</a></li>
</ol>
<p>The list of applicable checks could be defined by whether it is a health check or a data gathering check &#8211; but we&#8217;d end up with a lot of overlap. Instead I&#8217;ve divided the list into the checks that can be performed remotely (without an installed agent on the server) and those that are best performed locally to the Solr server.</p>
<h4>Remote (agent-less) checks</h4>
<p>What should we look for over the network?</p>
<p>Firstly we can have a host-level check which may perform a network level ping.<br />
Next we can check TCP connectivity to the servlet container port and then make an HTTP GET request to the Solr &#8216;front page&#8217; and check for a known string (e.g. <em>Welcome to Solr</em>).</p>
<p>Now we&#8217;ve made it up to the application layer so can start to perform Solr specific checks. Items to monitor may include (delete as applicable): </p>
<ol>
<li>Ping status</li>
<li>Number of docs</li>
<li>Number of queries / queries per second</li>
<li>Average response time</li>
<li>Number of updates</li>
<li>Cache hit ratios</li>
<li>Replication status</li>
<li>Synthetic queries</li>
</ol>
<h4>Agent-based checks</h4>
<p>Installing an Opsview agent on the Solr server means we can run additional checks over NRPE (Nagios Remote Plugin Executor). This could be operating system level checks such as memory/disk utilisation or CPU load, or the following:</p>
<ol>
<li>Java servlet container process is running</li>
<li>JMX checks e.g. heap memory or custom MBeans</li>
<li>File age</li>
<li>Log parsing for exceptions</li>
</ol>
<p>For more detail on some of the non-Solr specific checks, see my previous post on <a href="http://leanjavaengineering.wordpress.com/2011/05/29/monitoring-grails-apps-part-2/">monitoring Grails</a> (though broadly applicable to any JVM application).</p>
<p>The Solr wiki describes how to configure JMX support: <a href="http://wiki.apache.org/solr/SolrJmx">http://wiki.apache.org/solr/SolrJmx</a>.</p>
<h3>Opsview configuration</h3>
<p>The rest of the article assumes that you&#8217;ve got Opsview (or the <a href="http://www.opsview.com/downloads/opsview-3-vmware-virtual-appliance">Opsview VMWare appliance</a>) installed &amp; have completed the <a href="http://docs.opsview.com/doku.php?id=opsview-community:quickstart">Quick Start</a>.</p>
<h4>Solr-specific Plugin</h4>
<p>We&#8217;ll install the plugin from <a href="https://github.com/rbramley/Opsview-solr-checks">https://github.com/rbramley/Opsview-solr-checks</a> into <em>/usr/local/nagios/libexec/</em></p>
<p>The <em>check_solr</em> plugin was developed using Perl, so that it could be contributed back to Opsview (and it would have used Nagios::Plugin had I known about it sooner). It requires the CPAN XML::XPath module (<em>sudo cpan -i XML::XPath</em>).</p>
<p>The plugin includes usage instructions, <em>check_solr -h</em> which can also be viewed in Opsview by selecting the &#8216;<em>Show Plugin Help</em>&#8216; link beneath the <strong>Plugin</strong> drop down (see Figure 1).<br />
The <em>-u</em> option can be used to specify the URL path for multi-core set-ups.</p>
<h4>Service check setup</h4>
<p>Figure 1 gives an example of a service check configuration.<br />
<div id="attachment_492" class="wp-caption alignnone" style="width: 510px"><a href="http://leanjavaengineering.files.wordpress.com/2011/12/figure_1_with_help.png"><img src="http://leanjavaengineering.files.wordpress.com/2011/12/figure_1_with_help.png?w=500&#038;h=644" alt="" title="Figure_1_with_help" width="500" height="644" class="size-full wp-image-492" /></a><p class="wp-caption-text">Figure 1 - Service check definition (showing plugin help)</p></div></p>
<p>Figure 2 shows the <em>agentless</em> service check group with plugins and their arguments.<br />
<div id="attachment_494" class="wp-caption alignnone" style="width: 510px"><a href="http://leanjavaengineering.files.wordpress.com/2011/12/figure_2.png"><img src="http://leanjavaengineering.files.wordpress.com/2011/12/figure_2.png?w=500&#038;h=251" alt="" title="Figure_2" width="500" height="251" class="size-full wp-image-494" /></a><p class="wp-caption-text">Figure 2 - Service check group</p></div></p>
<h4>Host configuration</h4>
<p>Figure 3 shows a simplistic host setup with a ping check.<br />
<div id="attachment_478" class="wp-caption alignnone" style="width: 510px"><a href="http://leanjavaengineering.files.wordpress.com/2011/12/set_up_host.png"><img src="http://leanjavaengineering.files.wordpress.com/2011/12/set_up_host.png?w=500&#038;h=596" alt="" title="Figure 3 - Host configuration" width="500" height="596" class="size-full wp-image-478" /></a><p class="wp-caption-text">Figure 3 - Host configuration</p></div></p>
<p>Figure 4 is an extract from the <strong>Monitors</strong> tab, where we select the checks we want performed for the current host.<br />
<div id="attachment_495" class="wp-caption alignnone" style="width: 298px"><a href="http://leanjavaengineering.files.wordpress.com/2011/12/figure_4.png"><img src="http://leanjavaengineering.files.wordpress.com/2011/12/figure_4.png?w=500" alt="" title="Figure_4"   class="size-full wp-image-495" /></a><p class="wp-caption-text">Figure 4 - Picking monitors for a host</p></div></p>
<h4>Viewing output</h4>
<p>The check results shown in Figure 5 are visible by navigating through the host group hierarchy.<br />
<div id="attachment_496" class="wp-caption alignnone" style="width: 510px"><a href="http://leanjavaengineering.files.wordpress.com/2011/12/figure_5.png"><img src="http://leanjavaengineering.files.wordpress.com/2011/12/figure_5.png?w=500&#038;h=192" alt="" title="Figure_5" width="500" height="192" class="size-full wp-image-496" /></a><p class="wp-caption-text">Figure 5 - Host check results</p></div></p>
<p>If you click on the graph icon of <em>Solr Cache Hit Ratios</em> this will drill down onto the graph shown in Figure 6.<br />
Clicking on the graph icon for <em>Solr Avg Response Time &#8211; standard</em> will take you to the graphs in Figure 7.</p>
<div id="attachment_474" class="wp-caption alignnone" style="width: 510px"><a href="http://leanjavaengineering.files.wordpress.com/2011/12/cache_hit_ratios.png"><img src="http://leanjavaengineering.files.wordpress.com/2011/12/cache_hit_ratios.png?w=500&#038;h=208" alt="" title="Figure 6 - Cache hit ratios" width="500" height="208" class="size-full wp-image-474" /></a><p class="wp-caption-text">Figure 6 - Cache hit ratios</p></div>
<div id="attachment_473" class="wp-caption alignnone" style="width: 510px"><a href="http://leanjavaengineering.files.wordpress.com/2011/12/avg_req_time.png"><img src="http://leanjavaengineering.files.wordpress.com/2011/12/avg_req_time.png?w=500&#038;h=449" alt="" title="Figure 7 - Average request time" width="500" height="449" class="size-full wp-image-473" /></a><p class="wp-caption-text">Figure 7 - Average request time</p></div>
<p>If you shutdown Solr, then the check results will start to turn critical and show in red as per Figure 8.<br />
<div id="attachment_497" class="wp-caption alignnone" style="width: 310px"><a href="http://leanjavaengineering.files.wordpress.com/2011/12/figure_8.png"><img src="http://leanjavaengineering.files.wordpress.com/2011/12/figure_8.png?w=300&#038;h=118" alt="" title="Figure_8" width="300" height="118" class="size-medium wp-image-497" /></a><p class="wp-caption-text">Figure 8 - Post shutdown alert</p></div></p>
<h3>Alternatives</h3>
<p>Of course I&#8217;m not the first person who&#8217;s wanted to monitor Solr from Opsview or a Nagios system, so there are a few plugins available &#8211; although the ones I found didn&#8217;t meet my personal needs (am happy to share my more detailed assessment notes if there is interest).</p>
<ul>
<li><a href="http://code.google.com/p/nagios-plugins-shamil/" rel="nofollow">http://code.google.com/p/nagios-plugins-shamil/</a> &#8211; provides ping, replication status and num docs</li>
<li><a href="http://code.google.com/p/solr-nagios-check/" rel="nofollow">http://code.google.com/p/solr-nagios-check/</a> &#8211; provides QPS, response time and num docs</li>
</ul>
<p>Also, chapter 8 of the recently published <a href="http://www.amazon.co.uk/gp/product/1849516065/ref=as_li_ss_tl?ie=UTF8&amp;tag=leanjavaengi-21&amp;linkCode=as2&amp;camp=1634&amp;creative=19450&amp;creativeASIN=1849516065">Apache Solr 3 Enterprise Search Server</a><img src="http://www.assoc-amazon.co.uk/e/ir?t=leanjavaengi-21&amp;l=as2&amp;o=2&amp;a=1849516065" width="1" height="1" border="0" alt="" style="border:none!important;margin:0!important;" /> book includes a section on Monitoring Solr Performance.</p>
<h3>Summary</h3>
<p>Using <em>check_solr</em> in conjunction with Opsview allows you to ensure that your Solr server is available and provides you with metrics that can help you tune your Solr configuration. This can be complemented with additional agent-based operating system and JMX checks to give you a full picture view.</p>
<p>Hopefully <em>check_solr</em> and the demonstrated <em>agentless</em> service checks will soon be incorporated into Opsview to join my <em>check_hudson_job</em> contribution.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/leanjavaengineering.wordpress.com/461/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/leanjavaengineering.wordpress.com/461/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=leanjavaengineering.wordpress.com&#038;blog=15402167&#038;post=461&#038;subd=leanjavaengineering&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://leanjavaengineering.wordpress.com/2011/12/07/monitoring-apache-solr/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
	
		<media:content url="http://2.gravatar.com/avatar/ba996199c3dd2c7095b45f3a645c52ef?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">leanjavaengineering</media:title>
		</media:content>

		<media:content url="http://leanjavaengineering.files.wordpress.com/2011/12/figure_1_with_help.png" medium="image">
			<media:title type="html">Figure_1_with_help</media:title>
		</media:content>

		<media:content url="http://leanjavaengineering.files.wordpress.com/2011/12/figure_2.png" medium="image">
			<media:title type="html">Figure_2</media:title>
		</media:content>

		<media:content url="http://leanjavaengineering.files.wordpress.com/2011/12/set_up_host.png" medium="image">
			<media:title type="html">Figure 3 - Host configuration</media:title>
		</media:content>

		<media:content url="http://leanjavaengineering.files.wordpress.com/2011/12/figure_4.png" medium="image">
			<media:title type="html">Figure_4</media:title>
		</media:content>

		<media:content url="http://leanjavaengineering.files.wordpress.com/2011/12/figure_5.png" medium="image">
			<media:title type="html">Figure_5</media:title>
		</media:content>

		<media:content url="http://leanjavaengineering.files.wordpress.com/2011/12/cache_hit_ratios.png" medium="image">
			<media:title type="html">Figure 6 - Cache hit ratios</media:title>
		</media:content>

		<media:content url="http://leanjavaengineering.files.wordpress.com/2011/12/avg_req_time.png" medium="image">
			<media:title type="html">Figure 7 - Average request time</media:title>
		</media:content>

		<media:content url="http://leanjavaengineering.files.wordpress.com/2011/12/figure_8.png?w=300" medium="image">
			<media:title type="html">Figure_8</media:title>
		</media:content>

		<media:content url="http://www.assoc-amazon.co.uk/e/ir?t=leanjavaengi-21&#38;l=as2&#38;o=2&#38;a=1849516065" medium="image" />
	</item>
	</channel>
</rss>
