<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-29871611</id><updated>2012-02-17T23:50:16.814-05:00</updated><category term='math'/><category term='java'/><category term='erlang'/><category term='engineering'/><category term='ejabberd'/><category term='perl'/><category term='startup'/><category term='maven'/><category term='lisp'/><category term='art'/><category term='flex'/><category term='cs'/><category term='regex'/><category term='grails'/><category term='agile'/><category term='git'/><category term='sql'/><category term='groovy'/><category term='spring'/><category term='haskell'/><category term='rabbitmq'/><category term='nosql'/><category term='ubuntu'/><category term='review'/><category term='subversion'/><category term='humor'/><title type='text'>Side Notes</title><subtitle type='html'>my externalized experience</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>42</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-29871611.post-4449166856105597777</id><published>2011-11-23T13:09:00.010-05:00</published><updated>2011-11-23T22:37:36.669-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='git'/><category scheme='http://www.blogger.com/atom/ns#' term='subversion'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><title type='text'>Cygwin git-svn messed up</title><content type='html'>I upgraded my Cygwin from version 1.5 to 1.7 (finally), and found that git-svn command was broken&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ git svn rebase&lt;br /&gt;Can't locate SVN/Core.pm in @INC (@INC contains:&lt;br /&gt;/usr/lib/perl5/site_perl/5.10&lt;br /&gt;/usr/lib/perl5/site_perl/5.14/i686-cygwin&lt;br /&gt;/usr/lib/perl5/site_perl/5.14&lt;br /&gt;/usr/lib/perl5/vendor_perl/5.14/i686-cygwin&lt;br /&gt;/usr/lib/perl5/vendor_perl/5.14&lt;br /&gt;/usr/lib/perl5/5.14/i686-cygwin&lt;br /&gt;/usr/lib/perl5/5.14&lt;br /&gt;/usr/lib/perl5/vendor_perl/5.10&lt;br /&gt;/usr/lib/perl5/site_perl/5.8 .) at /usr/lib/git-core/git-svn line 42.&lt;/pre&gt;&lt;br /&gt;Usually this error indicates that &lt;code&gt;subversion-perl&lt;/code&gt; package is not installed, but that was not the case as we can see it from &lt;code&gt;cygcheck&lt;/code&gt; output:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ cygcheck -c subversion-perl&lt;br /&gt;Cygwin Package Information&lt;br /&gt;Package              Version        Status&lt;br /&gt;subversion-perl      1.7.1-1        OK&lt;/pre&gt;&lt;br /&gt;As it turned out, the problem was in the package dependency management. From the error above we saw that perl was looking for &lt;code&gt;SVN/Core.pm&lt;/code&gt; in &lt;code&gt;/usr/lib/perl5/vendor_perl/5.14/i686-cygwin&lt;/code&gt; directory. But the latest &lt;code&gt;subversion-perl&lt;/code&gt; installed it in the different place&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ cygcheck -l subversion-perl&lt;br /&gt;...&lt;br /&gt;/usr/lib/perl5/vendor_perl/5.10/i686-cygwin/SVN/Core.pm&lt;br /&gt;...&lt;/pre&gt;&lt;br /&gt;The problem was solved by downgrading perl from 5.14.x-x to 5.10.x-x.&lt;br /&gt;&lt;br /&gt;If you have the same problem, check which version of perl package you have installed&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ cygcheck -f /usr/bin/perl&lt;br /&gt;perl-5.10.1-5&lt;/pre&gt;&lt;br /&gt;It must be the same as the version used by &lt;code&gt;subversion-perl&lt;/code&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-4449166856105597777?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/4449166856105597777/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=4449166856105597777' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/4449166856105597777'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/4449166856105597777'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2011/11/cygwin-git-svn-messed-up.html' title='Cygwin git-svn messed up'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-8045611081792126657</id><published>2011-11-05T14:19:00.008-04:00</published><updated>2011-11-05T16:18:47.110-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='art'/><category scheme='http://www.blogger.com/atom/ns#' term='math'/><title type='text'>Ford Marbles</title><content type='html'>I found these marvelous renderings of Ford circles on &lt;a href="http://www.flickr.com/photos/fdecomite/2564742912/in/photostream"&gt;flickr&lt;/a&gt;. I can't help but share them here.&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;a href="http://www.flickr.com/photos/fdecomite/2564742912/sizes/z/in/photostream/"&gt;&lt;img src="http://farm4.static.flickr.com/3189/2564742912_d0faf59e38_z.jpg"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.flickr.com/photos/fdecomite/2563846663/sizes/z/in/photostream/"&gt;&lt;img src="http://farm4.static.flickr.com/3076/2563846663_6d0f428f08_z.jpg"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.flickr.com/photos/fdecomite/2564630336/sizes/z/in/photostream/"&gt;&lt;img src="http://farm4.static.flickr.com/3165/2564630336_d35c4ccf04_z.jpg"&gt;&lt;/a&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;As &lt;a href="http://ndpar.blogspot.com/2011/08/thomaes-function.html"&gt;Thomae's function&lt;/a&gt;, &lt;a href="http://en.wikipedia.org/wiki/Ford_circle"&gt;Ford circles&lt;/a&gt; is another visual representation of rational numbers. You can investigate them &lt;a href="http://demonstrations.wolfram.com/FordCircles/"&gt;here&lt;/a&gt; with interactive Wolfram demo.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-8045611081792126657?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/8045611081792126657/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=8045611081792126657' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/8045611081792126657'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/8045611081792126657'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2011/11/ford-marbles.html' title='Ford Marbles'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://farm4.static.flickr.com/3189/2564742912_d0faf59e38_t.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-2119816632823971120</id><published>2011-09-16T12:59:00.004-04:00</published><updated>2011-09-16T13:18:20.869-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='cs'/><category scheme='http://www.blogger.com/atom/ns#' term='math'/><title type='text'>Modulo who?</title><content type='html'>When programmer and mathematician are talking about modulus or modulo, there is often a confusion what this term means. For programmer &lt;a href="http://en.wikipedia.org/wiki/Modulo_operator"&gt;modulo&lt;/a&gt; means an operator that finds the &lt;i&gt;remainder&lt;/i&gt; of division of one number by another, e.g. 5&amp;nbsp;mod&amp;nbsp;2 = 1. For mathematician &lt;a href="http://en.wikipedia.org/wiki/Modular_arithmetic"&gt;modulo&lt;/a&gt; is a &lt;i&gt;congruence&lt;/i&gt; relation between two numbers: &lt;i&gt;a&lt;/i&gt; and &lt;i&gt;b&lt;/i&gt; are said to be congruent modulo &lt;i&gt;n&lt;/i&gt;, written &lt;i&gt;a&lt;/i&gt;&amp;nbsp;≡&amp;nbsp;b&amp;nbsp;(mod&amp;nbsp;n), if their difference &lt;i&gt;a&lt;/i&gt;&amp;nbsp;−&amp;nbsp;&lt;i&gt;b&lt;/i&gt; is an integer multiple of &lt;i&gt;n&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;These two definitions are not equivalent. The former is a special case of the latter: if &lt;i&gt;b&lt;/i&gt;&amp;nbsp;mod&amp;nbsp;&lt;i&gt;n&lt;/i&gt; = &lt;i&gt;a&lt;/i&gt; then &lt;i&gt;a&lt;/i&gt;&amp;nbsp;≡&amp;nbsp;&lt;i&gt;b&lt;/i&gt;&amp;nbsp;(mod&amp;nbsp;&lt;i&gt;n&lt;/i&gt;). The inverse is not true in general case. 5&amp;nbsp;mod&amp;nbsp;2 = 1, and 1&amp;nbsp;≡&amp;nbsp;5&amp;nbsp;(mod&amp;nbsp;2) because 1&amp;nbsp;-&amp;nbsp;5 = -4 is integer multiple of 2. Now 5&amp;nbsp;≡&amp;nbsp;1&amp;nbsp;(mod&amp;nbsp;2) because 5&amp;nbsp;-&amp;nbsp;1=4 is evenly divisible by 2, but 1&amp;nbsp;mod&amp;nbsp;2 = 1, not 5.&lt;br /&gt;&lt;br /&gt;The biggest confusion happens when programmer and mathematician start arguing about Gauss' famous &lt;a href="http://mathworld.wolfram.com/QuadraticReciprocityTheorem.html"&gt;golden theorem&lt;/a&gt; where both definitions of modulus can be used.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-2119816632823971120?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/2119816632823971120/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=2119816632823971120' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/2119816632823971120'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/2119816632823971120'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2011/09/modulo-who.html' title='Modulo who?'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-3197488742027663569</id><published>2011-08-06T09:04:00.005-04:00</published><updated>2011-08-06T09:37:41.893-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='math'/><title type='text'>Thomae's function</title><content type='html'>&lt;a href="http://en.wikipedia.org/wiki/Thomae%27s_function"&gt;Thomae's function&lt;/a&gt; (a.k.a. Riemann function) is defined on the interval (0, 1) as follows&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-ynF3Wr2_PZk/Tj08KSOzhBI/AAAAAAAAF3c/4KB-w5do5eQ/s1600/thomae-def.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 452px; height: 56px;" src="http://4.bp.blogspot.com/-ynF3Wr2_PZk/Tj08KSOzhBI/AAAAAAAAF3c/4KB-w5do5eQ/s600/thomae-def.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5637728455928022034" /&gt;&lt;/a&gt;&lt;br /&gt;Here is the graph of this function with some points highlighted as plus symbols for better view.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-bg5a7jIaCNs/Tj08dF6omBI/AAAAAAAAF3k/VII4-7dc-pQ/s1600/thomae.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 846px; height: 594px;" src="http://4.bp.blogspot.com/-bg5a7jIaCNs/Tj08dF6omBI/AAAAAAAAF3k/VII4-7dc-pQ/s1200/thomae.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5637728779039709202" /&gt;&lt;/a&gt;&lt;br /&gt;This function has interesting property: it's continuous at all irrational numbers. It's easy to see this if you notice that for any positive &lt;i&gt;ε&lt;/i&gt; there is finite number of points above the line &lt;i&gt;y = ε&lt;/i&gt;. That means for any irrational number &lt;i&gt;x&lt;sub&gt;0&lt;/sub&gt;&lt;/i&gt; you can always construct a &lt;i&gt;δ&lt;/i&gt;-neighbourhood that doesn't contain any point from the area above the line &lt;i&gt;y = ε&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-lgMstQGPyMo/Tj09GPyQS6I/AAAAAAAAF3s/uZLB2y9C4RE/s1600/thomae-e-d.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 846px; height: 594px;" src="http://4.bp.blogspot.com/-lgMstQGPyMo/Tj09GPyQS6I/AAAAAAAAF3s/uZLB2y9C4RE/s1200/thomae-e-d.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5637729486063553442" /&gt;&lt;/a&gt;&lt;br /&gt;To generate the data file with point coordinates I used Common Lisp program:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;(defun rational-numbers (max-denominator)&lt;br /&gt;  (let ((result (list)))&lt;br /&gt;    (loop for q from 2 to max-denominator do&lt;br /&gt;      (loop for p from 1 to (1- q) do&lt;br /&gt;        (pushnew (/ p q) result)))&lt;br /&gt;    result))&lt;br /&gt; &lt;br /&gt;(defun thomae-rational-points (abscissae)&lt;br /&gt;  (mapcar (lambda (x) (list x (/ 1 (denominator x)))) abscissae))&lt;br /&gt; &lt;br /&gt;(defun thomae (max-denominator)&lt;br /&gt;  (let ((points (thomae-rational-points (rational-numbers max-denominator))))&lt;br /&gt;    (with-open-file (stream "thomae.dat" :direction :output)&lt;br /&gt;      (loop for point in points do&lt;br /&gt;        (format stream "~4$ ~4$~%" (first point) (second point))))))&lt;br /&gt; &lt;br /&gt;(thomae 500)&lt;/pre&gt;&lt;br /&gt;To create the images I used &lt;a href="http://www.gnuplot.info/"&gt;gnuplot&lt;/a&gt; commands:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;plot "thomae.dat" using 1:2 with dots&lt;br /&gt;plot "thomae.dat" using 1:2 with points&lt;/pre&gt;&lt;br /&gt;and Photoshop.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-3197488742027663569?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/3197488742027663569/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=3197488742027663569' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/3197488742027663569'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/3197488742027663569'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2011/08/thomaes-function.html' title='Thomae&apos;s function'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-ynF3Wr2_PZk/Tj08KSOzhBI/AAAAAAAAF3c/4KB-w5do5eQ/s72-c/thomae-def.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-5684545172267981915</id><published>2011-06-30T18:35:00.002-04:00</published><updated>2011-06-30T18:39:21.612-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='startup'/><title type='text'>Rod Johnson on Entrepreneurialism</title><content type='html'>One thing I think that you really need to be careful of as well, particularly if you, like me, are a programmer, is don’t get carried away writing code. Typically in my experience anyone who is a good programmer is pretty passionate about it, love writing code, get addicted to the process of writing code, fell pretty good about their code basis. As soon as you get down that path you are not thinking straight anymore and now you are increasing your emotional investment, you are having lots of fun writing interesting code and you are no longer in a place mentally where you are going to be trying to find some reason that you shouldn’t write that code. That has been a big lesson for me that the quicker I get to coding, the longer it takes me to ask the kind of questions I should ask upfront.&lt;br /&gt;...&lt;br /&gt;It is really, really hard to decide not to do things. One of the biggest killers of companies is trying to do too much. If you try to take on too many things you will assuredly fail, even if every one of those things is a good thing to do. It is incredibly hard to realize that a particular thing is a good idea, but you are not going to do it.&lt;br /&gt;...&lt;br /&gt;I think the biggest way to decide frankly if you are trading off business priorities is do the boring stuff like, look at the total addressable market, go and talk to customers, figure out what they will pay for. You really need to be guided by what the revenue is likely to be, and make sure you don’t just do something just because it’s &lt;a href="http://www.infoq.com/interviews/rod-johnson-entrepreneurialism"&gt;cool&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-5684545172267981915?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/5684545172267981915/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=5684545172267981915' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/5684545172267981915'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/5684545172267981915'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2011/06/rod-johnson-on-entrepreneurialism.html' title='Rod Johnson on Entrepreneurialism'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-7290307797950287485</id><published>2011-06-27T21:07:00.010-04:00</published><updated>2011-06-27T22:29:09.967-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='humor'/><category scheme='http://www.blogger.com/atom/ns#' term='math'/><title type='text'>Math and Physics of Benderama</title><content type='html'>The last episode of &lt;a href="http://theinfosphere.org/Benderama"&gt;Futurama&lt;/a&gt; has interesting formula involved. The entire plot is based on the Professor's latest invention &amp;mdash; &lt;a href="http://en.wikipedia.org/wiki/Banach–Tarski_paradox"&gt;Banach-Tarski&lt;/a&gt; Dupla-Shrinker &amp;mdash; the machine that produces two copies of any object at a 60% scale. It was just a matter of time when Bender found a good usage of this machine: to replicate himself. Then two small copies of Bender replicated themselves making four smaller copies, and so forth. At some point the Professor horrified the crew that if they don't stop this unlimited growth, the total mass of all Benders will eventually be so big that the entire Earth will be consumed during the process of replication. As a proof he demonstrated this formula of the mass of all generations of Bender&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;img src="http://pool.theinfosphere.org/images/thumb/0/04/Benderama_Maths.png/800px-Benderama_Maths.png"&gt;&lt;/center&gt;&lt;br /&gt;This is a perfect toy for a science geek. The first obvious question it brings: is this formula mathematically correct? As it turns out, it is not. Considering the scale of 60%, the cubic dependency of volume on linear dimension, and the constant density of all copies, the formula should be the following&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-sxQRqO-R4-M/TgkqQqYGsNI/AAAAAAAAFxs/dVbYzAgAhHQ/s1600/benderama-mass.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/-sxQRqO-R4-M/TgkqQqYGsNI/AAAAAAAAFxs/dVbYzAgAhHQ/s800/benderama-mass.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5623072075490439378" /&gt;&lt;/a&gt;&lt;br /&gt;As you can see the total mass of infinite number of Benders actually converges to approximately 1.76 M&lt;sub&gt;0&lt;/sub&gt;. So from Math perspective there is nothing to worry about. But what if our assumption of constant density is invalid. Would it be a problem from Physics perspective? Let's see.&lt;br /&gt;&lt;br /&gt;Knowing that every new copy has a size of 0.6 of the original it was made from, we have the following formula for the size of Bender in the n&lt;sup&gt;th&lt;/sup&gt; generation&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/-OiRd2vfi4VQ/Tgksnot3QaI/AAAAAAAAFx0/kgKLLQVhSYc/s1600/benderama-length.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/-OiRd2vfi4VQ/Tgksnot3QaI/AAAAAAAAFx0/kgKLLQVhSYc/s800/benderama-length.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5623074669205078434" /&gt;&lt;/a&gt;&lt;br /&gt;This exponential function becomes very small pretty soon. In the &lt;a href="http://www.wolframalpha.com/input/?i=0.6%5E154"&gt;154&lt;/a&gt;&lt;sup&gt;th&lt;/sup&gt; generation it already reaches the &lt;a href="http://en.wikipedia.org/wiki/Planck_length"&gt;Planck length&lt;/a&gt;, after which the further replication is physically impossible. If we calculate the total mass of 154 Bender's generations using the Professor's formula, we get &lt;a href="http://www.wolframalpha.com/input/?i=H%28154%29"&gt;H(154)&lt;/a&gt; × &lt;a href="http://www.peelified.com/Futurama-Forum-1/Topic-4095-0-Benders_Weight.html"&gt;238&lt;/a&gt; kg ≈ 1,337.56 kg, which is nothing comparing to the Earth mass.&lt;br /&gt;&lt;br /&gt;So we have to admit that from both Math and Physics perspective the Professor was wrong, and there was no real threat to the Earth.&lt;br /&gt;&lt;br /&gt;Although the Professor's formula doesn't describe the replication process adequately, it's still a beautiful piece of Math because it's a formula of &lt;a href="http://en.wikipedia.org/wiki/Harmonic_series_(mathematics)"&gt;harmonic series&lt;/a&gt;. If you want to know why harmonic series is beautiful and which real processes it describes, read this nice &lt;a href="http://plus.maths.org/content/perfect-harmony"&gt;article&lt;/a&gt; of John H. Webb.&lt;br /&gt;&lt;br /&gt;And don't miss the next &lt;a href="http://theinfosphere.org/Ghost_in_the_Machines"&gt;episode&lt;/a&gt; of Futurama this Thursday :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-7290307797950287485?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/7290307797950287485/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=7290307797950287485' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/7290307797950287485'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/7290307797950287485'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2011/06/math-and-physics-of-benderama.html' title='Math and Physics of Benderama'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-sxQRqO-R4-M/TgkqQqYGsNI/AAAAAAAAFxs/dVbYzAgAhHQ/s72-c/benderama-mass.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-3706188656027230529</id><published>2011-06-08T21:30:00.002-04:00</published><updated>2011-06-08T21:39:40.423-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>Functional Groovy switch statement</title><content type='html'>In the previous &lt;a href="http://ndpar.blogspot.com/2011/06/reversing-groovy-switch-statement.html"&gt;post&lt;/a&gt; I showed how to replace chained if-else statements in Groovy with one concise switch. It was done for the special case of if-stement where every branch was evaluated using the same condition function. Today I want to make a generalization of that technique by allowing to use different conditionals.&lt;br /&gt;&lt;br /&gt;Suppose your code looks like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;if (param % 2 == 0) {&lt;br /&gt;    'even'&lt;br /&gt;} else if (param % 3 == 0) {&lt;br /&gt;    'threeven'&lt;br /&gt;} else if (0 &lt; param) {&lt;br /&gt;    'positive'&lt;br /&gt;} else {&lt;br /&gt;    'negative'&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;As soon as every condition operates on the same parameter, you can replace the entire chain with a switch. In this scenario &lt;code&gt;param&lt;/code&gt; becomes a switch parameter and conditions become case parameters of Closure type. The only thing we need to do is to override Closure.isCase() method as I described in the previous post. The safest way to do it is to create a category class:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;class CaseCategory {&lt;br /&gt;    static boolean isCase(Closure casePredicate, Object switchParameter) {&lt;br /&gt;        casePredicate.call switchParameter&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Now we can replace if-statement with the following switch:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;use (CaseCategory) {&lt;br /&gt;    switch (param) {&lt;br /&gt;        case { it % 2 == 0 } : return 'even'&lt;br /&gt;        case { it % 3 == 0 } : return 'threeven'&lt;br /&gt;        case { 0 &lt; it }      : return 'positive'&lt;br /&gt;        default              : return 'negative'&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;We can actually go further and extract in-line closures:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;def even = {&lt;br /&gt;    it % 2 == 0&lt;br /&gt;}&lt;br /&gt;def threeven = {&lt;br /&gt;    it % 3 == 0&lt;br /&gt;}&lt;br /&gt;def positive = {&lt;br /&gt;    0 &lt; it&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;After which the code becomes even more readable:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;use (CaseCategory) {&lt;br /&gt;    switch (param) {&lt;br /&gt;        case even     : return 'even'&lt;br /&gt;        case threeven : return 'threeven'&lt;br /&gt;        case positive : return 'positive'&lt;br /&gt;        default       : return 'negative'&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-3706188656027230529?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/3706188656027230529/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=3706188656027230529' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/3706188656027230529'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/3706188656027230529'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2011/06/functional-groovy-switch-statement.html' title='Functional Groovy switch statement'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-7911297985527151173</id><published>2011-06-07T12:46:00.002-04:00</published><updated>2011-06-07T12:50:45.950-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='engineering'/><title type='text'>Nothing new under the sun</title><content type='html'>Every generation of software developers needs its own fad. For my generation it was Agile, for generation before it was OOP, and before that it was another big thing. Gerald Weinberg, one of the most influential people in our industry, blogged yesterday about this issue. With over 50 years of experience in software development he knows what he is talking about. Read his &lt;a href="http://secretsofconsulting.blogspot.com/2011/06/beyond-agile-programming.html"&gt;blog post&lt;/a&gt; &amp;mdash; he has a very good point.&lt;br /&gt;&lt;br /&gt;P.S. I'm wondering what will be the next big thing. Will it be Cloud?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-7911297985527151173?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/7911297985527151173/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=7911297985527151173' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/7911297985527151173'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/7911297985527151173'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2011/06/nothing-new-under-sun.html' title='Nothing new under the sun'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-259893236178559563</id><published>2011-06-05T20:52:00.003-04:00</published><updated>2011-06-05T21:16:09.153-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>Multimethods in Groovy</title><content type='html'>Every time I switch from Groovy to Java I have to remind myself that some things that seem so natural and work as expected in Groovy, don't work in Java. One of such differences is method dispatching. Groovy supports &lt;a href="http://en.wikipedia.org/wiki/Multiple_dispatch"&gt;multiple dispatch&lt;/a&gt;, while Java does not. Therefore the following &lt;a href="http://www.gigamonkeys.com/book/object-reorientation-generic-functions.html#multimethods"&gt;code&lt;/a&gt; works differently in Groovy and Java:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;public class A {&lt;br /&gt;    public void foo(A a) { System.out.println("A/A"); }&lt;br /&gt;    public void foo(B b) { System.out.println("A/B"); }&lt;br /&gt;}&lt;br /&gt;public class B extends A {&lt;br /&gt;    public void foo(A a) { System.out.println("B/A"); }&lt;br /&gt;    public void foo(B b) { System.out.println("B/B"); }&lt;br /&gt;}&lt;br /&gt;public class Main {&lt;br /&gt;    public static void main(String[] args) {&lt;br /&gt;        A a = new A();&lt;br /&gt;        A b = new B();&lt;br /&gt;        a.foo(a);&lt;br /&gt;        b.foo(b);&lt;br /&gt;    }  &lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;$ java Main&lt;br /&gt;A/A&lt;br /&gt;B/A&lt;br /&gt;&lt;br /&gt;$ groovy Main.groovy&lt;br /&gt;A/A&lt;br /&gt;B/B&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-259893236178559563?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/259893236178559563/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=259893236178559563' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/259893236178559563'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/259893236178559563'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2011/06/multimethods-in-groovy.html' title='Multimethods in Groovy'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-8276532313564011897</id><published>2011-06-01T14:15:00.002-04:00</published><updated>2011-06-01T14:37:02.566-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>Reversing Groovy switch statement</title><content type='html'>Recently I've been working on a Groovy code that had many methods with long multibranch conditionals like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;def parse(message, options) {&lt;br /&gt;    if (options.contains('A')) {&lt;br /&gt;        parseARule message&lt;br /&gt;    } else if (options.contains(2)) {&lt;br /&gt;        parseSmallDigitRule message&lt;br /&gt;    ...&lt;br /&gt;    } else if (options.contains(something)) {&lt;br /&gt;        parseSomeRule message&lt;br /&gt;    } else {&lt;br /&gt;        parseSomeOtherRule message&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Although this code is working, it is hard to see which branch is called under which condition. It would be much better if we could replace this code with something like Lisp &lt;code&gt;cond&lt;/code&gt; macro. The best candidate for such a task in Groovy would be a &lt;code&gt;switch&lt;/code&gt; statement. If we could only refactor the code above to something like following, it would significantly improve readability:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;def parse(message, options) {&lt;br /&gt;    switch (options) {&lt;br /&gt;        case 'A' : return parseARule(message)&lt;br /&gt;        case 2   : return parseSmallDigitRule(message)&lt;br /&gt;        ...&lt;br /&gt;        case ... : return parseSomeRule(message)&lt;br /&gt;        default  : return parseSomeOtherRule(message)&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Unfortunately, this code doesn't work out of the box in Groovy, but it works if we do some metaprogramming.&lt;br /&gt;&lt;br /&gt;The way &lt;code&gt;switch&lt;/code&gt; statement works in Groovy is a bit &lt;a href="http://docs.codehaus.org/display/GROOVY/Logical+Branching#LogicalBranching-switchstatement"&gt;different&lt;/a&gt; than in Java. Instead of equals() it uses isCase() method to match case-value and switch-value. The default implementation of isCase() method falls back to equals() method, but some classes, including &lt;a href="http://groovy.codehaus.org/groovy-jdk/java/util/Collection.html#isCase(java.lang.Object)"&gt;Collection&lt;/a&gt;, override this behaviour. That's why in Groovy you can do things like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;switch (value) {&lt;br /&gt;    case ['A','E','I','O','U'] : return 'vowel'&lt;br /&gt;    case 0..9                  : return 'digit'&lt;br /&gt;    case Date                  : return 'date'&lt;br /&gt;    default                    : return 'something else'&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;For our purposes we need some sort of reverse &lt;code&gt;switch&lt;/code&gt;, where collection is used as a switch-value, and String and Integer are used as a case-value. To do this we need to override default implementation of isCase() method on String and Integer classes. It's not possible in Java, but is very easy in Groovy. You can change method implementation globally by replacing it in corresponding meta class, or locally with the help of categories. Let's create a category that swaps object and subject of isCase() method:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;class CaseCategory {&lt;br /&gt;    static boolean isCase(String string, Collection col) {&lt;br /&gt;        reverseCase(string, col)&lt;br /&gt;    }&lt;br /&gt;    static boolean isCase(Integer integer, Collection col) {&lt;br /&gt;        reverseCase(integer, col)&lt;br /&gt;    }&lt;br /&gt;    // Add more overloaded methods here if needed&lt;br /&gt;&lt;br /&gt;    private static boolean reverseCase(left, right) {&lt;br /&gt;        right.isCase(left)&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Now we can use this category to achieve the goal we stated at the beginning of this post:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;def parse(message, options) {&lt;br /&gt;    use (CaseCategory) {&lt;br /&gt;        switch (options) {&lt;br /&gt;            case 'A' : return parseARule(message)&lt;br /&gt;            case 2   : return parseSmallDigitRule(message)&lt;br /&gt;            ...&lt;br /&gt;            case ... : return parseSomeRule(message)&lt;br /&gt;            default  : return parseSomeOtherRule(message)&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;If you are comfortable with global method replacement, you can amend String and Integer meta classes. In this case you don't need to wrap &lt;code&gt;switch&lt;/code&gt; statement with &lt;code&gt;use&lt;/code&gt; keyword.&lt;br /&gt;&lt;br /&gt;Anyways, with or without category, the final code looks better than the original noisy if-else chain. And you have learned the technique of reversing &lt;code&gt;switch&lt;/code&gt; statement.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-8276532313564011897?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/8276532313564011897/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=8276532313564011897' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/8276532313564011897'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/8276532313564011897'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2011/06/reversing-groovy-switch-statement.html' title='Reversing Groovy switch statement'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-2834302033270128239</id><published>2011-02-03T22:16:00.015-05:00</published><updated>2011-02-06T09:45:29.365-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>Lazy lists in Groovy</title><content type='html'>I like lazy evaluation, and it's one of the reasons I like Haskell language so much. Although from engineering perspective lazy evaluation is probably not the most needed feature, it's definitely very useful for solving some mathematical problems.&lt;br /&gt;&lt;br /&gt;Most languages don't have lazy evaluation out of the box, but you can implement it using some other language features. This is an interesting task, and I use it as a code &lt;a href="http://en.wikipedia.org/wiki/Kata_(programming)"&gt;kata&lt;/a&gt; which I practice every time I learn a new strict language.&lt;br /&gt;&lt;br /&gt;So, how to implement lazy lists in strict languages? Very simple, if the language has functional capabilities. Namely, you &lt;i&gt;build lazy list recursively by wrapping strict list within a function&lt;/i&gt;. Here is, for example, the strict empty list in Groovy:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;[]&lt;/pre&gt;&lt;br /&gt;If we wrap it with a closure, it becomes lazy empty list:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;{-&gt; [] }&lt;/pre&gt;&lt;br /&gt;If we need a list with one element, we prepend (or speaking Lisp terminology 'cons') an element to lazy empty list, and make the result lazy again:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;{-&gt; [ element, {-&gt; [] } ] }&lt;/pre&gt;&lt;br /&gt;To add more elements we continue the same process until all elements are lazily consed. Here is, for example, a lazy list with three elements a, b and c:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;{-&gt; [a, {-&gt; [b, {-&gt; [ c, {-&gt; [] } ] } ] } ] }&lt;/pre&gt;&lt;br /&gt;Now, when you have an idea how to build lazy lists, let's build them Groovy way. We start with creating a class:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;class LazyList {&lt;br /&gt;    private Closure list&lt;br /&gt;&lt;br /&gt;    private LazyList(list) {&lt;br /&gt;        this.list = list&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;The variable &lt;tt&gt;list&lt;/tt&gt; encapsulates the closure wrapper of the list. We just need to expose some methods that allow constructing lists using procedure described above:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;    static LazyList nil() {&lt;br /&gt;        new LazyList( {-&gt; []} )&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    LazyList cons(head) {&lt;br /&gt;        new LazyList( {-&gt; [head, list]} )&lt;br /&gt;    }&lt;/pre&gt;&lt;br /&gt;Now we can construct lists by consing elements to empty list:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;def lazylist = LazyList.nil().cons(4).cons(3).cons(2).cons(1)&lt;/pre&gt;&lt;br /&gt;To access elements of the list we implement two standard functions, &lt;tt&gt;car&lt;/tt&gt; and &lt;tt&gt;cdr&lt;/tt&gt;, that return head and tail of the list respectively.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;    def car() {&lt;br /&gt;        def lst = list.call()&lt;br /&gt;        lst ? lst[0] : null&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    def cdr() {&lt;br /&gt;        def lst = list.call()&lt;br /&gt;        lst ? new LazyList(lst.tail()[0]) : nil()&lt;br /&gt;    }&lt;/pre&gt;&lt;br /&gt;Here is how you use these functions to get first and second elements of the list constructed above&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;assert lazylist.car() == 1&lt;br /&gt;assert lazylist.cdr().car() == 2&lt;/pre&gt;&lt;br /&gt;In Lisp there are built-in functions for various &lt;tt&gt;car&lt;/tt&gt; and &lt;tt&gt;cdr&lt;/tt&gt; compositions. For example, the previous assertion would be equivalent to function &lt;tt&gt;cadr&lt;/tt&gt;. Instead of implementing all possible permutations, let's use Groovy metaprogramming to achieve the same goal.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;    def methodMissing(String name, args) {&lt;br /&gt;        def matcher = name =~ /c([ad])([ad]+)r/&lt;br /&gt;        if (matcher) {&lt;br /&gt;            matcher[0][2].reverse().toList().inject(this) {&lt;br /&gt;                del, index -&gt; del."c${index}r"()&lt;br /&gt;            }."c${matcher[0][1]}r"()&lt;br /&gt;        } else {&lt;br /&gt;            throw new MissingMethodException(name, this.class, args)&lt;br /&gt;        }&lt;br /&gt;    }&lt;/pre&gt;&lt;br /&gt;It might look complicated, but in reality it's pretty simple if you are familiar with Groovy regex and functional programming. It's easier to explain by example. If we pass "caddr" as a value of name parameter, the method will create a chain on method calls &lt;tt&gt;.cdr().cdr().car()&lt;/tt&gt; which will be applied to delegate of the operation which is our LazyList object.&lt;br /&gt;&lt;br /&gt;With this method in place we can call car/cdr functions with arbitrary depth.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;assert lazylist.caddr() == 3&lt;/pre&gt;&lt;br /&gt;If you create nested lazy lists, you can access any element of any nested list with this dynamic method.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;def lmn = LazyList.nil().cons('N').cons('M').cons('L')&lt;br /&gt;def almnz = LazyList.nil().cons('Z').cons(lmn).cons('A')&lt;br /&gt;assert almnz.cadadr() == 'M'&lt;/pre&gt;&lt;br /&gt;With so many cons methods it's hard to see the structure of the list. Let's implement &lt;tt&gt;lazy&lt;/tt&gt; method on ArrayList class that converts strict list to lazy. Again, we will use metaprogramming and functional techniques.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;ArrayList.metaClass.lazy = {&lt;br /&gt;    -&gt; delegate.reverse().inject(LazyList.nil()) {list, item -&gt; list.cons(item)}&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Now we can rewrite the previous example as follows&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;def lazyfied = ['A', ['L','M','N'].lazy(), 'Z'].lazy()&lt;br /&gt;assert lazyfied.cadadr() == 'M'&lt;/pre&gt;&lt;br /&gt;What have we accomplished so far? We learned how to build lazy lists from scratch and from strict lists. We know how to add elements to lazy lists, and how to access them. The next step is to implement &lt;tt&gt;fold&lt;/tt&gt; function. &lt;tt&gt;fold&lt;/tt&gt; is the &lt;a href="http://www.cs.kent.ac.uk/people/staff/dat/miranda/whyfp90.pdf"&gt;fundamental&lt;/a&gt; operation in functional languages, so our lazy lists must provide it.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;    boolean isEmpty() {&lt;br /&gt;        list.call() == []&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    def fold(n, acc, f) {&lt;br /&gt;        n == 0 || isEmpty() ? acc : cdr().fold(n-1, f.call(acc, car()), f)&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    def foldAll(acc, f) {&lt;br /&gt;        isEmpty() ? acc : cdr().foldAll(f.call(acc, car()), f)&lt;br /&gt;    }&lt;/pre&gt;&lt;br /&gt;The only difference between this &lt;tt&gt;fold&lt;/tt&gt; function and the standard one is the additional parameter n. We will need it later when we implement infinite lists. &lt;tt&gt;foldAll&lt;/tt&gt; function to lazy lists is the same as standard &lt;tt&gt;fold&lt;/tt&gt; to strict lists.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;assert [1,2,3,4,5].lazy().foldAll(0){ acc, i -&gt; acc + i } == 15&lt;br /&gt;assert [1,2,3,4,5].lazy().fold(3, 1){ acc, i -&gt; acc * i } == 6&lt;/pre&gt;&lt;br /&gt;First example calculates the sum of all elements of the list, second calculates the product of first three elements.&lt;br /&gt;&lt;br /&gt;If you have &lt;tt&gt;fold&lt;/tt&gt; functions you can easily implement &lt;tt&gt;take&lt;/tt&gt; functions&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;    def take(n) {&lt;br /&gt;        fold(n, []) {acc, item -&gt; acc &lt;&lt; item}&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    def takeAll() {&lt;br /&gt;        foldAll([]) {acc, item -&gt; acc &lt;&lt; item}&lt;br /&gt;    }&lt;br /&gt;   &lt;br /&gt;    def toList() {&lt;br /&gt;        takeAll()&lt;br /&gt;    }&lt;/pre&gt;&lt;br /&gt;&lt;tt&gt;take&lt;/tt&gt; is an inverse operation to &lt;tt&gt;lazy&lt;/tt&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;assert [1,2,3,4,5].lazy().takeAll() == [1,2,3,4,5]&lt;br /&gt;assert [1,2,3,4,5].lazy().take(3) == [1,2,3]&lt;/pre&gt;&lt;br /&gt;Our next goal is &lt;tt&gt;map&lt;/tt&gt; function on lazy lists. Ideally I want the implementation look like this&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;    def map(f) {&lt;br /&gt;        isEmpty() ? nil() : cdr().map(f).cons(f.call(car()))&lt;br /&gt;    }&lt;/pre&gt;&lt;br /&gt;For some reason it doesn't work lazy way in Groovy &amp;mdash; it's still strictly evaluated. Therefore I have to implement it directly with closure syntax&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;    def map(f) {&lt;br /&gt;        isEmpty() ? nil() : new LazyList( {-&gt; [f.call(car()), cdr().map(f).list]} )&lt;br /&gt;    }&lt;/pre&gt;&lt;br /&gt;Unlike &lt;tt&gt;fold&lt;/tt&gt;, lazy &lt;tt&gt;map&lt;/tt&gt; is identical to strict &lt;tt&gt;map&lt;/tt&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;assert [1,2,3,4,5].lazy().map{ 2 * it }.take(3) == [2,4,6]&lt;/pre&gt;&lt;br /&gt;The following example shows one of the benefits of laziness&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;assert [1,2,3,0,6].lazy().map{ 6 / it }.take(3) == [6,3,2]&lt;/pre&gt;&lt;br /&gt;&lt;tt&gt;map&lt;/tt&gt; didn't evaluate the entire list, hence there was no exception. If you evaluate expression for all elements, the exception will be thrown &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;try {&lt;br /&gt;    [1,2,3,0,6].lazy().map{ 6 / it }.takeAll()&lt;br /&gt;}&lt;br /&gt;catch (Exception e) {&lt;br /&gt;    assert e instanceof ArithmeticException&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;For strict lists this is a default behaviour of &lt;tt&gt;map&lt;/tt&gt; function.&lt;br /&gt;&lt;br /&gt;The last function I want to implement is &lt;tt&gt;filter&lt;/tt&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;    def filter(p) {&lt;br /&gt;        isEmpty() ? nil() :&lt;br /&gt;            p.call(car()) ? new LazyList( {-&gt; [car(), cdr().filter(p).list]} ) :&lt;br /&gt;                cdr().filter(p)&lt;br /&gt;    }&lt;/pre&gt;&lt;br /&gt;In the following example we find first two elements greater than 2&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;assert [1,2,3,4,5].lazy().filter{ 2 &lt; it }.take(2) == [3,4]&lt;/pre&gt;&lt;br /&gt;With the help of &lt;tt&gt;car&lt;/tt&gt;/&lt;tt&gt;cdr&lt;/tt&gt;, &lt;tt&gt;fold&lt;/tt&gt;, &lt;tt&gt;map&lt;/tt&gt; and &lt;tt&gt;filter&lt;/tt&gt; you can implement any other function on lazy lists yourself. Here is, for example, the implementation of &lt;tt&gt;zipWith&lt;/tt&gt; function&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;    static def zipWith(alist, blist, f) {&lt;br /&gt;        alist.isEmpty() || blist.isEmpty() ? nil() :&lt;br /&gt;            new LazyList( {-&gt; [&lt;br /&gt;                f.call(alist.car(), blist.car()),&lt;br /&gt;                zipWith(alist.cdr(), blist.cdr(), f).list&lt;br /&gt;            ]} )&lt;br /&gt;    }&lt;/pre&gt;&lt;br /&gt;Now, after we implemented all lazy functions we need, let's define infinite lists&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;    private static sequence(int n) {&lt;br /&gt;        {-&gt; [n, sequence(n+1)]}&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    static LazyList integers(int n) {&lt;br /&gt;        new LazyList(sequence(n))&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    static LazyList naturals() {&lt;br /&gt;        integers(1)&lt;br /&gt;    }&lt;/pre&gt;&lt;br /&gt;Infinite lists, from my point of view, is the most useful application of lazy lists&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;def naturals = LazyList.naturals()&lt;br /&gt;assert naturals.take(3) == [1,2,3]&lt;br /&gt;&lt;br /&gt;def evens = naturals.map { 2 * it }&lt;br /&gt;assert evens.take(3) == [2,4,6]&lt;br /&gt;&lt;br /&gt;def odds = naturals.filter { it % 2 == 1 }&lt;br /&gt;assert odds.take(3) == [1,3,5]&lt;br /&gt;&lt;br /&gt;assert naturals.cadddddddddr() == 10&lt;br /&gt;&lt;br /&gt;def nonnegatives = naturals.cons(0)&lt;br /&gt;assert nonnegatives.cadr() == 1&lt;br /&gt;&lt;br /&gt;assert LazyList.zipWith(evens, odds){ x, y -&gt; x * y }.take(4) == [2,12,30,56]&lt;/pre&gt;&lt;br /&gt;At this point you have all basic functionality implemented, and you should be able to extend this model to whatever you need in regards to lazy (infinite) lists. Happy lazy programming!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Resources and links&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;&lt;a href="http://gist.github.com/810702"&gt;Source code&lt;/a&gt; for this blog&lt;br /&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;Lazy list implementation in &lt;a href="https://github.com/ndpar/erlang/blob/master/src/lazy.erl"&gt;Erlang&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;Lazy list implementation in &lt;a href="https://github.com/ndpar/land-of-lisp/blob/master/lazy.lisp"&gt;Lisp&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-2834302033270128239?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/2834302033270128239/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=2834302033270128239' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/2834302033270128239'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/2834302033270128239'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2011/02/lazy-lists-in-groovy.html' title='Lazy lists in Groovy'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-7451156352860033872</id><published>2011-01-29T21:51:00.003-05:00</published><updated>2011-01-29T22:18:05.587-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='git'/><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>Counting modifications in Git repository</title><content type='html'>Recently Michael Feathers wrote a &lt;a href="http://michaelfeathers.typepad.com/michael_feathers_blog/2011/01/measuring-the-closure-of-code.html"&gt;blog&lt;/a&gt; about Open-Closed Principle, where he described simple technique that measures the closure of code. I created a Groovy &lt;a href="https://github.com/ndpar/utilities/blob/master/git-files-modified.groovy"&gt;script&lt;/a&gt; which implements this technique for Git repositories. If you run it from the root of your Git project, it produces a CSV file with the statistics of how many times files have been modified. Feel free to use this script to find hot spots in your Git repository.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-7451156352860033872?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/7451156352860033872/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=7451156352860033872' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/7451156352860033872'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/7451156352860033872'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2011/01/counting-modifications-in-git.html' title='Counting modifications in Git repository'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-4698438345800620407</id><published>2011-01-24T20:13:00.004-05:00</published><updated>2011-01-24T21:09:24.418-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='git'/><category scheme='http://www.blogger.com/atom/ns#' term='maven'/><title type='text'>Git + Maven</title><content type='html'>When I first started working with Git in my Maven projects (three years ago), it was very awkward. Half of the release commands didn't work at all. Second half worked, but with ugly workaround via faked remote repository, which violated the entire Git philosophy.&lt;br /&gt;&lt;br /&gt;Since then most of the issues have been resolved, including the following three which I mostly needed:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;support for &lt;i&gt;local&lt;/i&gt; Git repositories;&lt;br /&gt;&lt;li&gt;separation of &lt;i&gt;git-commit&lt;/i&gt; and &lt;i&gt;git-push&lt;/i&gt; commands in Maven release plugin;&lt;br /&gt;&lt;li&gt;critical bug fixes in Maven release and scm plugins.&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;I created a &lt;a href="http://github.com/ndpar/gitmaven"&gt;cheat sheet&lt;/a&gt; describing the way I typically set up and manage Git-Maven projects. Feel free to use it in your projects as well.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-4698438345800620407?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/4698438345800620407/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=4698438345800620407' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/4698438345800620407'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/4698438345800620407'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2011/01/git-maven.html' title='Git + Maven'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-4094058866827363594</id><published>2010-11-18T22:24:00.002-05:00</published><updated>2010-11-18T22:30:38.830-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='engineering'/><title type='text'>Joe Armstrong on optimization</title><content type='html'>Make it work, then make it beautiful, then if you really, really have to, make it fast. 90 percent of the time, if you make it beautiful, it will already be fast. So really, just make it beautiful! (from the &lt;a href="http://www.manning.com/logan/"&gt;book&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;I think that people first of all write a problem, they solve the problem and then they sort of optimize this code, work on it and the code becomes very efficient but unreadable. What I think they should doing is specifying it with a domain specific language or some higher thing and then writing a compiler and then changing the compiler because it’s not efficient. Because then they would have the benefits of a clear specification and a fast implementation. What they do is they don’t keep these things separated and the language doesn’t support them separating it like that. (from the &lt;a href="http://www.infoq.com/interviews/functional-langs"&gt;interview&lt;/a&gt;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-4094058866827363594?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/4094058866827363594/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=4094058866827363594' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/4094058866827363594'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/4094058866827363594'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2010/11/joe-armstrong-on-optimization.html' title='Joe Armstrong on optimization'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-5697576081589193471</id><published>2010-11-10T19:49:00.006-05:00</published><updated>2010-11-10T20:12:49.370-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><title type='text'>Erlang explained: Selective receive</title><content type='html'>If you worked with Erlang you've probably heard about selective receive. But do you actually know how it works? I want to post here an excerpt from Joe Armstrong's book &lt;a href="http://www.pragprog.com/titles/jaerlang/programming-erlang"&gt;Programming Erlang&lt;/a&gt; where he explains how it works exactly (&lt;a href="http://media.pragprog.com/titles/jaerlang/Concurrent.pdf"&gt;Section 8.6&lt;/a&gt;, p.153):&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;&lt;pre&gt;receive&lt;br /&gt;    Pattern1 [when Guard1] -&gt; Expressions1;&lt;br /&gt;    Pattern2 [when Guard2] -&gt; Expressions2;&lt;br /&gt;    ...&lt;br /&gt;after&lt;br /&gt;    Time -&gt; ExpressionTimeout&lt;br /&gt;end&lt;/pre&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;When we enter a &lt;b&gt;receive&lt;/b&gt; statement, we start a timer (but only if an &lt;b&gt;after&lt;/b&gt; section is present in the expression).&lt;br /&gt;&lt;li&gt;Take the first message in the mailbox and try to match it against &lt;tt&gt;Pattern1&lt;/tt&gt;, &lt;tt&gt;Pattern2&lt;/tt&gt;, and so on. If the match succeeds, the message is removed from the mailbox, and the expressions following the pattern are evaluated.&lt;br /&gt;&lt;li&gt;If none of the patterns in the &lt;b&gt;receive&lt;/b&gt; statement matches the first message in the mailbox, then the first message is removed from the mailbox and put into a "save queue." The second message in the mailbox is then tried. This procedure is repeated until a matching message is found or until all the messages in the mailbox have been examined.&lt;br /&gt;&lt;li&gt;If none of the messages in the mailbox matches, then the process is suspended and will be rescheduled for execution the next time a new message is put in the mailbox. Note that when a new message arrives, the messages in the save queue are not rematched; only the new message is matched.&lt;br /&gt;&lt;li&gt;As soon as a message has been matched, then all messages that have been put into the save queue are reentered into the mailbox in the order in which they arrived at the process. If a timer was set, it is cleared.&lt;br /&gt;&lt;li&gt;If the timer elapses when we are waiting for a message, then evaluate the expressions &lt;tt&gt;ExpressionsTimeout&lt;/tt&gt; and put any saved messages back into the mailbox in the order in which they arrived at the process.&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;Did you notice the concept of "save queue"? That's what many people are not aware of. Let's play with various scenarios and see the mailbox and save queue in action.&lt;br /&gt;&lt;br /&gt;The first scenario is simple, nothing to test there in regards to mailbox. The second one is also straightforward:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;1&gt; self() ! a.&lt;br /&gt;a&lt;br /&gt;2&gt; process_info(self()).&lt;br /&gt; ...&lt;br /&gt; {message_queue_len,1},&lt;br /&gt; {messages,[a]},&lt;br /&gt; ...&lt;br /&gt;3&gt; receive a -&gt; 1; b -&gt; 2 end.&lt;br /&gt;1&lt;br /&gt;4&gt; process_info(self()).&lt;br /&gt; ...&lt;br /&gt; {message_queue_len,0},&lt;br /&gt; {messages,[]},&lt;br /&gt; ...&lt;/pre&gt;&lt;br /&gt;You send a message to the shell, you see it in the process mailbox, then you receive it by matching, after which the queue is empty. Standard queue behaviour.&lt;br /&gt;&lt;br /&gt;Now let's test scenario 3,5:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;1&gt; self() ! c, self() ! d, self() ! a.&lt;br /&gt;a&lt;br /&gt;2&gt; process_info(self()).&lt;br /&gt; ...&lt;br /&gt; {message_queue_len,3},&lt;br /&gt; {messages,[c,d,a]},&lt;br /&gt; ...&lt;br /&gt;3&gt; receive a -&gt; 1; b -&gt; 2 end.&lt;br /&gt;1&lt;br /&gt;4&gt; process_info(self()).&lt;br /&gt; ...&lt;br /&gt; {message_queue_len,2},&lt;br /&gt; {messages,[c,d]},&lt;br /&gt; ...&lt;/pre&gt;&lt;br /&gt;Again, no surprises. Actually, this example demonstrates what people think when they hear about selective receive. Unfortunately we don't see what happened internally between lines 3 and 4. We are going to investigate it now by testing scenario 3,4.&lt;br /&gt;&lt;br /&gt;This time start the shell in distributed mode so that we can connect to it later from the remote shell.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;(foo@bar)1&gt; register(shell, self()).&lt;br /&gt;true&lt;br /&gt;(foo@bar)2&gt; shell ! c, shell ! d.&lt;br /&gt;d&lt;br /&gt;(foo@bar)3&gt; process_info(whereis(shell)).&lt;br /&gt; ...&lt;br /&gt; {current_function,{erl_eval,do_apply,5}},&lt;br /&gt; ...&lt;br /&gt; {message_queue_len,2},&lt;br /&gt; {messages,[c,d]},&lt;br /&gt; ...&lt;br /&gt;(foo@bar)4&gt; receive a -&gt; 1; b -&gt; 2 end.&lt;/pre&gt;&lt;br /&gt;At this moment the shell is suspended - we are exactly at step 4. Go to remote shell, and type the following:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;(foo@bar)1&gt; process_info(whereis(shell)).&lt;br /&gt; ...&lt;br /&gt; {current_function,{erl_eval,receive_clauses,6}},&lt;br /&gt; ...&lt;br /&gt; &lt;b&gt;{message_queue_len,0}&lt;/b&gt;,&lt;br /&gt; &lt;b&gt;{messages,[]}&lt;/b&gt;,&lt;br /&gt; ...&lt;/pre&gt;&lt;br /&gt;That's interesting: no messages in the mailbox. As Joe said, they are in the save queue. Now send a matching message:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;(foo@bar)2&gt; shell ! a.&lt;br /&gt;a&lt;/pre&gt;&lt;br /&gt;Go back to initial shell, which should be resumed now, and check the mailbox again:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;1&lt;br /&gt;(foo@bar)5&gt; process_info(whereis(shell)).&lt;br /&gt; ...&lt;br /&gt; {current_function,{erl_eval,do_apply,5}},&lt;br /&gt; ...&lt;br /&gt; &lt;b&gt;{message_queue_len,2}&lt;/b&gt;,&lt;br /&gt; &lt;b&gt;{messages,[c,d]}&lt;/b&gt;,&lt;br /&gt; ...&lt;/pre&gt;&lt;br /&gt;That's what we saw in the previous test, but now you know what happens behind the scenes: &lt;i&gt;messages are moved from the mailbox to the save queue and then back to the mailbox after the matching message arrives&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;Now you should understand better how selective receive works. Next time you explore your Erlang process, keep in mind the save queue and disappearing and reappearing messages.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-5697576081589193471?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/5697576081589193471/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=5697576081589193471' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/5697576081589193471'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/5697576081589193471'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2010/11/erlang-explained-selective-receive.html' title='Erlang explained: Selective receive'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-6312901095699174993</id><published>2010-11-06T16:44:00.017-04:00</published><updated>2010-11-07T16:07:00.784-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='review'/><title type='text'>Book review: Erlang and OTP in Action</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_R95qtkBZFng/TNXCNfzEmVI/AAAAAAAAEIM/QsQCC7GlXZA/s1600/otp-action-book-cover.png"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 205px; height: 255px;" src="http://1.bp.blogspot.com/_R95qtkBZFng/TNXCNfzEmVI/AAAAAAAAEIM/QsQCC7GlXZA/s400/otp-action-book-cover.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5536544854051690834" /&gt;&lt;/a&gt;Title: Erlang and OTP in Action&lt;br /&gt;Author: Martin Logan, Eric Merritt, and Richard Carlsson&lt;br /&gt;Paperback: 432 pages&lt;br /&gt;Publisher: &lt;a href="http://www.manning.com/logan/"&gt;Manning Publications&lt;/a&gt;; November 2010&lt;br /&gt;Language: English&lt;br /&gt;ISBN-10: 1933988789&lt;br /&gt;ISBN-13: 978-1933988788&lt;br /&gt;$38.99 (&lt;a href="http://www.amazon.com/Erlang-OTP-Action-Martin-Logan/dp/1933988789"&gt;amazon.com&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Overview&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Even though this book has Erlang in its title, it's only about 15% of the content dedicated to Erlang language itself &amp;mdash; the biggest portion of the book is about OTP. Nowadays, when more and more developers get familiar with Erlang, they need a new book that can boost them to the next level of proficiency, where they can produce industry standard code leveraging all the power of Erlang platform. This book is supposed to fill this gap!&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Part One &amp;mdash; The OTP basics&lt;/h2&gt;&lt;br /&gt;&lt;b&gt;Chapter 1 &amp;mdash; The Erlang/OTP platform&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;This chapter gives an overview of the important &lt;i&gt;concepts and features&lt;/i&gt; of Erlang/OTP: concurrency, fault-tolerance, distribution. It discusses four inter-process communication paradigms &amp;mdash; shared memory, STM, futures, message passing &amp;mdash; and shows how the latter makes the distribution trivial to implement in Erlang. You will see how the linked processes and supervision trees build the foundation of Erlang famous fault-tolerance, and how three aspects of Erlang runtime system &amp;mdash; sophisticated scheduler, non-blocking IO, and per-process garbage collection &amp;mdash; complete the picture.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Chapter 2 &amp;mdash; Erlang language essentials&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Take a deep breath &amp;mdash; this long chapter is going to be an Erlang Crash Course. If you already worked with the language, most of it won't be new for you, but it's still worthy to read it because there are many small things that you are probably not aware of or don't use very often. For example,&lt;br /&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;are you familiar with all available shell functions, and break menu options?&lt;br /&gt;&amp;bull;&amp;nbsp;do you know how to work with multiple shells in one window?&lt;br /&gt;&amp;bull;&amp;nbsp;how lists are implemented internally, and how to use ++ operator efficiently?&lt;br /&gt;&amp;bull;&amp;nbsp;what's the difference between arithmetic and exact equality operators?&lt;br /&gt;&amp;bull;&amp;nbsp;do you know that all operators are actually functions, and &lt;tt&gt;1+2&lt;/tt&gt; is the same as &lt;tt&gt;erlang:'+'(1,2)&lt;/tt&gt;?&lt;br /&gt;&amp;bull;&amp;nbsp;that assignment operator is a form of pattern matching?&lt;br /&gt;&amp;bull;&amp;nbsp;that you can use pattern matching instead of regex: &lt;tt&gt;"http://" ++ Rest = "http://www.erlang.org"&lt;/tt&gt;?&lt;br /&gt;&amp;bull;&amp;nbsp;what's the difference between case- and if-expressions, and between pattern matching and guards?&lt;br /&gt;&amp;bull;&amp;nbsp;that besides list comprehensions there are also bitstring comprehensions: &lt;tt&gt;&amp;lt;&amp;lt; &amp;lt;&amp;lt;X:3&amp;gt;&amp;gt; || X &amp;lt;- [1,2,3,4,5,6,7] &amp;gt;&amp;gt;&lt;/tt&gt;?&lt;br /&gt;&amp;bull;&amp;nbsp;which steps Erlang preprocessor performs?&lt;br /&gt;&amp;bull;&amp;nbsp;what's the difference between linked and monitored processes?&lt;br /&gt;&amp;bull;&amp;nbsp;what's the relationship between messages and signals?&lt;br /&gt;&lt;br /&gt;There is also nice introduction to algorithms in this chapter with excellent examples of how to use tail-recursion and accumulators to improve performance.&lt;br /&gt;&lt;br /&gt;Some important topics are covered briefly, like selective receive mechanism, for example. But at the end of the chapter authors give a list of useful Erlang resources, including books and web sites, so you should be able to find there the answers to all your language related questions.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Chapter 3 &amp;mdash; Writing a TCP based RPC service&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;This chapter is about OTP &lt;i&gt;behaviours&lt;/i&gt;. It describes what behaviour is, what are the benefits of it comparing to pure Erlang implementation, and which parts the behaviour consists of. As a 'Hello, World' example authors implemented TCP server!&lt;br /&gt;&lt;br /&gt;Over the course of the chapter you will learn how to model client-server communication using &lt;i&gt;gen_server&lt;/i&gt; behaviour, and how to implement active socket connection using &lt;i&gt;get_tcp&lt;/i&gt; module.&lt;br /&gt;&lt;br /&gt;It also shows the industry conventions and best practices of how to implement and layout behaviour module. You can use this chapter as a reference every time you need to implement a behaviour.&lt;br /&gt;&lt;br /&gt;Code snippet: &lt;a href="http://github.com/erlware/Erlang-and-OTP-in-Action-Source/blob/master/chapter_03/tr_server.erl"&gt;TCP server&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Chapter 4 &amp;mdash; OTP applications and supervision&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;An OTP &lt;i&gt;application&lt;/i&gt; is what ties your modules into a single unit. A &lt;i&gt;supervisor&lt;/i&gt; is what makes your application fault-tolerant. From this chapter you will learn how to implement both behaviours properly: how to layout application directory, how to structure application descriptor, how to write child specifications and restart strategies, and how to generate application documentation. As before, all examples are accompanied by standard conventions and best practices.&lt;br /&gt;&lt;br /&gt;Sample code: &lt;a href="http://github.com/erlware/Erlang-and-OTP-in-Action-Source/tree/master/chapter_04/tcp_rpc/"&gt;application directory layout&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Chapter 5 &amp;mdash; Using the main graphical introspection tools&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;This chapter demonstrates how to use some of the Erlang graphical tools: appmon, webtool, pman, debugger, and table viewer. It's good to know that those tools exist, so when you encounter a problem in your code, you would be able to find the root cause and resolve it quickly.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_R95qtkBZFng/TNXDb7X669I/AAAAAAAAEIU/DIxTRFFHa4Q/s1600/otp-action-book-tools.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 226px;" src="http://1.bp.blogspot.com/_R95qtkBZFng/TNXDb7X669I/AAAAAAAAEIU/DIxTRFFHa4Q/s400/otp-action-book-tools.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5536546201483799506" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Part Two &amp;mdash; Building a production system&lt;/h2&gt;&lt;br /&gt;In the second part of the book you are going to apply all the knowledge you obtained in the first part to build a real world production system: &lt;i&gt;distributed cache&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Chapter 6 &amp;mdash; Implementing a caching system&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;How do you implement a cache? I guess there are many ways to do it, but I would never come up with the idea the authors of the book came up with. They use a &lt;i&gt;separate process to store each value&lt;/i&gt;, and they map each key to its corresponding process. How cool is that?! This way of thinking is possible only in Erlang.&lt;br /&gt;&lt;br /&gt;During the implementation of process management you will learn a new strategy when the supervisor creates multiple child-processes in runtime based on preconfigured template. It's different from what you saw in Chapter 4 where single child process was created on the application startup. One interesting twist here is an inversion of control &amp;mdash; the worker process will call the supervisor to start it.&lt;br /&gt;&lt;br /&gt;The rest of the chapter is dedicated to ETS tables (which are used here to store the mapping). You will see how to create tables and how to perform CRUD operations.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Chapter 7 &amp;mdash; Logging and event handling&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Logging is very important part of any system. In OTP there are two logging utilities: error_logger and SASL. &lt;i&gt;error_logger&lt;/i&gt; is similar to log4x libraries in other languages. It provides basic functions (info, warning and error) that you can call from your code to print messages to the standard output. SASL is more sophisticated. It's an OTP application that logs life cycle events, including crash reports, from other applications. Both methods are thoroughly described in the first part of this chapter.&lt;br /&gt;&lt;br /&gt;The second part explains how to implement &lt;i&gt;custom event handler&lt;/i&gt;. An OTP event handler is just another behaviour that models observer pattern. You can use it for example to implement your own log appender which you can plug in to the error_logger.&lt;br /&gt;&lt;br /&gt;The final section of the chapter provides a step-by-step guide of how to build a &lt;i&gt;custom event stream&lt;/i&gt;, and how to integrate it with the cache application. This technique was totally new to me. I never worked with event handlers on such advanced level.&lt;br /&gt;&lt;br /&gt;Code snippets: &lt;a href="http://github.com/erlware/Erlang-and-OTP-in-Action-Source/blob/master/chapter_07/simple_cache/src/sc_event.erl"&gt;event manager&lt;/a&gt; and &lt;a href="http://github.com/erlware/Erlang-and-OTP-in-Action-Source/blob/master/chapter_07/simple_cache/src/sc_event_logger.erl"&gt;event handler&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Chapter 8 &amp;mdash; Distributed Erlang/OTP&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Distribution is one of the famous features of Erlang. It's very easy to build distributed applications, and more important, it's such a fun to play with Erlang &lt;i&gt;clusters&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;This chapter will guide you through all the methods and techniques you need to know to make your application distributed. You will learn how to start Erlang nodes in different modes, how to combine them into the clusters, how to define topology and isolate clusters from each other, and how to send messages between nodes in the same cluster.&lt;br /&gt;&lt;br /&gt;One of the cool things I learned from this chapter is a &lt;i&gt;remote shell&lt;/i&gt;. It's very similar to SSH but more powerful. Unlike SSH, Erlang remote shell is not a session &amp;mdash; it's a real shell of the remote node where you can start any application including graphical tools!&lt;br /&gt;&lt;br /&gt;The second half of the chapter discusses the problem of &lt;i&gt;resource discovery&lt;/i&gt;: What's the best way to add a new node to the cluster and synchronize its state with existing nodes? The authors come up with a simple and elegant algorithm. You will use this algorithm in the next chapter to build distributed cache.&lt;br /&gt;&lt;br /&gt;Code snippet: resource discovery &lt;a href="http://github.com/erlware/Erlang-and-OTP-in-Action-Source/blob/master/chapter_08/resource_discovery.erl"&gt;algorithm&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Chapter 9 &amp;mdash; Adding distribution to the cache with Mnesia&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;When you design a distributed system you have to make a choice which inter-node communication strategy you are going to use: &lt;i&gt;synchronous&lt;/i&gt; or &lt;i&gt;asynchronous&lt;/i&gt;. Chapter 9 starts with the comparison of these two approaches, their advantages and drawbacks.&lt;br /&gt;&lt;br /&gt;The next step towards the distributed cache is obvious: making the cache storage distributed. As you remember from the chapter 6 the storage was implemented as ETS table. The easiest way to make it distributed is to replace it with Mnesia database. Why and how? You will find it in the next section of this chapter.&lt;br /&gt;&lt;br /&gt;You will learn what &lt;i&gt;Mnesia&lt;/i&gt; is, how to configure it properly, and how to manipulate the data. At the end you will meet beauty and the beast of read operations - query list comprehensions and match specifications. Equipped with all these knowledge you will easily replace ETS table with Mnesia, and make your cache distributed.&lt;br /&gt;&lt;br /&gt;The most amazing part of the last section is an algorithm of &lt;i&gt;dynamic table replication&lt;/i&gt;. You will definitely appreciate it after you learn it &amp;mdash; it's the heart of true scalability.&lt;br /&gt;&lt;br /&gt;Code snippet: working with &lt;a href="http://github.com/erlware/Erlang-and-OTP-in-Action-Source/blob/master/chapter_09/simple_cache/src/sc_store.erl"&gt;Mnesia&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Chapter 10 &amp;mdash; Packaging, services, deployment&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;At this moment you should be able to write non-trivial OTP applications. It's time to think now how to make your application easy to install and start. So far you have started it manually from the shell, and if your app had many dependencies, it was a tedious process. This chapter describes how to &lt;i&gt;automate&lt;/i&gt; it.&lt;br /&gt;&lt;br /&gt;In OTP a deployment unit is called &lt;i&gt;release&lt;/i&gt;. In this chapter you will learn how to build it properly, i.e. how to create release metadata and configuration, resolve dependencies, and generate boot scripts. You will see different ways to start your application: locally in shell, as a daemon or in embedded mode.&lt;br /&gt;&lt;br /&gt;After you release the application, you might want to share it with other people. That's what the next section is about. It shows you how to make a &lt;i&gt;package&lt;/i&gt; &amp;mdash; standard or customized, universal or OS-dependent &amp;mdash; and how to install it on a different machine.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Part Three &amp;mdash; Integrating and refining&lt;/h2&gt;&lt;br /&gt;In the previous part you built a distributed cache in OTP, and you can use it now from any Erlang application. This is already a big achievement, and you must be proud of it, but you can make it even bigger if you expose this wonderful functionality to other platforms. Erlang is known for its robustness and scalability, and it would be very beneficial for non-Erlang clients as well to utilize these qualities.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Chapter 11 &amp;mdash; Text and REST (Communication via TCP and HTTP)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The first non-Erlang interface you are going to implement is TCP. If you remember, you already did it in Chapter 3 when you implemented &lt;i&gt;TCP server&lt;/i&gt;. That server though had one significant limitation: it handled only one connection. The new implementation in this chapter is more efficient: it supports multiple concurrent connections.&lt;br /&gt;&lt;br /&gt;The next interface is HTTP. Although it sounds similar to the previous one, the way you will implement it is totally different. You won't use standard gen_server behaviour. Instead, you will implement a &lt;i&gt;custom behaviour&lt;/i&gt; which you are going to define yourself. This is a very advanced topic, and if you want to build extensible systems in Erlang, you need to understand all the details of how to do it. Fortunately, this section provides thorough instructions.&lt;br /&gt;&lt;br /&gt;Over the course of this chapter you will also learn bunch of other useful things besides server behaviours. You will see how HTTP protocol works and how to design RESTful services on top of it, how to use TCP sockets more effectively, and how to increase stability of your system with well-designed OTP supervisors. You will have lots of fun doing binary pattern matching.&lt;br /&gt;&lt;br /&gt;Code snippets: &lt;a href="http://github.com/erlware/Erlang-and-OTP-in-Action-Source/tree/master/chapter_11/tcp_interface/src/"&gt;TCP interface&lt;/a&gt;, &lt;a href="http://github.com/erlware/Erlang-and-OTP-in-Action-Source/tree/master/chapter_11/gen_web_server/src/"&gt;HTTP server behaviour&lt;/a&gt;, &lt;a href="http://github.com/erlware/Erlang-and-OTP-in-Action-Source/tree/master/chapter_11/http_interface/src/"&gt;REST interface&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Chapter 12 &amp;mdash; Drivers (Communication with C programs)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;This chapter is tough &amp;mdash; you have to be a C programmer to understand all the details. If you are not involved into C programming, it's still worthy to read it, just to understand the concept, although it might be hard to get through the entire text.&lt;br /&gt;&lt;br /&gt;There are two types of drivers in Erlang: &lt;i&gt;port drivers&lt;/i&gt; and &lt;i&gt;linked-in drivers&lt;/i&gt;. The chapter starts with an overview of them both. It explains their benefits and drawbacks, how you should design the driver, and where you should handle the driver's state, global vs. instance variables.&lt;br /&gt;&lt;br /&gt;The rest of the chapter is a tutorial of how to implement drivers. It describes three components that comprise driver implementation: C side, Erlang side and the protocol between them. It shows the differences in each component for both types of drivers, and it gives you recommendations of how you should approach the problem required communication with C code.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Chapter 13 &amp;mdash; Jinterface (Communication with Java programs)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Unlike the C driver implementation, connecting together Erlang and Java is pretty simple: you instantiate OtpNode class in the JVM thread, and it becomes available as Erlang node to any running Erlang application. You can start sending messages between Java and Erlang, and all Erlang terms will be properly converted to Java classes, and vice versa. All the magic is done in &lt;i&gt;Jinterface&lt;/i&gt; library, which is a part of OTP distribution, and what you need to know to start using it is perfectly explained in this chapter.&lt;br /&gt;&lt;br /&gt;After you learn how to work with Jinterface, you will apply this knowledge to building the bridge between the cache you implemented in the previous chapters and HBase. &lt;i&gt;HBase&lt;/i&gt; is one of the modern NoSQL databases. If you didn't work with it before, don't worry &amp;mdash; the authors will show you how to get started with it, and how to implement HBase connector using Java API. Having this API in place, all you need to do is to link it with the Erlang cache using the technique described above.&lt;br /&gt;&lt;br /&gt;By the end of the chapter (and in fact end of the book) you will have a distributed cache written in Erlang backed by NoSQL database via Erlang-Java bridge. I don't know about you, but I was actually very impressed after I finished the coding and saw the entire solution working on my machine. It's really amazing that you can build pretty sophisticate piece of software with such a small amount of code.&lt;br /&gt;&lt;br /&gt;Code snippets: &lt;a href="http://github.com/erlware/Erlang-and-OTP-in-Action-Source/blob/master/chapter_13/simple_cache/java_src/HBaseNode.java"&gt;message receiver&lt;/a&gt; and &lt;a href="http://github.com/erlware/Erlang-and-OTP-in-Action-Source/blob/master/chapter_13/simple_cache/java_src/HBaseTask.java"&gt;message responder&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Chapter 14 &amp;mdash; Optimization and performance&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;As we all know, premature optimization is the root of all evil. In other words, don't spend time optimizing your solution before you actually measured the performance. That implies you must know what and how to measure. In this chapter authors describe the approach you should take when you prepare performance test, as well as the basic tools available in Erlang for performance testing: &lt;i&gt;cprof&lt;/i&gt; and &lt;i&gt;fprof&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;The second part of the chapter explains the Erlang programming language &lt;i&gt;caveats&lt;/i&gt;. You will see&lt;br /&gt;&amp;bull;&amp;nbsp;how primitive data types stored in memory, and which data structures you should use to fulfil performance requirements;&lt;br /&gt;&amp;bull;&amp;nbsp;how to use some built-in functions and operators properly;&lt;br /&gt;&amp;bull;&amp;nbsp;how to call function in different ways, and how performant those calls are;&lt;br /&gt;&amp;bull;&amp;nbsp;how compiler optimizes pattern matching and tail recursion;&lt;br /&gt;&amp;bull;&amp;nbsp;whether to use OTP behaviours or plain Erlang processes.&lt;br /&gt;&lt;br /&gt;That concludes the main content of the book.&lt;br /&gt;&lt;br /&gt;There are also two appendices in this book. The first one describes how to install Erlang on the OS of your choice. The second explains what &lt;i&gt;referential transparency&lt;/i&gt; is and why lists in Erlang are implemented as they are.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Conclusion&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;If you are an intermediate Erlang developer, go and buy this book! It will teach you how to build robust production systems following proven design principles and standard conventions. It will make your code easy to read and maintain. You will learn lots of new things and it will be a big step towards Erlang mastery.&lt;br /&gt;&lt;br /&gt;Happy OTPing!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-6312901095699174993?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/6312901095699174993/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=6312901095699174993' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/6312901095699174993'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/6312901095699174993'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2010/11/book-review-erlang-and-otp-in-action.html' title='Book review: Erlang and OTP in Action'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_R95qtkBZFng/TNXCNfzEmVI/AAAAAAAAEIM/QsQCC7GlXZA/s72-c/otp-action-book-cover.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-7220700231127763150</id><published>2010-10-31T14:12:00.018-04:00</published><updated>2011-03-23T11:21:36.532-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='rabbitmq'/><category scheme='http://www.blogger.com/atom/ns#' term='spring'/><category scheme='http://www.blogger.com/atom/ns#' term='nosql'/><category scheme='http://www.blogger.com/atom/ns#' term='grails'/><category scheme='http://www.blogger.com/atom/ns#' term='review'/><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>SpringOne2GX 2010</title><content type='html'>Last week I attended SpringOne2GX conference in Chicago, the main event in Spring/Groovy/Grails community. Here I want to post my brief review of this conference.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;First impressions&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The hotel (Westin Lombard) was nice and clean. Internet: there were 2 wireless networks and one cable - everything was free and worked pretty well, signal was good in almost all rooms. The conference reception was well organized - every participant received bunch of souvenirs and special edition of NFJS magazine. I saw hundreds of smiling and happy people of different ages and different outfits. Most of them with Macs. Most of them know each other. The food was fantastic, especially dinner with wine and beer.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Day 1&lt;/h2&gt;&lt;br /&gt;The first day was mostly introduction and orientation. There was only one talk on the schedule.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Rod Johnson - Keynote&lt;/b&gt; (&lt;a href="http://www.infoq.com/presentations/SpringOne-2GX-2010-Keynote"&gt;video&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;I thought Spring was initially created 7 years ago but the oldest class in the source tree is dated by January 17, 2001, so Spring is actually almost 10 years old. Because of the anniversary the main theme of the presentation was: Where Spring goes in the next decade.&lt;br /&gt;&lt;br /&gt;Since the core framework is well crafted already, the focus will be on the integration and making Spring portfolio as a platform for applications. There are three key values in Spring - portability, productivity and innovation - and the platform will be built along those dimensions.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Portability&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;In the past SpringSource made a good job by providing a framework that make Java applications easily portable across different application servers. The goal for the next decade is to expand the same portability to the cloud - Google AppEngine, vFabric, vmforce, etc.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Productivity&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;As we all know the ultimate reason of the Spring existence is to make the life of application developer easier, our work is more productive. The framework hides the low-level boilerplate, and provides well defined abstractions. In the next year there will be several features added to the Spring portfolio. Rod mentioned some of them:&lt;br /&gt;- Seamless GWT integration&lt;br /&gt;- Database reverse engineering with roundtripping support in Spring Roo 1.1. You will be able to generate the domain object tree based on your database schema, and it will be updated every time you change the database.&lt;br /&gt;- Spring Payment Services project with Visa integration.&lt;br /&gt;&lt;br /&gt;Another aspect of productivity is a tool suite, and here Spring gives you STS. Rod invited Christian Dupuis on the stage, where he demoed how to developed Grails applications in STS. If you are a Grails developer you should definitely take a look at the latest version of STS - it will increase your productivity significantly.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Innovation&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;There will be several new projects released in the Spring portfolio soon:&lt;br /&gt;- Spring Social - application abstraction for social networks.&lt;br /&gt;- Spring Mobile - platform for multi-device applications.&lt;br /&gt;- Spring-AMQP - API for integration with RabbitMQ.&lt;br /&gt;- Spring Data - API to work with NoSQL databases, in particular Neo4J support in Spring Roo.&lt;br /&gt;&lt;br /&gt;Keith Donald demoed &lt;a href="http://greenhouse.springsource.org"&gt;GreenHouse&lt;/a&gt; project and corresponding &lt;a href="http://itunes.apple.com/us/app/greenhouse/id395862873"&gt;iPhone app&lt;/a&gt;. This is a reference implementation of Spring Mobile and Spring Social, and this app was really really useful during the conference when I needed to check the schedule and find the room.&lt;br /&gt;&lt;br /&gt;At the end of the presentation Rod introduced, and Mik Kersten demoed, the next big thing - Code2Cloud. It's basically a tool that allows you to keep and manage your entire development environment in the cloud: the running app, the source code, the issue tracker, and the build server. Everything is in the cloud and configured by mouse click. It looks cool, and it definitely will be a buzz word in the next year, but I'm not sure if many people will use it. We'll see.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Day 2&lt;/h2&gt;&lt;br /&gt;I'm going to write only about technical sessions I attended.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;J&amp;uuml;rgen H&amp;ouml;ller - What's new in Spring Framework 3.1?&lt;/b&gt; (&lt;a href="http://www.infoq.com/presentations/Spring-3.0-3.1-3.2"&gt;video&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;That was one of the best talks of this conference: technical, right to the point, with well-wrtten slides, and personal charm of the presenter. Despite the number 3.1 in the title, J&amp;uuml;rgen actually covered three versions of Spring framework: 3.0, 3.1, and 3.2. I'm going to briefly mention the interesting features, and if you want more details you can check the excellent on-line documentation.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Spring 3.0&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;- Custom annotations. You can create your own annotation by combining multiple existing annotations in one group. Spring automatically detects your annotation during the application context startup, and no special configuration is required. This is a very handy feature, especially when you copy-paste the same annotation group over and over again.&lt;br /&gt;&lt;br /&gt;- Configuration classes and annotated factory methods. If you annotate a method with @Bean annotation Spring framework will make the output of the method a Spring bean. There are some other annotations supported, e.g. @Lazy.&lt;br /&gt;&lt;br /&gt;- Standardized annotations. Spring now supports JSR-330 @Inject, JSR-250 @ManagedBean, and EJB 3.x @TransactionAnnotation.&lt;br /&gt;&lt;br /&gt;- EL++. Expression language can be used now in bean definitions inside appcontext XML, and also in component annotations. Very powerful feature.&lt;br /&gt;&lt;br /&gt;- REST support. Spring provides RestTemplate for client code, @PathVariable annotation, and special view resolvers on the server side. It's very interesting topic - check the documentation for details.&lt;br /&gt;&lt;br /&gt;- Declarative model validation. You can specify data constraints right in your code by using annotations - very similar to what you have in GORM.&lt;br /&gt;&lt;br /&gt;- Improved scheduling. New namespace, and @Scheduled and @Async annotations makes your appcontext smaller and more readable.&lt;br /&gt;&lt;br /&gt;If you follow Spring releases, you probably use some or most of these features already. Now let's see what Spring 3.1 brings to us.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Spring 3.1&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;- Environment profiles for beans. Similar to Maven profiles but works in runtime. The idea here is to create a single deployment unit for all environments and enable certain Spring beans for specific environment. I can't wait to try this feature in our enterprise project.&lt;br /&gt;&lt;br /&gt;- Cache abstraction. After 5 years of hibernation this feature is finally implemented. Spring provides an API to work with distributed cache, in particular in cloud environments. There will be adapters for most popular cache implementations, such as EhCache, GemFire, Coherence.&lt;br /&gt;&lt;br /&gt;- Conversation management, or how J&amp;uuml;rgen calls it HttpSession++. It's basically an extension of HttpSession shared across multiple browsers and window tabs. Looks very interesting.&lt;br /&gt;&lt;br /&gt;- Enhanced Groovy support.&lt;br /&gt;&lt;br /&gt;- c: namespace, which is a shortcut for &amp;lt;constructor-arg&amp;gt;, analogous to p: namespace for properties. Small feature that makes your appcontext consistent and more readable.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Spring 3.2&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Java SE 7 support, JDBC 4.1, support for fork-join framework, general focus on concurrent programming.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Jeff Brown - GORM inside and out&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;This talk was also good. I worked a bit with GORM before, and had an idea how it's implemented, but it was useful to hear more details from one of the developers.&lt;br /&gt;&lt;br /&gt;Jeff started with the background of GORM, the complexity of Hibernate and JPA, and how GORM solves this problem following convention-over-configuration and sensible defaults strategy. He showed how to model the domain objects, what happens behind the scene when you link objects together, how to specify uni- and bi-directional relationships, and how to change default collection implementation in case of one-to-many relationship.&lt;br /&gt;&lt;br /&gt;During the presentation he was switching back and forth between sides and terminal, so it was easy to follow and understand the evolution of the sample application. He explained how to introduce various constraints into the model and how Grails would validate them. One of the interesting features I didn't know about was how to test internationalized error messages. You don't need to change your locale for that, simply add &lt;code&gt;lang=your_language&lt;/code&gt; parameter to the URL, and Grails will switch to that language for all subsequent requests. Pretty handy.&lt;br /&gt;&lt;br /&gt;He concluded the talk by showing how dynamic finders are implemented in GORM using Groovy metaprogramming feature. Interesting part here is that you can implement similar things in your Groovy code using the same technique, basically having custom mini-GORM in your Groovy project.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Venkat Subramaniam - Improving your Groovy code quality&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The title of this presentation was little bit misleading for me. I expected Venkat to show some Groovy specific mistakes and how to avoid them. Instead, he was talking about the errors that in most cases are equally applied to any programming language. He mentioned various code smells and explained how to fix them. If you are interested, you can download the slides from Venkat's &lt;a href="http://www.agiledeveloper.com/presentations/improving_your_groovy_code_quality.zip"&gt;web site&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;He also gave an advise how to maintain the high code quality:&lt;br /&gt;- Have a respectable colleague review your code.&lt;br /&gt;- Use code analysis tools like CodeNarc and Sonar Groovy plugin.&lt;br /&gt;&lt;br /&gt;One of the topics he covered was the usage of the 'return' keyword in Groovy. That was interesting. Compare the following two functions and guess what they return:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;def func1() {&lt;br /&gt;    try {&lt;br /&gt;        5&lt;br /&gt;    } finally {&lt;br /&gt;        return 22&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;def func2() {&lt;br /&gt;    try {&lt;br /&gt;        5&lt;br /&gt;    } finally {&lt;br /&gt;        22&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Paul King, Guillaume Laforge - Groovy.DSLs (from: beginner, to: expert)&lt;/b&gt; (&lt;a href="http://www.infoq.com/presentations/Groovy-DSLs"&gt;video&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;This would be very nice presentation if the speakers didn't try to cover too much. This talk could be easily split into two: one is an overview of Groovy language and another one is DSL. Unfortunately they spent lot of time on theoretical DSL part and Groovy overview, so the practical DSL part was too short from my perspective. The good thing though is that I have slides now, so I can dig deeper into this subject at my spare time.&lt;br /&gt;&lt;br /&gt;In the second part of the talk Paul and Guillaume explained which features of Groovy language make it so simple to create DSLs. Here are some of them:&lt;br /&gt;- Static imports and import aliases.&lt;br /&gt;- Simplified collection syntax.&lt;br /&gt;- Small or no language noise.&lt;br /&gt;- Aggregating multiple method calls using 'with' construct.&lt;br /&gt;- Closures.&lt;br /&gt;- Operator overloading.&lt;br /&gt;- Metaprogramming.&lt;br /&gt;&lt;br /&gt;In the last part speakers talked about different patterns and techniques of DSL implementations. They provided a comprehensive list of books you might want to read if you are interested in building DSLs.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Adrian Colyer - Technical keynote&lt;/b&gt; (&lt;a href="http://www.infoq.com/presentations/Spring-2010-Technology"&gt;video&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;Adrian's talk was mostly a reiteration of Rod's keynote from the previous day with some technical details. He mentioned Spring Payment and Spring Data projects, bean profiles and cache support in the Spring core. He showed Spring portability in action by providing links to Spring applications deployed on &lt;a href="http://springtravelsql.appspot.com/login"&gt;Google AppEngine&lt;/a&gt; and &lt;a href="http://booking-mvc.internal.vmforce.com/spring/intro"&gt;vmForce&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Another interesting part was 20 minutes dedicated to RabbitMQ and Spring-AMQP. He even mentioned Spring-Erlang project which is supposed to be a convenient abstraction on top of standard Jinterface library.&lt;br /&gt;&lt;br /&gt;As a continuation of innovation theme Graeme Rocher demoed GORM support for NoSQL databases. That was cool. He simply uninstalled Hibernate plugin and installed Redis plugin, without touching data model. Everything worked perfect. Right now Spring works with Redis and GemFire, but soon they are going to add support for CouchDB, Cassandra, Riak, Neo4j, and MongoDB. Another interesting thing Graeme showed was grails-console. It's a pretty nice tool, you should check it out. It allows you to interact with the Grails data storage using GORM features. Very handy.&lt;br /&gt;&lt;br /&gt;Another co-presenter was Keith Donald who demoed Spring Social and Spring Mobile. He explained how OAuth works, and how interoperability with social networks was implemented in GreenHouse.&lt;br /&gt;&lt;br /&gt;The keynote was concluded by Jon Travis who demoed SpringInsight.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Day 3&lt;/h2&gt;&lt;br /&gt;&lt;b&gt;Venkat Subramaniam - Functional programming in Groovy&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;That was an excellent talk and nice start of the new conference day. Venkat explained main concepts and values of functional programming, and illustrated the theory with comprehensible examples.&lt;br /&gt;&lt;br /&gt;He compared imperative and functional style of programming by showing how to implement for-loop using inject() function in Groovy. I think it was one of the best explanations of functional folding I've ever heard. He also demonstrated map and filter operations using collect() and findAll() methods.&lt;br /&gt;&lt;br /&gt;He clarified the difference between function value and closure, and between iterative procedure and iterative process. He gave an example on how to pass closure as a parameter to simulate function object in Groovy. He also showed how to replace tail-recursion, which Groovy doesn't support, with inject() method call.&lt;br /&gt;&lt;br /&gt;The presentation was concluded with an example of how to use functional techniques to build DSLs in Groovy.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Matthias Radestock, Mark Pollack, Mark Fisher - RabbitMQ and Spring-AMQP&lt;/b&gt; (&lt;a href="http://www.infoq.com/presentations/Spring-AMQP"&gt;video&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;If you read my blog, you know that RabbitMQ is one of my latest interests. I decided to go to this talk just to see how the creators would present their projects. It turned out to be a nice introduction to RabbitMQ and &lt;a href="http://www.springsource.org/spring-amqp"&gt;Spring-AMQP&lt;/a&gt;. They explained main concepts of AMQP and how it is different from JMS. Here I want to give you some ideas which were not obvious for me when I started working with RabbitMQ.&lt;br /&gt;&lt;br /&gt;- Messaging is all about decoupling, and AMQP is much more flexible than JMS in terms of publisher-consumer decomposition.&lt;br /&gt;- All resources are dynamically created and destroyed by clients - the static pre-configuration is optional.&lt;br /&gt;- Exchanges are stateless, they don't keep messages, they only copy and dispatch them. Queues hold the messages and deliver one message to a single client. They neither do routing nor message copying.&lt;br /&gt;- Queue never receives the same message twice.&lt;br /&gt;- If the message doesn't match routing key it's dropped.&lt;br /&gt;- Because of the open protocol, you can use all available TCP tools to monitor your message traffic.&lt;br /&gt;&lt;br /&gt;Besides AMQP implementation RabbitMQ also provides some other useful features like custom exchanges, exchange-to-exchange routing, different protocol adaptors, etc. Spring, as usual, gives you a consistent API on top of the RabbitMQ client which hides all low-level boilerplate and makes your application code more readable.&lt;br /&gt;&lt;br /&gt;Good presentation, great guys.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Craig Walls - Developing social-ready web applications&lt;/b&gt; (&lt;a href="http://www.infoq.com/presentations/Developing-Social-Ready-Web-Apps"&gt;video&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;This presentation was about integrating your Java code with different social networks. There are three types of such integration: widgets, embedded code, REST API. Craig briefly explained first two, and then dived into REST.&lt;br /&gt;&lt;br /&gt;All popular social networks provide REST API which allows you to communicate with them. For simple operations, like search, you can just use standard Spring RestTemplate class to retrieve the data. Try for example the following URLs:&lt;br /&gt;- &lt;a href="http://api.twitter.com/1/friends/ids.xml?screen_name=ndpar"&gt;http://api.twitter.com/1/friends/ids.xml?screen_name=ndpar&lt;/a&gt;&lt;br /&gt;- &lt;a href="http://search.twitter.com/search.json?q=s2gx"&gt;http://search.twitter.com/search.json?q=s2gx&lt;/a&gt;&lt;br /&gt;- &lt;a href="https://graph.facebook.com/ndpar"&gt;https://graph.facebook.com/ndpar&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This basic approach fails though if you try to post a new message, because you have to be authorized for update operations. That's where OAuth comes in. The idea behind OAuth is pretty simple: instead of sharing your user-password with different clients, it uses generated tokens. This model is more flexible because if you want to revoke the permission from particular client you don't need to change your password and notify rest of the clients - you just remove that client's token from the list of authorized clients and that's it. The only problem with OAuth and social networks is that they support different versions of OAuth. This problem is solved by Spring Social project.&lt;br /&gt;&lt;br /&gt;Spring Social offers consistent template-based API across different social providers. It basically gives you an OAuth aware RestTemplate, so you can do something like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;TwitterTemplate twitter = new TwitterTemplate(API_KEY, API_SECRET, ACCESS_TOKEN, ACCESS_TOKEN_SECRET);&lt;br /&gt;twitter.updateStatus("Hello #s2gx !");&lt;br /&gt;twitter.retweet(26887414177L);&lt;/pre&gt;&lt;br /&gt;If you are in a social network business, definitely take a look at &lt;a href="http://www.springsource.org/spring-social"&gt;Spring Social&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Mark Pollack, Chris Richardson - Using Spring with non-relational databases&lt;/b&gt; (&lt;a href="http://www.infoq.com/presentations/Using-Spring-with-NoSQL-Databases"&gt;video&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;Relational databases are great, right? They've been with us for ages. Everybody knows how to work with them, how to build SQL statements. Every language provides ODBC library. There are bunch of frameworks that make developer's life easier. So why so sudden buzz around NoSQL?&lt;br /&gt;&lt;br /&gt;Mark and Chris started their talk highlighting some problems that exist in relational database world:&lt;br /&gt;- Object-relational impedance mismatch. Complicated mapping of rich domain model to relational schema. Relational schema rigidity.&lt;br /&gt;- Extremely difficult/impossible to scale write operations.&lt;br /&gt;- Suboptimal performance in some cases.&lt;br /&gt;&lt;br /&gt;All these issues are addressed in NoSQL databases. Although keep in mind that it's not coming for free - you have to trade off ACID semantics, transactions and some other features of RDBMS. But if scalability is more important for you than consistency then NoSQL is your way to go.&lt;br /&gt;&lt;br /&gt;There are tons of NoSQL databases available for you, but they all can be split into 4 categories based on their data model:&lt;br /&gt;- Key-Value: Amazon Dynamo, Redis, Riak, Voldemort.&lt;br /&gt;- Column: Google Bigtable, HBase, Cassandra.&lt;br /&gt;- Document: CouchDB, MongoDB.&lt;br /&gt;- Graph: Neo4j, Sones, InfiniteGraph.&lt;br /&gt;&lt;br /&gt;Mark and Chris talked about each type, what their typical use cases are, and how their APIs look like. They showed examples for Redis, Cassandra, MongoDB, CouchDB and Neo4j. Then they introduced &lt;a href="http://www.springsource.org/spring-data"&gt;Spring Data&lt;/a&gt; project which, as everything from SpringSource, simplifies the application development and eliminates low-level code. Right now they support most of the popular NoSQL databases, and they plan to add more in the future.&lt;br /&gt;&lt;br /&gt;The project is in active development phase, and the new contributors are welcome. So if it sounds interesting for you, go and check it out.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Day 4&lt;/h2&gt;&lt;br /&gt;&lt;b&gt;Hans Dockter - Gradle - a better way to build&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;I never played with Gradle, so I was very curious to see how it looks like. According to Hans, who is the creator of this tool, Gradle is a general purpose build system with Groovy DSL interface. It's written in Java and provides build-in support for Java, Groovy, Scala, web and OSGi projects. It's a build language, so you can extend it for your own purposes if needed.&lt;br /&gt;&lt;br /&gt;If you compare it with Ant, Gradle is definitely much better because it's more compact and flexible. It offers dependency resolution with integration with Maven and Ivy repositories. It also has some advanced features like incremental builds for custom tasks and parallel testing.&lt;br /&gt;&lt;br /&gt;The only problem I had with this presentation was that Hans kept comparing Gradle with Maven. In my opinion they are not comparable. They have different philosophy if you want. All Maven 'constraints' are imposed by design, so it makes no sense to blame Maven for them. I think Ant-Gradle comparison is more appropriate and that's what Hans should have emphasized.&lt;br /&gt;&lt;br /&gt;Other than that the session was pretty informative, and I have a better picture of Gradle now.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Brian Sletten - Groovy + The Semantic Web&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;I had no idea what Semantic Web was. I saw this term first time on the conference schedule, so I decided to go to this talk just to educate myself. I cannot even briefly describe all the discoveries I made during this presentation because I still feel little bit overwhelmed. I just want to provide some links from Brian's slides that can guide you if you want to learn this concept.&lt;br /&gt;&lt;br /&gt;- &lt;a href="http://en.wikipedia.org/wiki/Semantic_Web"&gt;Semantic Web&lt;/a&gt; - article from wikipedia.&lt;br /&gt;- Formal W3C specs: &lt;a href="http://www.w3.org/RDF/"&gt;RDF&lt;/a&gt;, &lt;a href="http://www.w3.org/TR/rdfa-syntax/"&gt;RDFa&lt;/a&gt;, &lt;a href="http://www.w3.org/2004/02/skos/"&gt;SKOS&lt;/a&gt;, &lt;a href="http://www.w3.org/TR/rdf-sparql-query/"&gt;SPARQL&lt;/a&gt;, &lt;a href="http://www.w3.org/TR/owl2-overview/"&gt;OWL&lt;/a&gt;.&lt;br /&gt;- SPARQL &lt;a href="http://librdf.org/query"&gt;demo&lt;/a&gt;.&lt;br /&gt;- &lt;a href="http://www.w3.org/2007/08/pyRdfa"&gt;RDFa distiller and parser&lt;/a&gt;. Try to feed Brian's test page URL (http://bosatsu.net/nfjs/test.html) to the distiller and see what it returns.&lt;br /&gt;- &lt;a href="http://opengraphprotocol.org"&gt;OG&lt;/a&gt; - open graph protocol.&lt;br /&gt;- &lt;a href="http://jena.sourceforge.net"&gt;Jena&lt;/a&gt; - Java API to work with Semantic Web.&lt;br /&gt;- &lt;a href="http://github.com/shellac/java-rdfa"&gt;Java-RDFa&lt;/a&gt; parser.&lt;br /&gt;- &lt;a href="http://clarkparsia.com/pellet/"&gt;Pellet&lt;/a&gt; - Java API for OWL.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Conclusion&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Whew! This happens to be longer review than I planned initially. If you are still with me you deserve my applause!&lt;br /&gt;&lt;br /&gt;There were much more presentations at this conference but because of the tight schedule I had to sacrifice 80% of them. My overall impression from this conference is very positive. If you are a Spring/Groovy/Grails developer I encourage you to go to this event next year. The biggest benefit of it: &lt;i&gt;You start seeing the Spring as a universe&lt;/i&gt;, not as a bunch of separate projects. You cannot get this feeling from the documentation, even if it's perfect as the Spring one.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-7220700231127763150?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/7220700231127763150/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=7220700231127763150' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/7220700231127763150'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/7220700231127763150'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2010/10/springone2gx-2010.html' title='SpringOne2GX 2010'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-3072169188790801625</id><published>2010-08-02T18:53:00.005-04:00</published><updated>2010-09-25T16:07:30.725-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rabbitmq'/><category scheme='http://www.blogger.com/atom/ns#' term='spring'/><title type='text'>Working with RabbitMQ in Spring applications</title><content type='html'>Recently SpringSource released &lt;a href="http://www.springsource.org/node/2715"&gt;Spring AMQP 1.0.0.M1&lt;/a&gt;. Now, if you are a Spring shop working with RabbitMQ, you don't need to write low level code to connect to RabbitMQ server anymore. Instead, you can use well-known Spring abstractions (message templates and containers) to produce/consume AMQP messages, the same approach you would use for JMS. Here is my previous &lt;a href="http://ndpar.blogspot.com/2010/03/get-started-with-rabbitmq.html"&gt;example&lt;/a&gt; re-implemented using Spring AMQP.&lt;br /&gt;&lt;br /&gt;Very simple application classes (sender and receiver)&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;import org.springframework.amqp.core.AmqpTemplate;&lt;br /&gt;import org.springframework.beans.factory.annotation.Autowired;&lt;br /&gt;&lt;br /&gt;public class MessageSender {&lt;br /&gt;&lt;br /&gt;    @Autowired&lt;br /&gt;    private AmqpTemplate template;&lt;br /&gt;&lt;br /&gt;    public void send(String text) {&lt;br /&gt;        template.convertAndSend(text);&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;import org.springframework.amqp.core.Message;&lt;br /&gt;import org.springframework.amqp.core.MessageListener;&lt;br /&gt;&lt;br /&gt;public class MessageHandler implements MessageListener {&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    public void onMessage(Message message) {&lt;br /&gt;        System.out.println("Received message: " + message);&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;and pretty standard application context&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;context:annotation-config /&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;bean id="rabbitConnectionFactory" class="org.springframework.amqp.rabbit.connection.SingleConnectionFactory"&lt;br /&gt;    p:username="guest" p:password="guest" p:virtualHost="/" p:port="5672"&amp;gt;&lt;br /&gt;    &amp;lt;constructor-arg value="lab.ndpar.com" /&amp;gt;&lt;br /&gt;&amp;lt;/bean&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;bean id="rabbitTemplate" class="org.springframework.amqp.rabbit.core.RabbitTemplate"&lt;br /&gt;    p:connectionFactory-ref="rabbitConnectionFactory"&lt;br /&gt;    p:routingKey="myRoutingKey"&lt;br /&gt;    p:exchange="myExchange" /&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;bean id="messageSender" class="com.ndpar.spring.rabbitmq.MessageSender" /&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;bean class="org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer"&lt;br /&gt;    p:connectionFactory-ref="rabbitConnectionFactory"&lt;br /&gt;    p:queueName="myQueue"&lt;br /&gt;    p:messageListener-ref="messageListener" /&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;bean id="messageListener" class="com.ndpar.spring.rabbitmq.MessageHandler" /&amp;gt;&lt;/pre&gt;&lt;br /&gt;That's it, simple and clean.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Resources&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;&lt;a href="http://www.springsource.org/spring-amqp"&gt;Spring AMQP&lt;/a&gt; official page&lt;br /&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;&lt;a href="http://github.com/ndpar/rabbitmq-spring-demo"&gt;Source code&lt;/a&gt; for this blog&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-3072169188790801625?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/3072169188790801625/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=3072169188790801625' title='27 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/3072169188790801625'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/3072169188790801625'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2010/08/working-with-rabbitmq-in-spring.html' title='Working with RabbitMQ in Spring applications'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><thr:total>27</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-7845207785745465571</id><published>2010-03-31T19:06:00.012-04:00</published><updated>2010-04-06T11:10:44.316-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ejabberd'/><category scheme='http://www.blogger.com/atom/ns#' term='rabbitmq'/><title type='text'>Integrating RabbitMQ with ejabberd</title><content type='html'>Last few days I've been trying to make RabbitMQ and ejabberd work smoothly together by means of &lt;a href="http://hg.rabbitmq.com/rabbitmq-xmpp/raw-file/default/doc/index.html"&gt;mod_rabbitmq&lt;/a&gt; gateway. The official mod_rabbitmq document is pretty clear but the &lt;a href="http://hg.rabbitmq.com/rabbitmq-xmpp/raw-file/default/doc/overview-summary.html#Compiling_the_gateway"&gt;installation&lt;/a&gt; chapter is rather short. Plus, it presumes that ejabberd is installed from the source tree, which might not be the case. Here I want to give you more detailed instructions on the installation/configuration process in case mod_rabbitmq doesn't work for you out of the box.&lt;br /&gt;&lt;br /&gt;My environment is Ubuntu 9.10 with rabbitmq-server and ejabberd packages installed via apt-get. Both RabbitMQ and ejabberd are up and running. Now I want them to talk to each other and route messages properly.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Compiling mod_rabbitmq&lt;/h4&gt;&lt;br /&gt;If you have the same environment as mine you can just download the &lt;a href="http://github.com/ndpar/erlang/raw/master/ebin/mod_rabbitmq.beam"&gt;binary&lt;/a&gt; and the &lt;a href="http://hg.rabbitmq.com/rabbitmq-xmpp/raw-file/73c129561101/src/rabbit.hrl"&gt;header&lt;/a&gt; files, and copy them to the corresponging ejabberd folders (see last two lines in the bash snippet below). Alternatively you can compile mod_rabbitmq.beam file yourself:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ git clone git://git.process-one.net/ejabberd/mainline.git ejabberd&lt;br /&gt;$ cd ejabberd&lt;br /&gt;$ git checkout -b 2.1.x origin/2.1.x&lt;br /&gt;$ cd src&lt;br /&gt;$ wget http://hg.rabbitmq.com/rabbitmq-xmpp/raw-file/73c129561101/src/mod_rabbitmq.erl&lt;br /&gt;$ wget http://hg.rabbitmq.com/rabbitmq-xmpp/raw-file/73c129561101/src/rabbit.hrl&lt;br /&gt;$ ./configure --disable-tls&lt;br /&gt;$ make&lt;br /&gt;$ sudo cp mod_rabbitmq.beam /usr/lib/ejabberd/ebin/&lt;br /&gt;$ sudo cp rabbit.hrl /usr/lib/ejabberd/include/&lt;/pre&gt;&lt;br /&gt;&lt;h4&gt;Configuring mod_rabbitmq&lt;/h4&gt;&lt;br /&gt;You need to know the short name of the machine you are running RabbitMQ on. Use &lt;code&gt;hostname -s&lt;/code&gt; command for this. Open &lt;code&gt;/etc/ejabberd/ejabberd.cfg&lt;/code&gt; file for edit, find modules section, and add mod_rabbitmq stanza to the list&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;{modules,&lt;br /&gt;[&lt;br /&gt;  {mod_adhoc,    []},&lt;br /&gt;  ...&lt;br /&gt;  &lt;b&gt;{mod_rabbitmq, [{rabbitmq_node, rabbit@&lt;i&gt;yourhostname&lt;/i&gt;}]},&lt;/b&gt;&lt;br /&gt;  ...&lt;br /&gt;  {mod_version,  []}&lt;br /&gt;]}.&lt;/pre&gt;&lt;br /&gt;Replace &lt;i&gt;yourhostname&lt;/i&gt; with your machine short name. In my case it was &lt;i&gt;ubuntu&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Setting up cookie&lt;/h4&gt;&lt;br /&gt;To make RabbitMQ and ejabberd work together, they have to run in the same Erlang cluster. That means they have to use the same cookie file. By default RabbitMQ is installed under rabbitmq user with &lt;code&gt;/var/lib/rabbitmq&lt;/code&gt; home directory, and ejabberd under ejabberd user with &lt;code&gt;/var/lib/ejabberd&lt;/code&gt; home directory. If you compare their cookies&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ sudo cat /var/lib/rabbitmq/.erlang.cookie&lt;br /&gt;$ sudo cat /var/lib/ejabberd/.erlang.cookie&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;they will most likely be different. That's why if you restarted ejabberd now you would see exception in RabbitMQ log: "Connection attempt from disallowed node ejabberd@ubuntu". To fix it just copy one cookie file to another&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ sudo /etc/init.d/ejabberd stop&lt;br /&gt;$ sudo mv /var/lib/ejabberd/.erlang.cookie /var/lib/ejabberd/.erlang.cookie.orig&lt;br /&gt;$ sudo cp /var/lib/rabbitmq/.erlang.cookie /var/lib/ejabberd/.erlang.cookie&lt;br /&gt;$ sudo chown ejabberd:ejabberd /var/lib/ejabberd/.erlang.cookie&lt;br /&gt;$ sudo /etc/init.d/ejabberd start&lt;/pre&gt;&lt;br /&gt;The installation part is now done, and you are good to go.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Adding rabbit buddy to your roster&lt;/h4&gt;&lt;br /&gt;The rabbit's JID comprises two parts: exchange name and routing domain. To find the latter one, look at the &lt;code&gt;/var/log/ejabberd/ejabberd.log&lt;/code&gt; file. Searching for "Routing" you should get something like this&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;=INFO REPORT==== 2010-03-30 21:35:22 ===&lt;br /&gt;{contacted_rabbitmq,rabbit@ubuntu}&lt;br /&gt;&lt;br /&gt;=INFO REPORT==== 2010-03-30 21:35:22 ===&lt;br /&gt;I(&lt;0.314.0&gt;:mod_rabbitmq:90) : Routing: "&lt;b&gt;rabbitmq.jabber.ndpar.com&lt;/b&gt;"&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This is the buddy's domain. For the name you can use any exchange name available in the RabbitMQ server. Run &lt;code&gt;sudo rabbitmqctl list_exchanges&lt;/code&gt; command and pick up the name from the list. I use &lt;code&gt;amq.fanout&lt;/code&gt; exchange which exists in every RabbitMQ server. So I go to my IM client (Adium) and add this user to the buddies list&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;amq.fanout@rabbitmq.jabber.ndpar.com&lt;/pre&gt;&lt;br /&gt;&lt;h4&gt;Rabbit's greetings&lt;/h4&gt;&lt;br /&gt;To publish a message to RabbitMQ I use the same Groovy script as in the previous &lt;a href="http://ndpar.blogspot.com/2010/03/get-started-with-rabbitmq.html"&gt;post&lt;/a&gt;. I just amended the exchange name and routing key&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;channel.basicPublish 'amq.fanout', '', null, 'Hello, world!'.bytes&lt;/pre&gt;&lt;br /&gt;Run the script and voil&amp;agrave;, you've got mail&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_R95qtkBZFng/S7Pq4V3M2JI/AAAAAAAAD_E/JV2Hdz9nKgU/s1600/mod_rabbitmq.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 208px;" src="http://3.bp.blogspot.com/_R95qtkBZFng/S7Pq4V3M2JI/AAAAAAAAD_E/JV2Hdz9nKgU/s400/mod_rabbitmq.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5454961827338049682" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Troubleshooting&lt;/h4&gt;&lt;br /&gt;Here are some hints for you if something goes wrong.&lt;br /&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;While working with mod_rabbitmq keep an eye on the log files of both RabbitMQ and ejabberd:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ tail -f /var/log/ejabberd/ejabberd.log&lt;br /&gt;$ tail -f /var/log/rabbitmq/rabbit.log&lt;/pre&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;Check which exchanges, queues and bindings the RabbitMQ server has:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ sudo rabbitmqctl list_exchanges&lt;br /&gt;$ sudo rabbitmqctl list_queues&lt;br /&gt;$ sudo rabbitmqctl list_bindings&lt;/pre&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;If you screw up something there, you can roll back to the default values:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ sudo rabbitmqctl stop_app&lt;br /&gt;$ sudo rabbitmqctl reset&lt;br /&gt;$ sudo rabbitmqctl start_app&lt;/pre&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;Check ejabberd web admin, it has lots of information there&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;http://&lt;i&gt;yourdomainname&lt;/i&gt;:5280/admin&lt;/pre&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;If your IM client is Adium, check its folder periodically &amp;mdash; it tends to collect some garbage there:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;~/Library/Application Support/Adium 2.0/Users/Default/libpurple&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Resources&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;Tony Garnock-Jones' &lt;a href="http://www.erlang-factory.com/upload/presentations/229/ErlangFactorySFBay2010-TonyGarnock-Jones.pdf"&gt;presentation slides&lt;/a&gt; about RabbitMQ and its extensions&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-7845207785745465571?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/7845207785745465571/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=7845207785745465571' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/7845207785745465571'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/7845207785745465571'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2010/03/integrating-rabbitmq-with-ejabberd.html' title='Integrating RabbitMQ with ejabberd'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_R95qtkBZFng/S7Pq4V3M2JI/AAAAAAAAD_E/JV2Hdz9nKgU/s72-c/mod_rabbitmq.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-9146106741236123564</id><published>2010-03-14T14:54:00.009-04:00</published><updated>2010-04-12T19:04:52.405-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rabbitmq'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>Get started with RabbitMQ</title><content type='html'>&lt;a href="http://www.rabbitmq.com"&gt;RabbitMQ&lt;/a&gt; is an open-source implementation of &lt;a href="http://en.wikipedia.org/wiki/AMQP"&gt;AMQP&lt;/a&gt;. If you don't know what AMQP is, I encourage you to check it out on the official web site, or alternatively read articles listed on the reference &lt;a href="http://www.rabbitmq.com/how.html"&gt;page&lt;/a&gt;. Here I want to mention only the reasons why it drew &lt;i&gt;my&lt;/i&gt; attention as an Erlang enthusiast and Java developer working in financial industry:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;AMQP is a replacement for TIBCO Randezvous;&lt;br /&gt;&lt;li&gt;in terms of functionality it's a superset of JMS;&lt;br /&gt;&lt;li&gt;it's written in Erlang, which means fault-tolerance, reliability and high performance.&lt;/ul&gt;&lt;br /&gt;In this blog post I just want to show how to install RabbitMQ on Ubuntu box, and verify that it works with simple Groovy client.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Installing RabbitMQ server&lt;/h4&gt;&lt;br /&gt;As everything with Ubuntu, this step is pretty trivial:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ sudo apt-get install rabbitmq-server&lt;/pre&gt;&lt;br /&gt;The only requirement for this package is Erlang distribution. If you already have Erlang installed on your system, the installation of rabbitmq-server is a quick procedure. The following directories will be created during the installation: &lt;table&gt;&lt;tr&gt;&lt;td&gt;/usr/lib/rabbitmq/bin&lt;/td&gt;&lt;td&gt;executables added to the path&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;/usr/lib/erlang/lib/rabbitmq_server-1.x.x&lt;/td&gt;&lt;td&gt;compiled modules&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;/var/lib/rabbitmq/mnesia&lt;/td&gt;&lt;td&gt;persistent storage for messages&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;/var/log/rabbitmq&lt;/td&gt;&lt;td&gt;log files (e.g. startup_log, rabbit.log)&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;After installation is finished the RabbitMQ server is started and listens to incoming requests on port 5672. You can check &lt;code&gt;/var/log/rabbitmq/startup_log&lt;/code&gt; file to see if everything was ok.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Groovy clients&lt;/h4&gt;&lt;br /&gt;I followed official &lt;a href="http://www.rabbitmq.com/api-guide.html"&gt;Java client API&lt;/a&gt; to build two scripts: &lt;i&gt;consumer.groovy&lt;/i&gt;&lt;br /&gt;&lt;pre&gt;import com.rabbitmq.client.*&lt;br /&gt;&lt;br /&gt;@Grab(group='com.rabbitmq', module='amqp-client', version='1.7.2')&lt;br /&gt;params = new ConnectionParameters(&lt;br /&gt;    username: 'guest',&lt;br /&gt;    password: 'guest',&lt;br /&gt;    virtualHost: '/',&lt;br /&gt;    requestedHeartbeat: 0&lt;br /&gt;)&lt;br /&gt;factory = new ConnectionFactory(params)&lt;br /&gt;conn = factory.newConnection('lab.ndpar.com', 5672)&lt;br /&gt;channel = conn.createChannel()&lt;br /&gt;&lt;br /&gt;exchangeName = 'myExchange'; queueName = 'myQueue'&lt;br /&gt;&lt;br /&gt;channel.exchangeDeclare exchangeName, 'direct'&lt;br /&gt;channel.queueDeclare queueName&lt;br /&gt;channel.queueBind queueName, exchangeName, 'myRoutingKey'&lt;br /&gt;&lt;br /&gt;def consumer = new QueueingConsumer(channel)&lt;br /&gt;channel.basicConsume queueName, false, consumer&lt;br /&gt;&lt;br /&gt;while (true) {&lt;br /&gt;    delivery = consumer.nextDelivery()&lt;br /&gt;    println "Received message: ${new String(delivery.body)}"&lt;br /&gt;    channel.basicAck delivery.envelope.deliveryTag, false&lt;br /&gt;}&lt;br /&gt;channel.close()&lt;br /&gt;conn.close()&lt;/pre&gt;&lt;br /&gt;and &lt;i&gt;publisher.groovy&lt;/i&gt;&lt;br /&gt;&lt;pre&gt;import com.rabbitmq.client.*&lt;br /&gt;&lt;br /&gt;@Grab(group='com.rabbitmq', module='amqp-client', version='1.7.2')&lt;br /&gt;params = new ConnectionParameters(&lt;br /&gt;    username: 'guest',&lt;br /&gt;    password: 'guest',&lt;br /&gt;    virtualHost: '/',&lt;br /&gt;    requestedHeartbeat: 0&lt;br /&gt;)&lt;br /&gt;factory = new ConnectionFactory(params)&lt;br /&gt;conn = factory.newConnection('lab.ndpar.com', 5672)&lt;br /&gt;channel = conn.createChannel()&lt;br /&gt;&lt;br /&gt;channel.basicPublish 'myExchange', 'myRoutingKey', null, "Hello, world!".bytes&lt;br /&gt;&lt;br /&gt;channel.close()&lt;br /&gt;conn.close()&lt;/pre&gt;&lt;br /&gt;Now start consumer in one terminal window&lt;br /&gt;&lt;pre&gt;$ groovy consumer.groovy&lt;/pre&gt;&lt;br /&gt;and run publisher in another:&lt;br /&gt;&lt;pre&gt;$ groovy publisher.groovy&lt;/pre&gt;&lt;br /&gt;On the consumer window you should see &lt;i&gt;Received message: Hello, world!&lt;/i&gt; text, which means RabbitMQ works correctly.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Monitoring logs&lt;/h4&gt;&lt;br /&gt;You can check RabbitMQ logs by doing &lt;code&gt;tail -f /var/log/rabbitmq/rabbit.log&lt;/code&gt; For example, starting the consumer results the following log entries:&lt;br /&gt;&lt;pre&gt;=INFO REPORT==== 14-Mar-2010::11:20:53 ===&lt;br /&gt;accepted TCP connection on 0.0.0.0:5672 from 192.168.2.10:62424&lt;br /&gt;&lt;br /&gt;=INFO REPORT==== 14-Mar-2010::11:20:53 ===&lt;br /&gt;starting TCP connection &lt;0.24154.1&gt; from 192.168.2.10:62424&lt;/pre&gt;&lt;br /&gt;Running the publisher:&lt;br /&gt;&lt;pre&gt;=INFO REPORT==== 14-Mar-2010::11:22:08 ===&lt;br /&gt;accepted TCP connection on 0.0.0.0:5672 from 192.168.2.10:62432&lt;br /&gt;&lt;br /&gt;=INFO REPORT==== 14-Mar-2010::11:22:08 ===&lt;br /&gt;starting TCP connection &lt;0.24232.1&gt; from 192.168.2.10:62432&lt;br /&gt;&lt;br /&gt;=INFO REPORT==== 14-Mar-2010::11:22:08 ===&lt;br /&gt;closing TCP connection &lt;0.24232.1&gt; from 192.168.2.10:62432&lt;/pre&gt;&lt;br /&gt;Now if we terminate the consumer by ^C there will be a warning&lt;br /&gt;&lt;pre&gt;=WARNING REPORT==== 14-Mar-2010::11:25:03 ===&lt;br /&gt;exception on TCP connection &lt;0.24154.1&gt; from 192.168.2.10:62424&lt;br /&gt;connection_closed_abruptly&lt;br /&gt;&lt;br /&gt;=INFO REPORT==== 14-Mar-2010::11:25:03 ===&lt;br /&gt;closing TCP connection &lt;0.24154.1&gt; from 192.168.2.10:62424&lt;/pre&gt;&lt;br /&gt;but the connection is closed properly by the server.&lt;br /&gt;&lt;br /&gt;That's it for now. Stay tuned for the future updates on my RabbitMQ experience.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Links&lt;/h4&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;Rapid application prototyping with &lt;a href="http://jbrisbin.wordpress.com/2010/04/12/rapid-application-prototyping-with-groovy-and-rabbitmq"&gt;Groovy DSL&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-9146106741236123564?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/9146106741236123564/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=9146106741236123564' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/9146106741236123564'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/9146106741236123564'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2010/03/get-started-with-rabbitmq.html' title='Get started with RabbitMQ'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-4685652502776725774</id><published>2010-02-23T21:40:00.008-05:00</published><updated>2010-04-06T11:22:33.013-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ejabberd'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>Installing ejabberd on Ubuntu</title><content type='html'>Recently I've installed &lt;a href="http://www.process-one.net/en/ejabberd/"&gt;ejabberd&lt;/a&gt; server on Ubuntu box. Thanks to this nice &lt;a href="http://sysmonblog.co.uk/2008/06/ot-installing-ejabberd-on-debian-ubuntu.html"&gt;document&lt;/a&gt;, the process was pretty straightforward. My experience was little bit different from the author's one, so I want to show here exact steps I did to make it work, maybe it will be helpful for you too.&lt;br /&gt;&lt;br /&gt;The first step is to install the required package. You can use Synaptic Package Manager or just command line:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;sudo apt-get install ejabberd&lt;/pre&gt;&lt;br /&gt;During the installation a new user, ejabberd, will be created in the system. This is the user the server will be running on. When installation is finished ejabberd server is started. To configure the server you need to stop it&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;sudo /etc/init.d/ejabberd stop&lt;/pre&gt;&lt;br /&gt;Next step is to configure administrator and hosts. Open &lt;tt&gt;/etc/ejabberd/ejabberd.cfg&lt;/tt&gt; file for edit and make the following change&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;%% Admin user&lt;br /&gt;{acl, admin, {user, "&lt;b&gt;andrey&lt;/b&gt;", "&lt;b&gt;jabber.ndpar.com&lt;/b&gt;"}}.&lt;br /&gt;%% Hostname&lt;br /&gt;{hosts, ["localhost", "&lt;b&gt;ubuntu&lt;/b&gt;", "jabber.ndpar.com"]}.&lt;/pre&gt;&lt;br /&gt;For admin you need to specify the user name and domain name that you want to use as a Jabber ID. By default it's localhost and it's functional but it's better to change it to something meaningful. The list of hostnames is tricky. In theory you can provide there just localhost but in practice it didn't work for me. After digging into some Erlang exceptions I got while registering admin account (see next step) I came to conclusion that in the list of hostnames there must be a short hostname of the box. You can get it by running &lt;tt&gt;hostname -s&lt;/tt&gt; command (in my case it was "ubuntu"). In addition you can provide other hostnames you like, but the short one is mandatory.&lt;br /&gt;&lt;br /&gt;When you are done with editing, start the server&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;sudo /etc/init.d/ejabberd start&lt;/pre&gt;&lt;br /&gt;Now it's time to register the admin user we configured on the previous step. Run the following command replacing password placeholders with the actual password and providing user name and domain name from &lt;tt&gt;ejabberd.cfg&lt;/tt&gt; file&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;sudo ejabberdctl register andrey jabber.ndpar.com xxxxxx&lt;/pre&gt;&lt;br /&gt;That's it! You have now working XMPP server with one registered user. To verify that everything is ok, in your browser go to the admin page of the server (http://jabber.ndpar.com:5280/admin) and check the statistics. You'll be asked to type your JID and password, so use the information you entered on the previous step&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_R95qtkBZFng/S4STdJMEylI/AAAAAAAAD6U/S8YTTmIJzmk/s1600-h/ejabberd-web.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 171px;" src="http://3.bp.blogspot.com/_R95qtkBZFng/S4STdJMEylI/AAAAAAAAD6U/S8YTTmIJzmk/s400/ejabberd-web.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5441636378662455890" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;As a note, I didn't create my own SSL certificate because for isolated intranet the default one is quite enough. If you are not comfortable with that feel free to create a new certificate following the steps from the original &lt;a href="http://sysmonblog.co.uk/2008/06/ot-installing-ejabberd-on-debian-ubuntu.html"&gt;article&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Now you are ready to add newly created account to your Jabber client. In Adium, for example, go to File -&gt; Add Acount -&gt; Jabber and provide server hostname/IP, JID and password.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_R95qtkBZFng/S4SVHJEh0cI/AAAAAAAAD6c/mbpaMxjkjYc/s1600-h/ejabberd-adium-1.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 276px;" src="http://4.bp.blogspot.com/_R95qtkBZFng/S4SVHJEh0cI/AAAAAAAAD6c/mbpaMxjkjYc/s400/ejabberd-adium-1.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5441638199696937410" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_R95qtkBZFng/S4SVsPSTniI/AAAAAAAAD6k/kcJ7ZafeoNk/s1600-h/ejabberd-adium-2.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 276px;" src="http://2.bp.blogspot.com/_R95qtkBZFng/S4SVsPSTniI/AAAAAAAAD6k/kcJ7ZafeoNk/s400/ejabberd-adium-2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5441638837020499490" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Click OK button, accept security certificate permanently and go online.&lt;br /&gt;&lt;br /&gt;Now, to really enjoy IM you need more users on your server. The best part here is that you can create new users just from your Jabber client. You can actually do many things from the client, and you don't need to ssh to the remote server and run command for that. Just go to File -&gt; your ejabberd account, and chose whatever you need from the menu&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_R95qtkBZFng/S4SV9aM7ZnI/AAAAAAAAD6s/kKDdOqNCq9M/s1600-h/ejabberd-admin-client.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 222px; height: 400px;" src="http://3.bp.blogspot.com/_R95qtkBZFng/S4SV9aM7ZnI/AAAAAAAAD6s/kKDdOqNCq9M/s400/ejabberd-admin-client.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5441639132008506994" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Pretty cool, eh &amp;mdash; client and admin tool in one place.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-4685652502776725774?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/4685652502776725774/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=4685652502776725774' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/4685652502776725774'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/4685652502776725774'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2010/02/installing-ejabberd-on-ubuntu.html' title='Installing ejabberd on Ubuntu'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_R95qtkBZFng/S4STdJMEylI/AAAAAAAAD6U/S8YTTmIJzmk/s72-c/ejabberd-web.png' height='72' width='72'/><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-7536588460051842052</id><published>2010-02-10T19:33:00.009-05:00</published><updated>2010-02-13T10:59:01.545-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>Multithreaded XmlSlurper</title><content type='html'>Groovy XmlSlurper is a nice tool to parse XML documents, mostly because of the elegant GPath dot-notation. But how efficient is XmlSlurper when it comes to parsing of thousands of XMLs per second? Let's do some simple test&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;class XmlParserTest {&lt;br /&gt;&lt;br /&gt;    static int iterations = 1000&lt;br /&gt;&lt;br /&gt;    def xml = """&lt;br /&gt;       &amp;lt;root&amp;gt;&lt;br /&gt;         &amp;lt;node1 aName='aValue'&amp;gt;&lt;br /&gt;           &amp;lt;node1.1 aName='aValue'&amp;gt;1.1&amp;lt;/node1.1&amp;gt;&lt;br /&gt;           &amp;lt;node1.2 aName='aValue'&amp;gt;1.2&amp;lt;/node1.2&amp;gt;&lt;br /&gt;           &amp;lt;node1.3 aName='aValue'&amp;gt;1.3&amp;lt;/node1.3&amp;gt;&lt;br /&gt;         &amp;lt;/node1&amp;gt;&lt;br /&gt;         &amp;lt;node2 aName='aValue'&amp;gt;&lt;br /&gt;           &amp;lt;node2.1 aName='aValue'&amp;gt;2.1&amp;lt;/node2.1&amp;gt;&lt;br /&gt;           &amp;lt;node2.2 aName='aValue'&amp;gt;2.2&amp;lt;/node2.2&amp;gt;&lt;br /&gt;           &amp;lt;node2.3 aName='aValue'&amp;gt;2.3&amp;lt;/node2.3&amp;gt;&lt;br /&gt;         &amp;lt;/node2&amp;gt;&lt;br /&gt;         &amp;lt;nodeN aName='aValue'&amp;gt;&lt;br /&gt;           &amp;lt;nodeN.1 aName='aValue'&amp;gt;N.1&amp;lt;/nodeN.1&amp;gt;&lt;br /&gt;           &amp;lt;nodeN.2 aName='aValue'&amp;gt;N.2&amp;lt;/nodeN.2&amp;gt;&lt;br /&gt;           &amp;lt;nodeN.3 aName='aValue'&amp;gt;N.3&amp;lt;/nodeN.3&amp;gt;&lt;br /&gt;         &amp;lt;/nodeN&amp;gt;&lt;br /&gt;       &amp;lt;/root&amp;gt;&lt;br /&gt;    """&lt;br /&gt;&lt;br /&gt;    def parseSequential() {&lt;br /&gt;        iterations.times {&lt;br /&gt;            def root = &lt;b&gt;new XmlSlurper().parseText(xml)&lt;/b&gt;&lt;br /&gt;            assert 'aValue' == root.node1.@aName.toString()&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Test void testSequentialXmlParsing() {&lt;br /&gt;        long start = System.currentTimeMillis()&lt;br /&gt;        parseSequential()&lt;br /&gt;        long stop = System.currentTimeMillis()&lt;br /&gt;        println "${iterations} XML documents parsed sequentially in ${stop-start} ms"&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;I ran this test on my 4-core machine and I got&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;1000 XML documents parsed sequentially in 984 ms&lt;/pre&gt;&lt;br /&gt;Not really good (0.984 ms per document) but we didn't expect much from single threaded application. Let's parallelize this process&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;class XmlParserTest {&lt;br /&gt;    ...&lt;br /&gt;    static int threadCount = 5&lt;br /&gt;    ...&lt;br /&gt;    @Test void testParallelXmlParsing() {&lt;br /&gt;        def threads = []&lt;br /&gt;        long start = System.currentTimeMillis()&lt;br /&gt;        threadCount.times {&lt;br /&gt;            threads &lt;&lt; Thread.start { parseSequential() }&lt;br /&gt;        }&lt;br /&gt;        threads.each { it.join() }&lt;br /&gt;        long stop = System.currentTimeMillis()&lt;br /&gt;        println "${threadCount * iterations} XML documents parsed parallelly by ${threadCount} threads in ${stop - start} ms"&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;And the result is&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;5000 XML documents parsed parallelly by 5 threads in 1750 ms&lt;/pre&gt;&lt;br /&gt;This is definitely better (0.35 ms per document) but doesn't look like parallel processing &amp;mdash; the test time shouldn't increase in true parallelism.&lt;br /&gt;&lt;br /&gt;The problem here is the default constructor of XmlSlurper. It does too much: first, it initializes XML parser factory loading bunch of classes; second, it creates new XML parser, which is quite expensive operation. Now imaging this happens thousand times per second.&lt;br /&gt;&lt;br /&gt;Luckily, XmlSlurper has another constructor, with XML parser parameter, so we can create the parser up-front and pass it to the slurper. Unfortunately, we cannot reuse one parser instance between several slurpers because XML parser is not thread-safe &amp;mdash; you have to finish parsing one document before you can use the same parser to parse another.&lt;br /&gt;&lt;br /&gt;The solution here is to &lt;i&gt;use preconfigured pool of parsers&lt;/i&gt;. Let's create one based on Apache commons-pool library.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;public class XmlParserPoolableObjectFactory implements PoolableObjectFactory {&lt;br /&gt;    private SAXParserFactory parserFactory;&lt;br /&gt;&lt;br /&gt;    public XmlParserPoolableObjectFactory() {&lt;br /&gt;        parserFactory = SAXParserFactory.newInstance();&lt;br /&gt;    }&lt;br /&gt;    public Object makeObject() throws Exception {&lt;br /&gt;        return parserFactory.newSAXParser();&lt;br /&gt;    }&lt;br /&gt;    public boolean validateObject(Object obj) {&lt;br /&gt;        return true;&lt;br /&gt;    }&lt;br /&gt;    // Other methods left empty&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class XmlParserPool {&lt;br /&gt;    private final GenericObjectPool pool;&lt;br /&gt;&lt;br /&gt;    public XmlParserPool(int maxActive) {&lt;br /&gt;        pool = new GenericObjectPool(new XmlParserPoolableObjectFactory(), maxActive,&lt;br /&gt;                GenericObjectPool.WHEN_EXHAUSTED_BLOCK, 0);&lt;br /&gt;    }&lt;br /&gt;    public Object borrowObject() throws Exception {&lt;br /&gt;        return pool.borrowObject();&lt;br /&gt;    }&lt;br /&gt;    public void returnObject(Object obj) throws Exception {&lt;br /&gt;        pool.returnObject(obj);&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Now we can change our test&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;class XmlParserTest {&lt;br /&gt;    static XmlParserPool parserPool = new XmlParserPool(1000)&lt;br /&gt;    ...&lt;br /&gt;    def parseSequential() {&lt;br /&gt;        iterations.times {&lt;br /&gt;            &lt;b&gt;def parser = parserPool.borrowObject()&lt;/b&gt;&lt;br /&gt;            def root = new XmlSlurper(&lt;b&gt;parser&lt;/b&gt;).parseText(xml)&lt;br /&gt;            &lt;b&gt;parserPool.returnObject(parser)&lt;/b&gt;&lt;br /&gt;            assert 'aValue' == root.node1.@aName.toString()&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;and run it again&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;1000 XML documents parsed sequentially in 203 ms&lt;br /&gt;5000 XML documents parsed parallelly by 5 threads in 172 ms&lt;/pre&gt;&lt;br /&gt;That's much better (0.034 ms per document), and most importantly multi-threading really works now.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Resources&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;&lt;a href="http://github.com/ndpar/utilities/tree/master/xmlparser/"&gt;Source code&lt;/a&gt; for this blog&lt;br /&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;&lt;a href="http://www.ibm.com/developerworks/xml/library/x-perfap2.html"&gt;Article&lt;/a&gt; "Improve performance in your XML applications"&lt;br /&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;&lt;a href="http://www.nearinfinity.com/blogs/steven_farley/gpath_versus_xpath.html"&gt;GPath vs XPath&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;&lt;a href="http://commons.apache.org/pool/"&gt;commons-pool&lt;/a&gt; home page&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-7536588460051842052?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/7536588460051842052/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=7536588460051842052' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/7536588460051842052'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/7536588460051842052'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2010/02/multithreaded-xmlslurper.html' title='Multithreaded XmlSlurper'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-7852687024351730781</id><published>2010-01-23T20:21:00.011-05:00</published><updated>2010-01-23T22:08:05.352-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><title type='text'>Distributed cache in Erlang</title><content type='html'>Implementing distributed cache in Erlang is relatively simple task because concurrency, distribution and failover mechanisms are built in the language. In fact, it's so simple that this task is a part of Erlang tutorial. Here I want to show the complete solution which is only 100 lines of code.&lt;br /&gt;&lt;br /&gt;I'm going to implement the cache as a typical Erlang server application, that means set of three modules: server, supervisor and application. As underlined storage I'm using Mnesia database which is a part of standard Erlang distribution. It doesn't probably give you the best performance, but it does provide automatic replication. The cache is deployed on three nodes, each node on a separate machine.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_R95qtkBZFng/S1uhza2bmMI/AAAAAAAAD04/OykCXhcW14M/s1600-h/erlcache-1.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 282px;" src="http://2.bp.blogspot.com/_R95qtkBZFng/S1uhza2bmMI/AAAAAAAAD04/OykCXhcW14M/s400/erlcache-1.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5430111680478288066" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Clients will connect to in-memory slave nodes, the master node is dedicated to persistence.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Configure Erlang cluster&lt;/h3&gt;&lt;br /&gt;Create file &lt;code&gt;.erlang.cookie&lt;/code&gt; containing one line with random text. Copy this file to every machine in a cluster to home directory of the user who will start Erlang VM. Make sure this file has unix permissions 600.&lt;br /&gt;&lt;br /&gt;Check &lt;code&gt;/etc/hosts&lt;/code&gt; on every box to verify that every machine knows others by name.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Set up Mnesia database&lt;/h3&gt;&lt;br /&gt;Open terminals on all machines and enter Erlang prompt&lt;br /&gt;&lt;pre&gt;ubuntu$ &lt;b&gt;erl -sname master&lt;/b&gt;&lt;br /&gt;Erlang R13B01 (erts-5.7.2) [source] [rq:1] [async-threads:0] [kernel-poll:false]&lt;br /&gt;Eshell V5.7.2  (abort with ^G)&lt;br /&gt;&lt;br /&gt;macBook$ &lt;b&gt;erl -sname slave1&lt;/b&gt;&lt;br /&gt;Erlang R13B02 (erts-5.7.3) [source] [smp:2:2] [rq:2] [async-threads:0] [kernel-poll:false]&lt;br /&gt;Eshell V5.7.3  (abort with ^G)&lt;br /&gt;&lt;br /&gt;iMac$ &lt;b&gt;erl -sname slave2&lt;/b&gt;&lt;br /&gt;Erlang R13B03 (erts-5.7.4) [source] [smp:2:2] [rq:2] [async-threads:0] [kernel-poll:false]&lt;br /&gt;Eshell V5.7.4  (abort with ^G)&lt;/pre&gt;&lt;br /&gt;From one machine ping other two&lt;br /&gt;&lt;pre&gt;(slave1@macBook)1&gt; &lt;b&gt;net_adm:ping(master@ubuntu).&lt;/b&gt;&lt;br /&gt;pong&lt;br /&gt;(slave1@macBook)2&gt; &lt;b&gt;net_adm:ping(slave2@iMac).&lt;/b&gt;&lt;br /&gt;pong&lt;/pre&gt;&lt;br /&gt;Create database configuration&lt;br /&gt;&lt;pre&gt;(slave1@macBook)3&gt; &lt;b&gt;mnesia:create_schema([slave1@macBook, slave2@iMac, master@ubuntu]).&lt;/b&gt;&lt;br /&gt;ok&lt;/pre&gt;&lt;br /&gt;Start database on all nodes&lt;br /&gt;&lt;pre&gt;(master@ubuntu)1&gt; &lt;b&gt;application:start(mnesia).&lt;/b&gt;&lt;br /&gt;ok&lt;br /&gt;(slave1@macBook)4&gt; &lt;b&gt;application:start(mnesia).&lt;/b&gt;&lt;br /&gt;ok&lt;br /&gt;(slave2@iMac)1&gt; &lt;b&gt;application:start(mnesia).&lt;/b&gt;&lt;br /&gt;ok&lt;/pre&gt;&lt;br /&gt;Create cache table&lt;br /&gt;&lt;pre&gt;(slave1@macBook)5&gt; &lt;b&gt;rd(mycache, {key, value}).&lt;/b&gt;&lt;br /&gt;mycache&lt;br /&gt;(slave1@macBook)6&gt; &lt;b&gt;mnesia:create_table(mycache, [{attributes, record_info(fields, mycache)},&lt;br /&gt;{disc_only_copies, [master@ubuntu]}, {ram_copies, [slave1@macBook, slave2@iMac]}]).&lt;/b&gt;&lt;br /&gt;{atomic,ok}&lt;/pre&gt;&lt;br /&gt;Stop database and quit Erlang VM&lt;br /&gt;&lt;pre&gt;(slave1@macBook)7&gt; &lt;b&gt;application:stop(mnesia).&lt;/b&gt;&lt;br /&gt;ok&lt;br /&gt;(slave2@iMac)2&gt; &lt;b&gt;application:stop(mnesia).&lt;/b&gt;&lt;br /&gt;ok&lt;br /&gt;(master@ubuntu)2&gt; &lt;b&gt;application:stop(mnesia).&lt;/b&gt;&lt;br /&gt;ok&lt;/pre&gt;&lt;br /&gt;&lt;h3&gt;Implement Erlang application&lt;/h3&gt;&lt;br /&gt;The main module of this application is &lt;code&gt;mycache.erl&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;-module(mycache).&lt;br /&gt;-export([start/0, stop/0]).&lt;br /&gt;-export([put/2, get/1, remove/1]).&lt;br /&gt;-export([init/1, terminate/2, handle_call/3, handle_cast/2]).&lt;br /&gt;-behaviour(gen_server).&lt;br /&gt;-include("mycache.hrl").&lt;br /&gt;&lt;br /&gt;% Start/stop functions&lt;br /&gt;&lt;br /&gt;start() -&gt;&lt;br /&gt;    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).&lt;br /&gt;&lt;br /&gt;stop() -&gt;&lt;br /&gt;    gen_server:cast(?MODULE, stop).&lt;br /&gt;&lt;br /&gt;% Functional interface&lt;br /&gt;&lt;br /&gt;put(Key, Value) -&gt;&lt;br /&gt;    gen_server:call(?MODULE, {put, Key, Value}).&lt;br /&gt;&lt;br /&gt;get(Key) -&gt;&lt;br /&gt;    gen_server:call(?MODULE, {get, Key}).&lt;br /&gt;&lt;br /&gt;remove(Key) -&gt;&lt;br /&gt;    gen_server:call(?MODULE, {remove, Key}).&lt;br /&gt;&lt;br /&gt;% Callback functions&lt;br /&gt;&lt;br /&gt;init(_) -&gt;&lt;br /&gt;    application:start(mnesia),&lt;br /&gt;    mnesia:wait_for_tables([mycache], infinity),&lt;br /&gt;    {ok, []}.&lt;br /&gt;&lt;br /&gt;terminate(_Reason, _State) -&gt;&lt;br /&gt;    application:stop(mnesia).&lt;br /&gt;&lt;br /&gt;handle_cast(stop, State) -&gt;&lt;br /&gt;    {stop, normal, State}.&lt;br /&gt;&lt;br /&gt;handle_call({put, Key, Value}, _From, State) -&gt;&lt;br /&gt;    Rec = #mycache{key = Key, value = Value},&lt;br /&gt;    F = fun() -&gt;&lt;br /&gt;            case mnesia:read(mycache, Key) of&lt;br /&gt;                [] -&gt;&lt;br /&gt;                    mnesia:write(Rec),&lt;br /&gt;                    null;&lt;br /&gt;                [#mycache{value = OldValue}] -&gt;&lt;br /&gt;                    mnesia:write(Rec),&lt;br /&gt;                    OldValue&lt;br /&gt;            end&lt;br /&gt;        end,&lt;br /&gt;    {atomic, Result} = mnesia:transaction(F),&lt;br /&gt;    {reply, Result, State};&lt;br /&gt;&lt;br /&gt;handle_call({get, Key}, _From, State) -&gt;&lt;br /&gt;    case mnesia:dirty_read({mycache, Key}) of&lt;br /&gt;        [#mycache{value = Value}] -&gt; {reply, Value, []};&lt;br /&gt;        _ -&gt; {reply, null, State}&lt;br /&gt;    end;&lt;br /&gt;&lt;br /&gt;handle_call({remove, Key}, _From, State) -&gt;&lt;br /&gt;    F = fun() -&gt;&lt;br /&gt;            case mnesia:read(mycache, Key) of&lt;br /&gt;                [] -&gt; null;&lt;br /&gt;                [#mycache{value = Value}] -&gt;&lt;br /&gt;                    mnesia:delete({mycache, Key}),&lt;br /&gt;                    Value&lt;br /&gt;            end&lt;br /&gt;        end,&lt;br /&gt;    {atomic, Result} = mnesia:transaction(F),&lt;br /&gt;    {reply, Result, State}.&lt;/pre&gt;&lt;br /&gt;It implements Erlang generic server behaviour and provides three client functions &amp;ndash; put, get, remove &amp;ndash; with the same signature as similar methods in &lt;code&gt;java.util.Map&lt;/code&gt; interface.&lt;br /&gt;&lt;br /&gt;Next file is a supervisor for the cache, &lt;code&gt;mycache_sup.erl&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;-module(mycache_sup).&lt;br /&gt;-export([start/0]).&lt;br /&gt;-export([init/1]).&lt;br /&gt;-behaviour(supervisor).&lt;br /&gt;&lt;br /&gt;start() -&gt;&lt;br /&gt;    supervisor:start_link({local, ?MODULE}, ?MODULE, []).&lt;br /&gt;&lt;br /&gt;init(_) -&gt;&lt;br /&gt;    MycacheWorker = {mycache, {mycache, start, []}, permanent, 30000, worker, [mycache, mnesia]},&lt;br /&gt;    {ok, {{one_for_all, 5, 3600}, [MycacheWorker]}}.&lt;/pre&gt;&lt;br /&gt;It's going to monitor the main cache process and restart it in case of crash.&lt;br /&gt;&lt;br /&gt;Next file, &lt;code&gt;mycache_app.erl&lt;/code&gt;, provides methods to start and stop our cache gracefully within Erlang VM&lt;br /&gt;&lt;pre&gt;-module(mycache_app).&lt;br /&gt;-export([start/2, stop/1]).&lt;br /&gt;-behaviour(application).&lt;br /&gt;&lt;br /&gt;start(_Type, _StartArgs) -&gt;&lt;br /&gt;    mycache_sup:start().&lt;br /&gt;&lt;br /&gt;stop(_State) -&gt;&lt;br /&gt;    ok.&lt;/pre&gt;&lt;br /&gt;Create application descriptor, &lt;code&gt;mycache.app&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;{application, mycache,&lt;br /&gt; [{description, "Distributed cache"},&lt;br /&gt;  {vsn, "1.0"},&lt;br /&gt;  {modules, [mycache, mycache_sup, mycache_app]},&lt;br /&gt;  {registered, [mycache, mycache_sup]},&lt;br /&gt;  {applications, [kernel, stdlib]},&lt;br /&gt;  {env, []},&lt;br /&gt;  {mod, {mycache_app, []}}]}.&lt;/pre&gt;&lt;br /&gt;The last module is optional, it provides a quick way to load our application on VM startup&lt;br /&gt;&lt;pre&gt;-module(mycache_boot).&lt;br /&gt;-export([start/0]).&lt;br /&gt;&lt;br /&gt;start() -&gt;&lt;br /&gt;    application:start(mycache).&lt;/pre&gt;&lt;br /&gt;That's it. Compile all these modules and copy binaries to all machines in the cluster. Place the binaries in the same folder you created Mnesia configuration.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Run Erlang application&lt;/h3&gt;&lt;br /&gt;Start Erlang VMs and load the application&lt;br /&gt;&lt;pre&gt;ubuntu$ &lt;b&gt;erl -sname master -s mycache_boot&lt;/b&gt;&lt;br /&gt;Erlang R13B01 (erts-5.7.2) [source] [rq:1] [async-threads:0] [kernel-poll:false]&lt;br /&gt;&lt;br /&gt;macBook$ &lt;b&gt;erl -sname slave1 -s mycache_boot&lt;/b&gt;&lt;br /&gt;Erlang R13B02 (erts-5.7.3) [source] [smp:2:2] [rq:2] [async-threads:0] [kernel-poll:false]&lt;br /&gt;&lt;br /&gt;iMac$ &lt;b&gt;erl -sname slave2 -s mycache_boot&lt;/b&gt;&lt;br /&gt;Erlang R13B03 (erts-5.7.4) [source] [smp:2:2] [rq:2] [async-threads:0] [kernel-poll:false]&lt;/pre&gt;&lt;br /&gt;The cache is ready. You can start using it&lt;br /&gt;&lt;pre&gt;(slave1@macBook)1&gt; &lt;b&gt;mycache:put("mykey", "myvalue").&lt;/b&gt;&lt;br /&gt;null&lt;br /&gt;(slave2@iMac)1&gt; &lt;b&gt;mycache:get("mykey").&lt;/b&gt;&lt;br /&gt;"myvalue"&lt;br /&gt;(master@ubuntu)1&gt; &lt;b&gt;mycache:put("mykey", "newvalue").&lt;/b&gt;&lt;br /&gt;"myvalue"&lt;br /&gt;(slave1@macBook)2&gt; &lt;b&gt;mycache:remove("mykey").&lt;/b&gt;&lt;br /&gt;"newvalue"&lt;br /&gt;(master@ubuntu)2&gt; &lt;b&gt;mycache:get("mykey").&lt;/b&gt;&lt;br /&gt;null&lt;/pre&gt;&lt;br /&gt;It works! So, what do we actually achieve here with about 100 lines of Erlang code and bit of scripting?&lt;br /&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;&lt;b&gt;&lt;i&gt;Distribution&lt;/i&gt;&lt;/b&gt; I run the app on three physical boxes, and it's transparent for the clients.&lt;br /&gt;&amp;bull;&amp;nbsp;&lt;b&gt;&lt;i&gt;Scaleability&lt;/i&gt;&lt;/b&gt; To add a new node to the cluster is just a matter of Mnesia re-configuration and copying of binary files to the new box.&lt;br /&gt;&amp;bull;&amp;nbsp;&lt;b&gt;&lt;i&gt;Concurrency&lt;/i&gt;&lt;/b&gt; Write and remove operations are transactional, and because of concurrent nature of Erlang itself our data is consistent and can be accessed by thousands of client processes.&lt;br /&gt;&amp;bull;&amp;nbsp;&lt;b&gt;&lt;i&gt;Fault tolerance&lt;/i&gt;&lt;/b&gt; Try to kill mycache process inside Erlang VM; it will be restarted automatically by supervisor and data will be replicated from other nodes to the new process.&lt;br /&gt;&amp;bull;&amp;nbsp;&lt;b&gt;&lt;i&gt;Persistence&lt;/i&gt;&lt;/b&gt; is optional and provided by Mnesia module.&lt;br /&gt;&lt;br /&gt;All these benefits are given for free by Erlang/OTP, and it's not the end.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Call Erlang cache from Java&lt;/h3&gt;&lt;br /&gt;There are several ways of integrating Erlang applications with other languages. For Java the most convenient one is JInterface library. Here is the implementation of &lt;code&gt;java.util.Map&lt;/code&gt; interface that communicates with the cache application we've just developed&lt;br /&gt;&lt;pre&gt;import com.ericsson.otp.erlang.*;&lt;br /&gt;&lt;br /&gt;public class ErlStringMap implements Map&amp;lt;String, String&amp;gt; {&lt;br /&gt;&lt;br /&gt;    private final OtpSelf self;&lt;br /&gt;    private final OtpPeer other;&lt;br /&gt;    private final String cacheModule;&lt;br /&gt;&lt;br /&gt;    public ErlStringMap(String client, String cookie, String serverNode, String cacheModule) {&lt;br /&gt;        try {&lt;br /&gt;            self = new OtpSelf(client, cookie);&lt;br /&gt;            other = new OtpPeer(serverNode);&lt;br /&gt;            this.cacheModule = cacheModule;&lt;br /&gt;        } catch (Exception e) {&lt;br /&gt;            throw new RuntimeException(e.getMessage(), e);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public String put(String key, String value) {&lt;br /&gt;        return remoteCall("put", key, value);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public String get(Object key) {&lt;br /&gt;        return remoteCall("get", (String) key);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public String remove(Object key) {&lt;br /&gt;        return remoteCall("remove", (String) key);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private String remoteCall(String method, String... args) {&lt;br /&gt;        try {&lt;br /&gt;            OtpConnection connection = self.connect(other);&lt;br /&gt;            connection.sendRPC(cacheModule, method, stringsToErlangStrings(args));&lt;br /&gt;            OtpErlangObject received = connection.receiveRPC();&lt;br /&gt;            connection.close();&lt;br /&gt;            return parse(received);&lt;br /&gt;        } catch (Exception e) {&lt;br /&gt;            throw new RuntimeException(e.getMessage(), e);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private OtpErlangObject[] stringsToErlangStrings(String[] strings) {&lt;br /&gt;        OtpErlangObject[] result = new OtpErlangObject[strings.length];&lt;br /&gt;        for (int i = 0; i &lt; strings.length; i++) result[i] = new OtpErlangString(strings[i]);&lt;br /&gt;        return result;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private String parse(OtpErlangObject otpObj) {&lt;br /&gt;        if (otpObj instanceof OtpErlangAtom) {&lt;br /&gt;            OtpErlangAtom atom = (OtpErlangAtom) otpObj;&lt;br /&gt;            if (atom.atomValue().equals("null")) return null;&lt;br /&gt;            else throw new IllegalArgumentException("Only atom null is supported");&lt;br /&gt;&lt;br /&gt;        } else if (otpObj instanceof OtpErlangString) {&lt;br /&gt;            OtpErlangString str = (OtpErlangString) otpObj;&lt;br /&gt;            return str.stringValue();&lt;br /&gt;        }&lt;br /&gt;        throw new IllegalArgumentException("Unexpected type " + otpObj.getClass().getName());&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // Other methods are omitted&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Now from the Java application we can use our distributed cache same way we are using HashMap&lt;br /&gt;&lt;pre&gt;String cookie = FileUtils.readFileToString(new File("/Users/andrey/.erlang.cookie"));&lt;br /&gt;Map&amp;lt;String, String&amp;gt; map = new ErlStringMap("client1", cookie, "slave1@macBook", "mycache");&lt;br /&gt;map.put("foo", "bar")&lt;/pre&gt;&lt;br /&gt;&lt;h3&gt;Performance?&lt;/h3&gt;&lt;br /&gt;Let's deploy Erlang and Java nodes following this topology&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_R95qtkBZFng/S1ulCChwPAI/AAAAAAAAD1A/y4aQgwMVIwM/s1600-h/erlcache-2.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 385px;" src="http://2.bp.blogspot.com/_R95qtkBZFng/S1ulCChwPAI/AAAAAAAAD1A/y4aQgwMVIwM/s400/erlcache-2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5430115230182030338" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Here is the speed of the cache operations I get in the Java client:&lt;br /&gt;&lt;br /&gt;write 30.385 ms&lt;br /&gt;read 1.23 ms&lt;br /&gt;delete 21.665 ms&lt;br /&gt;&lt;br /&gt;If we remove network, i.e. move all VMs, Java and Erlang, to the same box, we'll get the following performance:&lt;br /&gt;&lt;br /&gt;write 2.091 ms&lt;br /&gt;read 1.35 ms&lt;br /&gt;delete 2.057 ms&lt;br /&gt;&lt;br /&gt;And if we also disable persistence, the numbers will be&lt;br /&gt;&lt;br /&gt;write 1.75 ms&lt;br /&gt;read 1.38 ms&lt;br /&gt;delete 1.75 ms&lt;br /&gt;&lt;br /&gt;As you can see, performance is not the best, but keep in mind that the purpose of this post is not to build production ready cache application, but show the power of Erlang/OTP in building distributed fault-tolerant systems. As an exercise, try to implement the same functionality using JDK only.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Resources&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;&lt;a href="http://github.com/ndpar/erlang/tree/master/mycache/"&gt;Source code&lt;/a&gt; used in the blog.&lt;br /&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;Upcoming &lt;a href="http://www.manning.com/logan/"&gt;book&lt;/a&gt; where authors seem to implement similar application.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-7852687024351730781?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/7852687024351730781/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=7852687024351730781' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/7852687024351730781'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/7852687024351730781'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2010/01/distributed-cache-in-erlang.html' title='Distributed cache in Erlang'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_R95qtkBZFng/S1uhza2bmMI/AAAAAAAAD04/OykCXhcW14M/s72-c/erlcache-1.png' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-1064700358377353828</id><published>2009-12-04T22:30:00.002-05:00</published><updated>2009-12-04T22:37:04.874-05:00</updated><title type='text'>Ralph Johnson on learning to program</title><content type='html'>In general, there are 2 types of books. There is theory books and there is practical books. You can learn theory from a book. &lt;i&gt;You cannot learn anything practical from a book. You learn practical things by practicing.&lt;/i&gt; Practical books are great, you read them, they tell you some ideas, but they tell you ideas of things to practice and you have to go off and practice it. It's only after you practice that you actually know. Then you'll say "Oh, now I know what that guy meant when he said that!"&lt;br /&gt;&lt;br /&gt;That's how you learn. You only learn that stuff by doing. It's like any program - you can't actually learn to program by reading a book. Of course you read the book, but you have to get to the system and you write little programs and you write bigger and bigger programs and after a while you know how to program in the language, but you've got to do it to learn. You are not really going to learn by just reading the book.&lt;br /&gt;&lt;br /&gt;When I was young I was a sort of person who listened to older people. Some young people don't, but I was a young person who did, but even so, they would tell me a lot of things. I would remember it, I wasn't like I disbelieved them, but it just a little bit hard to believe and I wasn't sure. Now, I'm over 50 and a lot of stuff makes an awful lot more sense to me and I say "Those old guys were right when telling me things", &lt;i&gt;but you can't until you live it, until you go through it yourself, you really can't know it for sure&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;When you get a pattern book, you need to be bold. You need to just try the stuff out and you need to expect you are going to make mistakes, too, so don't try it out when someone's life is on the line. You try it out in the privacy of your own home, where you are not going to damage anybody too much. I think that's actually a &lt;i&gt;problem with industry - companies don't let people just experiment and play around&lt;/i&gt;. Everything is "This is production code, we're going live next week and here is a book, read that and figure out how to put it in there." It doesn't matter what the book is on - whether it's going to be a database, or parallel programming, &lt;i&gt;you have to do it 3 or 4 times until you really get comfortable with it&lt;/i&gt; and people are always putting this code out there that they just barely figured out how to make it run. That's not smart. Managers should give people more time to get it right before they push it out to the world. Some do, but probably yours doesn't, if yours is like most of them.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.infoq.com/interviews/Ralph-Johnson-Parallel-Programming-Patterns"&gt;watch&lt;/a&gt; the interview&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-1064700358377353828?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/1064700358377353828/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=1064700358377353828' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/1064700358377353828'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/1064700358377353828'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2009/12/ralph-johnson-on-learning-to-program.html' title='Ralph Johnson on learning to program'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-4670183500911716462</id><published>2009-11-26T20:32:00.005-05:00</published><updated>2011-01-24T20:23:15.587-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='git'/><category scheme='http://www.blogger.com/atom/ns#' term='maven'/><title type='text'>Finding first broken build with git-bisect</title><content type='html'>With git it's so easy to find which commit broke the build. Suppose you know that commit with tag &lt;tt&gt;release-1.0&lt;/tt&gt; was good, i.e. all tests passed. The latest commit, however, is failing.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_R95qtkBZFng/Sw8sc7IdjUI/AAAAAAAADuU/-FAtB9ytnSM/s1600/gitbisect-1.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 315px;" src="http://2.bp.blogspot.com/_R95qtkBZFng/Sw8sc7IdjUI/AAAAAAAADuU/-FAtB9ytnSM/s400/gitbisect-1.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5408590552916397378" /&gt;&lt;/a&gt;&lt;br /&gt;So the build was broken somewhere in between &lt;tt&gt;release-1.0&lt;/tt&gt; and master &lt;tt&gt;HEAD&lt;/tt&gt;. To find the exact one you just need to run &lt;i&gt;two&lt;/i&gt; git commands. First&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ git bisect start HEAD release-1.0&lt;/pre&gt;&lt;br /&gt;Here you specify which commit you know is broken, and which one you remember was successful. After running this command you can see that git marked two commits with good and bad labels, and put the search starting point at the revision in the middle.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_R95qtkBZFng/Sw8uMQA9BUI/AAAAAAAADuc/GKyS5X5vFJQ/s1600/gitbisect-2.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 315px;" src="http://4.bp.blogspot.com/_R95qtkBZFng/Sw8uMQA9BUI/AAAAAAAADuc/GKyS5X5vFJQ/s400/gitbisect-2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5408592465487529282" /&gt;&lt;/a&gt;&lt;br /&gt;The second command is &lt;tt&gt;git bisect run &lt;i&gt;cmd&lt;/i&gt;&lt;/tt&gt;, where cmd is the script used as a criterion of successful build. If you use Maven then your command would be:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ git bisect run mvn clean test&lt;/pre&gt;&lt;br /&gt;After you hit Enter button in the terminal, git starts running your criterion script using binary search algorithm. It might take time to finish this task, which depends on the number of revisions and the execution time of the script. Eventually it stops and shows you which commit caused the build failure.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;b&gt;7933e4658ea852754120fbc8fec34b2b85932e48 is first bad commit&lt;/b&gt;&lt;br /&gt;commit 7933e4658ea852754120fbc8fec34b2b85932e48&lt;br /&gt;Author: Andrey Paramonov &lt;git@ndpar.com&gt;&lt;br /&gt;Date:   Wed Nov 25 21:07:30 2009 -0500&lt;br /&gt;&lt;br /&gt;    Changed method implementation&lt;br /&gt;&lt;br /&gt;:040000 040000 c0f3f9ef13d7daa4671205b9518c168a9ac10fe3 5a1cf3d6fb28d6f815c172319630b5d55ce4dc10 M     src&lt;br /&gt;bisect run success&lt;/pre&gt;&lt;br /&gt;If you look at the visual tool, you will spot the first bad commit by the label &lt;tt&gt;bisect/bad&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_R95qtkBZFng/Sw8vjFle0hI/AAAAAAAADuk/pevx7IKimPo/s1600/gitbisect-3.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 315px;" src="http://4.bp.blogspot.com/_R95qtkBZFng/Sw8vjFle0hI/AAAAAAAADuk/pevx7IKimPo/s400/gitbisect-3.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5408593957336568338" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;b&gt;Resources&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;&lt;a href="http://www.kernel.org/pub/software/scm/git/docs/git-bisect.html"&gt;git-bisect&lt;/a&gt; manual page&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-4670183500911716462?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/4670183500911716462/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=4670183500911716462' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/4670183500911716462'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/4670183500911716462'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2009/11/finding-first-broken-build-with-git.html' title='Finding first broken build with git-bisect'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_R95qtkBZFng/Sw8sc7IdjUI/AAAAAAAADuU/-FAtB9ytnSM/s72-c/gitbisect-1.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-577638067721218371</id><published>2009-11-12T22:43:00.003-05:00</published><updated>2009-11-12T22:54:44.461-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><title type='text'>State Machines in Erlang</title><content type='html'>There is some sort of confusion in the object-oriented community about functional languages. How is it possible to implement stateful application if the language has no concept of state? It turns out that it's actually quite possible, although the solution is completely deferent from what we see in the OO realm. In Erlang, for example, state can be implemented by using &lt;i&gt;process messaging&lt;/i&gt; and &lt;i&gt;tail recursion&lt;/i&gt;. This approach is so elegant that after you've learned it, the OO way of doing this looks unnatural. The code below is the Erlang implementation of Uncle Bob's &lt;a href="http://www.objectmentor.com/resources/articles/umlfsm.pdf"&gt;FSM example&lt;/a&gt;. Look at it. Isn't that code clean and expressive? It looks almost like DSL but it's actually regular Erlang syntax.&lt;br /&gt;&lt;pre&gt;-module(turnstile).&lt;br /&gt;-export([start/0]).&lt;br /&gt;-export([coin/0, pass/0]).&lt;br /&gt;-export([init/0]).&lt;br /&gt;&lt;br /&gt;start() -&gt; register(turnstile, spawn(?MODULE, init, [])).&lt;br /&gt;&lt;br /&gt;% Initial state&lt;br /&gt;&lt;br /&gt;init() -&gt; locked().&lt;br /&gt;&lt;br /&gt;% Events&lt;br /&gt;&lt;br /&gt;coin() -&gt; turnstile ! coin.&lt;br /&gt;pass() -&gt; turnstile ! pass.&lt;br /&gt;&lt;br /&gt;% States and transitions&lt;br /&gt;&lt;br /&gt;&lt;b&gt;locked()&lt;/b&gt; -&gt;&lt;br /&gt;    receive&lt;br /&gt;        pass -&gt;&lt;br /&gt;            alarm(),&lt;br /&gt;            &lt;b&gt;locked()&lt;/b&gt;;&lt;br /&gt;        coin -&gt;&lt;br /&gt;            unlock(),&lt;br /&gt;            &lt;b&gt;unlocked()&lt;/b&gt;&lt;br /&gt;    end.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;unlocked()&lt;/b&gt; -&gt;&lt;br /&gt;    receive&lt;br /&gt;        pass -&gt;&lt;br /&gt;            lock(),&lt;br /&gt;            &lt;b&gt;locked()&lt;/b&gt;;&lt;br /&gt;        coin -&gt;&lt;br /&gt;            thankyou(),&lt;br /&gt;            &lt;b&gt;unlocked()&lt;/b&gt;&lt;br /&gt;    end.&lt;br /&gt;&lt;br /&gt;% Actions&lt;br /&gt;&lt;br /&gt;alarm() -&gt; io:format("You shall not pass!~n").&lt;br /&gt;unlock() -&gt; io:format("Unlocking...~n").&lt;br /&gt;lock() -&gt; io:format("Locking...~n").&lt;br /&gt;thankyou() -&gt; io:format("Thank you for donation~n").&lt;/pre&gt;&lt;br /&gt;The idea behind this code is simple. Every state is implemented as a function that does two things: it listens for messages sent by other processes; when message is received the appropriate action is taken and one of the &lt;i&gt;state-functions called recursively&lt;/i&gt;. Simple, right? And thread-safe!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-577638067721218371?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/577638067721218371/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=577638067721218371' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/577638067721218371'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/577638067721218371'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2009/11/state-machines-in-erlang.html' title='State Machines in Erlang'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-4883391720459090282</id><published>2009-10-22T19:36:00.005-04:00</published><updated>2009-10-27T21:59:07.133-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='regex'/><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>Parsing files using Groovy regex</title><content type='html'>In my previous &lt;a href="http://ndpar.blogspot.com/2009/09/building-regular-expressions-in-groovy.html"&gt;post&lt;/a&gt; I mentioned several ways of defining regular expressions in Groovy. Here I want to show how we can use Groovy regex to find/replace data in the files.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Parsing properties file (simplified)&lt;/b&gt;&lt;sup&gt;1&lt;/sup&gt;&lt;br /&gt;&lt;br /&gt;Data: each line in the file has the same structure; the entire line can be matched by single regex. Problem: transform each line to the object. Solution: construct regex with capturing parentheses, apply it to each line, extract captured data. Demonstrates: File.eachLine method, matrix syntax of Matcher object.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;def properties = [:]&lt;br /&gt;new File('path/to/some.properties').eachLine { line -&gt;&lt;br /&gt;    if ((matcher = line =~ /^([^#=].*?)=(.+)$/)) {&lt;br /&gt;        properties[matcher[0][1]] = matcher[0][2]&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;println properties&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Parsing csv files (simplified)&lt;/b&gt;&lt;sup&gt;2&lt;/sup&gt;&lt;br /&gt;&lt;br /&gt;Data: each line in the file has the same structure; the line consists of the blocks separated by some character sequence. Problem: transform each line to the list of objects. Solution: construct regex with capturing parentheses, parse each line with the regex in a loop extracting captured data. Demonstrates: &lt;tt&gt;~//&lt;/tt&gt; Pattern defenition, Matcher.group method, &lt;tt&gt;\G&lt;/tt&gt; regex meta-sequence.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;def regex = ~/\G(?:^|,)(?:"([^"]*+)"|([^",]*+))/&lt;br /&gt;new File('path/to/file.csv').eachLine { line -&gt;&lt;br /&gt;    def fields = []&lt;br /&gt;    def matcher = regex.matcher(line)&lt;br /&gt;    while (matcher.find()) {&lt;br /&gt;        fields &lt;&lt; (matcher.group(1) ?: matcher.group(2))&lt;br /&gt;    }&lt;br /&gt;    println fields&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Finding snapshot dependencies in the pom (simplified)&lt;/b&gt;&lt;sup&gt;3&lt;/sup&gt;&lt;br /&gt;&lt;br /&gt;Data: file contains blocks with known boundaries (possibly crossing multiple lines). Problem: extract the blocks satisfying some criteria. Solution: read the entire file into the string, construct regex with capturing parentheses, apply the regex to the string in a loop. Demonstrates: File.text property, list syntaxt of Matcher object, named capture, global &lt;tt&gt;\x&lt;/tt&gt; regex modifier, local &lt;tt&gt;\s&lt;/tt&gt; regex modifier.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;def pom = new File('path/to/pom.xml').text&lt;br /&gt;def matcher = pom =~ '''(?x)&lt;br /&gt;    &amp;lt;dependency&amp;gt;                          \\s*&lt;br /&gt;      &amp;lt;groupId&amp;gt;([^&lt;]+)&amp;lt;/groupId&amp;gt;          \\s*&lt;br /&gt;      &amp;lt;artifactId&amp;gt;([^&lt;]+)&amp;lt;/artifactId&amp;gt;    \\s*&lt;br /&gt;      &amp;lt;version&amp;gt;(.+?-SNAPSHOT)&amp;lt;/version&amp;gt;   (?s:.*?)&lt;br /&gt;    &amp;lt;/dependency&amp;gt;&lt;br /&gt;'''&lt;br /&gt;matcher.each { matched, groupId, artifactId, version -&gt;&lt;br /&gt;    println "$groupId:$artifactId:$version"&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Finding stacktraces in the log&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Data: file contains entries each of which starts with the same pattern and can span multiple lines. Typical example is log4j log files:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;2009-10-16 15:32:12,157 DEBUG [com.ndpar.web.RequestProcessor] Loading user&lt;br /&gt;2009-10-16 15:32:13,258 ERROR [com.ndpar.web.UserController] id to load is required for loading&lt;br /&gt;java.lang.IllegalArgumentException: id to load is required for loading&lt;br /&gt;     at org.hibernate.event.LoadEvent.&lt;init&gt;(LoadEvent.java:74)&lt;br /&gt;     at org.hibernate.event.LoadEvent.&lt;init&gt;(LoadEvent.java:56)&lt;br /&gt;     at org.hibernate.impl.SessionImpl.get(SessionImpl.java:839)&lt;br /&gt;     at org.hibernate.impl.SessionImpl.get(SessionImpl.java:835)&lt;br /&gt;     at org.springframework.orm.hibernate3.HibernateTemplate$1.doInHibernate(HibernateTemplate.java:531)&lt;br /&gt;     at org.springframework.orm.hibernate3.HibernateTemplate.doExecute(HibernateTemplate.java:419)&lt;br /&gt;     at org.springframework.orm.hibernate3.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:374)&lt;br /&gt;     at org.springframework.orm.hibernate3.HibernateTemplate.get(HibernateTemplate.java:525)&lt;br /&gt;     at org.springframework.orm.hibernate3.HibernateTemplate.get(HibernateTemplate.java:519)&lt;br /&gt;     at com.ndpar.dao.UserManager.getUser(UserManager.java:90)&lt;br /&gt;     ... 62 more&lt;br /&gt;2009-10-16 15:32:14,659 DEBUG [com.ndpar.jms.MessageListener] Received message:&lt;br /&gt;    ... multi-line message ...&lt;br /&gt;2009-10-16 15:32:15,169 INFO  [com.ndpar.dao.UserManager] User: ...&lt;/pre&gt;&lt;br /&gt;Problem: find entries satisfying some criteria. Solution: read the entire file into the string&lt;sup&gt;4&lt;/sup&gt;, construct regex with capturing parentheses and lookahead, split the string into entries, loop through the result and apply criteria to each entry. Demonstrates: regex interpolation, combined global regex modifiers &lt;tt&gt;\s&lt;/tt&gt; and &lt;tt&gt;\m&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;def log = new File('path/to/your.log').text&lt;br /&gt;def logLineStart = /^\d{4}-\d{2}-\d{2}/&lt;br /&gt;def splitter = log =~ """(?xms)&lt;br /&gt;    (    ${logLineStart}   .*?)&lt;br /&gt;    (?=  ${logLineStart} | \\Z)&lt;br /&gt;"""&lt;br /&gt;splitter.each { matched, entry -&gt;&lt;br /&gt;    if (entry =~ /(?m)^(?:\t| {8})at/) println entry&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Replacing text in the file&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Use Groovy one-liner to perform the replacement. Here is the &lt;a href="http://timmeighen.blogspot.com/2007/03/switching-version-numbers-in-poms-maven.html"&gt;Tim's example&lt;/a&gt; in Groovy:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ groovy -p -i -e '(line =~ /1\.6/).replaceAll("2.0-alpha-1-SNAPSHOT")' `find . -name pom.xml`&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Resources&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;Groovy &lt;a href="http://groovy.codehaus.org/Regular+Expressions"&gt;regexes&lt;/a&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;Groovy &lt;a href="http://groovy.codehaus.org/Groovy+CLI"&gt;one-liners&lt;/a&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;Using &lt;a href="http://mrhaki.blogspot.com/2009/10/groovy-goodness-using-replaceall.html"&gt;String.replaceAll&lt;/a&gt; method&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Footnotes&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;small&gt;1. This example is for demonstration purposes only. In real program you would just use Properties.load method.&lt;br /&gt;2. The regex is simplified. If you want the real one, take a look at &lt;a href="http://regex.info/listing.cgi?ed=3&amp;p=401"&gt;Jeffrey Friedl's example&lt;/a&gt;.&lt;br /&gt;3. Again, in reality you would find snapshots using &lt;tt&gt;mvn dependency:resolve | grep SNAPSHOT&lt;/tt&gt; command.&lt;br /&gt;4. This approach won't work for big files. Take a look at &lt;a href="http://github.com/ndpar/utilities/blob/master/stacktraces.groovy"&gt;this script&lt;/a&gt; for practical solution.&lt;/small&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-4883391720459090282?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/4883391720459090282/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=4883391720459090282' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/4883391720459090282'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/4883391720459090282'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2009/10/parsing-files-using-groovy-regex.html' title='Parsing files using Groovy regex'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-8314902869219996323</id><published>2009-10-14T22:44:00.018-04:00</published><updated>2009-10-21T21:15:43.649-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>GParallelizer Performance</title><content type='html'>&lt;a href="http://gpars.codehaus.org/Parallelizer"&gt;GParallelizer&lt;/a&gt; is a Groovy wrapper for new Java &lt;a href="http://gee.cs.oswego.edu/dl/concurrency-interest/"&gt;concurrency library&lt;/a&gt;. It allows you to perform list and map operations using parallel threads, which in theory leverages the full power of multi-processor computations. Here I want to check if it's true in reality. I run the following tests on my dual-core MacBook&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;import static org.gparallelizer.Parallelizer.*&lt;br /&gt;import org.gparallelizer.ParallelEnhancer&lt;br /&gt;import org.junit.Before&lt;br /&gt;import org.junit.Test&lt;br /&gt;&lt;br /&gt;class GParsTest {&lt;br /&gt;&lt;br /&gt;    def list = []&lt;br /&gt;&lt;br /&gt;    @Before void setUp() {&lt;br /&gt;        1000000.times {&lt;br /&gt;            list &lt;&lt; (float) Math.random()&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Test void sequential() {&lt;br /&gt;        def start = System.currentTimeMillis()&lt;br /&gt;        list.&lt;b&gt;findAll&lt;/b&gt; { it &lt; 0.4 }&lt;br /&gt;        def duration = System.currentTimeMillis() - start&lt;br /&gt;&lt;br /&gt;        println "Sequential: ${duration}ms"&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Test void parallel_with_enhancer() {&lt;br /&gt;        ParallelEnhancer.enhanceInstance list&lt;br /&gt;&lt;br /&gt;        def start = System.currentTimeMillis()&lt;br /&gt;        list.findAllAsync { it &lt; 0.4 }&lt;br /&gt;        def duration = System.currentTimeMillis() - start&lt;br /&gt;&lt;br /&gt;        println "Parallel with enhancer: ${duration}ms"&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Test void parallel_with_parallelizer_2() {&lt;br /&gt;        parallelWithParallelizer 2&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Test void parallel_with_parallelizer_3() {&lt;br /&gt;        parallelWithParallelizer 3&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Test void parallel_with_parallelizer_5() {&lt;br /&gt;        parallelWithParallelizer 5&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Test void parallel_with_parallelizer_10() {&lt;br /&gt;        parallelWithParallelizer 10&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    def parallelWithParallelizer(threads) {&lt;br /&gt;        def start = System.currentTimeMillis()&lt;br /&gt;        withParallelizer(threads) {&lt;br /&gt;            list.&lt;b&gt;findAllAsync&lt;/b&gt; { it &lt; 0.4 }&lt;br /&gt;        }&lt;br /&gt;        def duration = System.currentTimeMillis() - start&lt;br /&gt;&lt;br /&gt;        println "Parallel with parallelizer (${threads}): ${duration}ms"&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;And here is the output&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Sequential: 774ms&lt;br /&gt;Parallel with enhancer: 9311ms&lt;br /&gt;Parallel with parallelizer (2): 1785ms&lt;br /&gt;Parallel with parallelizer (3): 769ms&lt;br /&gt;Parallel with parallelizer (5): 500ms&lt;br /&gt;Parallel with parallelizer (10): 722ms&lt;/pre&gt;&lt;br /&gt;Something strange happened with mixed-in ParallelEnhancer, but with Parallelizer performance improved indeed. With optimal thread pool size parallel processing is 35% faster than sequential.&lt;br /&gt;&lt;br /&gt;Conclusion: &lt;i&gt;Use GPars methods if you need to process big amount of data. Try different config parameters to find the best solution for your particular problem.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Resources&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;&lt;a href="http://www.infoq.com/presentations/goetz-concurrency-past-present"&gt;Brian Goetz&lt;/a&gt; on new concurrency library&lt;br /&gt;&amp;bull;&amp;nbsp;&lt;a href="http://www.grailspodcast.com/blog/id/516"&gt;Vaclav Pech&lt;/a&gt; on GPars&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-8314902869219996323?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/8314902869219996323/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=8314902869219996323' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/8314902869219996323'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/8314902869219996323'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2009/10/gparallelizer-performance.html' title='GParallelizer Performance'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-5969370847414760790</id><published>2009-10-06T19:34:00.011-04:00</published><updated>2009-11-21T22:29:36.514-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>Converting XML to POGO</title><content type='html'>Suppose we want to convert XML to Groovy bean:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;class MyBean {&lt;br /&gt;    String strField&lt;br /&gt;    float floatField&lt;br /&gt;    int intField&lt;br /&gt;    boolean boolField&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;def message = "&amp;lt;xml stringAttr='String Value' boolAttr='true' /&amp;gt;"&lt;br /&gt;&lt;br /&gt;def xml = new XmlSlurper().parseText(message)&lt;br /&gt;&lt;br /&gt;def bean = new MyBean(&lt;br /&gt;    strField: xml.@stringAttr,&lt;br /&gt;    &lt;b&gt;boolField: xml.@boolAttr&lt;/b&gt;&lt;br /&gt;)&lt;/pre&gt;&lt;br /&gt;Everything looks good, even assertions succeed&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;assert 'String Value' == bean.strField&lt;br /&gt;assert bean.boolField&lt;/pre&gt;&lt;br /&gt;Now let's try &lt;tt&gt;false&lt;/tt&gt; value:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;message = "&amp;lt;xml stringAttr='String Value' boolAttr='false' /&amp;gt;"&lt;br /&gt;assert !bean.boolField&lt;/pre&gt;&lt;br /&gt;Oops, the assertion failed. Why? Because &lt;tt&gt;xml.@boolAttr&lt;/tt&gt; cast to boolean always returns true. The correct implementation must be like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;message = "&amp;lt;xml stringAttr='String Value' floatAttr='3.14' intAttr='9' boolAttr='false' /&amp;gt;"&lt;br /&gt;&lt;br /&gt;xml = new XmlSlurper().parseText(message)&lt;br /&gt;&lt;br /&gt;bean = new MyBean(&lt;br /&gt;    strField: xml.@stringAttr.toString(),&lt;br /&gt;    floatField: xml.@floatAttr.toFloat(),&lt;br /&gt;    intField: xml.@intAttr.toInteger(),&lt;br /&gt;    boolField: xml.@boolAttr.&lt;b&gt;toBoolean()&lt;/b&gt;&lt;br /&gt;)&lt;br /&gt;assert 'String Value' == bean.strField&lt;br /&gt;assert 3.14F == bean.floatField&lt;br /&gt;assert 9 == bean.intField&lt;br /&gt;assert !bean.boolField&lt;/pre&gt;&lt;br /&gt;Now everything works properly. The moral of this blog post: &lt;i&gt;Create more unit tests (assertions), especially when you work with dynamic language&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Resources&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;&lt;a href="http://mrhaki.blogspot.com/2009/11/groovy-goodness-convert-string-to.html"&gt;Converting String to Boolean&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-5969370847414760790?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/5969370847414760790/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=5969370847414760790' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/5969370847414760790'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/5969370847414760790'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2009/10/converting-xml-to-pogo.html' title='Converting XML to POGO'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-1118664378531349841</id><published>2009-09-25T21:43:00.009-04:00</published><updated>2009-10-21T20:34:46.904-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='regex'/><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>Building regular expressions in Groovy</title><content type='html'>Because of compact syntax regular expressions in Groovy are more readable than in Java. Here is how Jeffrey Friedl's &lt;a href="http://regex.info/listing.cgi?ed=3&amp;p=208"&gt;example&lt;/a&gt; would look like in Groovy:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;def subDomain  = '(?i:[a-z0-9]|[a-z0-9][-a-z0-9]*[a-z0-9])' // simple regex in single quotes&lt;br /&gt;def topDomains = """&lt;br /&gt;       (?x-i : com        \\b     # you can put whitespaces and comments&lt;br /&gt;             | edu        \\b     # inside regex in eXtended mode&lt;br /&gt;             | biz        \\b&lt;br /&gt;             | in(?:t|fo) \\b     # but you have to escape&lt;br /&gt;             | mil        \\b     # backslashes in multiline strings&lt;br /&gt;             | net        \\b&lt;br /&gt;             | org        \\b&lt;br /&gt;             | [a-z][a-z] \\b&lt;br /&gt;       )"""&lt;br /&gt;&lt;br /&gt;def hostname = /(?:${subDomain}\.)${topDomains}/  // variable substitution in slashy strings&lt;br /&gt;&lt;br /&gt;def NOT_IN   = /;\"'&lt;&gt;()\[\]{}\s\x7F-\xFF/  // backslash is not escaped in slashy strings&lt;br /&gt;def NOT_END  = /!.,?/&lt;br /&gt;def ANYWHERE = /[^${NOT_IN}${NOT_END}]/&lt;br /&gt;def EMBEDDED = /[$NOT_END]/                 // you can ommit {} around var name&lt;br /&gt;&lt;br /&gt;def urlPath  = "/$ANYWHERE*($EMBEDDED+$ANYWHERE+)*"&lt;br /&gt;&lt;br /&gt;def url =&lt;br /&gt;    """(?x:&lt;br /&gt;             \\b&lt;br /&gt;&lt;br /&gt;             # match the hostname part&lt;br /&gt;             (&lt;br /&gt;               (?: ftp | http s? ): // [-\\w]+(\\.\\w[-\\w]*)+&lt;br /&gt;             |&lt;br /&gt;               $hostname&lt;br /&gt;             )&lt;br /&gt;&lt;br /&gt;             # allow optional port&lt;br /&gt;             (?: :\\d+ )?&lt;br /&gt;&lt;br /&gt;             # rest of url is optional, and begins with /&lt;br /&gt;             (?: $urlPath )?&lt;br /&gt;       )"""&lt;br /&gt;&lt;br /&gt;assert 'http://www.google.com/search?rls=en&amp;q=regex&amp;ie=UTF-8&amp;oe=UTF-8' ==~ url&lt;/pre&gt;&lt;br /&gt;As you can see, there are several options, and for every subexpression you can choose the one that's more expressive.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Resources&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;&lt;a href="http://martinfowler.com/bliki/ComposedRegex.html"&gt;Martin Fowler&lt;/a&gt; on composed regexes&lt;br /&gt;&amp;bull;&amp;nbsp;&lt;a href="http://pragdave.blogs.pragprog.com/pragdave/2008/10/fun-with-ruby-19-regular-expressions.html"&gt;Pragmatic Dave&lt;/a&gt; on regexes in Ruby&lt;br /&gt;&amp;bull;&amp;nbsp;&lt;a href="http://jira.codehaus.org/browse/GROOVY-2701"&gt;Feature request&lt;/a&gt; to make regexes even groovier&lt;br /&gt;&amp;bull;&amp;nbsp;&lt;a href="http://regex.info"&gt;Mastering Regular Expressions&lt;/a&gt; &amp;mdash; best regex book&lt;br /&gt;&amp;bull;&amp;nbsp;Groovy &lt;a href="http://mrhaki.blogspot.com/2009/09/groovy-goodness-using-regular.html"&gt;Pattern&lt;/a&gt; and &lt;a href="http://mrhaki.blogspot.com/2009/09/groovy-goodness-matchers-for-regular.html"&gt;Matcher&lt;/a&gt; classes&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-1118664378531349841?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/1118664378531349841/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=1118664378531349841' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/1118664378531349841'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/1118664378531349841'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2009/09/building-regular-expressions-in-groovy.html' title='Building regular expressions in Groovy'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-2718589314822636119</id><published>2009-09-22T20:24:00.015-04:00</published><updated>2009-10-21T21:09:59.683-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='flex'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><title type='text'>Fill parameters in LCDS Assembler methods</title><content type='html'>Last few days we spent debugging some nasty bug in the code that uses LiveCycle managed collections. We were adding/removing items to/from collections on the server side. We saw that server sent a message to the client, client did receive the message, but then it ignored it and didn't update the collection (data grid). After digging into the client side logs we found the reason of such a misbehaviour.&lt;br /&gt;&lt;br /&gt;If you have two destinations that share the same channel&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;service id="data-service" class="flex.data.DataService"&amp;gt;&lt;br /&gt;   &amp;lt;destination id="MyFirstDestination"&amp;gt;&lt;br /&gt;       &amp;lt;channels&amp;gt;&lt;br /&gt;           &amp;lt;channel ref="my-rtmp-channel"/&amp;gt;&lt;br /&gt;       &amp;lt;/channels&amp;gt;&lt;br /&gt;       &amp;lt;properties&amp;gt;&lt;br /&gt;           &amp;lt;source&amp;gt;example.MyFirstAssembler&amp;lt;/source&amp;gt;&lt;br /&gt;       &amp;lt;/properties&amp;gt;&lt;br /&gt;   &amp;lt;/destination&amp;gt;&lt;br /&gt;   &amp;lt;destination id="MySecondDestination"&amp;gt;&lt;br /&gt;       &amp;lt;channels&amp;gt;&lt;br /&gt;           &amp;lt;channel ref="my-rtmp-channel"/&amp;gt;&lt;br /&gt;       &amp;lt;/channels&amp;gt;&lt;br /&gt;       &amp;lt;properties&amp;gt;&lt;br /&gt;           &amp;lt;source&amp;gt;example.MySecondAssembler&amp;lt;/source&amp;gt;&lt;br /&gt;       &amp;lt;/properties&amp;gt;&lt;br /&gt;   &amp;lt;/destination&amp;gt;&lt;br /&gt;&amp;lt;/service&amp;gt;&lt;/pre&gt;&lt;br /&gt;&lt;i&gt;LCDS uses &lt;/i&gt;fillParameters&lt;i&gt; as a key in the managed collections cache. That means fillParameters must be immutable.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;public class MyFirstAssembler extends flex.data.assemblers.AbstractAssembler {&lt;br /&gt;&lt;br /&gt;  @Override&lt;br /&gt;  public int refreshFill(List fillParameters, Object newItem, boolean isCreate, Object oldItem, List changes) {&lt;br /&gt;    // &lt;b&gt;Never change fillParameters!&lt;/b&gt;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  @Override&lt;br /&gt;  public Collection fill(List fillParameters) {&lt;br /&gt;    // &lt;b&gt;Never change fillParameters!&lt;/b&gt;&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Adobe documentation says nothing about this, so keep this rule in mind when working with LCDS managed collections.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Resources&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;&lt;a href="http://code.google.com/p/jpragma-utils/downloads/detail?name=JPLogViewer.air"&gt;Flex log viewer&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-2718589314822636119?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/2718589314822636119/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=2718589314822636119' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/2718589314822636119'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/2718589314822636119'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2009/09/fill-parameters-in-lcds-assembler.html' title='Fill parameters in LCDS Assembler methods'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-7301914053433294363</id><published>2009-08-20T19:15:00.007-04:00</published><updated>2009-10-21T21:19:55.504-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='flex'/><title type='text'>Measuring LiveCycle Performance: Message Size</title><content type='html'>The &lt;a href="http://livedocs.adobe.com/livecycle/8.2/programLC/programmer/lcds/help.html?content=mpi_3.html"&gt;method&lt;/a&gt; of measuring performance provided by LCDS works only in situations when producer and consumer of messages are both on the Flex side. For Data Services that means you can obtain some metrics only for initial &lt;a href="http://livedocs.adobe.com/livecycle/8.2/programLC/common/langref/mx/data/DataService.html#fill()"&gt;collection fill&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Original message size(B): 499&lt;br /&gt;Response message size(B): 17687&lt;br /&gt;Total time (s): -1250809384.8&lt;br /&gt;Network Roundtrip time (s): -1250809384.868&lt;br /&gt;Server processing time (s): 0.068&lt;br /&gt;Server adapter time (s): 0.014&lt;br /&gt;Server non-adapter time (s): 0.054&lt;/pre&gt;&lt;br /&gt;If you want to know message size and response time for messages pushed from Java server to Flex client, this method doesn't help&lt;a href="#lcds-performance-2-footnote"&gt;*&lt;/a&gt; in the current version of LCDS (2.6.1). Adobe promised to add this feature in the future release but for now you have to use other methods. Here is what I use to measure message size.&lt;br /&gt;&lt;br /&gt;1. &lt;b&gt;JMX&lt;/b&gt;. By default LCDS exposes some useful metrics through JMX:&lt;br /&gt;&lt;br /&gt;&lt;a href='http://4.bp.blogspot.com/_R95qtkBZFng/So3ZyY7_Q-I/AAAAAAAADS4/PsqrTzM7SXM/s1600-h/lcds-jconsole.jpg'&gt;&lt;img src='http://4.bp.blogspot.com/_R95qtkBZFng/So3ZyY7_Q-I/AAAAAAAADS4/PsqrTzM7SXM/s400/lcds-jconsole.jpg' border='0' alt='' /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;2. &lt;b&gt;Flex log&lt;/b&gt;. If you enable log in the services-config.xml, you will see something like this in the output console for every data push:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Thread[1563082333@qtp0-0,5,main] registering write interest for Connection '1752654181'.&lt;br /&gt;Thread[my-rtmp-SocketServer-Reactor1,5,main] unregistering write interest for Connection '1752654181'.&lt;br /&gt;Thread[my-rtmp-SocketServer-Reactor1Writer,5,main] Connection '1752654181' starting a write.&lt;br /&gt;Thread[my-rtmp-SocketServer-Reactor1Writer,5,main] chunk output stream writing message; ack state: 3&lt;br /&gt;...&lt;br /&gt;Thread[my-rtmp-SocketServer-Reactor1Writer,5,main] Connection '1752654181' finished a write. &lt;b&gt;233 bytes were written&lt;/b&gt;.&lt;/pre&gt;&lt;br /&gt;3. If you don't have access to the server, you can use any &lt;b&gt;network protocol analyzer&lt;/b&gt; (&lt;a href="http://www.wireshark.org"&gt;WireShark&lt;/a&gt; is really good) on the client side to monitor size of packets received from the server.&lt;br /&gt;&lt;br /&gt;&lt;a name="lcds-performance-2-footnote"&gt;*&lt;/a&gt; &lt;small&gt;Actually, there is one undocumented feature that can be used with the described method to measure size of "create" messages, but Adobe does not recommend to use it.&lt;/small&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Resources&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&amp;bull; &lt;a href="http://ndpar.blogspot.com/2009/06/measuring-livecycle-performance.html"&gt;Part 1:&lt;/a&gt; Measuring LiveCycle Performance: Errors&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-7301914053433294363?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/7301914053433294363/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=7301914053433294363' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/7301914053433294363'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/7301914053433294363'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2009/08/measuring-livecycle-performance-2.html' title='Measuring LiveCycle Performance: Message Size'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_R95qtkBZFng/So3ZyY7_Q-I/AAAAAAAADS4/PsqrTzM7SXM/s72-c/lcds-jconsole.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-2964378996112145807</id><published>2009-08-13T20:07:00.003-04:00</published><updated>2009-08-13T20:25:47.550-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='git'/><title type='text'>Git on Cygwin</title><content type='html'>Here are the steps I perform to use Git on Cygwin:&lt;br /&gt;&lt;br /&gt;&amp;bull; Install necessary packages: &lt;tt&gt;git&lt;/tt&gt;, &lt;tt&gt;gitk&lt;/tt&gt;, &lt;tt&gt;subversion&lt;/tt&gt;, &lt;tt&gt;subversion-perl&lt;/tt&gt;. That's because I use Subversion via Git bridge.&lt;br /&gt;&lt;br /&gt;&amp;bull; Amend &lt;tt&gt;C:\cygwin\lib\perl5\vendor_perl\5.xx\Git.pm&lt;/tt&gt; file using &lt;a href="http://thelilaeanbellis.com/shared/git-handle.patch"&gt;this patch&lt;/a&gt;. That's to fix small bug causing "Permission denied: Can't open '/cygdrive/c/DOCUME~1/myself/LOCALS~1/Temp/report.tmp'" error.&lt;br /&gt;&lt;br /&gt;&amp;bull; Configure white spaces using &lt;a href="http://www.jroller.com/ndpar/entry/git_carriage_return"&gt;this command&lt;/a&gt; to fix trailing spaces warnings.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-2964378996112145807?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/2964378996112145807/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=2964378996112145807' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/2964378996112145807'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/2964378996112145807'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2009/08/git-on-cygwin.html' title='Git on Cygwin'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-898055225788663143</id><published>2009-07-23T22:14:00.006-04:00</published><updated>2009-10-21T20:41:10.578-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sql'/><category scheme='http://www.blogger.com/atom/ns#' term='flex'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><title type='text'>Double in ActionScript, Java, and MS SQL</title><content type='html'>&lt;b&gt;ActionScript 3&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;There are three numeric data types in AS3: int, uint, and Number.&lt;br /&gt;&amp;bull;&amp;nbsp;They are not primitives because they can be instantiated using constructors.&lt;br /&gt;&amp;bull;&amp;nbsp;They are not "real" objects because they cannot be null, and they have default values:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;myNumber:Number;&lt;br /&gt;myNumber.toString(); // No NPE thrown&lt;/pre&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;Default value for type Number is NaN (not zero).&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Java&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;BlazeDS converts AS3 Number type to Java Double.&lt;br /&gt;&amp;bull;&amp;nbsp;NaN is idempotent of conversion:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;NaN (Java) -&gt; NaN (AS3) -&gt; NaN (Java)&lt;/pre&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;null is not! Keep it in mind when you work with BlazeDS:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;b&gt;null (Java) -&gt; 0 (AS3) -&gt; 0.0 (Java)&lt;/b&gt;&lt;/pre&gt;&lt;br /&gt;If Java NaN doesn't have special meaning in your application, you can use it as a "replacement" for null in Java-Flex communication.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;MS SQL&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;&lt;a href="http://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=239674"&gt;Doesn't support NaN&lt;/a&gt; value for numeric columns.&lt;br /&gt;&amp;bull;&amp;nbsp;All NaN values should be replaced by null before saving entity in the database, otherwise you will get exception:&lt;br /&gt;&lt;br /&gt;com.microsoft.sqlserver.jdbc.SQLServerException: The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Parameter 24 (""): The supplied value is not a valid instance of data type real. Check the source data for invalid values. An example of an invalid value is data of numeric type with scale greater than precision.&lt;br /&gt;&lt;br /&gt;In my current project I'm using all three languages, and I have to convert NaN to null back and forth for every object:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;NaN (AS3) &lt;-&gt; NaN (Java) &lt;-&gt; null (Java) &lt;-&gt; null (MS SQL)&lt;/pre&gt;&lt;br /&gt;So I created small &lt;a href="http://code.google.com/p/ndpar/source/browse/trunk/utils/src/main/java/com/ndpar/utils/bean/ExtendedPropertyUtils.java"&gt;utility class&lt;/a&gt; that replaces all JavaBean properties of particular type from one value to another:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;ExtendedPropertyUtils.replacePropertyValue(myBean, Double.NaN, null);&lt;br /&gt;ExtendedPropertyUtils.replacePropertyValue(myBean, null, Double.NaN);&lt;/pre&gt;&lt;br /&gt;Feel free to use it if you have the same problem.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Resources&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;&lt;a href="http://bugs.adobe.com/jira/browse/ASC-3645"&gt;Feature request&lt;/a&gt; to Adobe to introduce nullable Number type.&lt;br /&gt;&amp;bull;&amp;nbsp;&lt;a href="http://bugs.adobe.com/jira/browse/BLZ-305"&gt;Other solutions&lt;/a&gt; for similar issues in BlazeDS.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-898055225788663143?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/898055225788663143/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=898055225788663143' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/898055225788663143'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/898055225788663143'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2009/07/double-in-actionscript-java-and-ms-sql.html' title='Double in ActionScript, Java, and MS SQL'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-458469674211410533</id><published>2009-07-20T20:36:00.003-04:00</published><updated>2009-07-20T21:03:26.855-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='agile'/><category scheme='http://www.blogger.com/atom/ns#' term='engineering'/><title type='text'>Tom DeMarco on Software Engineering</title><content type='html'>&lt;i&gt;Software development is inherently different from a natural science&lt;/i&gt; such as physics, and its metrics are accordingly much less precise in capturing the things they set out to describe.&lt;br /&gt;...&lt;br /&gt;&lt;i&gt;Strict control is something that matters a lot on relatively useless projects&lt;/i&gt; and much less on useful projects. It suggests that the more you focus on control, the more likely you’re working on a project that’s striving to deliver something of relatively minor value.&lt;br /&gt;To my mind, the question that’s much more important than how to control a software project is, &lt;i&gt;why on earth are we doing so many projects that deliver such marginal value?&lt;/i&gt;&lt;br /&gt;...&lt;br /&gt;So, how do you manage a project without controlling it? Well, &lt;i&gt;you manage the people and control the time and money&lt;/i&gt;. You say to your team leads, for example, “I have a finish date in mind, and I’m not even going to share it with you. When I come in one day and tell you the project will end in one week, you have to be ready to package up and deliver what you’ve got as the final product. Your job is to go about the project incrementally, adding pieces to the whole in the order of their relative value, and doing integration and documentation and acceptance testing incrementally as you go.”&lt;br /&gt;...&lt;br /&gt;For the past 40 years we’ve tortured ourselves over our inability to finish a software project on time and on budget. But this never should have been the supreme goal. &lt;i&gt;The more important goal is transformation, creating software that changes the world&lt;/i&gt; or that transforms a company or how it does business.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www2.computer.org/cms/Computer.org/ComputingNow/homepage/2009/0709/rW_SO_Viewpoints.pdf"&gt;read&lt;/a&gt; the whole article&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-458469674211410533?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/458469674211410533/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=458469674211410533' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/458469674211410533'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/458469674211410533'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2009/07/tom-demarco-on-software-engineering.html' title='Tom DeMarco on Software Engineering'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-6539313768879137373</id><published>2009-07-17T21:07:00.005-04:00</published><updated>2009-07-20T21:04:58.479-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><title type='text'>Michael Feathers on Functional Programming</title><content type='html'>One of the things that seems like a rather pessimistic observation, but I think it's true to a degree, that the number of programmers who are able to or willing to think in a mathematically sophisticated way about code is relatively small, in comparison to the total population of programmers. I think that even though functional programming is becoming more popular, it is a bit of uphill battle for the industry and it may become just a very strong good niche tool for the people who are able to use it very well. I'm glad to see it's being brought up in prominence now, but I'm wondering if we'll ever see a day when everybody is doing work in functional programming. On the other hand, we got to the point where closures are becoming part of practically every programming language. It only took 30 years, so there is hope, I guess.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.infoq.com/interviews/michael-feathers-programming-languages"&gt;watch&lt;/a&gt; the interview&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-6539313768879137373?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/6539313768879137373/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=6539313768879137373' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/6539313768879137373'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/6539313768879137373'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2009/07/interview-with-michael-feathers.html' title='Michael Feathers on Functional Programming'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-8921833338556740983</id><published>2009-07-16T22:29:00.006-04:00</published><updated>2009-10-21T20:41:54.277-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='spring'/><title type='text'>Spring integration tests with mocked collaborators</title><content type='html'>Sometimes when you write integration tests that load Spring application context, you want to mock some collaborators just to verify that they've been called with correct data. This is a quite legitimate approach, but you have to keep in mind that Spring caches application contexts during the test execution. That means, after you replace a bean with a mock, all subsequent tests that share the same application context will use mocked instance instead of real one. That might cause tests to fail in very peculiar way. For example, test succeeds in Eclipse but fails in command line; you create new test and the old one starts failing, etc.&lt;br /&gt;&lt;br /&gt;To fix the problem, you need to reload application context after every test that uses mocks. The best way to do this is to use @DirtiesContext annotation. In Spring 2.5 this was a method level annotation, but starting with Spring 3.0RC1 you can use it on the class level (thanks Spring!). So the rule of thumb is:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;If you mock a bean in the Spring integration test, annotate the test class with @DirtiesContext&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;@RunWith(SpringJUnit4ClassRunner.class)&lt;br /&gt;@ContextConfiguration(locations={"classpath:appclicationContext.xml"})&lt;br /&gt;&lt;b&gt;@DirtiesContext&lt;/b&gt;&lt;br /&gt;public class IntegrationTest {&lt;br /&gt;&lt;br /&gt;    @Autowired&lt;br /&gt;    private Application application;&lt;br /&gt;&lt;br /&gt;    private Collaborator mockCollaborator;&lt;br /&gt;&lt;br /&gt;    @Before&lt;br /&gt;    public void setUp() {&lt;br /&gt;        &lt;b&gt;mockCollaborator = mock(Collaborator.class);&lt;br /&gt;        application.setCollaborator(mockCollaborator);&lt;/b&gt;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Test&lt;br /&gt;    public void collaborator_is_called_once() {&lt;br /&gt;        ...&lt;br /&gt;        verify(mockCollaborator, times(1)).methodCall(...);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Resources&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&amp;bull; &lt;a href="http://github.com/ndpar/spring-test/tree/master"&gt;Source&lt;/a&gt; files for this post&lt;br /&gt;&amp;bull; Spring annotations &lt;a href="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/ch10s03.html#integration-testing-annotations"&gt;documentation&lt;/a&gt;&lt;br /&gt;&amp;bull; &lt;a href="http://jira.springframework.org/browse/SPR-4702"&gt;Class level&lt;/a&gt; @DirtiesContext annotation&lt;br /&gt;&amp;bull; How to &lt;a href="http://blog.springsource.com/2009/03/03/building-spring-3/"&gt;build Spring 3&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-8921833338556740983?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/8921833338556740983/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=8921833338556740983' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/8921833338556740983'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/8921833338556740983'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2009/07/spring-integration-tests-with-mocked.html' title='Spring integration tests with mocked collaborators'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-1081350966514464318</id><published>2009-07-07T19:12:00.003-04:00</published><updated>2009-07-07T19:20:34.870-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='agile'/><title type='text'>Iteration Length</title><content type='html'>&lt;i&gt;Iteration length in agile software development is the time between two consecutive customer feedbacks.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;You can set up iteration length to 2 weeks in the JIRA, but if you meet your customer twice a year then your iteration length is 6 months. You can do iteration demo every 2 weeks, but if nobody except your manager sees the demo then your iteration length is not 2 weeks. You can even release some artifacts and change the version of your application every 2 weeks, but if your customer never tried to use your application then your iteration length is definitely not 2 weeks.&lt;br /&gt;&lt;br /&gt;In general, without customer feedback there is no iteration. It's a cascade.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-1081350966514464318?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/1081350966514464318/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=1081350966514464318' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/1081350966514464318'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/1081350966514464318'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2009/07/iteration-length.html' title='Iteration Length'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-7747113903547642665</id><published>2009-07-06T20:11:00.006-04:00</published><updated>2009-07-20T21:05:28.852-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='engineering'/><category scheme='http://www.blogger.com/atom/ns#' term='cs'/><title type='text'>Tony Hoare: 50 Years with Legacy Code</title><content type='html'>&lt;b&gt;Q:&lt;/b&gt; With your time in the computer science field, I'm guessing that you've seen some common trends, things that have remained consistent throughout time since 1960. What are those trends that have remained consistent and how do you think it will continue to the future?  &lt;br /&gt;&lt;br /&gt;&lt;b&gt;A:&lt;/b&gt; I'm sorry to say it's the mistakes that remained consistent, not the virtues. The problems that afflicted us in the 1960s were the difficulty of predicting performance of large systems, the difficulty of discovering requirements, the difficulty of implementing code that was coherent across large-scale module boundaries. All of these things are still with us. I suppose I could say that even in 1960 living with legacy code was there. Dykstra once said that every day when he comes into work he thanks providence that he is not charged with the responsibility of living with legacy code - that's certainly still with us.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.infoq.com/interviews/tony-hoare-qcon-interview"&gt;watch&lt;/a&gt; the interview&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-7747113903547642665?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/7747113903547642665/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=7747113903547642665' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/7747113903547642665'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/7747113903547642665'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2009/07/interview-with-tony-hoare.html' title='Tony Hoare: 50 Years with Legacy Code'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-4383499005199029315</id><published>2009-07-03T20:22:00.008-04:00</published><updated>2009-10-21T20:47:43.997-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='git'/><category scheme='http://www.blogger.com/atom/ns#' term='subversion'/><title type='text'>Collaborating Using git-svn</title><content type='html'>I like Git, and I'm using it everywhere. It gives you so much power that once you taste it, you won't want to come back to traditional source control systems. One of the Git benefits is collaboration friendliness. Git encourages collaboration, ideas exchange, and code review. If your team is using Git then you know how easy it is to share your code with your co-workers. But sometimes you are the only person in the team who uses Git, and everybody else is on Subversion. Don't worry, you still can share your ideas by means of git-svn tool, and here I want to show you how. The process is not as simple as native Git (via pull/push or patch/apply) but it's better than nothing.&lt;br /&gt;&lt;br /&gt;Suppose you have an idea and you are eager to try it. You don't want to create a branch in Subversion because you don't know if your idea will work out, and committing all your crazy stuff in Subversion can easily pollute it. So you create a local Git branch and start working.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ git checkout -b topic/great-idea&lt;/pre&gt;&lt;br /&gt;You code, test, git-add, git-commit, code, test, etc. At some point you see that your idea was great indeed and it's time to show the amazing results to your teammates. Now you need to "push" your Git branch to Subversion. To do this you have to create Subversion brunch first&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ svn copy http://svn.repo.path/project-name/trunk \&lt;br /&gt;http://svn.repo.path/project-name/branches/great-idea \&lt;br /&gt;-m "Created branch for my cool stuff"&lt;/pre&gt;&lt;br /&gt;Next step is to add this Subversion branch as a remote branch to Git configuration. Open .git/config file with a text editor. You should see something like this&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;[core]&lt;br /&gt;       repositoryformatversion = 0&lt;br /&gt;       filemode = true&lt;br /&gt;       bare = false&lt;br /&gt;       logallrefupdates = true&lt;br /&gt;[svn-remote "svn"]&lt;br /&gt;       url = http://svn.repo.path&lt;br /&gt;       fetch = project-name/trunk:refs/remotes/trunk&lt;/pre&gt;&lt;br /&gt;If you built your Git repository by cloning Subversion repository (which you most likely did), you will have one or more svn-remote sections in this configuration file. You need to add another one for new Subversion branch.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;[core]&lt;br /&gt;       repositoryformatversion = 0&lt;br /&gt;       filemode = true&lt;br /&gt;       bare = false&lt;br /&gt;       logallrefupdates = true&lt;br /&gt;[svn-remote "svn"]&lt;br /&gt;       url = http://svn.repo.path&lt;br /&gt;       fetch = project-name/trunk:refs/remotes/trunk&lt;br /&gt;[svn-remote "svn-great-idea"]&lt;br /&gt;       url = http://svn.repo.path&lt;br /&gt;       fetch = project-name/branches/great-idea:refs/remotes/great-idea&lt;/pre&gt;&lt;br /&gt;Next step is to fetch Subversion branch, but first you need to know revision number when this branch was created. Run this command&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ svn log --stop-on-copy http://svn.repo.path/project-name/branches/great-idea&lt;/pre&gt;&lt;br /&gt;You should get something like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;------------------------------------------------------------------------&lt;br /&gt;r2165 | andy | 2009-07-03 14:36:59 -0400 (Fri, 03 Jul 2009) | 1 line&lt;br /&gt; &lt;br /&gt;Created branch for my cool stuff&lt;br /&gt;------------------------------------------------------------------------&lt;/pre&gt;&lt;br /&gt;The first number of the output is what we are looking for:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ git svn fetch svn-great-idea -r2165&lt;/pre&gt;&lt;br /&gt;Now you have the Subversion branch in you Git. If you run &lt;tt&gt;git branch -a&lt;/tt&gt; command you will see "great-idea" branch in the list.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt; master&lt;br /&gt;* topic/great-idea&lt;br /&gt; great-idea&lt;br /&gt; trunk&lt;/pre&gt;&lt;br /&gt;You shouldn't work on remote branch, so let's create local one:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ git branch svn-branch/great-idea remotes/great-idea&lt;/pre&gt;&lt;br /&gt;I put it in svn-branch namespace just to make it visually clear that this branch is in synch with Subversion. The next set of commands is a standard way to bring your work from one local branch to another. In our case: from initial topic/great-idea to svn-branch/great-idea&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ git rebase svn-branch/great-idea&lt;br /&gt;$ git checkout svn-branch/great-idea&lt;br /&gt;$ git rebase topic/great-idea&lt;/pre&gt;&lt;br /&gt;That's it. Now you are ready to commit your code to Subversion:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ git svn dcommit&lt;/pre&gt;&lt;br /&gt;Done. As an option, you can delete initial branch because you have a Subversion backed copy of it:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ git branch -D topic/great-idea&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Resources&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;&lt;a href="http://blog.tfd.co.uk/2008/11/21/adding-a-branch-to-git/"&gt;Ian Boston&lt;/a&gt;'s post explaining how to add Subversion branches to Git.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-4383499005199029315?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/4383499005199029315/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=4383499005199029315' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/4383499005199029315'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/4383499005199029315'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2009/07/collaborating-using-git-svn.html' title='Collaborating Using git-svn'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-4014437861472183728</id><published>2009-06-30T20:51:00.015-04:00</published><updated>2009-10-21T20:45:31.071-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='flex'/><title type='text'>Measuring LiveCycle Performance: Errors</title><content type='html'>There are several ways to measure LiveCycle performance. One of them is to call appropriate method on &lt;a href="http://livedocs.adobe.com/livecycle/8.2/programLC/programmer/lcds/help.html?content=mpi_3.html"&gt;MessagePerformanceUtils&lt;/a&gt; class. This approach is pretty straightforward but sometimes you might get an error:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Destination '...' either does not exist or the destination has no channels defined (and the application does not define any default channels.)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;That means your are using statically configured channels and you don't package services configuration into the SWF file. To fix it, in the Flex Builder add config files folder to Flex source path and specify 'services' compiler argument:&lt;br /&gt;&lt;br /&gt;&lt;a href='http://3.bp.blogspot.com/_R95qtkBZFng/SkqzAp_E4kI/AAAAAAAADNQ/D5NkianbFzg/s1600-h/flex-build-path-properties.jpg'&gt;&lt;img src='http://3.bp.blogspot.com/_R95qtkBZFng/SkqzAp_E4kI/AAAAAAAADNQ/D5NkianbFzg/s400/flex-build-path-properties.jpg' border='0' alt='' width="320" height="247"/&gt;&lt;/a&gt;&amp;nbsp;&lt;a href='http://3.bp.blogspot.com/_R95qtkBZFng/SkqzA7HzhUI/AAAAAAAADNY/rBAYwxR929Y/s1600-h/flex-compiler-properties.jpg'&gt;&lt;img src='http://3.bp.blogspot.com/_R95qtkBZFng/SkqzA7HzhUI/AAAAAAAADNY/rBAYwxR929Y/s400/flex-compiler-properties.jpg' border='0' alt='' width="320" height="247" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Although it solves the problem, this approach is not suitable for real project as you don't want to compile SWF file with hard coded services configuration. Instead of that you would create dynamic channels on the client side, and configure them using IoC framework (i.e. &lt;a href="http://www.spicefactory.org/parsley/"&gt;Parsley&lt;/a&gt;, &lt;a href="http://www.pranaframework.org/"&gt;Prana&lt;/a&gt; or your own). And if you do that you will most likely get the following error:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Error: Message is missing MPI headers. Verify that all participants have it enabled&lt;/b&gt;&lt;br /&gt; &lt;br /&gt;The reason of that is: you configured MPI headers only on the server side, but not on the Flex side. To fix it, you need to set recordMessageTimes and recordMessageSizes properties of &lt;a href="http://livedocs.adobe.com/flex/3/langref/mx/messaging/Channel.html"&gt;Channel&lt;/a&gt; class to true. The problem is that those properties are read-only, so you cannot assign them to any value directly. But here is a trick: you can use applySettings() method:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;br /&gt;&amp;lt;mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init()"&amp;gt;&lt;br /&gt;    &amp;lt;mx:Script&amp;gt;&lt;br /&gt;        &amp;lt;![CDATA[&lt;br /&gt;            import mx.messaging.Channel;&lt;br /&gt;            import mx.messaging.ChannelSet;&lt;br /&gt;            import mx.messaging.channels.RTMPChannel;&lt;br /&gt;            import mx.messaging.events.MessageEvent;&lt;br /&gt;            import mx.messaging.messages.MessagePerformanceUtils;&lt;br /&gt;&lt;br /&gt;            private function init():void {&lt;br /&gt;                ds.channelSet = createChannelSet();&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            private function createChannelSet():ChannelSet {&lt;br /&gt;                var channels:Array = new Array();&lt;br /&gt;                channels.push(createRtmpChannel());&lt;br /&gt;&lt;br /&gt;                var result:ChannelSet = new ChannelSet();&lt;br /&gt;                result.channels = channels;&lt;br /&gt;                return result;&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            private function createRtmpChannel():Channel {&lt;br /&gt;                var result:Channel = ... // get it from IoC&lt;br /&gt;                &lt;b&gt;result.applySettings(customSettings());&lt;/b&gt;&lt;br /&gt;                return result;&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            &lt;b&gt;private function customSettings():XML {&lt;br /&gt;                return &amp;lt;channel-definition&amp;gt;&lt;br /&gt;                    &amp;lt;properties&amp;gt;&lt;br /&gt;                        &amp;lt;record-message-times&amp;gt;true&amp;lt;/record-message-times&amp;gt;&lt;br /&gt;                        &amp;lt;record-message-sizes&amp;gt;true&amp;lt;/record-message-sizes&amp;gt;&lt;br /&gt;                    &amp;lt;/properties&amp;gt;&lt;br /&gt;                &amp;lt;/channel-definition&amp;gt;;&lt;br /&gt;            }&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;            private function messageHandler(event:MessageEvent):void {&lt;br /&gt;                var performanceUtils:MessagePerformanceUtils = new MessagePerformanceUtils(event.message);&lt;br /&gt;                statistics.text = performanceUtils.prettyPrint();&lt;br /&gt;            }&lt;br /&gt;        ]]&amp;gt;&lt;br /&gt;    &amp;lt;/mx:Script&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;mx:DataService id="ds" destination="MyDestination" result="messageHandler(event)" /&amp;gt;&lt;br /&gt;    &amp;lt;mx:ArrayCollection id="domainObjects" /&amp;gt;&lt;br /&gt;    &amp;lt;mx:TextArea id="statistics" /&amp;gt;&lt;br /&gt;&amp;lt;/mx:Application&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Resources&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&amp;bull;&amp;nbsp;&lt;a href="http://github.com/ndpar/mpi-headers/tree/master"&gt;Check out&lt;/a&gt; example sources from GitHub.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-4014437861472183728?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/4014437861472183728/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=4014437861472183728' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/4014437861472183728'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/4014437861472183728'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2009/06/measuring-livecycle-performance.html' title='Measuring LiveCycle Performance: Errors'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_R95qtkBZFng/SkqzAp_E4kI/AAAAAAAADNQ/D5NkianbFzg/s72-c/flex-build-path-properties.jpg' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29871611.post-115092064454463209</id><published>2006-06-21T16:09:00.002-04:00</published><updated>2009-07-01T20:09:36.358-04:00</updated><title type='text'>Let's get started</title><content type='html'>This blog becomes a successor to my &lt;a href="http://jroller.com/ndpar/"&gt;jroller&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29871611-115092064454463209?l=ndpar.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://ndpar.blogspot.com/feeds/115092064454463209/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29871611&amp;postID=115092064454463209' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/115092064454463209'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29871611/posts/default/115092064454463209'/><link rel='alternate' type='text/html' href='http://ndpar.blogspot.com/2006/06/lets-start.html' title='Let&apos;s get started'/><author><name>Andrey Paramonov</name><uri>http://www.blogger.com/profile/14375033587137190103</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_R95qtkBZFng/S6F_TrOwzhI/AAAAAAAAD-I/uvp5eoA--ew/S220/me3.jpg'/></author><thr:total>0</thr:total></entry></feed>
