<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

 <title>Br0kenB1ts - Yan's blog</title>
 <link type="application/atom+xml" href="http://www.pongasoft.com/blog/yan/atom.xml" rel="self"/>
 <link type="text/html" href="http://www.pongasoft.com/blog/yan" rel="alternate" />
 <updated>2011-09-30T11:15:56-07:00</updated>
 <id>http://www.pongasoft.com/blog/yan</id>
 <author>
   <name>Yan Pujante</name>
 </author>

 
 <entry>
   <title>The future of glu - glu in the cloud</title>
   <link href="http://www.pongasoft.com/blog/yan/glu/2011/09/30/future-of-glu-glu-in-the-clouds/"/>
   <updated>2011-09-30T00:00:00-07:00</updated>
   <id>http://www.pongasoft.com/blog/yan/glu/2011/09/30/future-of-glu-glu-in-the-clouds</id>
   <content type="html">&lt;p&gt;I have started thinking of where I would like to see &lt;a href=&quot;http://linkedin.github.com/glu/docs/latest/html/index.html&quot;&gt;glu&lt;/a&gt; go in the future (3 to 6 months timeframe). It seems to me that what makes the most sense for the evolution of glu is to embrace the cloud.&lt;/p&gt;
&lt;h2&gt;glu today&amp;#8230;&lt;/h2&gt;
&lt;p&gt;Today glu works really well (at least I assume :) when the glu environment is setup: machines are up and running with a glu agent on it, ZooKeeper is installed, the console is up and running. The &lt;a href=&quot;http://linkedin.github.com/glu/docs/latest/html/tutorial.html&quot;&gt;tutorial&lt;/a&gt; that comes with glu has &lt;em&gt;streamlined&lt;/em&gt; this effort for a quick and easy demonstration when everything runs locally on the same machine. The &lt;a href=&quot;http://linkedin.github.com/glu/docs/latest/html/production-setup.html&quot;&gt;production setup&lt;/a&gt; is a bit more involved and could also be made more &lt;em&gt;streamlined&lt;/em&gt; (there are several tickets tracking this like &lt;a href=&quot;https://github.com/linkedin/glu/issues/58&quot;&gt;#58&lt;/a&gt; and &lt;a href=&quot;https://github.com/linkedin/glu/issues/84&quot;&gt;#84&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;In the end, glu is a deployment automation platform which allows you to automate the deployment of your applications and services on a set of nodes. The missing piece is that glu does not know how to provision itself. One of the challenge of course is that in order to provision glu, first you need a node. In many cases (and this is the case at LinkedIn), this step involves an actual human being who is going to rack a physical piece of hardware, then install the OS on it, etc&amp;#8230;&lt;/p&gt;
&lt;p&gt;One of the key concept of glu is that it turned provisioning into an &lt;span class=&quot;caps&quot;&gt;API&lt;/span&gt;. Since it is an &lt;span class=&quot;caps&quot;&gt;API&lt;/span&gt;, you can programmatically call it (which means you can programmatically provision a node) and of course you can automate it.&lt;/p&gt;
&lt;p&gt;In my mind, one of the biggest gain that the &lt;em&gt;cloud&lt;/em&gt; is bringing, is the fact that you can now treat hardware as software: you now have an &lt;span class=&quot;caps&quot;&gt;API&lt;/span&gt; that allows you to bring machines up and down. This is a big mind shift. The beauty of it is since it is an &lt;span class=&quot;caps&quot;&gt;API&lt;/span&gt; you can automate it!&lt;/p&gt;
&lt;h2&gt;glu in the cloud&lt;/h2&gt;
&lt;p&gt;At the heart of glu relies the &lt;a href=&quot;http://linkedin.github.com/glu/docs/latest/html/static-model.html#static-model&quot;&gt;&lt;em&gt;model&lt;/em&gt;&lt;/a&gt; which defines what applications (services) need to run where. The &lt;em&gt;where&lt;/em&gt; part is already pretty abstract but glu assumes that it is a node that is already &lt;em&gt;glu aware&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Can we remove this assumption? After all, provisioning your applications is simply a matter of calling some &lt;span class=&quot;caps&quot;&gt;API&lt;/span&gt;, and in the cloud, provisioning glu is also simply a matter of calling some &lt;span class=&quot;caps&quot;&gt;API&lt;/span&gt; (albeit a very different one).&lt;/p&gt;
&lt;p&gt;In this scenario we could envision that the &lt;em&gt;model&lt;/em&gt; defines nodes in a more abstract way, for example: &lt;code&gt;ec2:standard:XL/agent-1&lt;/code&gt;, which essentially defines &lt;code&gt;agent-1&lt;/code&gt; to be running on an extra large standard EC2 instance.&lt;/p&gt;
&lt;p&gt;The same way that glu computes a delta to provision your applications, it could also determine that the node &lt;code&gt;agent-1&lt;/code&gt; does not exist and as a result, needs to be provisioned with glu, in which case it would use the EC2 apis to create a brand new VM and install what is necessary on the node for glu to run.&lt;/p&gt;
&lt;h2&gt;Mind map&lt;/h2&gt;
&lt;p&gt;In order to bootstrap the process I have created a mind map to capture my first thoughts (you can click the image to view the full version).&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.pongasoft.com/blog/yan/resource/images/2011-09-30/mind-map.png&quot; target=&quot;#&quot; title=&quot;Click to enlarge&quot;&gt;&lt;img src=&quot;http://www.pongasoft.com/blog/yan/resource/images/2011-09-30/mind-map.png&quot; alt=&quot;Mind Map&quot; border=&quot;0&quot; width=&quot;100%&quot; style=&quot;&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;My dream&lt;/h2&gt;
&lt;p&gt;Currently I deploy my personal website &lt;a href=&quot;http://www.kiwidoc.com&quot;&gt;kiwidoc&lt;/a&gt; on rackspace. The process is manual (this was done prior to glu and I did not &lt;em&gt;port&lt;/em&gt; it). My dream is that I can use glu to deploy kiwidoc. And this is what I really want to be able to do: &lt;em&gt;rackspace is having technical difficulties today&amp;#8230; not a problem let&amp;#8217;s switch to ec2 while it gets fixed&lt;/em&gt;. Dreaming is good :)&lt;/p&gt;
&lt;p&gt;Joke aside, kiwidoc would be a fairly interesting use case. Although very small there are already a bunch of &amp;#8216;issues&amp;#8217; that glu in the cloud would need to address. For example, kiwidoc is split amongst 2 machines (a &lt;em&gt;nginx&lt;/em&gt; frontend and a &lt;em&gt;tomcat&lt;/em&gt; backend). The frontend needs to talk to the backend and for that it needs to use the &amp;#8216;internal&amp;#8217; IP so that it not counted as external traffic. Also kiwidoc uses data (which is &lt;strong&gt;not&lt;/strong&gt; code), like the search (&lt;em&gt;Lucene&lt;/em&gt;) index. What happens if the node hosting the backend (and hence the index) needs to be recreated?&lt;/p&gt;
&lt;h2&gt;Feedback&lt;/h2&gt;
&lt;p&gt;In the end, the community is what makes glu both successful and interesting. I am soliciting feedback. Please leave some. Even if it is just a simple idea or comment, it doesn&amp;#8217;t matter. I would really like this process to be collaborative. I would love to be able to derive a roadmap and start implementing it.  Feel free to leave feedback on the &lt;a href=&quot;http://glu.977617.n3.nabble.com/RFC-The-future-of-glu-glu-in-the-clouds-td3383234.html&quot;&gt;forum&lt;/a&gt;.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>java.io.File.deleteOnExit() is evil</title>
   <link href="http://www.pongasoft.com/blog/yan/java/2011/05/17/file-dot-deleteOnExit-is-evil/"/>
   <updated>2011-05-17T00:00:00-07:00</updated>
   <id>http://www.pongasoft.com/blog/yan/java/2011/05/17/file-dot-deleteOnExit-is-evil</id>
   <content type="html">&lt;p&gt;If you take a look at the javadoc for &lt;a href=&quot;http://www.kiwidoc.com/java/l/p/java/j2se/1.6/p/java.io/c/File#deleteOnExit%28%29&quot;&gt;&lt;code&gt;File.deleteOnExit()&lt;/code&gt;&lt;/a&gt; it clearly states:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Once deletion has been requested, it is not possible to cancel the request. This method should therefore be used with care.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It is relatively easy to overlook this kind of statement. In my case it led to a slow memory leak that was reported in &lt;a href=&quot;http://linkedin.github.com/glu/docs/latest/html/index.html&quot;&gt;glu&lt;/a&gt;. In the end, finding out the memory leak turned out to be relatively simple thanks to &lt;code&gt;jmap&lt;/code&gt; and &lt;a href=&quot;http://www.yourkit.com/&quot;&gt;YourKit&lt;/a&gt; (although it seems to always be &lt;em&gt;simple&lt;/em&gt; after the fact :)).&lt;/p&gt;
&lt;p&gt;The piece of (groovy) code that was causing the issue was the following:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;  &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;cat&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IOException&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;File&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tempFile&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;createTempFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;GroovyIOUtils.cat&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;.txt&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;tempFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;deleteOnExit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;fetchContent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tempFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tempFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;text&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;tempFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;What the code does: it creates a temporary file, then &lt;em&gt;downloads&lt;/em&gt; the &lt;code&gt;location&lt;/code&gt; (&lt;span class=&quot;caps&quot;&gt;URL&lt;/span&gt;) in this temporary file and simply returns the content as a &lt;code&gt;String&lt;/code&gt;. In order to be safe, I called &lt;code&gt;tempFile.deleteOnExit()&lt;/code&gt;, but as you can see the file is deleted in the &lt;code&gt;finally&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Non intuitively (but to be fair as stated in the javadoc!), once deletion has been requested, there is no way to cancel it, &lt;strong&gt;even if you delete the file!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The implementation of &lt;code&gt;java.io.File.deleteOnExit()&lt;/code&gt; simply keeps a list of &lt;code&gt;String&lt;/code&gt; representing all the files that need to be deleted when the VM exits. And this list grows and grows as the &lt;code&gt;cat&lt;/code&gt; method is called (which in this specific scenario was every 15 seconds, so the leak took many weeks to manifest itself!).&lt;/p&gt;
&lt;p&gt;Looking back at the issue and this specific java call, I actually do not see a real use for it and I have decided to ban it from my code for several reasons as I actually think it is evil:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;it gives you a &lt;strong&gt;false&lt;/strong&gt; sense of security: &amp;#8220;oh the VM will take care of it&amp;#8230;&amp;#8221; which is &lt;strong&gt;not&lt;/strong&gt; true: if the VM does not terminate properly, for any particular reason (power outage, &lt;code&gt;kill -9&lt;/code&gt;,&amp;#8230;) then the files will simply &lt;strong&gt;not&lt;/strong&gt; be deleted. If your code relies on the fact that those files should not be present on VM restart then one day you will have a nasty surprise.&lt;/li&gt;
	&lt;li&gt;calling this method will eventually lead to a memory leak (granted of course it is called repeatedly, even if the frequency is very slow!)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In the end, dealing with files is hard. If you generate a lot of temporary files you need to delete them at some point or another or you will fill up the disk. It is not always easy to know when a temporary file is &lt;em&gt;safe&lt;/em&gt; to be deleted, especially if a method creates it and returns it. I think this is where &lt;code&gt;java.io.File.deleteOnExit()&lt;/code&gt; totally fails: it gives you the impression that it implements GC for files but as I mentionned before there are a lot of shortcommings.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>How to treat documentation as code: gradle + sphinx</title>
   <link href="http://www.pongasoft.com/blog/yan/glu/2011/04/25/documentation-as-code/"/>
   <updated>2011-04-25T00:00:00-07:00</updated>
   <id>http://www.pongasoft.com/blog/yan/glu/2011/04/25/documentation-as-code</id>
   <content type="html">&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;About a month ago, I embarked on a complete rewrite of the glu documentation (here is the &lt;a href=&quot;http://linkedin.github.com/glu/docs/latest/html/index.html&quot;&gt;result&lt;/a&gt;). The first iteration of the documentation was created using the wiki hosted on github, alongside the &lt;a href=&quot;https://github.com/linkedin/glu&quot;&gt;source code&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I was not very happy with this solution for several reasons:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;I knew that writing the documentation was going to be a pretty big effort and I did not want to have the wiki broken as I was writing it&lt;br /&gt;
&lt;div class=&quot;info&quot;&gt;&lt;img src=&quot;http://www.pongasoft.com/blog/yan/resource/info_48x48.png&quot; alt=&quot;info_48x48.png&quot; border=&quot;0&quot; width=&quot;21&quot; height=&quot;21&quot; /&gt; You can argue that the wiki on github is managed, under the cover, as a git repository and that you can check it out locally, even create branches, and push it all at once. But this is where it falls apart:
	&lt;ul&gt;
		&lt;li&gt;the wiki is &lt;strong&gt;not&lt;/strong&gt; wysiwyg (far from it), and since github uses its own processing pipeline (which to my knowledge is not available as a local command line), there is no way to actually see what you get until you push it live (how would you see a branch for example ?)&lt;/li&gt;
		&lt;li&gt;github seems to be using different pipelines depending on where you are on the website (for example the rendering of a &lt;code&gt;gist&lt;/code&gt; is different from the exact same input in the wiki given a file with the same extension!)&lt;/div&gt;&lt;/li&gt;
	&lt;/ul&gt;&lt;/li&gt;
	&lt;li&gt;it is hard to control the rendering of a wiki (wiki is usually rendered inside a &lt;em&gt;shell&lt;/em&gt; and you cannot control too much the layout or add your own javascript)&lt;/li&gt;
	&lt;li&gt;I needed to &lt;em&gt;inject&lt;/em&gt; some information in the wiki (like the latest changes, or the version number)&lt;/li&gt;
	&lt;li&gt;you cannot &lt;em&gt;bundle&lt;/em&gt; the documentation with the product&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Choosing a different solution&lt;/h2&gt;
&lt;p&gt;I settled for using a markup based solution like what I have done for &lt;a href=&quot;http://www.pongasoft.com/blog/yan/misc/2011/03/02/migrating-jroller-401-to-jekyll/&quot;&gt;this blog&lt;/a&gt;. The main reasons are:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;it is easy to write&lt;/li&gt;
	&lt;li&gt;it can be checked in with the rest of the source code&lt;/li&gt;
	&lt;li&gt;it can have its own build process (treat the documentation as code!)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Choosing a solution was not easy as there seems to be a lot of choices. &lt;a href=&quot;http://jekyllrb.com/&quot;&gt;jekyll&lt;/a&gt; was actually not a practical solution because it is really well suited for blogs, but not really for heavy documentation with cross references.&lt;/p&gt;
&lt;p&gt;I ended but choosing &lt;a href=&quot;http://sphinx.pocoo.org/&quot;&gt;sphinx&lt;/a&gt;. &lt;em&gt;sphinx&lt;/em&gt; was originally created to write the &lt;a href=&quot;http://docs.python.org/&quot;&gt;new python documentation&lt;/a&gt;. The &lt;em&gt;sphinx&lt;/em&gt; documentation is written with &lt;em&gt;sphinx&lt;/em&gt; and it is beautiful (as they mention it themselves) and very comprehensive. It has a lot of bells and whistles like:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;changing the theme&lt;/li&gt;
	&lt;li&gt;generating different outputs (html, pdf, man pages, etc&amp;#8230;)&lt;/li&gt;
	&lt;li&gt;setting up the project for you (&lt;code&gt;Makefile&lt;/code&gt;, configuration file, directory structure, etc&amp;#8230;)&lt;/li&gt;
	&lt;li&gt;generating a self contained web site&lt;/li&gt;
	&lt;li&gt;&amp;#8230;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the end I am really happy with the choice as it has been fairly easy to concentrate on the documentation itself rather than the tool. I am also very happy with the &lt;a href=&quot;http://linkedin.github.com/glu/docs/latest/html/index.html&quot;&gt;final result&lt;/a&gt;: it is using the &lt;em&gt;agogo&lt;/em&gt; theme with some customizations (which are fairly easy to do).&lt;/p&gt;
&lt;h2&gt;Integrating sphinx in the build process&lt;/h2&gt;
&lt;p&gt;glu uses &lt;a href=&quot;http://gradle.org/&quot;&gt;gradle&lt;/a&gt; for its build framework and in order to integrate the documentation generation as part of the build, I simply treated the documentation as another project in the source tree. You can check it out on &lt;a href=&quot;https://github.com/linkedin/glu/tree/master/docs/manual&quot;&gt;github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;sphinx&lt;/em&gt; generates a &lt;code&gt;Makefile&lt;/code&gt; as well as a configuration file (&lt;code&gt;conf.py&lt;/code&gt;) for you. I simply had to create a &lt;em&gt;gradle&lt;/em&gt; task to do the following:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;copy/inject the version (coming from the build)&lt;/li&gt;
	&lt;li&gt;copy the release notes in the sphinx directory structure&lt;/li&gt;
	&lt;li&gt;invoke the &lt;code&gt;Makefile&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is what it looks like (you can view the &lt;a href=&quot;https://github.com/linkedin/glu/blob/master/docs/manual/build.gradle&quot;&gt;full build file&lt;/a&gt;)&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;n&quot;&gt;sphinxDir&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;buildDir&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;sphinx&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;htmlDocDir&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sphinxDir&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;build/html&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;// copy/replace @version@&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;copy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;src/main/sphinx&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;tokens:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;version&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ReplaceTokens&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;into&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sphinxDir&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;exclude&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;**/*.png&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;exclude&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;**/*.gif&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;// copy images (no replacement)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;copy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;src/main/sphinx&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;into&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sphinxDir&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;**/*.png&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;**/*.gif&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;// copy the release notes at the root of the tree&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;copy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rootDir&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;RELEASE.rst&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;into&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sphinxDir&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;source&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;// execute the makefile&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ant&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;executable:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;make&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;dir:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sphinxDir&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;failonerror:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;arg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;line:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;html&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Finally, the documentation gets copied in the final product: in the end it is a static web site with a self contained set of html pages, css and images.&lt;/p&gt;
&lt;div class=&quot;info&quot;&gt;&lt;img src=&quot;http://www.pongasoft.com/blog/yan/resource/info_48x48.png&quot; alt=&quot;info_48x48.png&quot; border=&quot;0&quot; width=&quot;21&quot; height=&quot;21&quot; /&gt; To generate the tables, I used the &lt;code&gt;table-capture&lt;/code&gt; mode from emacs :)&lt;/div&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Treating documentation as code has a lot of advantages:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;it can be checked in with the rest of the source code thus sharing the same lifecycle, branching strategy, etc&amp;#8230;&lt;/li&gt;
	&lt;li&gt;using a markup language (text based) allows for easy source control management of the documentation (you can do diffs, and whatever else you can on regular source code)&lt;/li&gt;
	&lt;li&gt;it can have its own build process allowing you to inject information from the build itself (in this case I am injecting only the build version, but it opens up the door to a lot more (like test results, etc&amp;#8230;))&lt;/li&gt;
	&lt;li&gt;you can build any kind of automation around it&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;info&quot;&gt;&lt;img src=&quot;http://www.pongasoft.com/blog/yan/resource/info_48x48.png&quot; alt=&quot;info_48x48.png&quot; border=&quot;0&quot; width=&quot;21&quot; height=&quot;21&quot; /&gt; &lt;strong&gt;Note about github&lt;/strong&gt;&lt;br /&gt;
&lt;br /&gt;
I may seem to be ranting about &lt;a href=&quot;http://www.github.com&quot;&gt;github&lt;/a&gt;, but really I love what they have done. It is an amazing idea and if you are a software developer, you &lt;strong&gt;should&lt;/strong&gt; have an account there! From my point of view, github is for developers what a portfolio is for artists!&lt;/div&gt;</content>
 </entry>
 
 <entry>
   <title>Building a monitoring solution with glu</title>
   <link href="http://www.pongasoft.com/blog/yan/glu/2011/03/18/building-monitoring-solution-with-glu/"/>
   <updated>2011-03-18T00:00:00-07:00</updated>
   <id>http://www.pongasoft.com/blog/yan/glu/2011/03/18/building-monitoring-solution-with-glu</id>
   <content type="html">&lt;h3&gt;Introduction&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.github.com/linkedin/glu&quot;&gt;glu&lt;/a&gt; is a deployment automation platform which allows you to easily and efficiently deploy applications on many hosts. In the glu model, there is an active agent (aka, the glu agent) running on each host. Since this agent is always up and running on the host, it can be used to monitor the host itself and/or the applications running on it. By using the &lt;em&gt;timers&lt;/em&gt; feature of the agent and ZooKeeper, it is possible to build a very solid monitoring solution by reusing the infrastructure already in place for glu.&lt;/p&gt;
&lt;h3&gt;glu agent &lt;em&gt;timers&lt;/em&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;em&gt;timers&lt;/em&gt; feature of the glu agent allows you to schedule a timer, defined by a piece of code (a closure), which is executed at regular intervals. The agent manages timers appropriately, taking care of running them for you and restarting them in the event that the agent restarts. The full api of the timers feature can be found on &lt;a href=&quot;https://github.com/linkedin/glu/blob/master/agent/org.linkedin.glu.agent-api/src/main/groovy/org/linkedin/glu/agent/api/Timers.groovy&quot;&gt;github&lt;/a&gt;. Here is how you schedule and cancel a timer in a glu script:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MonitorScript&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;monitor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;// code that will run every 5s&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

 &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;timers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;schedule&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;timer:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;monitor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;repeatFrequency:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;5s&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

 &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stop&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;timers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;cancel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;timer:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;monitor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The code defined in the &lt;em&gt;monitor&lt;/em&gt; closure will be executed with the frequency defined at scheduling time until cancelled.&lt;/p&gt;
&lt;h3&gt;Adding monitoring code to the script&lt;/h3&gt;
&lt;p&gt;Now that we have timer, we can add the monitoring code. For the sake of this blog post, we will simply execute the &lt;code&gt;uptime&lt;/code&gt; shell command.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MonitorScript&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;monitor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;info&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;uptime&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

 &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;schedule&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;timer:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;monitor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;repeatFrequency:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;5s&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stop&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;cancel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;timer:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;monitor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Here is the result of running this script: every 5 seconds, a message containing the output of the &lt;code&gt;uptime&lt;/code&gt; command gets displayed in the agent log file.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://www.pongasoft.com/blog/yan/resource/images/2011-03-18/monitor-uptime-1.png&quot; title=&quot;uptime running inside the agent&quot; alt=&quot;uptime running inside the agent&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Detecting and reporting errors from within the script&lt;/h3&gt;
&lt;p&gt;We can add logic to the script to change the state based on the result of the command (using the &lt;a href=&quot;https://github.com/linkedin/glu/blob/master/agent/org.linkedin.glu.agent-api/src/main/groovy/org/linkedin/glu/agent/api/StateManager.groovy&quot;&gt;stateManager&lt;/a&gt;). The state will automatically be reported in ZooKeeper and propagated to the console.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MonitorScript&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CMD&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 
    &lt;span class=&quot;s&quot;&gt;&amp;quot;uptime | grep -o &amp;#39;[0-9]\\+\\.[0-9]\\+*&amp;#39; | xargs&amp;quot;&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;monitor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;// capturing current state&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;currentError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stateManager&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;error&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uptime&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CMD&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;load&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uptime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;// check for load (provided optionally as an init parameter)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;maxLoad&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?:&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;4.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;newError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;High load detected...&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;// change the state when currentError != newError&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;currentError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newError&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;stateManager&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;forceChangeState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newError&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
 
 &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;schedule&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;timer:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;monitor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;repeatFrequency:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;5s&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stop&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;cancel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;timer:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;monitor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;In the console, you simply add this entry for each agent (in the model):&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text&quot;&gt;{
  &amp;quot;agent&amp;quot;: &amp;quot;agent-1&amp;quot;,
  &amp;quot;initParameters&amp;quot;: {&amp;quot;maxLoad&amp;quot;: 1.0},
  &amp;quot;mountPoint&amp;quot;: &amp;quot;/monitor&amp;quot;,
  &amp;quot;script&amp;quot;: &amp;quot;http://localhost:8080/glu/repository/scripts/MonitorScript.groovy&amp;quot;,
  &amp;quot;tags&amp;quot;: [&amp;quot;monitor&amp;quot;]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Note how I set the &lt;code&gt;maxLoad&lt;/code&gt; parameter to 1.0 for being able to test more easily.&lt;/p&gt;
&lt;p&gt;When the load is normal, the console dashboard looks like this:&lt;br /&gt;
&lt;img src=&quot;http://www.pongasoft.com/blog/yan/resource/images/2011-03-18/monitor-normal-dashboard.png&quot; title=&quot;Everything is fine&quot; alt=&quot;Everything is fine&quot; /&gt;&lt;/p&gt;
&lt;p&gt;As soon as the load is too high, the console looks like this:&lt;br /&gt;
&lt;img src=&quot;http://www.pongasoft.com/blog/yan/resource/images/2011-03-18/monitor-high-load-dashboard.png&quot; title=&quot;Everything is fine&quot; alt=&quot;Everything is fine&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Making monitoring information available to ZooKeeper&lt;/h3&gt;
&lt;p&gt;We have seen how we can change the state so that it gets reported right away in the console. This is fine, and it also means that the detection logic lives in the script. If we want to be able to do more sophisticated monitoring (trending, etc&amp;#8230;) the logic needs to live outside the glu script. We still want the script to collect the monitoring data and simply make it available so that an external process can consume it. This is actually trivial to do:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MonitorScript&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CMD&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 
    &lt;span class=&quot;s&quot;&gt;&amp;quot;uptime | grep -o &amp;#39;[0-9]\\+\\.[0-9]\\+*&amp;#39; | xargs&amp;quot;&lt;/span&gt;

  &lt;span class=&quot;c&quot;&gt;// load is automatically available in ZooKeeper&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;load&lt;/span&gt;    

  &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;monitor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;currentError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stateManager&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;error&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uptime&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CMD&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    
    &lt;span class=&quot;c&quot;&gt;// load is simply assigned to the field rather than a &lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;// local variable&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;load&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uptime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;maxLoad&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?:&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;4.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;newError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;High load detected...&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;currentError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newError&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;stateManager&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;forceChangeState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newError&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
 
 &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;schedule&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;timer:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;monitor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;repeatFrequency:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;5s&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stop&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;cancel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;timer:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;monitor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The code is almost identical: the only difference is that we simply use a field rather than a local variable!&lt;/p&gt;
&lt;div class=&quot;info&quot;&gt;&lt;img src=&quot;http://www.pongasoft.com/blog/yan/resource/info_48x48.png&quot; alt=&quot;info_48x48.png&quot; border=&quot;0&quot; width=&quot;21&quot; height=&quot;21&quot; /&gt; In the glu world, all fields are automatically made available to ZooKeeper.&lt;/div&gt;
&lt;p&gt;We can verify that it makes it in the console:&lt;br /&gt;
&lt;img src=&quot;http://www.pongasoft.com/blog/yan/resource/images/2011-03-18/monitor-load-zk.png&quot; title=&quot;Load in the console through ZooKeeper&quot; alt=&quot;Load in the console through ZooKeeper&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In this screenshot, everything below &amp;#8216;View Details&amp;#8217; is coming directly from ZooKeeper. We can see the error message set by the script because it detected a high load, as well as the load itself. Note how the load is actually an array (under the cover the representation is json). This entry will change every time the timer executes (unless the load does not change of course).&lt;/p&gt;
&lt;h3&gt;Collecting and processing the monitoring data&lt;/h3&gt;
&lt;p&gt;As we have seen the data makes it in ZooKeeper. The fact that you can view it in the console is nice, but if you want to process the data, you can write an external program that will connect directly to ZooKeeper. You can reuse the convenient class that the console uses: &lt;a href=&quot;https://github.com/linkedin/glu/blob/master/agent/org.linkedin.glu.agent-tracker/src/main/groovy/org/linkedin/glu/agent/tracker/AgentsTracker.groovy&quot;&gt;AgentsTracker&lt;/a&gt; and register a set of listeners to be notified when things change.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;lineno&quot;&gt; 1&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MonitorMain&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zk&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;localhost:2181&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fabric&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;glu-dev-1&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt; 6&lt;/span&gt;   &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8&lt;/span&gt;     &lt;span class=&quot;c&quot;&gt;// establishes connection to ZooKeeper&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zkClient&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ZKClient&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;zk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Timespan&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;30s&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;zkClient&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;11&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;zkClient&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;waitForStart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Timespan&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;5s&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;12&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt;13&lt;/span&gt;     &lt;span class=&quot;c&quot;&gt;// creates a tracker to track events in ZooKeeper&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;14&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zkAgentRoot&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;/org/glu/agents/fabrics/${fabric}&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;15&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tracker&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AgentsTrackerImpl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;zkClient&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zkAgentRoot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;16&lt;/span&gt;     
&lt;span class=&quot;lineno&quot;&gt;17&lt;/span&gt;     &lt;span class=&quot;c&quot;&gt;// registers the listeners&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;18&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;tracker&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;registerAgentListener&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;agentEvents&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TrackerEventsListener&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;19&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;tracker&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;registerMountPointListener&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mountPointEvents&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TrackerEventsListener&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;20&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;tracker&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;registerErrorListener&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;errorListener&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ErrorListener&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;21&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt;22&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;tracker&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;23&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;tracker&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;waitForStart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;24&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;awaitTermination&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;25&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;26&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt;27&lt;/span&gt;   &lt;span class=&quot;c&quot;&gt;// track agent events (agent up or down)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;28&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;agentEvents&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Collection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NodeEvent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AgentInfo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;events&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;29&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;events&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NodeEvent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AgentInfo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;30&lt;/span&gt;       &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;eventType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;31&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;32&lt;/span&gt;         &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NodeEventType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ADDED&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;33&lt;/span&gt;         &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NodeEventType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;UPDATED&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;34&lt;/span&gt;           &lt;span class=&quot;c&quot;&gt;// ignoring add/update event&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;35&lt;/span&gt;           &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;36&lt;/span&gt;         &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NodeEventType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;DELETED&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;37&lt;/span&gt;           &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;warn&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;Detected agent down: ${event.nodeInfo.agentName}&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;38&lt;/span&gt;         &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;39&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;40&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;41&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;42&lt;/span&gt; 
&lt;span class=&quot;lineno&quot;&gt;43&lt;/span&gt;   &lt;span class=&quot;c&quot;&gt;// track mountPoint events&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;44&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mountPointEvents&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Collection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NodeEvent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MountPointInfo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;events&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;45&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;events&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NodeEvent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MountPointInfo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;46&lt;/span&gt;       &lt;span class=&quot;c&quot;&gt;// only interested in the monitor events (/monitor was defined in &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;47&lt;/span&gt;       &lt;span class=&quot;c&quot;&gt;// the console entry)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;48&lt;/span&gt;       &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;nodeInfo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;mountPoint&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MountPoint&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;fromPath&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;/monitor&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;49&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;50&lt;/span&gt;         &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;eventType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;51&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;52&lt;/span&gt;           &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NodeEventType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ADDED&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;53&lt;/span&gt;           &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NodeEventType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;UPDATED&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;54&lt;/span&gt;             &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;info&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;${event.nodeInfo.agentName} |&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;55&lt;/span&gt;                &lt;span class=&quot;s&quot;&gt;&amp;quot;${event.nodeInfo.data?.scriptState?.script?.load?.join(&amp;#39;,&amp;#39;)}&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;56&lt;/span&gt;             &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;57&lt;/span&gt;           &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NodeEventType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;DELETED&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;58&lt;/span&gt;             &lt;span class=&quot;c&quot;&gt;// ignoring delete event&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;59&lt;/span&gt;           &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;60&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;61&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;62&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;63&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;64&lt;/span&gt;   
&lt;span class=&quot;lineno&quot;&gt;65&lt;/span&gt;   &lt;span class=&quot;c&quot;&gt;// when an error happens&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;66&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;errorListener&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WatchedEvent&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Throwable&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;throwable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;67&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;warn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;Error detected in agent with ${event}&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;throwable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;68&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;ul&gt;
	&lt;li&gt;8-11: uses &lt;code&gt;ZKClient&lt;/code&gt; to establish a connection with ZooKeeper&lt;/li&gt;
	&lt;li&gt;13-23: creates a tracker, registers the listeners and starts the tracker&lt;/li&gt;
	&lt;li&gt;28-41: this listener gets called back whenever an agent appears/disappears from ZooKeeper. Agents use the ephemeral node capability of ZooKeeper which means that if the agent fails for any reason (ex: machine reboots), the ephemeral node will go away and as a result this listener will be called. &lt;div class=&quot;info&quot;&gt;&lt;img src=&quot;http://www.pongasoft.com/blog/yan/resource/info_48x48.png&quot; alt=&quot;info_48x48.png&quot; border=&quot;0&quot; width=&quot;21&quot; height=&quot;21&quot; /&gt; This is a very cheap and effective way to monitor the fact that a machine is still alive.&lt;/div&gt;&lt;/li&gt;
	&lt;li&gt;44-63: this listener gets called back whenever there are changes in the mountPoints installed on any agents. For the sake of monitoring we do not care about any other but &lt;code&gt;/monitor&lt;/code&gt; which is why we filter it out. The expression: &lt;code&gt;event.nodeInfo?.data?.scriptState?.script&lt;/code&gt; gives access to any of the fields of the glu script and in this case we are only interested in the &lt;code&gt;load&lt;/code&gt; field.&lt;/li&gt;
	&lt;li&gt;66-68: this listener gets called back whenever there are any errors (ex: loss of communication with ZooKeeper)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here is the result of running this program:&lt;br /&gt;
&lt;img src=&quot;http://www.pongasoft.com/blog/yan/resource/images/2011-03-18/monitor-external.png&quot; title=&quot;MonitorMain output&quot; alt=&quot;MonitorMain output&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Note that I intentionally killed the agent to demonstrate that I was receiving the event, which can be seen on line 5.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;This post shows you how to:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;create a glu script which installs a timer on an agent/host&lt;/li&gt;
	&lt;li&gt;run a monitoring command on the host and react based on the result (change the state)&lt;/li&gt;
	&lt;li&gt;propagate monitoring information to ZooKeeper&lt;/li&gt;
	&lt;li&gt;capture and handle this information in an external program&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In a real monitoring solution, you probably will want to do a lot more than capturing the load and displaying it on the standard output. But the concepts will remain the same. I think the beauty of this solution lies in the fact that:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;if you use glu for deployment then the infrastructure is already in place! And if you don&amp;#8217;t use glu (yet), then maybe you should consider using it as you can kill 2 birds with one stone!&lt;/li&gt;
	&lt;li&gt;it is event based so you get notified only when things change or in other words you don&amp;#8217;t need to ask every host what their status is: every host tell ZooKeeper what their status is and the external program is simply listening to ZooKeeper events.&lt;br /&gt;
&lt;img src=&quot;http://www.pongasoft.com/blog/yan/resource/images/2011-03-18/monitoring-conclusion.png&quot; title=&quot;Monitoring Overview&quot; alt=&quot;Monitoring Overview&quot; /&gt;&lt;/li&gt;
	&lt;li&gt;it is all open source and free.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;References&lt;/h3&gt;
&lt;ul&gt;
	&lt;li&gt;For more information about glu, check the &lt;a href=&quot;https://github.com/linkedin/glu/wiki&quot;&gt;wiki&lt;/a&gt;.&lt;/li&gt;
	&lt;li&gt;You can download glu including the tutorial from the &lt;a href=&quot;https://github.com/linkedin/glu/downloads&quot;&gt;download page&lt;/a&gt;.&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;#&quot; onclick=&quot;toggleShowHide('gist-877590-MonitorScript');return false;&quot;&gt;&lt;code&gt;MonitorScript&lt;/code&gt; source code&lt;/a&gt; from github.&lt;br /&gt;
&lt;div id=&quot;gist-877590-MonitorScript&quot; class=&quot;hidden&quot;&gt;&lt;script src=&quot;https://gist.github.com/877590.js?file=MonitorScript.groovy&quot;&gt;&lt;/script&gt;&lt;/div&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;#&quot; onclick=&quot;toggleShowHide('gist-877590-MonitorMain');return false;&quot;&gt;&lt;code&gt;MonitorMain&lt;/code&gt; source code&lt;/a&gt; from github.&lt;br /&gt;
&lt;div id=&quot;gist-877590-MonitorMain&quot; class=&quot;hidden&quot;&gt;&lt;script src=&quot;https://gist.github.com/877590.js?file=MonitorMain.groovy&quot;&gt;&lt;/script&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;</content>
 </entry>
 
 <entry>
   <title>Migrating my blog from JRoller 4.0.1 to Jekyll</title>
   <link href="http://www.pongasoft.com/blog/yan/misc/2011/03/02/migrating-jroller-401-to-jekyll/"/>
   <updated>2011-03-02T00:00:00-08:00</updated>
   <id>http://www.pongasoft.com/blog/yan/misc/2011/03/02/migrating-jroller-401-to-jekyll</id>
   <content type="html">&lt;p&gt;Let&amp;#8217;s face it, JRoller is dead. Like many others I have decided to move my blog away from it. I have decided to use &lt;a href=&quot;http://jekyllrb.com/&quot;&gt;Jekyll&lt;/a&gt; because it basically generates a static website so I don&amp;#8217;t need a java web server or a database to run my blog. The main issue of course is comments but there is a solution for this as well: &lt;a href=&quot;http://disqus.com/&quot;&gt;Disqus&lt;/a&gt;. This post relates my experience.&lt;/p&gt;
&lt;h3&gt;Extracting content from JRoller 4.0.1&lt;/h3&gt;
&lt;p&gt;The biggest problem to solve is how to extract the blog posts from JRoller including the comments. This is how I did it:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;Add a new template&lt;br /&gt;
In the roller interface, under the &lt;em&gt;Design&lt;/em&gt; tab,  &lt;em&gt;Templates&lt;/em&gt; subtab, I added a template. The template looks like this:&lt;br /&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;xml&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;posts&amp;gt;&lt;/span&gt;
#set($entries = $model.weblog.getRecentWeblogEntries(&amp;#39;nil&amp;#39;, 100))
#foreach( $entry in $entries )
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;post&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;$entry.id&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;permalink=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;$utils.escapeXML($entry.permalink)&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;$utils.escapeXML($entry.title)&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;date=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;$utils.formatIso8601Date($entry.pubTime)&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;tags=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;$utils.escapeXML($entry.tagsAsString)&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;category=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;$utils.escapeXML($entry.category.name)&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;content&amp;gt;&lt;/span&gt;$utils.escapeXML($entry.displayContent)&lt;span class=&quot;nt&quot;&gt;&amp;lt;/content&amp;gt;&lt;/span&gt;
    #set($comments = $entry.getComments(true, true))
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;comments&amp;gt;&lt;/span&gt;
    #foreach( $comment in $comments )
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;comment&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;$utils.escapeXML($comment.name)&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;email=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;$utils.escapeXML($comment.email)&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;url=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;$utils.escapeXML($comment.url)&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;remoteHost=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;$utils.escapeXML($comment.remoteHost)&amp;quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;date=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;$utils.formatIso8601Date($comment.postTime)&amp;quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;content&amp;gt;&lt;/span&gt;$utils.escapeXML($comment.content)&lt;span class=&quot;nt&quot;&gt;&amp;lt;/content&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;/comment&amp;gt;&lt;/span&gt;
    #end
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/comments&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/post&amp;gt;&lt;/span&gt;
#end
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/posts&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
	&lt;li&gt;Export the content&lt;br /&gt;
Note that on the page there is a text field called &lt;em&gt;Link&lt;/em&gt; and a link below. Simply use this link to export the content:&lt;br /&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;curl &amp;lt;link&amp;gt; &amp;gt; blog.xml
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now I have a file called &lt;code&gt;blog.xml&lt;/code&gt; which contains all my data.&lt;/p&gt;
&lt;h3&gt;Generating content for Jekyll&lt;/h3&gt;
&lt;p&gt;Jekyll requires you to create a specific directory structure with all your posts under a folder called &lt;code&gt;_posts&lt;/code&gt; with a specific naming convention. To generate the content I wrote a simple groovy script available on &lt;a href=&quot;https://gist.github.com/851246&quot;&gt;github&lt;/a&gt; (you may need to tweak the script for your own needs). The script uses the &lt;code&gt;blog.xml&lt;/code&gt; file previously generated and creates one file per blog post. It uses the &lt;em&gt;permalink&lt;/em&gt; tag so that the final link is the same as it was with JRoller!&lt;/p&gt;
&lt;p&gt;Click to view: &lt;a href=&quot;#&quot; onclick=&quot;toggleShowHide('gist-851246');return false;&quot;&gt;&lt;code&gt;jroller401_to_jekyll.groovy&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div id=&quot;gist-851246&quot; class=&quot;hidden&quot;&gt;&lt;script src=&quot;https://gist.github.com/851246.js?file=jroller401_to_jekyll.groovy&quot;&gt;&lt;/script&gt;&lt;/div&gt;
&lt;h3&gt;Importing comments in Disqus&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;http://disqus.com&quot;&gt;Disqus&lt;/a&gt; is a third party website which can host your comments. Thankfully the service has the capability to import comments formatted in &lt;span class=&quot;caps&quot;&gt;WPR&lt;/span&gt; xml format (which is a wordpress xml representation). I also wrote a simple groovy script to do the job. It is available on &lt;a href=&quot;https://gist.github.com/851313&quot;&gt;github&lt;/a&gt;. The script uses the &lt;code&gt;blog.xml&lt;/code&gt; file previously generated and ouputs the xml document.&lt;/p&gt;
&lt;p&gt;Click to view: &lt;a href=&quot;#&quot; onclick=&quot;toggleShowHide('gist-851313');return false;&quot;&gt;&lt;code&gt;jroller401_to_disqus.groovy&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div id=&quot;gist-851313&quot; class=&quot;hidden&quot;&gt;&lt;script src=&quot;https://gist.github.com/851313.js?file=jroller401_to_disqus.groovy&quot;&gt;&lt;/script&gt;&lt;/div&gt;
&lt;h3&gt;Final touches&lt;/h3&gt;
&lt;p&gt;Once the data was out of JRoller and in Jekyll and Disqus, I spent a lot of time (way too much :)), redesigning the blog, making it more up to date and adding a new social twist to it, integrating with Twitter and LinkedIn. In a way it is more dynamic than it ever was. Here are several interesting things I had to do with Jekyll:&lt;/p&gt;
&lt;h4&gt;1. Listing Categories&lt;/h4&gt;
&lt;p&gt;Although Jekyll supports the notion of categories, there is no native support to generate category pages. For this I used the &lt;a href=&quot;http://recursive-design.com/projects/jekyll-plugins&quot;&gt;generate_categories.rb&lt;/a&gt; plugin. I had to &lt;em&gt;enhance&lt;/em&gt; the plugin to list the categories (why isn&amp;#8217;t it something that Jekyll offers by default is beyond me&amp;#8230;): you can view my enhancement &lt;a href=&quot;https://github.com/ypujante/blog/blob/master/_plugins/generate_categories.rb&quot;&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# Yan addition: accessing the list of categories&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;alias&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;orig_site_payload&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;site_payload&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;site_payload&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;h&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;orig_site_payload&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;site&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;categories_list&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;categories&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sort&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;h&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Then I can simply use &lt;code&gt;site.categories_list&lt;/code&gt; in my code.&lt;/p&gt;
&lt;h4&gt;2. Dynamic css&lt;/h4&gt;
&lt;p&gt;Any file in Jekyll can be processed as a template file. This is what I did with my css: I defined various values in my config file:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;yaml&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# css related entries&lt;/span&gt;
&lt;span class=&quot;l-Scalar-Plain&quot;&gt;mainColor1&lt;/span&gt;&lt;span class=&quot;p-Indicator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;#39;#005a87&amp;#39;&lt;/span&gt;
&lt;span class=&quot;l-Scalar-Plain&quot;&gt;mainColor2&lt;/span&gt;&lt;span class=&quot;p-Indicator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;#39;#0075b0&amp;#39;&lt;/span&gt;
&lt;span class=&quot;l-Scalar-Plain&quot;&gt;contentBGColor&lt;/span&gt;&lt;span class=&quot;p-Indicator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;#39;#eeeeee&amp;#39;&lt;/span&gt;
&lt;span class=&quot;l-Scalar-Plain&quot;&gt;contentFGColor&lt;/span&gt;&lt;span class=&quot;p-Indicator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;#39;black&amp;#39;&lt;/span&gt;
&lt;span class=&quot;l-Scalar-Plain&quot;&gt;titleBGColor&lt;/span&gt;&lt;span class=&quot;p-Indicator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;#39;#600000&amp;#39;&lt;/span&gt;
&lt;span class=&quot;l-Scalar-Plain&quot;&gt;titleFGColor&lt;/span&gt;&lt;span class=&quot;p-Indicator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;#39;white&amp;#39;&lt;/span&gt;
&lt;span class=&quot;l-Scalar-Plain&quot;&gt;contentFontFamily&lt;/span&gt;&lt;span class=&quot;p-Indicator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;#39;Helvetica,Arial,sans-serif&amp;#39;&lt;/span&gt;
&lt;span class=&quot;l-Scalar-Plain&quot;&gt;monospaceFontFamily&lt;/span&gt;&lt;span class=&quot;p-Indicator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;#39;Courier&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;New,monospace&amp;#39;&lt;/span&gt;
&lt;span class=&quot;l-Scalar-Plain&quot;&gt;dateBackgroundColor&lt;/span&gt;&lt;span class=&quot;p-Indicator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;#39;#888888&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;and simply use them in my css file:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text&quot;&gt;---
---
a:visited {
  text-decoration: none;
  color: {{ site.mainColor1 }};
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Of course my css editor is unhappy about the invalid syntax but it totally works. The first 2 lines are the marker to tell Jekyll to process this file as a template and are very important. You can take a look at the &lt;a href=&quot;https://github.com/ypujante/blog/blob/master/resource/main.css&quot;&gt;css&lt;/a&gt; file.&lt;/p&gt;
&lt;h4&gt;3. Reusing the post layout&lt;/h4&gt;
&lt;p&gt;I wanted that the latest blog post, displayed on the home page, look exactly the same as it looks on each individual blog page without having to copy/paste the code. For this I used an include but it is pretty tricky (I literally spent a full day on this :(). Here is how I did it:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text&quot;&gt;_layouts/post.html:
# the post.html layout simply includes another template
---
layout: default
---
{% include post_include.html %}


index.html:
# the index.html main home page includes the same template
{% for post in site.posts limit:1 %}
{% include post_include.html %}
{% endfor %}


_includes/post_include.html:
# now for the tricky part: the template needs to have some conditional!
{% unless post %}
  {% assign post = page %}
{% else %}
  {% assign content = post.content %}
{% endunless %}
&amp;lt;div class=&amp;quot;post-title&amp;quot;&amp;gt;
  {{ post.title }}
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;post-content&amp;quot;&amp;gt;
  {{ content }}
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3&gt;Source code&lt;/h3&gt;
&lt;p&gt;The entire source code of this blog is available on &lt;a href=&quot;https://github.com/ypujante/blog&quot;&gt;github&lt;/a&gt; and is available under a &lt;a href=&quot;http://creativecommons.org/licenses/by/3.0/&quot;&gt;Commons Creative license&lt;/a&gt;.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>The nightmares of setting up a wireless N network</title>
   <link href="http://www.pongasoft.com/blog/yan/entry/the_nightmares_of_setting_up"/>
   <updated>2011-02-12T00:00:00-08:00</updated>
   <id>http://www.pongasoft.com/blog/yan/entry/the-nightmares-of-setting-up</id>
   <content type="html">After converting the guest room into a second office, I was faced with a dilemma: the internet is in one office but of course both offices need to access the internet. My home is fairly recent and as a result, extremely well wired. The only catch is that it is wired for coax and phone jacks but not for cat5 (we don't even use land line phones anymore!).

&lt;p&gt;
We opted for upgrading our network to wireless N (it was G) so that my MacBook Air could benefit from it and my desktop could also get good throughput.

&lt;p&gt;
First nightmare: I now own an 'old' Mac Pro. It is a dual Xeon Quad-Core 2.8Ghz with 14GB of memory purchased in early 2008. The catch is that all 'new' Apple computers now come with wireless built-in including the Desktops. And of course Apple is not selling a wireless N card (or B or G for that matter): this is too cheap and really Apple would prefer you to buy a new Mac. A trip to my local Apple Store revealed totally clueless employees who clearly did not give a damn about the 'past' and the only thing they cared about was for you to buy a brand new machine because it comes with wireless built-in (why would you buy a $100 card when you can buy a $3000 brand new Mac ? Really ?).

&lt;p&gt;
In the end it is the traditional pattern: there are a ton of choices if you own a PC. But if you own a Mac and it is not Apple supported, it is really hard/impossible to find anything decent. You can pretty much count on your fingers the number of available options for the Mac. And when you start adding more constraints, like 5Ghz then the number of choices drops even further.

&lt;p&gt;
So the solution that we opted for was to install a dual-band wireless N router that would be connected to the internet in one office and a wireless N bridge (5Ghz) in the other office. That way the Mac is connected to the wireless network through ethernet, thus being transparent to the Mac.

&lt;p&gt;
Second nightmare: have you shopped lately for anything wireless N ? This is a freaking nightmare wrapped into layers of marketing bullshit. N600, extreme, gigabit, ultimate, 12x, 300Mbps... you name it. It is claim after claim of false advertising. I am not entirely sure how this is even legal. I would just like to remind DLink, Netgear, Cisco/Lynksis and others that the fastest you can go will always be limited by the slowest link in the chain. So when you claim that your devide goes up 300Mbps and the ethernet jack is regular ethernet (100Mbps), then really you can't go faster than 100! If you have tried to buy a digital camera it is the same kind of marketing bullshit with a meaningless race on the megapixel count.

&lt;p&gt;
From my experience, with all the devices I tried, the fastest I ever saw with the devices as close as possible to each other (touching each other antennas!) I could reach ~140Mbps...

&lt;p&gt;
In the process, we ended up trying 5 different devices! 

&lt;p&gt;
First the 3 we did not keep:

&lt;ul&gt;
&lt;li&gt;
&lt;a href=&quot;http://www.amazon.com/gp/product/B000LIFB7S?ie=UTF8&amp;tag=pongasoft-20&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=B000LIFB7S&quot;&gt;DLink DIR-855&lt;/a&gt; which is a wireless N router with 4 gigabit ports. The major issue with this one was that the best reception I could get with my laptop touching the antenna was ~70%. Also the maximum transfer rate I ever saw was ~72Mbps in the best case scenario possible.
&lt;/li&gt;
&lt;li&gt;
&lt;a href=&quot;http://www.amazon.com/gp/product/B001769K3O?ie=UTF8&amp;tag=pongasoft-20&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=B001769K3O&quot;&gt;DLink DAP-1522&lt;/a&gt; which is a wireless N access point / bridge. This one has a serious issue: there is 1 page in the configuration menu (web based) which requires Internet Explorer to work. This is a very well known bug that has numerous people complaining about and it has been reported more than a year ago. DLink does not bother to fix it nor even change the text on the box or the web site still claiming it works for Windows and Mac with IE or Firefox. It is just a lie. It does not. Also during my experiments it seemed very flaky as it required many reboots.
&lt;/li&gt;
&lt;li&gt;
&lt;a href=&quot;http://www.amazon.com/gp/product/B001QVQ7JU?ie=UTF8&amp;tag=pongasoft-20&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=B001QVQ7JU&quot;&gt;Cisco/Linksys WET610N&lt;/a&gt; which is a dual band wireless N bridge. It is branded as a wireless gaming and video adapter but really it is bridge. The one I had was literally a piece of garbage (but to be fair I do not know if it was the unit I had that was defective or if it is really the way it works). The web based interface is slower than christmas with (I am not kidding!) a wait of over 10s to go from page to page! Not to change anything... just going from page to page! We had a really hard time to make it connect to the router and we finally changed the 5Ghz channel on the router and the bridge could finally see it (this is totally undocumented nor configurable in this device). Also the wizard would not accept our passphrase because it contains spaces (hello! a pass&lt;em&gt;phrase&lt;/em&gt;!). We managed to make it connect to the router but for no more than 5s: it would constantly connect/disconnect over and over. Note that during this experiment, the device was literally touching the router so it could hardly be closer!
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Then the 2 we ended up keeping:
&lt;ul&gt;
&lt;li&gt;
&lt;a href=&quot;http://www.amazon.com/gp/product/B0049YQVHE?ie=UTF8&amp;tag=pongasoft-20&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=B0049YQVHE&quot;&gt;Asus RT-N56U&lt;/a&gt; which is a dual band gigabit wireless N router. I know that it is supposed to look nice, but to be fair I prefer when it is more badass looking (I really like the blue Netgear switches with the connectors in the front :). This one is very impressive. The UI is actually really nice and it is a breath of fresh air. The same test done with the laptop sitting right next to it gave very different results: the reception was 98% and the transfer rate was ~140Mpbs. It also has 2 USB ports and we ended up buying a cheap 16GB USB key at Micro Center and plugging it directly in the router as our common storage. Very sweet and works like a charm. It has been working great since we had it. This one is highly recommended.
&lt;/li&gt;
&lt;li&gt;
&lt;a href=&quot;http://www.amazon.com/gp/product/B001P817YO?ie=UTF8&amp;tag=pongasoft-20&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=B001P817YO&quot;&gt;DLink DAP-2553&lt;/a&gt; which is a dual band access point but can also act as a bridge. This one is definitely more expensive and is in the business line so it is not as customer friendly. It definitely looks badass especially the antennas. Although not a customer oriented product, it was a million time easier to make it work than the stupid wireless gaming adapter! We simply selected to work as a bridge, entered the WPA2 key and voila... it just worked. And sitting in the kitchen which is really far from the router was getting 60% reception! Amazing! Also the Ethernet connection is Gigabit (it is not even mentioned on the box!). It has a lot more feature than I need, as it can run as a full access point, acting as a DHCP server, etc... It has been rock solid since we installed it.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Clearly it has been quite an adventure and I am still unclear why I can never see higher than ~140Mbps transfer rate. The reality is my Internet connection is less than 20Mbps so it will never be an issue with the Internet. If I want to transfer big files I can simply add a gigabit switch (which I already own) to my access point so that the transfer go through the switch.

&lt;p&gt;
In conclusion I wanted to emphasize 2 points:

&lt;li&gt;In this case more expensive really meant better quality. I ended up paying ~$160 for the Access Point but it is worth it! (I know it is cheaper on Amazon but I could buy it right away and return it quickly in the event it did not work) &lt;/li&gt;
&lt;li&gt;Make sure you buy at a store where you can easily return! Clearly Amazon is awesome in that regards but the turn around is way too slow. I ended up buying at Fry's. It was more expensive but I could buy and return several items the same day!&lt;/li&gt;</content>
 </entry>
 
 <entry>
   <title>LinkedEnd...</title>
   <link href="http://www.pongasoft.com/blog/yan/entry/linkedend"/>
   <updated>2010-12-07T00:00:00-08:00</updated>
   <id>http://www.pongasoft.com/blog/yan/entry/linkedend</id>
   <content type="html">When I look at &lt;a href=&quot;http://www.linkedin.com/company/1337&quot;&gt;LinkedIn's company page&lt;/a&gt; as of December 2010, I can see that the company has now 1000+ employees spread all around the world.

&lt;p&gt;
When I started at LinkedIn, 8 years ago, there wasn't such a page to give me information about LinkedIn. LinkedIn simply did not exist. Period. 

&lt;a href=&quot;http://www.youtube.com/watch?v=bkQI5ZuGuEU&quot;&gt;&lt;img src=&quot;http://www.pongasoft.com/blog/yan/resource/InBear.jpg&quot; alt=&quot;InBear.jpg&quot; border=&quot;0&quot; width=&quot;198&quot; height=&quot;148&quot; style=&quot;float:right;padding-left: 0.5em;&quot;/&gt;&lt;/a&gt;

&lt;p&gt;
Looking back, this is the true American Dream/Silicon Valley-garage-startup deal! Of course we did not really work in a garage. We started meeting in &lt;a href=&quot;http://www.linkedin.com/in/reid&quot;&gt;Reid&lt;/a&gt;'s apartment in downtown Mountain View (CA) with a &lt;a href=&quot;http://www.linkedin.com/static?key=founders&quot;&gt;handful of people&lt;/a&gt;. We had no office. We did not even have a name for the company (I am so glad we did not pick 'getin.com' which was on the shortlist!)

&lt;p&gt;
We just started from there and worked really hard. Official launch date was Cinco de Mayo 2003. The rest is history.

&lt;div style=&quot;clear:both;margin-top:2em;&quot;&gt;
Fast forward 8 years later. I have decided to leave. It is never an easy decision after all these years but I leave happy and extremely proud of what I have been able to accomplish at LinkedIn. I am not going anywhere, just taking some time off, for a little while. I have various projects that I will continue working on like my own website &lt;a href=&quot;http://www.kiwidoc.com&quot;&gt;kiwidoc&lt;/a&gt; or the &lt;a href=&quot;http://www.github.com/linkedin&quot;&gt;open source projects&lt;/a&gt; I just released. But more importantly, I also want to relax and enjoy life.
&lt;/div&gt;

&lt;h3&gt;My most favorite projects&lt;/h3&gt;
Throughout my career at LinkedIn, I have had the chance to work on a wide variety of projects. Here is a compilation of the ones that matter the most to me, either because they were particularly challenging or simply because they were solid building blocks on which a lot of the infrastructure was built.

&lt;ul&gt;
&lt;li&gt;
Resource framework: an easily extensible framework which hides the location of resources (files) no matter where they are located (local filesystem, in a local jar file, in a remote jar file, within the servlet context, on the classpath, in multiple locations...). This framework is part of the &lt;a href=&quot;http://www.github.com/linkedin/linkedin-utils&quot;&gt;linkedin-utils&lt;/a&gt; open source effort (&lt;a href=&quot;http://www.kiwidoc.com/java/l/p/org.linkedin/org.linkedin.util-core/1.0.0/p/org.linkedin.util.io.resource&quot;&gt;javadoc&lt;/a&gt;, &lt;a href=&quot;https://github.com/linkedin/linkedin-utils&quot;&gt;source&lt;/a&gt;). #java
&lt;/li&gt;
&lt;li&gt;
jsp20 / EL compiler: a highly efficient implementation of jsp20 and expression language from the ground up (uses Resource framework for easy development and centralized deployment) used at LinkedIn for generating the html. #java, #javacc, #compiler
&lt;/li&gt;
&lt;li&gt;
UI Framework: the framework used by the frontend applications to deal with forms and requests. #java
&lt;/li&gt;
&lt;li&gt;
&lt;a href=&quot;http://slidesha.re/cOc4R0&quot;&gt;LinkedIn Spring&lt;/a&gt;: built on top of Spring Framework 2.5 to support the notion of components (concept of nested beans). The core of the wiring system used at LinkedIn. #java, #spring
&lt;/li&gt;
&lt;li&gt;
GoBack framework: a framework that manages the concept of go back in a web application (after following a flow of several screens, you complete an action which brings you back to where you started, no matter where the starting point was) used on the website. #java
&lt;/li&gt;
&lt;li&gt;
Databus: a highly scalable database replication system which also offers an event based interface used for event propagation and replication in LinkedIn's infrastructure. #java, #jdbc
&lt;/li&gt;
&lt;li&gt;
Cloud Server: the initial scaleable implementation of the graph engine powering LinkedIn. #java, #graph
&lt;/li&gt;
&lt;li&gt;
Search Engine: the initial scaleable implementation of the search engine powering LinkedIn. #java, #lucene
&lt;/li&gt;
&lt;li&gt;
Comm system: initial design and prototype of the comm/inbox system. #ruby
&lt;/li&gt;
&lt;li&gt;
i18n profile: a massive effort to handle extensible profiles and internationalization. #java, #i18n
&lt;/li&gt;
&lt;li&gt;
&lt;a href=&quot;http://www.softwaresummit.com/2008/speakers/presentations/PujanteSecurity2.0.pdf&quot;&gt;Authentication server&lt;/a&gt;: design and implementation of the authentication server used for security at LinkedIn. #java, #security
&lt;/li&gt;
&lt;li&gt;
Payment System: initial design and implementation of the payment system which processes the credit card transactions. #java, #jms, #castor
&lt;/li&gt;
&lt;li&gt;
&lt;a href=&quot;http://slidesha.re/ddoHa4&quot;&gt;OSGi investigations&lt;/a&gt;: investigations and prototyping of an OSGi based infrastructure. #osgi
&lt;/li&gt;
&lt;li&gt;
Media server: design of the media server, the server which processes and delivers the pictures (and other media) on the website. #java
&lt;/li&gt;
&lt;li&gt;
glu: the deployment automation platform used at LinkedIn to provision (efficiently and in parallel) the services on multiple data centers that comprise the LinkedIn system. This project is now &lt;a href=&quot;http://www.github.com/linkedin/glu&quot;&gt;open source&lt;/a&gt;. #java, #groovy, #rest, #zookeeper, #grails, #devops
&lt;/li&gt;
&lt;li&gt;and many many more...&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;
A friend of mine was pointing out that I am no longer a LinkedIn employee, but I will always remain a LinkedIn co-founder, associated for the rest of my life to this amazing experience. I am extremely grateful to all the people that were (and still are) involved in this dream, because without them, it would not have been possible.
</content>
 </entry>
 
 <entry>
   <title>Automating publishing to maven central via Sonatype</title>
   <link href="http://www.pongasoft.com/blog/yan/entry/automating_publishing_to_maven_central"/>
   <updated>2010-11-27T00:00:00-08:00</updated>
   <id>http://www.pongasoft.com/blog/yan/entry/automating-publishing-to-maven-central</id>
   <content type="html">After releasing several &lt;a href=&quot;https://www.github.com/linkedin&quot;&gt;open-source projects&lt;/a&gt; which are not using maven for the build framework (they use &lt;a href=&quot;http://gradle.org/&quot;&gt;gradle&lt;/a&gt;), I was faced with the harsh reality that if you don't use maven, it is pretty hard to automate the process. The current version of gradle allows you to upload to a maven compatible repo, but to upload to maven central there are some constraints (like pgp signing) that gradle does not (currently) support.

In order to publish your artifacts to maven central via Sonatype (unclear if there is another way to be honest), you need to follow the &lt;a href=&quot;https://docs.sonatype.org/display/Repository/Sonatype+OSS+Maven+Repository+Usage+Guide&quot;&gt;instructions&lt;/a&gt; on how to set it up (you need to create a &lt;a href=&quot;https://issues.sonatype.org/browse/OSSRH&quot;&gt;Jira Ticket&lt;/a&gt;). You also need to have a pgp key and if you don't, you can follow these &lt;a href=&quot;http://www.sonatype.com/people/2010/01/how-to-generate-pgp-signatures-with-maven/&quot;&gt;instructions&lt;/a&gt;.

Using gradle, you can simply create a maven compatible layout of your artifacts:
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;c&quot;&gt;// example for one of my project&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;uploadArchives&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;repositories&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;mavenDeployer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;url:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;file://localhost/my/local/repo&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;pom&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;whenConfigured&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pomToConfigure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;pomToConfigure&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;A useful set of gradle plugins&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;http://github.com/linkedin/gradle-plugins&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;licenses&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;license&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;The Apache Software License, Version 2.0&amp;quot;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;http://www.apache.org/licenses/LICENSE-2.0&amp;quot;&lt;/span&gt;
          &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;developers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;developer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;ypujante&amp;quot;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;Yan Pujante&amp;quot;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;email&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;yan@pongasoft.com&amp;quot;&lt;/span&gt;
          &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;scm&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;connection&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;scm:git:${spec.scmUrl}&amp;quot;&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;developerConnection&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;scm:git:${spec.scmUrl}&amp;quot;&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;scmUrl&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

Note that a big part of the previous code is setting up the right information in the pom file to comply with the set of &lt;a href=&quot;https://docs.sonatype.org/display/Repository/Sonatype+OSS+Maven+Repository+Usage+Guide#SonatypeOSSMavenRepositoryUsageGuide-6.CentralSyncRequirement&quot;&gt;requirements&lt;/a&gt;. Also note that you do &lt;b&gt;not&lt;/b&gt; need to use gradle at all as long as you generate a maven compatible layout (with pom files).

You can then use the script I have 'gisted' on github: &lt;a href=&quot;https://gist.github.com/708980&quot;&gt;maven_staging_upload.groovy&lt;/a&gt;. The script simply takes the pom files you want to upload as an input and will prompt you for your pgp passphrase only once! Make sure you follow the initial setup:
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;c&quot;&gt;/**&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt; * This script uploads all your artifacts to maven.&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt; * &lt;/span&gt;
&lt;span class=&quot;c&quot;&gt; * To run this script there are several asumptions:&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt; * - gpg is installed and available in the path&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt; * - you have followed the instructions to set up your pgp key&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt; *   (http://www.sonatype.com/people/2010/01/how-to-generate-pgp-signatures-with-maven/)&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt; * - maven is installed and available in the path&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt; * - you have defined the proper server section in your ~/.m2/settings.xml file:&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt; *   &amp;lt;settings&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt; *    ...&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt; *    &amp;lt;servers&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt; *       &amp;lt;server&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt; *        &amp;lt;id&amp;gt;nexus-releases&amp;lt;/id&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt; *        &amp;lt;username&amp;gt;ypujante&amp;lt;/username&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt; *        &amp;lt;password&amp;gt;********&amp;lt;/password&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt; *       &amp;lt;/server&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt; *     &amp;lt;/servers&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt; *    ...&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt; *   &amp;lt;/settings&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt; *&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt; * You run the script this way: groovy maven_staging_upload.groovy pathToPomFile1 pathToPomFile2...&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt; *&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt; * groovy&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt; */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

You then need to login to &lt;a href=&quot;https://oss.sonatype.org/index.html#welcome&quot;&gt;Nexus&lt;/a&gt; to verify that the upload worked properly. You can then close the staging repository and release it. It will make its way to maven central shortly. The very first time, you need to &lt;a href=&quot;https://docs.sonatype.org/display/Repository/Sonatype+OSS+Maven+Repository+Usage+Guide#SonatypeOSSMavenRepositoryUsageGuide-9.ActivateCentralSync&quot;&gt;activate central sync&lt;/a&gt;!

I guess it is not fully automated as you still need to manually close/release the staging repository but the painful part (the part about uploading the artifacts to staging) is fully automated thanks to gradle and the script.

You can use the &lt;a href=&quot;https://github.com/linkedin/gradle-plugins&quot;&gt;gradle plugins&lt;/a&gt; that I open sourced to help with the release: the &lt;code&gt;org.linkedin.repository&lt;/code&gt; and &lt;code&gt;org.linkedin.release&lt;/code&gt; plugin can simplify your build code (for example the plugin takes care of the javadoc and sources requirements).

Note that this script can be used totally independently of gradle and you do not need to use gradle to use it!

Thanks to Jakub Holy for this &lt;a href=&quot;http://theholyjava.wordpress.com/2010/02/07/releasing-a-project-to-maven-centr/&quot;&gt;blog post&lt;/a&gt; which helped me in understanding the steps I needed to follow in order to automate them.

I am also hopeful that the 1.0 release of gradle will render my script obsolete!</content>
 </entry>
 
 <entry>
   <title>Using gradle with grails</title>
   <link href="http://www.pongasoft.com/blog/yan/entry/using_gradle_with_grails"/>
   <updated>2010-08-28T00:00:00-07:00</updated>
   <id>http://www.pongasoft.com/blog/yan/entry/using-gradle-with-grails</id>
   <content type="html">I just recently migrated my entire &lt;a href=&quot;http://www.kiwidoc.com/java&quot;&gt;kiwidoc&lt;/a&gt; project to use &lt;a href=&quot;http://www.gradle.org/&quot;&gt;gradle&lt;/a&gt; for its build framework (replacing maven). The biggest challenge I faced in the exercise was to have grails work with gradle. I spent way too much time on this part due to my lack of deep knowledge of each framework but in the end it is extremely simple. The reason why it was challenging in the first place is that my project is a multi-project build as gradle calls it (see &lt;a href=&quot;http://www.gradle.org/0.9-rc-1/docs/userguide/multi_project_builds.html&quot;&gt;Chapter 36&lt;/a&gt;). What I wanted was that my other projects be recompiled before starting grails to get the latest version.

There are 2 ways you can go about it:

&lt;h4&gt;1. Use the grails gradle plugin&lt;/h4&gt;
I actually opened a &lt;a href=&quot;http://jira.codehaus.org/browse/GRAILS-6621&quot;&gt;Jira Ticket&lt;/a&gt; for this and within a couple of days there was already a solution out there. The details are on the jira ticket and on the &lt;a href=&quot;http://github.com/grails/grails-gradle-plugin&quot;&gt;github project&lt;/a&gt;. Here is how you use it:

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;c&quot;&gt;// external plugin&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;buildscript&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;repositories&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;mavenCentral&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;mavenRepo&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;urls:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;http://repository.jboss.org/maven2/&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;dependencies&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;classpath&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;org.grails:grails-gradle-plugin:1.0&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;&amp;quot;org.grails:grails-bootstrap:${grailsVersion}&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;apply&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;plugin:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;grails&amp;quot;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;repositories&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;mavenRepo&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;urls:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;http://repository.jboss.org/maven2/&amp;quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;dependencies&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;compile&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;org.grails:grails-crud:${grailsVersion}&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;compile&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;org.grails:grails-gorm:${grailsVersion}&amp;quot;&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;compile&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;:kiwidoc:com.pongasoft.kiwidoc.index.api&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;compile&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;:kiwidoc:com.pongasoft.kiwidoc.index.impl&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;compile&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;:kiwidoc:com.pongasoft.kiwidoc.builder&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;compile&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;:utils:util.html&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;runtime&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;org.slf4j:slf4j-log4j12:1.5.8&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;runtime&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;org.aspectj:aspectjrt:1.6.8&amp;quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;tt&gt;grailsVersion&lt;/tt&gt; is a variable defined in my 'root' build script (&lt;tt&gt;grailsVersion=&quot;1.3.4&quot;&lt;/tt&gt;). As can be seen on this short example, I have dependencies to my other projects built by gradle as well. Please note the extra dependency on aspectj due to an improper pom file :(.

&lt;h4&gt;2. Use gradle to build the classpath&lt;/h4&gt;
Solution #1 works great but everything you do needs to go through gradle and it also downloads a new version of grails. So if you already have grails installed there is another solution:

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;n&quot;&gt;apply&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;plugin:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;groovy&amp;quot;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;dependencies&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;compile&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;:kiwidoc:com.pongasoft.kiwidoc.index.api&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;compile&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;:kiwidoc:com.pongasoft.kiwidoc.index.impl&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;compile&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;:kiwidoc:com.pongasoft.kiwidoc.builder&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;compile&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;:utils:util.html&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;nl&quot;&gt;groovy:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;org.codehaus.groovy:groovy:1.7.4&amp;quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;lib&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;dependsOn:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;fileTree&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;dir:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;lib&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;copy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configurations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;runtime&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;into&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;lib&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;run-app&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;war&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;taskName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;${taskName}&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;dependsOn:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lib&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ant&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;dir:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;.&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;executable:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;grails&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;arg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;line:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;${taskName}&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

By declaring the &lt;tt&gt;lib&lt;/tt&gt; task to depend on the &lt;tt&gt;jar&lt;/tt&gt; task, the groovy plugin will automatically build my classpath which I copy in the &lt;tt&gt;lib&lt;/tt&gt; folder that grails know about. I can then simply invoke the &lt;tt&gt;grails&lt;/tt&gt; command line (which is assumed to be in your path). The beauty with this solution is you can simply run &lt;tt&gt;gradle lib&lt;/tt&gt; and once you are done, you can go on the command line and simply run &lt;tt&gt;grails&lt;/tt&gt; directly as now all the dependencies you need are where &lt;tt&gt;grails&lt;/tt&gt; expects them.

If you want to run the app with gradle, you need to use &lt;tt&gt;gradle -i run-app&lt;/tt&gt; otherwise you won't see any of the messages that grails log.</content>
 </entry>
 
 <entry>
   <title>ZooKeeper loss of events problem... fixed</title>
   <link href="http://www.pongasoft.com/blog/yan/entry/zookeeper_loss_of_events_problem"/>
   <updated>2010-07-24T00:00:00-07:00</updated>
   <id>http://www.pongasoft.com/blog/yan/entry/zookeeper-loss-of-events-problem</id>
   <content type="html">In my latest project at LinkedIn, I have been using &lt;a href=&quot;http://hadoop.apache.org/zookeeper/&quot;&gt;ZooKeeper&lt;/a&gt; to track the state of all deployed services on every machine in production. The state is then used to drive the deployment as well as monitor the system.

I ran into a really tricky bug which took me several months to address (the hardest part was finding what the actual problem was).

The (simplified) code was the following:

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;n&quot;&gt;ZooKeeper&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zk&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[:]&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;synchronized&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trackChildren&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// handle add/delete of children&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Map&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;trackChildren&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Map&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newState&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;children&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getChildren&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;/state&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;childrenWatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Watcher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;child&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oldState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// ADD&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;trackChild&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;/state/${child}&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; 
      &lt;span class=&quot;n&quot;&gt;newState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// no change&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;// DELETE: newState does not contain the children that have disappeared&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newState&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// handle child update&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;trackChild&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Map&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;child&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getData&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;childWatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Watcher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NoNodeException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;  &lt;span class=&quot;c&quot;&gt;/* ok node is gone */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;childrenWatcher&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WatchedEvent&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; 
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;NodeChildrenChanged&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;synchronized&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trackChildren&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;childWatcher&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WatchedEvent&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; 
  &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;child&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;synchronized&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;NodeDataChanged&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; 
      &lt;span class=&quot;n&quot;&gt;trackChild&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NodeDeleted&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

What this code essentially does is the following: 
&lt;ul&gt;
 &lt;li&gt;Sets a children watcher on &lt;tt&gt;/state&lt;/tt&gt; to be notified of children addition/deletion&lt;/li&gt;
 &lt;li&gt;For each child, sets a node watcher to be notified of child modification and deletion. Note that you need this watcher because ZooKeeper will not call the parent watcher when a child gets modified!&lt;/li&gt;
 &lt;li&gt;As a result, the map called &lt;tt&gt;state&lt;/tt&gt; always contain a 'copy' of the data of all children.&lt;/li&gt;
&lt;/ul&gt;

From the ZooKeeper &lt;a href=&quot;http://hadoop.apache.org/zookeeper/docs/r3.2.1/zookeeperProgrammers.html#ch_zkWatches&quot;&gt;documentation&lt;/a&gt;, there are some key concepts to remember about watches:
&lt;em&gt;
&lt;ol&gt;
&lt;li&gt;Watches are one time triggers; if you get a watch event and you want to get notified of future changes, you must set another watch.&lt;/li&gt;
&lt;li&gt; Because watches are one time triggers and there is latency between getting the event and sending a new request to get a watch you cannot reliably see every change that happens to a node in ZooKeeper. Be prepared to handle the case where the znode changes multiple times between getting the event and setting the watch again. (You may not care, but at least realize it may happen.)&lt;/li&gt;
&lt;li&gt;A watch object, or function/context pair, will only be triggered once for a given notification. For example, if the same watch object is registered for an exists and a getData call for the same file and that file is then deleted, the watch object would only be invoked once with the deletion notification for the file.&lt;/li&gt;
&lt;/ol&gt;
&lt;/em&gt;

The code I wrote seems to handle #1 perfectly. On the other end, it does not handle #2 properly and this is where the bug gets triggered.

I ran into a situation where a server would delete and rewrite the data very fast:
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;child1State&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;zk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;/state/child1&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;zk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setData&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;/state/child1&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;child1State&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

In this scenario, I would receive 2 events:
&lt;ol&gt;
  &lt;li&gt;&lt;tt&gt;/state&lt;/tt&gt; childrenWatcher would be fired with a &lt;tt&gt;NodeChildrenChanged&lt;/tt&gt; event. And the bug gets triggered here. According to key concept #2, essentially events can be collapsed: in this case I am getting only 1 &lt;tt&gt;NodeChildrenChanged&lt;/tt&gt; event and when I list my children (&lt;tt&gt;getChildren&lt;/tt&gt;), I actually see no difference: child1 has been removed and added, but I don't see it. I am effectively loosing the 'add' child event with the code written this way. &lt;/li&gt;
&lt;li&gt;&lt;tt&gt;/state/child1&lt;/tt&gt; childWatcher would we fired with a &lt;tt&gt;NodeDeleted&lt;/tt&gt; event resulting in &lt;tt&gt;state.remove('child1')&lt;/tt&gt;... at this point I have indeed lost the state of &lt;tt&gt;child1&lt;/tt&gt;.&lt;/li&gt;
&lt;/ol&gt;

With the following code on the server side:
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;child1State&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;zk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;/state/child1&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5000&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;zk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setData&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;/state/child1&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;child1State&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/li&gt;
I actually get 2 events and I have enough time to process them so it works. But that is clearly not the right fix.

In order to fix the problem, I did 2 things:
&lt;ol&gt;
  &lt;li&gt;The children watcher is only dealing with 'ADDs' and does not handle 'DELETE' anymore&lt;/li&gt;
  &lt;li&gt;The child watcher handles 'DELETE' but also tries to set a watcher no matter what&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;java&quot;&gt;&lt;span class=&quot;n&quot;&gt;ZooKeeper&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zk&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[:]&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;synchronized&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trackChildren&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// handle ADD only... let the children watcher handle DELETE&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Map&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;trackChildren&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Map&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oldState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newState&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oldState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;children&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getChildren&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;/state&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;childrenWatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Watcher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;child&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 
      &lt;span class=&quot;n&quot;&gt;trackChild&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;/state/${child}&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newState&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// handle child update&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;trackChild&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Map&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;child&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zk&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getData&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;childWatcher&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Watcher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NoNodeException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;  &lt;span class=&quot;c&quot;&gt;/* ok node is gone */&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;childrenWatcher&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WatchedEvent&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; 
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;NodeChildrenChanged&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;synchronized&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trackChildren&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;childWatcher&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WatchedEvent&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; 
  &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;child&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;synchronized&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;NodeDataChanged&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;trackChild&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NodeDeleted&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; 
      &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;c&quot;&gt;// WARNING!!! UNINTUITIVE BUT CRITICAL!!!&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;trackChild&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;c&quot;&gt;// WARNING!!! UNINTUITIVE BUT CRITICAL!!!&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

The code (in the childWatcher) most likely looks very unintuitive, but what is really happening in this case, is that the child on which the watcher is set will always receive the &lt;tt&gt;NodeDeleted&lt;/tt&gt; event and according to key concept #1, if you want to be able to receive further events you need to set another watcher. It seems unintuitive to set a watcher on a node that just got deleted (and most of the time it will trigger a &lt;tt&gt;NoNodeException&lt;/tt&gt; which is handled properly), but between the time the node was deleted and the time you check, it may very well have been recreated in which case you guarantee that you will have a watcher set on it and you won't loose it. If the node was deleted and not recreated right away, then the childrenWatcher will be the one setting it up again in the future if it gets recreated (pay close attention to the synchronization in the code as it is pretty critical for the system to work properly). Here is an example:

&lt;ul&gt;
&lt;li&gt;/state/child1 simply deleted&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol&gt;
 &lt;li&gt;&lt;tt&gt;childrenWatcher&lt;/tt&gt; receives &lt;tt&gt;NodeChildrenChanged&lt;/tt&gt; =&gt; does nothing&lt;/li&gt;
 &lt;li&gt;&lt;tt&gt;childWatcher&lt;/tt&gt; receives &lt;tt&gt;NodeDeleted&lt;/tt&gt; =&gt; state.remove('child1')&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
or
&lt;li&gt;
&lt;ol&gt;
 &lt;li&gt;&lt;tt&gt;childWatcher&lt;/tt&gt; receives &lt;tt&gt;NodeDeleted&lt;/tt&gt; =&gt; state.remove('child1')&lt;/li&gt;
 &lt;li&gt;&lt;tt&gt;childrenWatcher&lt;/tt&gt; receives &lt;tt&gt;NodeChildrenChanged&lt;/tt&gt; =&gt; does nothing&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;/state/child1 deleted / recreated (events get collapsed)&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol&gt;
 &lt;li&gt;&lt;tt&gt;childrenWatcher&lt;/tt&gt; receives &lt;tt&gt;NodeChildrenChanged&lt;/tt&gt; =&gt; does nothing&lt;/li&gt;
 &lt;li&gt;&lt;tt&gt;childWatcher&lt;/tt&gt; receives &lt;tt&gt;NodeDeleted&lt;/tt&gt; =&gt; state.remove('child1') then state['child1']=new state + watcher&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
or
&lt;li&gt;
&lt;ol&gt;
 &lt;li&gt;&lt;tt&gt;childWatcher&lt;/tt&gt; receives &lt;tt&gt;NodeDeleted&lt;/tt&gt; =&gt; state.remove('child1') then state['child1']=new state + watcher&lt;/li&gt;
 &lt;li&gt;&lt;tt&gt;childrenWatcher&lt;/tt&gt; receives &lt;tt&gt;NodeChildrenChanged&lt;/tt&gt; =&gt; does nothing&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;/state/child1 deleted / recreated (events are not collapsed)&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol&gt;
 &lt;li&gt;&lt;tt&gt;childrenWatcher&lt;/tt&gt; receives &lt;tt&gt;NodeChildrenChanged&lt;/tt&gt; =&gt; does nothing&lt;/li&gt;
 &lt;li&gt;&lt;tt&gt;childWatcher&lt;/tt&gt; receives &lt;tt&gt;NodeDeleted&lt;/tt&gt; =&gt; state.remove('child1')&lt;/li&gt;
 &lt;li&gt;&lt;tt&gt;childrenWatcher&lt;/tt&gt; receives &lt;tt&gt;NodeChildrenChanged&lt;/tt&gt; =&gt; state['child1']=new state + watcher &lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
or
&lt;li&gt;
&lt;ol&gt;
 &lt;li&gt;&lt;tt&gt;childWatcher&lt;/tt&gt; receives &lt;tt&gt;NodeDeleted&lt;/tt&gt; =&gt; state.remove('child1')&lt;/li&gt;
 &lt;li&gt;&lt;tt&gt;childrenWatcher&lt;/tt&gt; receives &lt;tt&gt;NodeChildrenChanged&lt;/tt&gt; =&gt; state['child1']=new state + watcher &lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
or
&lt;li&gt;
&lt;ol&gt;
 &lt;li&gt;&lt;tt&gt;childrenWatcher&lt;/tt&gt; receives &lt;tt&gt;NodeChildrenChanged&lt;/tt&gt; =&gt; does nothing&lt;/li&gt;
 &lt;li&gt;&lt;tt&gt;childWatcher&lt;/tt&gt; receives &lt;tt&gt;NodeDeleted&lt;/tt&gt; =&gt; state.remove('child1') then state['child1']=new state + watcher&lt;/li&gt;
 &lt;li&gt;&lt;tt&gt;childrenWatcher&lt;/tt&gt; receives &lt;tt&gt;NodeChildrenChanged&lt;/tt&gt; =&gt; does nothing&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/ul&gt;

As always with this kind of problem, the fix is really not that complicated. It is understanding the problem in the first place which is hard...

You can check the &lt;a href=&quot;http://www.kiwidoc.com/java/l/p/org.apache.hadoop/zookeeper/3.2.1/p/org.apache.zookeeper/c/ZooKeeper&quot;&gt;ZooKeeper javadoc&lt;/a&gt; on kiwidoc :)
</content>
 </entry>
 

</feed>
