<?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:blogger='http://schemas.google.com/blogger/2008' 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-123791467816537526</id><updated>2025-02-20T04:00:53.738-08:00</updated><category term="java"/><category term="Data Structure"/><category term="LRU Cache"/><category term="Nutch"/><category term="OAuth"/><category term="TDD"/><category term="mysql"/><category term="CollectionHelper.EMPTY_LIST"/><category term="Facebook"/><category term="Heap"/><category term="Intro: Zookeeper"/><category term="JVM translation"/><category term="Nutch Parsing"/><category term="Parser"/><category term="PriorityQueue"/><category term="Trie"/><category term="WebappClassLoader memory leaks"/><category term="ZooKeeper Leader election and group membership"/><category term="computing next business day in sql."/><category term="configuration management using zookeeper"/><category term="custom plugin"/><category term="data import"/><category term="data migration"/><category term="deadlock"/><category term="debug"/><category term="eclipse"/><category term="elasticsearch"/><category term="es"/><category term="extending LinkedHashMap"/><category term="framework design flaw"/><category term="generics"/><category term="hacky solution to kill log4j FileWatchdog thread."/><category term="hadoop configuration."/><category term="hadoop mapper"/><category term="hibernate"/><category term="hibernate bug"/><category term="how not to design a framework"/><category term="index migration"/><category term="jetty"/><category term="junit"/><category term="linux"/><category term="livelock"/><category term="maven"/><category term="mysql query optimization"/><category term="mysql remote connection"/><category term="mysql tcp/ip connection."/><category term="nodejs"/><category term="plugin"/><category term="relational database import in solr"/><category term="service discovery"/><category term="solr tomcat setup"/><category term="spring"/><category term="starvation"/><category term="stored procedure"/><category term="test"/><category term="test private methods in java"/><category term="testing web controllers with spring"/><category term="troubleshooting passphrase less ssh"/><category term="unit testing for hadoop mapper."/><category term="zookeeper"/><title type='text'>Web problems and solutions.</title><subtitle type='html'>Finding simple yet elegant, time constrained solutions to technical problems.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://amrutbudihal.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://amrutbudihal.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default?start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>32</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-123791467816537526.post-782202928362847105</id><published>2016-06-25T22:23:00.000-07:00</published><updated>2016-06-26T08:31:16.012-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="data migration"/><category scheme="http://www.blogger.com/atom/ns#" term="elasticsearch"/><category scheme="http://www.blogger.com/atom/ns#" term="es"/><category scheme="http://www.blogger.com/atom/ns#" term="index migration"/><category scheme="http://www.blogger.com/atom/ns#" term="nodejs"/><title type='text'>Elasticsearch data migration</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Lately, I have been working on migrating from &lt;a href=&quot;https://www.elastic.co/&quot;&gt;elasticsearch&lt;/a&gt; 0.19 to 1.7 version. One of the tasks involves migrating data from old to new cluster. &amp;nbsp;In this post, I write about a tool for migrating the data over to the new version.&lt;br /&gt;
&lt;br /&gt;
Firstly, you cannot move the index files directly, attempting results in following error in 1.7 ES:&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Error injecting constructor, org.elasticsearch.ElasticsearchIllegalStateException: Could not find a state file to recover from among&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;Elasticsearch 0.19 version is ancient and not compatible for a direct migration. Hence the only solution is to read it from the old elasticsearch cluster and write to new elastic.&lt;br /&gt;
&lt;br /&gt;
Followed a simple guideline while coming up for the tool:&lt;br /&gt;
&lt;ol style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;Primary goal: &quot;Just move the data over to new version!&quot;&lt;/li&gt;
&lt;li&gt;Nice to haves: &quot;Monitor its progress&quot;&lt;/li&gt;
&lt;/ol&gt;
A bare bone version of it is here in &lt;a href=&quot;https://github.com/amrutbudihal/elastic-data-migrator&quot;&gt;github&lt;/a&gt;&amp;nbsp;capturing the essence of this migration. Primary goal is achieved by using &lt;a href=&quot;https://www.npmjs.com/package/elasticsearch&quot;&gt;Nodejs elasticsearch client library&lt;/a&gt;&amp;nbsp;where given a index name, migrates based on created field in the document. A assumption being made that a document with timestamp is present but the solution can be used for any increasing sequence. Secondary goal is achieved by a in memory map which stores and updates the index status.&lt;br /&gt;
&lt;br /&gt;
Checkout the &lt;a href=&quot;https://github.com/amrutbudihal/elastic-data-migrator/blob/master/README.md&quot;&gt;README&lt;/a&gt; on github to set it up locally.&lt;br /&gt;
&lt;br /&gt;
Whats pending:&lt;br /&gt;
A nice user experience.&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://amrutbudihal.blogspot.com/feeds/782202928362847105/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/123791467816537526/782202928362847105' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/782202928362847105'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/782202928362847105'/><link rel='alternate' type='text/html' href='http://amrutbudihal.blogspot.com/2016/06/elasticsearch-data-migration.html' title='Elasticsearch data migration'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-123791467816537526.post-7521847409597146617</id><published>2015-08-28T10:39:00.000-07:00</published><updated>2015-08-28T21:06:49.545-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="service discovery"/><category scheme="http://www.blogger.com/atom/ns#" term="zookeeper"/><title type='text'>Service continuity using Zookeeper</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
In service oriented architecture where one service is dependent on others, the challenges one would face is how to upgrade one service without coming to a grinding halt.&lt;br /&gt;
&lt;br /&gt;
Consider two servers (essentially both are services providing a broader service) A and B, with A depending on B. Lets assume for simplicity service B exposes REST api&#39;s to be consumed by service A.&lt;br /&gt;
&lt;br /&gt;
Old way: A and B running on the same physical machine. This setup is replicated on number of machines.&lt;br /&gt;
&lt;br /&gt;
Below are some of the concerns.&lt;br /&gt;
&lt;ol style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;How to guarantee service continuity during software upgrade? Lets say B needs to be upgraded, should be halt service provided A? This maybe acceptable if A&#39;s service is not that critical and a downtime is acceptable.&lt;/li&gt;
&lt;li&gt;How to avoid single point of failure? Lets say B stops responding, what should A do? Retry later? What should be the interval of retry?&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;
Why it didnt work for us?&lt;br /&gt;
We had a chain of services where customer downtime was not acceptable. The replication on different machines handled the downtime part in a hacky fashion where customer facing service was load balanced but didn&#39;t address the single point of failure issue. Also this was not efficient utilization of resource. Consider a case where we need to scale up, in that case, we would have to add the entire stack.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
New solution: Store B server state information in Zookeeper and let A discover B through Zookeeper. Curator&#39;s &lt;a href=&quot;http://curator.apache.org/curator-x-discovery&quot;&gt;service discovery&lt;/a&gt; framework has a nice library which can be used for the same. Store the hostname, ip address, port&amp;nbsp;(state information) when starting the service and remove this information during service stop.&lt;br /&gt;
&lt;br /&gt;
Next steps: Will see if I can hack together a bare bones code and post a link here.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://amrutbudihal.blogspot.com/feeds/7521847409597146617/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/123791467816537526/7521847409597146617' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/7521847409597146617'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/7521847409597146617'/><link rel='alternate' type='text/html' href='http://amrutbudihal.blogspot.com/2015/08/service-continuity-using-zookeeper.html' title='Service continuity using Zookeeper'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-123791467816537526.post-2142179929142238112</id><published>2012-10-01T16:54:00.002-07:00</published><updated>2012-10-01T16:54:46.400-07:00</updated><title type='text'>A little caution with &quot;Mysql&#39;s Timestamp&quot;</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
In this post, I would like to bookkeep a interesting fact on Timestamp.&lt;br /&gt;
&lt;br /&gt;
Fundamentally, Mysql &lt;a href=&quot;http://dev.mysql.com/doc/refman/5.1/en/datetime.html&quot;&gt;Timestamp &lt;/a&gt;is nothing but seconds from epoch and differs from datetime as it stores the date time in UTC and abstracts the date time conversion to system&#39;s timezone. Also it supports auto-initialization (&#39;CURRENT_TIMESTAMP or 0000-00-00 00:00:00&#39;) and auto-updates (CURRENT_TIMESTAMP).&lt;br /&gt;
&lt;br /&gt;
Now the thing which one has to understand is passing &quot;null&quot; references to Timestamp column coupled with not null references and hence want to emphasize it in this post.&lt;br /&gt;
&lt;br /&gt;
Consider a case where we create a table with definition of modified column as follows:&lt;br /&gt;
&lt;i&gt;&lt;b&gt;create table demo_null_passing_to_timestamp(...., modified timestamp not null default &#39;&lt;/b&gt;&lt;/i&gt;&lt;i&gt;&lt;b&gt;0000-00-00 00:00:00&#39;)&lt;/b&gt;&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
The behavior for &quot;modified&quot; column will be as one expects if there is no value passed in to modified column, it will use the default value of &lt;i&gt;&lt;b&gt;0000-00-00 00:00:00&lt;/b&gt;&lt;/i&gt;, but the behavior changes when null is passed in this case because of the &quot;not null&quot; constraint. In this case, it uses the value from &lt;b&gt;current timestamp&lt;/b&gt;. The only&lt;i&gt;&lt;b&gt; &lt;/b&gt;&lt;/i&gt;way is to allow &lt;i&gt;&lt;b&gt;null&lt;/b&gt;&lt;/i&gt; value for this modified column.&lt;br /&gt;
&lt;br /&gt;
Try passing &lt;i&gt;&lt;b&gt;null&lt;/b&gt;&lt;/i&gt; value to such a column to verify this behavior...&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://amrutbudihal.blogspot.com/feeds/2142179929142238112/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/123791467816537526/2142179929142238112' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/2142179929142238112'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/2142179929142238112'/><link rel='alternate' type='text/html' href='http://amrutbudihal.blogspot.com/2012/10/a-little-caution-with-mysqls-timestamp.html' title='A little caution with &quot;Mysql&#39;s Timestamp&quot;'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-123791467816537526.post-3746813782994010363</id><published>2012-07-20T18:28:00.001-07:00</published><updated>2012-07-20T23:10:58.627-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="configuration management using zookeeper"/><title type='text'>Configuration management Using ZooKeeper.</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Consider a case where dynamic reload of properties is required. An application can leverage ZooKeeper&#39;s service for providing such a capability.&lt;br /&gt;
&lt;br /&gt;
The concept is that of a Publish Subscribe model but spanned across mulitple VMs (either on same machine or different).&lt;br /&gt;
The solution is of two folds:&lt;br /&gt;
1) First one being a file change listener. This piece of code is responsible for detecting any change and updating the data stored in Zookeeper.&lt;br /&gt;
2) Set a watch from the client on this data node being changed.&lt;br /&gt;
&lt;br /&gt;
A quick demo should help clarify the concept.&lt;br /&gt;
1) zk-file-change-watcher.jar (executes java class &lt;a href=&quot;https://github.com/amrutbudihal/config-management/blob/master/src/main/java/com/mycompany/management/ZkFileChangeWatcher.java&quot;&gt;ZkFileChangeWatcher&lt;/a&gt;): All it does is creates node in Zookeeper data tree &quot;/CONFIG/DYNAMIC-FILE&quot; if it doesnt exist and sets a watch. If this node changes, ZooKeeper service will notify this client of the change which it takes action (prints to the screen). As seen in screenshot (Denoted by &quot;Waiting for changes&quot; messages in terminal 1 and 2 on L.H.S.)&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSEL_jAoPXhSds42o6iuO7RYGg6dcXF6eJadq30lTd8eOob3LZCngqPkiT3I9cRtKSAIdHjFvRdTSg3Okrdk7lfkYp-RzGPB-djjSJbVW4vpaRlbXfZcapGlVV1tafzbL13NAEg2kY7Xhv/s1600/Screen+Shot+2012-07-20+at+6.10.40+PM.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSEL_jAoPXhSds42o6iuO7RYGg6dcXF6eJadq30lTd8eOob3LZCngqPkiT3I9cRtKSAIdHjFvRdTSg3Okrdk7lfkYp-RzGPB-djjSJbVW4vpaRlbXfZcapGlVV1tafzbL13NAEg2kY7Xhv/s1600/Screen+Shot+2012-07-20+at+6.10.40+PM.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
2) fs-file-change-watcher.jar (executes java class &lt;a href=&quot;https://github.com/amrutbudihal/config-management/blob/master/src/main/java/com/mycompany/management/FSFileChangeWatcher.java&quot;&gt;FSFileChangeWatcher&lt;/a&gt;): 
This creates a file listener and checks (polling) every 5 seconds for file changes by 
checking its timestamp. If the file has changed, it reads data from the 
file and pushes that change to data node: /CONFIG/DYNAMIC-FILE created by #1. Note when this jar runs for the first time, it doesn&#39;t have last updated timestamp, hence it displays message &quot;Last modified timestamp changed&quot; and pushes latest data to the node. Terminal on the R.H.S. displays this execution of this jar.&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyubPjVzmYmXFQffvVCKyEeN-H74mEF8p3OInIgdP8UnJxASiHlHu3kVxOXgVYUCqvAbqlWhVnaEn774QMNlZ5cPsyRR-6ztKYcfFb53-JuIkYNzXeqiQflu6f9wO6mckYKSDfreNxotv_/s1600/Screen+Shot+2012-07-20+at+6.10.55+PM.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyubPjVzmYmXFQffvVCKyEeN-H74mEF8p3OInIgdP8UnJxASiHlHu3kVxOXgVYUCqvAbqlWhVnaEn774QMNlZ5cPsyRR-6ztKYcfFb53-JuIkYNzXeqiQflu6f9wO6mckYKSDfreNxotv_/s1600/Screen+Shot+2012-07-20+at+6.10.55+PM.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
3) When the file (dynamic-file.txt) changes, both the listeners created in #1 and #2 act accordingly and update is displayed to the clients.&lt;br /&gt;
Below screenshot displays just before updating the file (4 is being added to the file) in vi editor. Displayed in bottom left terminal.&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3fihLqlKM52SE0MwgmQBsSJdO9na5e2iMJP3jmdW7t30rn-LQcD-b3VYzWK3xcKCLtG3GIyg8NBx2l67CJiAgYw-9vddGICfoDFu5q-0XU_pFmFm0Oyev6UTDZ0VCDsQuoQS0U8c8D3Wx/s1600/Screen+Shot+2012-07-20+at+6.11.17+PM.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3fihLqlKM52SE0MwgmQBsSJdO9na5e2iMJP3jmdW7t30rn-LQcD-b3VYzWK3xcKCLtG3GIyg8NBx2l67CJiAgYw-9vddGICfoDFu5q-0XU_pFmFm0Oyev6UTDZ0VCDsQuoQS0U8c8D3Wx/s1600/Screen+Shot+2012-07-20+at+6.11.17+PM.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Below screenshot confirms the update received at the client in terminals #1 and #2. Terminal on right hand side displays File time stamp change message.&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLdIFLg128SUMhngTRkTLquKchJaq2IpBt3cHOJJK_9Lvub1yqcHMn_VZVLtaAr_E9w0oSDH21MariVS-PMDkH9hKzZRPoBA2vbor3OC2dx8Az5qPRxFXvEs_u2vmUUQhQNms558paotDj/s1600/Screen+Shot+2012-07-20+at+6.11.35+PM.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLdIFLg128SUMhngTRkTLquKchJaq2IpBt3cHOJJK_9Lvub1yqcHMn_VZVLtaAr_E9w0oSDH21MariVS-PMDkH9hKzZRPoBA2vbor3OC2dx8Az5qPRxFXvEs_u2vmUUQhQNms558paotDj/s1600/Screen+Shot+2012-07-20+at+6.11.35+PM.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://amrutbudihal.blogspot.com/feeds/3746813782994010363/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/123791467816537526/3746813782994010363' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/3746813782994010363'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/3746813782994010363'/><link rel='alternate' type='text/html' href='http://amrutbudihal.blogspot.com/2012/07/configuration-management-using-zookeeper.html' title='Configuration management Using ZooKeeper.'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSEL_jAoPXhSds42o6iuO7RYGg6dcXF6eJadq30lTd8eOob3LZCngqPkiT3I9cRtKSAIdHjFvRdTSg3Okrdk7lfkYp-RzGPB-djjSJbVW4vpaRlbXfZcapGlVV1tafzbL13NAEg2kY7Xhv/s72-c/Screen+Shot+2012-07-20+at+6.10.40+PM.png" height="72" width="72"/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-123791467816537526.post-6705620823628990004</id><published>2012-07-15T14:16:00.000-07:00</published><updated>2012-07-20T18:30:03.973-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Intro: Zookeeper"/><title type='text'>Zookeeper: Are you the HERO we were looking for?</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Recently I have been investigating on feasibility of using &lt;a href=&quot;http://zookeeper.apache.org/&quot;&gt;Zookeeper&lt;/a&gt; (super excited about it!!! :) ) for a existing platform.&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;&lt;b&gt;Existing Architecture:&lt;/b&gt;&lt;/i&gt;&lt;br /&gt;
Briefly, the module does a bunch of stuff from&lt;br /&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;Registering itself when instance is started. &lt;/li&gt;
&lt;li&gt;Work delegation among other instances through &lt;a href=&quot;http://en.wikipedia.org/wiki/Remote_procedure_call&quot;&gt;rpc&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Acquiring lock for doing work (note due to distributed nature, is it stored outside of VM)&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
I agree all this works just fine! Why is there a need to investigate alternatives? &lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;&lt;b&gt;Concerns:&lt;/b&gt;&lt;/i&gt; &lt;br /&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;Synchronous remote method invocation: When one instance delegates work to another, the request goes through &lt;a href=&quot;http://static.springsource.org/spring/docs/2.5.x/reference/remoting.html#remoting-httpinvoker&quot;&gt;Spring&#39;s HttpInvoker&lt;/a&gt; which under the hood uses apache commons &lt;a href=&quot;http://jakarta.apache.org/commons/httpclient&quot;&gt;HttpClient&lt;/a&gt;. Since the underlying methods are either a GET/POST, I believe the remote method invocation results in synchronous calls effectively blocked until results are retrieved. The remote calls are expensive ( atleast 4-5 times than a normal method invocation). More details can be found in the &lt;a href=&quot;http://research.sun.com/techrep/1994/smli_tr-94-29.pdf&quot;&gt;paper&lt;/a&gt;. Currently it works because there are two production instances invoking each other and it has to deal with transactions in the range 17-20k a day. Looking 6 months back we have doubled our processing volume, continuing that trend, in a years time we might be at 80k transactions a day. Will our current solution be sufficient for scale? &lt;/li&gt;
&lt;li&gt;Fail over / High availability: Both instances are parallel running and both try to do the same work. That is both are configured to run in Master mode. The way to do any work to avoid duplicate is by acquiring a shared lock. Thus High Availability is achieved through replication but it is not the right way to do it. In a ideal situation, they should be configured as Master/Slave with the ability to take over by Slave in case if Master fails.&lt;/li&gt;
&lt;li&gt;Locking:  Locks are granulated based on the type of work and are also classified into blocking/non-blocking locks. Concerning part is they are acquired and released everytime when a work is done. More instances, more of this, which will increase the load on database.&lt;/li&gt;
&lt;li&gt;Lastly, this allocation part makes the system highly coupled and confusing.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
It seems, Zookeeper can be used to provide Leader Election/ group membership, Distributed locking and is highly available. The &lt;a href=&quot;http://research.yahoo.com/node/3280&quot;&gt;original paper&lt;/a&gt; and &lt;a href=&quot;http://zookeeper.apache.org/doc/r3.1.2/recipes.html&quot;&gt;recipe&#39;s &lt;/a&gt;page gives a good overview of what &lt;a href=&quot;http://zookeeper.apache.org/&quot;&gt;Apache Zookeeper&lt;/a&gt; is.&lt;br /&gt;
&lt;br /&gt;
In my next blog, I demonstrate few features which can be used to correct above fallacies like &lt;a href=&quot;http://amrutbudihal.blogspot.com/2012/07/zookeeper-leader-election-and-group.html&quot;&gt;Leader Election&lt;/a&gt;, &lt;a href=&quot;http://amrutbudihal.blogspot.com/2012/07/configuration-management-using-zookeeper.html&quot;&gt;Configuration management&lt;/a&gt;, Distributed Locking, Group Membership.&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://amrutbudihal.blogspot.com/feeds/6705620823628990004/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/123791467816537526/6705620823628990004' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/6705620823628990004'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/6705620823628990004'/><link rel='alternate' type='text/html' href='http://amrutbudihal.blogspot.com/2012/07/zookeeper-are-you-hero-we-were-looking.html' title='Zookeeper: Are you the HERO we were looking for?'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-123791467816537526.post-667306255825019749</id><published>2012-07-15T14:15:00.001-07:00</published><updated>2012-07-17T20:24:53.591-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ZooKeeper Leader election and group membership"/><title type='text'>Zookeeper: Leader Election and Group membership.</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Its been less than a week since I started playing with Zookeeper to understand how can it be used for our existing platform and in this post I would like to document its Leader Election feature.&lt;br /&gt;
&lt;br /&gt;
Used the algorithm pointed out in the &lt;a href=&quot;http://zookeeper.apache.org/doc/r3.1.2/recipes.html#sc_leaderElection&quot;&gt;recipe page&lt;/a&gt;. Few key things to note:&lt;br /&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;ZooKeeper is analogous to a file system where each &lt;a href=&quot;http://zookeeper.apache.org/doc/r3.1.2/zookeeperOver.html#Nodes+and+ephemeral+nodes&quot;&gt;Node&lt;/a&gt; can be thought of either a DIRECTORY (intermediate nodes which contains file or subdirectory) or FILE (leaf nodes) with root node denoted as unix style &quot;/&quot;.&lt;/li&gt;
&lt;li&gt;Make the nodes         EPHEMERAL and SEQUENTIAL. This can be easily achieved by passing CreateMode.EPHEMERAL_SEQUENTIAL while create a node using create() method.&lt;/li&gt;
&lt;li&gt;Implement &lt;a href=&quot;http://zookeeper.apache.org/doc/r3.1.2/api/org/apache/zookeeper/Watcher.html&quot;&gt;Watcher Interface&lt;/a&gt;. Think of Watcher objects as Listeners which register with ZooKeeper for listening on an event.&lt;/li&gt;
&lt;li&gt;Following diagram should help understand the concept visually.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEEDGvaJw8gFoj2z7b-x8ysv6NrvA8Ll2T_iNWjxJADaYp7tcoPeA0-HxJXkl63Q4J0ppZTa4ygE_QLtFOAga2gEKaxKJ95sUKsF3zEFqwfgapodYsSw9ApaR5APw7_8-LlpUJGS8q-uL5/s1600/Screen+Shot+2012-07-13+at+5.31.08+PM.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEEDGvaJw8gFoj2z7b-x8ysv6NrvA8Ll2T_iNWjxJADaYp7tcoPeA0-HxJXkl63Q4J0ppZTa4ygE_QLtFOAga2gEKaxKJ95sUKsF3zEFqwfgapodYsSw9ApaR5APw7_8-LlpUJGS8q-uL5/s1600/Screen+Shot+2012-07-13+at+5.31.08+PM.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
The algorithm is self explanatory along with &lt;a href=&quot;https://github.com/amrutbudihal/leader-election/&quot;&gt;commented code&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Lets move to a working example. Compiled the code into jar and ran as independent processes. &amp;nbsp; The bottom right window in the screenshot is the zookeeper server&#39;s view, connected through its command line interface (zkCli.sh)&lt;br /&gt;
&lt;br /&gt;
1) Initial state:&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEih_jxuoyTSk5l0MjvwPlS9eehRugmEtaehH87d-Ec0gXFm7POYqJ2CUnaHZK6utzXbQHdWN1THJIjlmPne4OVkouXtNfI4S2thLP23gYMUiFaPxTChRBNgqfFVMtHhgzTIuTTpxMHxl8Ng/s1600/Screen+Shot+2012-07-13+at+6.18.38+PM.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEih_jxuoyTSk5l0MjvwPlS9eehRugmEtaehH87d-Ec0gXFm7POYqJ2CUnaHZK6utzXbQHdWN1THJIjlmPne4OVkouXtNfI4S2thLP23gYMUiFaPxTChRBNgqfFVMtHhgzTIuTTpxMHxl8Ng/s1600/Screen+Shot+2012-07-13+at+6.18.38+PM.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
2) When first node connects (First node, NODE-0000000060 connects to Zookeeper server)&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLhbQRebTrzADy3CUWoLGXU1gRm4cLBnElQrLH1QYtLPbzvWhq305kboRz_0-ue_F0rQ0OZePdD8AutWv6fHtQNG46U0hvaUWXPT3y6IiU4w2ZuqHRpxFR42jfXXjjbjdKyGAMVrTPI81O/s1600/Screen+Shot+2012-07-13+at+6.19.04+PM.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLhbQRebTrzADy3CUWoLGXU1gRm4cLBnElQrLH1QYtLPbzvWhq305kboRz_0-ue_F0rQ0OZePdD8AutWv6fHtQNG46U0hvaUWXPT3y6IiU4w2ZuqHRpxFR42jfXXjjbjdKyGAMVrTPI81O/s1600/Screen+Shot+2012-07-13+at+6.19.04+PM.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
3) Similarly, second and third instance connect to zookeeper server. (Notice the list commands issued in the bottom right window confirms that they have registered and NODE-0000000061 is following 60, NODE-0000000062 is following 61)&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbbDVy0PlvQa9K30RZeV_w8UAU29eKQK2HoAwCWP9IqNVh1pQapRWSdLuE5sXdJdiAXGp-_7Jqv690y4DmZtw8Zs0Qgu_takJv0IKbZpFfvSYXwcBbfRQgsPlGqPcd9gxF8LPBA1WNjS_E/s1600/Screen+Shot+2012-07-13+at+6.20.09+PM.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbbDVy0PlvQa9K30RZeV_w8UAU29eKQK2HoAwCWP9IqNVh1pQapRWSdLuE5sXdJdiAXGp-_7Jqv690y4DmZtw8Zs0Qgu_takJv0IKbZpFfvSYXwcBbfRQgsPlGqPcd9gxF8LPBA1WNjS_E/s1600/Screen+Shot+2012-07-13+at+6.20.09+PM.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
4) Leader, NODE-0000000060 is killed. Notice, how the NODE-0000000061 follower takes up Leader&#39;s responsibility.&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZEf2bVzIzFZ_5l_fMkUR37uPQMJo3WoFfkPuhddxN4CcYixyfdtFaz18gjFPd18nxq3RJL8cPloqu9jJ1MK72T4CY7BDsXkB8Oi4WpJv4iplxqeVJTZgQIGXfvnxVHUVFWZBRoQ5dTvP_/s1600/Screen+Shot+2012-07-13+at+6.20.35+PM.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZEf2bVzIzFZ_5l_fMkUR37uPQMJo3WoFfkPuhddxN4CcYixyfdtFaz18gjFPd18nxq3RJL8cPloqu9jJ1MK72T4CY7BDsXkB8Oi4WpJv4iplxqeVJTZgQIGXfvnxVHUVFWZBRoQ5dTvP_/s1600/Screen+Shot+2012-07-13+at+6.20.35+PM.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
5) Resurrecting the process in Top left window, notice it now becomes a follower, NODE-0000000064 and starts following node, NODE-0000000062.&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinWQsfwOKQhGY3QpToKMUVNd9TzFKpqZNS3r6rkqp6nCH9M-zjb-AS2gSprniwzYOR7uXeDAqVWmBDSMwSZ3yu8XBrf0Xl8LclWU8zrAkLnj1eyszF89HTxF4RaXI41jp984j58oODUeSd/s1600/Screen+Shot+2012-07-13+at+6.21.00+PM.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinWQsfwOKQhGY3QpToKMUVNd9TzFKpqZNS3r6rkqp6nCH9M-zjb-AS2gSprniwzYOR7uXeDAqVWmBDSMwSZ3yu8XBrf0Xl8LclWU8zrAkLnj1eyszF89HTxF4RaXI41jp984j58oODUeSd/s1600/Screen+Shot+2012-07-13+at+6.21.00+PM.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
6) Kill a intermediate follower (process which is not a leader nor follower but an in between node which forms a logical Watcher link. Note reelection should be initiated to fix the broken link so that Leader responsibilities can be passed to the last remaining node if all previous nodes happen to crash. In the following screenshot the intermediate node, NODE-00000062 is between the leader, NODE-00000061 and node, NODE-00000064. If the intermediate node crashes, the node, NODE-00000064 starts following the leader, NODE-00000061. Also Node, NODE-00000064 becomes leader when leader, NODE-00000064 crashes.&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaUa4V5aHi7m1bXFJCrsV4OYEnEaf6UIJZja58GEJ7XN1B6VzL2XmBysmT3wlDtqhqU8Ozp01vOsyrBxm5cbyRqO9Y3YTTGXNyy_zqg-hOM1-_7aoNGnIb3XycvOoLK2VxHJYYiO7HJo1i/s1600/Screen+Shot+2012-07-13+at+6.22.14+PM.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaUa4V5aHi7m1bXFJCrsV4OYEnEaf6UIJZja58GEJ7XN1B6VzL2XmBysmT3wlDtqhqU8Ozp01vOsyrBxm5cbyRqO9Y3YTTGXNyy_zqg-hOM1-_7aoNGnIb3XycvOoLK2VxHJYYiO7HJo1i/s1600/Screen+Shot+2012-07-13+at+6.22.14+PM.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Build a jar (leader-election.jar) using the &lt;a href=&quot;https://github.com/amrutbudihal/leader-election/&quot;&gt;source code&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Thus leader election and group management can be easily implemented using ZooKeeper. Next moving on to Distributed Locking using ZooKeeper.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://amrutbudihal.blogspot.com/feeds/667306255825019749/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/123791467816537526/667306255825019749' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/667306255825019749'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/667306255825019749'/><link rel='alternate' type='text/html' href='http://amrutbudihal.blogspot.com/2012/07/zookeeper-leader-election-and-group.html' title='Zookeeper: Leader Election and Group membership.'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEEDGvaJw8gFoj2z7b-x8ysv6NrvA8Ll2T_iNWjxJADaYp7tcoPeA0-HxJXkl63Q4J0ppZTa4ygE_QLtFOAga2gEKaxKJ95sUKsF3zEFqwfgapodYsSw9ApaR5APw7_8-LlpUJGS8q-uL5/s72-c/Screen+Shot+2012-07-13+at+5.31.08+PM.png" height="72" width="72"/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-123791467816537526.post-1120397172183441892</id><published>2012-07-08T21:52:00.002-07:00</published><updated>2012-07-09T10:29:12.531-07:00</updated><title type='text'>Conditional Probability</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
I was trying to rekindle my interest over the weekend in Text Classification and hence wanted to revisit my probability skills. I would like to document it so in future I don&#39;t have to spend time understanding the basics.&lt;br /&gt;
&lt;br /&gt;
Simply stated, conditional probability is given an event &quot;A&quot; what is the chance another event &quot;B&quot; can occur. As with anything, this statement can be further explained in depth with a example.&lt;br /&gt;
&lt;br /&gt;
Consider 5 marbles (2 red, 3 blue), what it the probability of having 2 blue marbles taken from a bag in consecutive attempts. Key thing to understand is the second event is dependent on the first. The first event should draw a blue marble, followed by the second marble drawn to be blue.&lt;br /&gt;
&lt;br /&gt;
So the chance that first event of drawing blue ball is 2/5. Now, the remaining marbles in the bag are 4, hence the chance of drawing another blue ball is 1/4. Conditional probability is multiplying the dependent events, hence we get: (2/5) * (1/4) = (1/10).&lt;br /&gt;
&lt;br /&gt;
Thus the chance of having two blue balls 10%.&lt;br /&gt;
&lt;br /&gt;
More at:&lt;br /&gt;
1) http://www.mathsisfun.com/data/probability-events-conditional.html&lt;br /&gt;
2) &lt;a href=&quot;http://www.mathsisfun.com/data/probability-shared-birthday.html&quot;&gt;Shared Birthday puzzle.&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
How is this related to Document Text Classification?&lt;br /&gt;
&lt;b&gt;What is the chance that a given document belongs to a classification (good/bad/unknown)?&lt;/b&gt; &lt;br /&gt;
I have been progressing through Programming Collective Intelligence and in Chapter 6, Document Classification has a nice description on how to find one!&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://amrutbudihal.blogspot.com/feeds/1120397172183441892/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/123791467816537526/1120397172183441892' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/1120397172183441892'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/1120397172183441892'/><link rel='alternate' type='text/html' href='http://amrutbudihal.blogspot.com/2012/07/conditional-probability.html' title='Conditional Probability'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-123791467816537526.post-7232923532816896409</id><published>2012-07-05T18:15:00.000-07:00</published><updated>2012-07-05T23:07:10.627-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="hacky solution to kill log4j FileWatchdog thread."/><category scheme="http://www.blogger.com/atom/ns#" term="WebappClassLoader memory leaks"/><title type='text'>Avoiding WebappClassLoader memory leaks.</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Recently, I have been working to solve memory leak issues of our Integration web application and would like to document my findings.&lt;br /&gt;
&lt;br /&gt;
Issue is WebappClassLoader object is not de-referenced when the application context is destroyed and hence cannot be garbage collected and as a result all objects which are referenced from WebappClassLoader reside in memory (to be precise in PermGenSpace part of the heap)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Following are interesting links about this issue:&lt;br /&gt;
1) &lt;a href=&quot;http://frankkieviet.blogspot.com/2006/10/classloader-leaks-dreaded-permgen-space.html&quot;&gt;Frank Kieviet&#39;s blog&lt;/a&gt;.&lt;br /&gt;
2) &lt;a href=&quot;http://www.infoq.com/presentations/Diagnosing-Memory-Leaks&quot;&gt;A Nice tutorial on diagnosing memory leak.&lt;/a&gt;&lt;br /&gt;
3)&lt;a href=&quot;http://stackoverflow.com/questions/6470651/creating-a-memory-leak-with-java&quot;&gt; Creating memory leaks in java.&lt;/a&gt;&lt;br /&gt;
4) &lt;a href=&quot;http://www.javaworld.com/javaworld/jw-10-1996/jw-10-indepth.html&quot;&gt;Class loader in deep.&lt;/a&gt;&lt;br /&gt;
5) &lt;a href=&quot;http://docs.oracle.com/javase/specs/&quot;&gt;As always, Java language specification is a great guide.&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
It turns out, the web application had a couple of issues:&lt;br /&gt;
1) Static final object reference: Static means only one object can persist in memory and making that object final makes it even harder to make it available for garbage collection by &quot;&lt;b&gt;nulling the reference&lt;/b&gt;&quot;.&lt;br /&gt;
&lt;b&gt;Issue:&lt;/b&gt; The application sneaky created a reference to this Log4j object through FileWatchdog (instantiated through PropertyConfigurer). Because these object references are maintained in HashTable (object &quot;ht&quot;) inside Log4j library&#39;s Hierarchy class restarting the web application didn&#39;t clear this reference as it was also referenced from a thread which was never stopped. More details at #4 below.&lt;br /&gt;
&lt;b&gt; Solution&lt;/b&gt;: Removed static keyword on the object because there was only one object of the class ever to exist. It worked for us, it might not for you, take a look at the &lt;a href=&quot;http://static.springsource.org/spring/docs/3.0.x/api/org/springframework/web/util/Log4jConfigListener.html&quot;&gt;Log4jConfigListener&lt;/a&gt; (define it in web.xml before other listeners) for another solution. This manages Log objects when the during the life cycle of the web application.&lt;br /&gt;
&lt;br /&gt;
2) There were threads which were started during web application startup but never stopped. This prevented from WebappClassLoader garbage collection. The code was not properly architected, even the InterruptedException was swallowed by the thread and continued processing indefinately.&lt;br /&gt;
&lt;b&gt;Solution&lt;/b&gt;: Making the thread response to interrupts and return on InterruptedException.&lt;br /&gt;
&lt;br /&gt;
3) A module in the web application fires jobs based on cron expression at regular intervals. The &lt;a href=&quot;http://static.springsource.org/spring/docs/2.5.6/api/org/springframework/scheduling/quartz/SchedulerFactoryBean.html#destroy%28%29&quot;&gt;SchedulerFactoryBean.destroy()&lt;/a&gt; was never called when the context was destroyed. &lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;Solution&lt;/b&gt;: Modified the spring bean with destroy-method=&quot;destroy&quot; in the application context spring bean definition. Note, this might not work for older version of spring as discussed &lt;a href=&quot;http://forum.springsource.org/showthread.php?34672-Quartz-doesn-t-shutdown/page2&quot;&gt;here&lt;/a&gt;. In this case, use the solution discussed of writing a your servlet listener which destroys the quartz classes when the app context is destroyed.&lt;br /&gt;
&lt;br /&gt;
4) FileWatchDog Thread in Log4j: This is most serious of all issues because there is no solution yet to this. This thread is created when dynamic loading of log4j.properties without having to re-starting web container (in our case, it is tomcat), by PropertyConfigurer.configureAndWatch. (note, every invocation of this method creates a new thread)&lt;br /&gt;
&lt;b&gt;Issue: &lt;/b&gt;The code doesn&#39;t take into account interruption (InterruptedException is swallowed), no way to shutdown this thread.&lt;br /&gt;
&amp;nbsp;From &lt;a href=&quot;http://stackoverflow.com/questions/2115900/is-it-possible-to-reload-log4j-xml-log4j-properties-file-dynamically-in-tomcat&quot;&gt;stackoverflow&lt;/a&gt;, some solutions can be concluded but they wont work unless VM shuts down. The reason being, there is no way to get to this thread (i.e. PropertyConfigurer does not have a reference of this instantiated thread)&lt;br /&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;Using LogManager.shutdown(), this method does shutdown every Log appenders and Category associated with the Root but not FileWatchDog thread.&lt;/li&gt;
&lt;li&gt;Defining &lt;a href=&quot;http://static.springsource.org/spring/docs/3.0.x/api/org/springframework/web/util/Log4jConfigListener.html&quot;&gt;Log4jConfigListener &lt;/a&gt;in web.xml doesnt help because when the context is destroyed, internally it calls LogManager.shutdown() (through Log4jWebConfigurer.shutdownLogging)&lt;/li&gt;
&lt;/ul&gt;
&lt;b&gt;Solution&lt;/b&gt;&lt;br /&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;I have to admit that not using the dynamic reloading of log files in tomcat is the easiest!&lt;/li&gt;
&lt;li&gt;More hacky way, the thread FileWatchdog, repeatedly executes the method: checkAndConfigure(). Below is the code snippet:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;brush:java; title: ; notranslate&quot; title=&quot;&quot;&gt;protected
&amp;nbsp; void checkAndConfigure() {
&amp;nbsp;&amp;nbsp;&amp;nbsp; boolean fileExists;
&amp;nbsp;&amp;nbsp;&amp;nbsp; try {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; fileExists = file.exists();
&amp;nbsp;&amp;nbsp;&amp;nbsp; } catch(SecurityException&amp;nbsp; e) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; LogLog.warn(&quot;Was not allowed to read check file existance, file:[&quot;+
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; filename+&quot;].&quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; interrupted = true; // there is no point in continuing
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }

&amp;nbsp;&amp;nbsp;&amp;nbsp; if(fileExists) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; long l = file.lastModified(); // this can also throw a SecurityException
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if(l &amp;gt; lastModif) {&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // however, if we reached this point this
&amp;nbsp;&amp;nbsp;&amp;nbsp; lastModif = l;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // is very unlikely.
&amp;nbsp;&amp;nbsp;&amp;nbsp; doOnChange();
&amp;nbsp;&amp;nbsp;&amp;nbsp; warnedAlready = false;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp; } else {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if(!warnedAlready) {
&amp;nbsp;&amp;nbsp;&amp;nbsp; LogLog.debug(&quot;[&quot;+filename+&quot;] does not exist.&quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp; warnedAlready = true;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp; }
&amp;nbsp;
&lt;/pre&gt;
If the file permission just before context destroyed (maybe define a custom listener) was made read only, the method checkAndConfigure will throw a SecurityException which causes the boolean &quot;interrupted&quot; to set and hence will break the loop. Dont forget to reset the file persmission on context initialization.&lt;br /&gt;
&lt;br /&gt;
We went ahead with the first solution as dynamic reload didn&#39;t add much value but hopefully the second solution might help someone...&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://amrutbudihal.blogspot.com/feeds/7232923532816896409/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/123791467816537526/7232923532816896409' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/7232923532816896409'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/7232923532816896409'/><link rel='alternate' type='text/html' href='http://amrutbudihal.blogspot.com/2012/07/avoiding-webappclassloader-memory-leaks.html' title='Avoiding WebappClassLoader memory leaks.'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-123791467816537526.post-3294030747462567291</id><published>2012-05-29T14:32:00.001-07:00</published><updated>2012-05-30T14:01:16.770-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="JVM translation"/><title type='text'>Jvm translations which are easy to overlook.</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Recently I came across a code which gave a false sense of synchronization guarantee. Also this code reminded me how easy it is to forget about JVM byte code translations.&lt;br /&gt;
&lt;br /&gt;
Issue was a synchronized list was being populated in one thread and accessed through another. For simplicity reasons, following code could be treated as a similar example (few threads updates the list and others iterate over it):&lt;br /&gt;
&lt;pre class=&quot;brush:java; title: ; notranslate&quot; title=&quot;&quot;&gt;List&amp;lt;String&amp;gt; list = new ArrayList&amp;lt;String&amp;gt;();
list = Collections.synchronizedList(list);
list.add(&quot;a&quot;);
list.add(&quot;b&quot;);
list.add(&quot;c&quot;);
list.add(&quot;d&quot;);
list.add(&quot;e&quot;);
for (String temp: list) {
   System.out.println(&quot;value:&quot;+temp);
}
&lt;/pre&gt;
Do you think above code would give synchronization guarantee when iterating in the loop? No, it wont. I am not doubting the synchronization policy on list operations (add, get, delete, etc) but over its element iteration. To prove the case, get a decompiler (eg: jad) and from the class file reconstruct the java file. Below is the snapshot of the same.
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgcULn-Z81G1zRoYjyh_KDYhAopHSjQQAikFuiKTnGJqaGidMHwCzDQfwap6kcHcxxO2z4AizGFZS8AfJg2VX2x2JGhKcDvKgPy45gAHwHuk9mKx4ObR45VIjD2QV6Cdw7kx1sReKUNxr8L/s1600/Screen+Shot+2012-05-29+at+12.47.32+PM.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgcULn-Z81G1zRoYjyh_KDYhAopHSjQQAikFuiKTnGJqaGidMHwCzDQfwap6kcHcxxO2z4AizGFZS8AfJg2VX2x2JGhKcDvKgPy45gAHwHuk9mKx4ObR45VIjD2QV6Cdw7kx1sReKUNxr8L/s1600/Screen+Shot+2012-05-29+at+12.47.32+PM.png&quot; /&gt;&amp;nbsp;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
Above snapshot confirms that there is no synchronization taking place during iteration. Changed the code to following to enforce synchronization (alternatively can use JAVA 6, concurrent variant &lt;a href=&quot;http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/CopyOnWriteArrayList.html&quot;&gt;CopyOnWriteArrayList&lt;/a&gt;):
&lt;/div&gt;
&lt;pre class=&quot;brush: java; title: ; notranslate&quot; title=&quot;&quot;&gt;List&amp;lt;String&amp;gt; list = new ArrayList&amp;lt;String&amp;gt;();
list = Collections.synchronizedList(list);
list.add(&quot;a&quot;);
list.add(&quot;b&quot;);
list.add(&quot;c&quot;);
list.add(&quot;d&quot;);
list.add(&quot;e&quot;);
synchronized (list) {
  for (String temp: list) {
     System.out.println(&quot;value:&quot;+temp);
  }
}
&lt;/pre&gt;
Below snapshot confirms (decompiled version) the enforcement (check for &lt;b&gt;&lt;i&gt;monitorenter&lt;/i&gt;&lt;/b&gt;):&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVZjGxAk05wvLWkD7zJDXpwdRR37O0-_cVC0Z-qM9lzBqHyy0_IMaa1sCiltDO0F05gEfgTiaa3TwkFD0RpzeQJo_41mKTYYNxFfDuatqRixZo6TTYFDxkjvFSYotAuIJsO2CUN_AwpxgC/s1600/Screen+Shot+2012-05-29+at+2.14.59+PM.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVZjGxAk05wvLWkD7zJDXpwdRR37O0-_cVC0Z-qM9lzBqHyy0_IMaa1sCiltDO0F05gEfgTiaa3TwkFD0RpzeQJo_41mKTYYNxFfDuatqRixZo6TTYFDxkjvFSYotAuIJsO2CUN_AwpxgC/s1600/Screen+Shot+2012-05-29+at+2.14.59+PM.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Side effect would have caused list corruption (update by one thread while in use by another thread. Also this issue can also be confirmed through test case (Iterators are fail fast and with an unlucky timing would result in ConcurrentModificationException) &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;i&gt;Few noteworthy things besides the synchronization issue:&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
1) &quot;&lt;i&gt;&lt;b&gt;For-each Loop&lt;/b&gt;&lt;/i&gt;&quot; construct introduced in Java 5 gets translated into &lt;b&gt;&lt;i&gt;Iterator&lt;/i&gt;&lt;/b&gt;.&lt;br /&gt;
2) String concatenation using &quot;+&quot; operator gets translated to &lt;b&gt;&lt;i&gt;&quot;new StringBuilder.append()...append().toString()&quot;&lt;/i&gt;&lt;/b&gt;.&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://amrutbudihal.blogspot.com/feeds/3294030747462567291/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/123791467816537526/3294030747462567291' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/3294030747462567291'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/3294030747462567291'/><link rel='alternate' type='text/html' href='http://amrutbudihal.blogspot.com/2012/05/false-synchronization-guarantee-and-jvm.html' title='Jvm translations which are easy to overlook.'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgcULn-Z81G1zRoYjyh_KDYhAopHSjQQAikFuiKTnGJqaGidMHwCzDQfwap6kcHcxxO2z4AizGFZS8AfJg2VX2x2JGhKcDvKgPy45gAHwHuk9mKx4ObR45VIjD2QV6Cdw7kx1sReKUNxr8L/s72-c/Screen+Shot+2012-05-29+at+12.47.32+PM.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-123791467816537526.post-4847355881365621035</id><published>2012-05-10T09:59:00.000-07:00</published><updated>2012-05-10T09:59:06.786-07:00</updated><title type='text'>Using maven to configure a hadoop project.</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
In this post, I plan to describe how and benefits of using maven to configure a hadoop project.&lt;br /&gt;
&lt;br /&gt;
First comes the benefits: There are many described &lt;a href=&quot;http://maven.apache.org/benefits-of-using-maven.html&quot;&gt;here&lt;/a&gt;, but it benefited me in writing &lt;a href=&quot;http://amrutbudihal.blogspot.com/2012/05/unit-testing-mapper.html&quot;&gt;testcode&lt;/a&gt;, creating and deploying jars using one single command.&lt;br /&gt;
&lt;br /&gt;
Now the how part: The first thing is to be execute the following command which creates the project directory (along with package structure and pom.xml)&lt;br /&gt;
&lt;pre class=&quot;brush: xml; title: ; notranslate&quot; title=&quot;&quot;&gt;mvn archetype:generate -DgroupId=com.mycompany.example1 -DartifactId=example1 -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
&lt;/pre&gt;
&lt;br /&gt;
Notice a example1 project would be created (which matches articfactId) and a package hierarchy inside src/com/mycompany/example1 will be present (this matches the groupId)&lt;br /&gt;
&lt;br /&gt;
Now open the pom.xml inside example1 and the following inside &amp;lt;dependencies&amp;gt;&amp;lt;/dependencies&amp;gt; tags.&lt;br /&gt;
&lt;pre class=&quot;brush: xml; title: ; notranslate&quot; title=&quot;&quot;&gt;&amp;lt;dependency&amp;gt;
&amp;nbsp; &amp;lt;groupId&amp;gt;junit&amp;lt;/groupId&amp;gt;
&amp;nbsp; &amp;lt;artifactId&amp;gt;junit&amp;lt;/artifactId&amp;gt;
&amp;nbsp; &amp;lt;version&amp;gt;3.8.1&amp;lt;/version&amp;gt;
&amp;nbsp; &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
&amp;nbsp; &amp;lt;groupId&amp;gt;org.apache.hadoop&amp;lt;/groupId&amp;gt;
&amp;nbsp; &amp;lt;artifactId&amp;gt;hadoop-core&amp;lt;/artifactId&amp;gt;
&amp;nbsp; &amp;lt;version&amp;gt;1.0.1&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
&amp;nbsp; &amp;lt;groupId&amp;gt;org.jmock&amp;lt;/groupId&amp;gt;
&amp;nbsp; &amp;lt;artifactId&amp;gt;jmock-junit4&amp;lt;/artifactId&amp;gt;
&amp;nbsp; &amp;lt;version&amp;gt;2.5.1&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
&amp;nbsp; &amp;lt;groupId&amp;gt;org.jmock&amp;lt;/groupId&amp;gt;
&amp;nbsp; &amp;lt;artifactId&amp;gt;jmock-legacy&amp;lt;/artifactId&amp;gt;
&amp;nbsp; &amp;lt;version&amp;gt;2.5.1&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;&lt;/pre&gt;
&lt;br /&gt;
Specifying above dependencies inside POM.xml (project object model) will grab the required jars (in this case, will pull hadoop-core 1.0.1, junit 3.8.1, jmock 2.5.1 respectively from the maven repository)&lt;br /&gt;
&lt;br /&gt;
Now we are all set, using your favorite IDE, start your coding!&lt;br /&gt;
&lt;br /&gt;
Check the directory structure using maven dependency tree.&lt;br /&gt;
&lt;pre class=&quot;brush: xml; title: ; notranslate&quot; title=&quot;&quot;&gt;mvn dependency:tree &lt;/pre&gt;

The above project should be as follows
&lt;pre class=&quot;brush: xml; title: ; notranslate&quot; title=&quot;&quot;&gt;
[INFO] [dependency:tree {execution: default-cli}]
[INFO] com.xoom.example3:example3:jar:1.0-SNAPSHOT
[INFO] +- junit:junit:jar:3.8.1:test (scope not updated to compile)
[INFO] +- org.apache.hadoop:hadoop-core:jar:1.0.1:compile
[INFO] |  +- commons-cli:commons-cli:jar:1.2:compile
[INFO] |  +- xmlenc:xmlenc:jar:0.52:compile
[INFO] |  +- commons-httpclient:commons-httpclient:jar:3.0.1:compile
[INFO] |  |  \- commons-logging:commons-logging:jar:1.0.3:compile
[INFO] |  +- commons-codec:commons-codec:jar:1.4:compile
[INFO] |  +- org.apache.commons:commons-math:jar:2.1:compile
[INFO] |  +- commons-configuration:commons-configuration:jar:1.6:compile
[INFO] |  |  +- commons-collections:commons-collections:jar:3.2.1:compile
[INFO] |  |  +- commons-lang:commons-lang:jar:2.4:compile
[INFO] |  |  +- commons-digester:commons-digester:jar:1.8:compile
[INFO] |  |  |  \- commons-beanutils:commons-beanutils:jar:1.7.0:compile
[INFO] |  |  \- commons-beanutils:commons-beanutils-core:jar:1.8.0:compile
[INFO] |  +- commons-net:commons-net:jar:1.4.1:compile
[INFO] |  +- org.mortbay.jetty:jetty:jar:6.1.26:compile
[INFO] |  |  \- org.mortbay.jetty:servlet-api:jar:2.5-20081211:compile
[INFO] |  +- org.mortbay.jetty:jetty-util:jar:6.1.26:compile
[INFO] |  +- tomcat:jasper-runtime:jar:5.5.12:compile
[INFO] |  +- tomcat:jasper-compiler:jar:5.5.12:compile
[INFO] |  +- org.mortbay.jetty:jsp-api-2.1:jar:6.1.14:compile
[INFO] |  |  \- org.mortbay.jetty:servlet-api-2.5:jar:6.1.14:compile
[INFO] |  +- org.mortbay.jetty:jsp-2.1:jar:6.1.14:compile
[INFO] |  |  \- ant:ant:jar:1.6.5:compile
[INFO] |  +- commons-el:commons-el:jar:1.0:compile
[INFO] |  +- net.java.dev.jets3t:jets3t:jar:0.7.1:compile
[INFO] |  +- net.sf.kosmosfs:kfs:jar:0.3:compile
[INFO] |  +- hsqldb:hsqldb:jar:1.8.0.10:compile
[INFO] |  +- oro:oro:jar:2.0.8:compile
[INFO] |  +- org.eclipse.jdt:core:jar:3.1.1:compile
[INFO] |  \- org.codehaus.jackson:jackson-mapper-asl:jar:1.0.1:compile
[INFO] |     \- org.codehaus.jackson:jackson-core-asl:jar:1.0.1:compile
[INFO] +- org.jmock:jmock-junit4:jar:2.5.1:compile
[INFO] |  +- org.jmock:jmock:jar:2.5.1:compile
[INFO] |  |  +- org.hamcrest:hamcrest-core:jar:1.1:compile
[INFO] |  |  \- org.hamcrest:hamcrest-library:jar:1.1:compile
[INFO] |  \- junit:junit-dep:jar:4.4:compile
[INFO] \- org.jmock:jmock-legacy:jar:2.5.1:compile
[INFO]    +- org.objenesis:objenesis:jar:1.0:compile
[INFO]    \- cglib:cglib-nodep:jar:2.1_3:compile
&lt;/pre&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://amrutbudihal.blogspot.com/feeds/4847355881365621035/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/123791467816537526/4847355881365621035' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/4847355881365621035'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/4847355881365621035'/><link rel='alternate' type='text/html' href='http://amrutbudihal.blogspot.com/2012/05/using-maven-to-configure-hadoop-project.html' title='Using maven to configure a hadoop project.'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-123791467816537526.post-7537165975727112595</id><published>2012-05-09T13:58:00.000-07:00</published><updated>2012-05-09T14:00:28.955-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="unit testing for hadoop mapper."/><title type='text'>Unit testing Mapper.</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
In this post, I am writing a simple unit test using &lt;a href=&quot;http://www.jmock.org&quot;&gt;JMock&lt;/a&gt; for mapper mentioned &lt;a href=&quot;http://amrutbudihal.blogspot.com/2012/05/hadoop-mapper.html&quot;&gt;previously&lt;/a&gt;. Briefly, the test case is validating the key and value collected by the context object. In this test case, context object is mocked and hence expectations can be set on the object. Below code is self explanatory.

&lt;pre class=&quot;brush: java; title: ; notranslate&quot; title=&quot;&quot;&gt;
package com.mycompany.example3;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.integration.junit4.JUnit4Mockery;
import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class MapClassTest {

    private MapClass mapper;
    private Text text;
    private Mapper.Context context;
    private Mockery mockery = new JUnit4Mockery() {{
        setImposteriser(ClassImposteriser.INSTANCE);
    }};

    @Before
    public void setUp() throws Exception {
        mapper = new MapClass();
        context = mockery.mock(Mapper.Context.class);
        text = new Text(&quot;01-Apr-12 00:00:00,026&amp;nbsp; INFO  [scheduler_Worker-4] com.mycompany.sync.DistributedXipQuartzJobHandler.executeInternal(97) - Lock was acquired by other xip instance Quartz-lock [checkReversals] at 2012-04-01T00:00:00.026-07:00&quot;);
    }

    @Test
    public void testMap() throws Exception {
        mockery.checking(new Expectations() {
            {
                oneOf(context).write(with(new Text(&quot;scheduler_Worker-4&quot;)), with(new IntWritable(1)));
            }
        });

        mapper.map(new LongWritable(1), text, context);

        mockery.assertIsSatisfied();
    }
}

&lt;/pre&gt;

&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://amrutbudihal.blogspot.com/feeds/7537165975727112595/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/123791467816537526/7537165975727112595' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/7537165975727112595'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/7537165975727112595'/><link rel='alternate' type='text/html' href='http://amrutbudihal.blogspot.com/2012/05/unit-testing-mapper.html' title='Unit testing Mapper.'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-123791467816537526.post-6529920736191935074</id><published>2012-05-08T09:39:00.000-07:00</published><updated>2012-05-09T13:59:30.388-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="hadoop mapper"/><title type='text'>Hadoop Mapper.</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
In this post, I make note of hadoop mapper, which are datastore containing key value pairs, similar to &lt;a href=&quot;http://docs.oracle.com/javase/6/docs/api/java/util/Map.html&quot;&gt;Map&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
The basic thing to do while writing a mapper class is to extend &lt;a href=&quot;http://hadoop.apache.org/mapreduce/docs/current/api/org/apache/hadoop/mapreduce/Mapper.html&quot;&gt;Mapper&lt;/a&gt;. Note this class has type safety, hence pass in appropriate input and output key value pairs.&amp;nbsp; Now override the method, map. The context object stores the output key value pair. Hence any logic extraction to be done is inside this map method.&lt;br /&gt;
&lt;br /&gt;
An example: The mapper works over a log file which has following sample output:&lt;br /&gt;
01-Apr-12 00:00:00,026&amp;nbsp; INFO [scheduler_Worker-4] com.xoom.sync.DistributedXipQuartzJobHandler.executeInternal(97) - Lock was acquired by other xip instance Quartz-lock [bacCheckReversals] at 2012-04-01T00:00:00.026-07:00&lt;br /&gt;
The MapClass class&#39;s map method extracts anything between [], for the above the line it is scheduler_Worker-4 and stores the value in the context object as 1 (encapsulated in IntWritable object).&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: java; title: ; notranslate&quot; title=&quot;&quot;&gt;public static class MapClass extends Mapper&amp;lt;LongWritable, Text, Text, IntWritable&amp;gt; {
&amp;nbsp;&amp;nbsp; public void map(LongWritable key, Text value, Context context) throws InterruptedException,IOException {
&amp;nbsp;&amp;nbsp;&amp;nbsp; String valueString = value.toString();
&amp;nbsp;&amp;nbsp;&amp;nbsp; String[] parts = valueString.split(&quot; &quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp; //fifth element in parts array should contain name of the thread being executed.
&amp;nbsp;&amp;nbsp;&amp;nbsp; // treat it as key.
&amp;nbsp;&amp;nbsp;&amp;nbsp; if (parts.length &amp;gt; 4 &amp;amp;&amp;amp; parts[4] != null &amp;amp;&amp;amp; parts[4].matches(&quot;^\\[.+\\]$&quot;)) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; context.write(new Text(parts[4].replace(&quot;[&quot;,&quot;&quot;).replace(&quot;]&quot;,&quot;&quot;)),new IntWritable(1));
&amp;nbsp;&amp;nbsp;&amp;nbsp; } else {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.out.println(&quot;Value not collected by output collector:&quot;+valueString);
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp; }
&amp;nbsp; }

&lt;/pre&gt;
Next we move on to &lt;a href=&quot;http://amrutbudihal.blogspot.com/2012/05/unit-testing-mapper.html&quot;&gt;writing&lt;/a&gt; a simple Junit test case for the mapper class.&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://amrutbudihal.blogspot.com/feeds/6529920736191935074/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/123791467816537526/6529920736191935074' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/6529920736191935074'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/6529920736191935074'/><link rel='alternate' type='text/html' href='http://amrutbudihal.blogspot.com/2012/05/hadoop-mapper.html' title='Hadoop Mapper.'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-123791467816537526.post-4007424693786865894</id><published>2012-04-25T14:11:00.000-07:00</published><updated>2012-04-26T09:43:51.666-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="hadoop configuration."/><category scheme="http://www.blogger.com/atom/ns#" term="troubleshooting passphrase less ssh"/><title type='text'>Passpharse less Ssh troubleshooting for Hadoop cluster setup.</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
In this post, I want share my debugging knowledge gained while setting up passphrase less login. I needed this to setup a hadoop cluster. The instructions on the &lt;a href=&quot;http://wiki.apache.org/hadoop/Running_Hadoop_On_OS_X_10.5_64-bit_%28Single-Node_Cluster%29&quot;&gt;setting up hadoop&lt;/a&gt; are straight forward on how to generate one but it does not mention about troubleshooting. Hopefully this might serve one!&lt;br /&gt;
&lt;br /&gt;
Recently I have been working on hadoop for analyzing production logs. As a proof of concept, I wanted my hadoop job to be run in production like environment (instead of pseudo distributed environment). Hence was setting up pass-phrase less ssh. Generated the public key on the name node and copied this key into another remote machine. Following are screenshots of the steps.&lt;br /&gt;
&lt;br /&gt;
1) Generate the rsa private/public keys and copy to authorized_keys. This will help facilitate localhost passphrase less entry.&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyEz3DgCRLn55-qdYZGagFZqBn03Bwg-3P9jGQV_AIEN6U4SPhaNViXqeaIr3ZA_-EX6JHjp_CRLE9snosA3V7gKX_82bSwjciuYcZe7i3JqbGL829LHS6pZyAg7rV8Sgj_uXzBGHPAP3E/s1600/Screen+Shot+2012-04-25+at+1.06.14+PM.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyEz3DgCRLn55-qdYZGagFZqBn03Bwg-3P9jGQV_AIEN6U4SPhaNViXqeaIr3ZA_-EX6JHjp_CRLE9snosA3V7gKX_82bSwjciuYcZe7i3JqbGL829LHS6pZyAg7rV8Sgj_uXzBGHPAP3E/s1600/Screen+Shot+2012-04-25+at+1.06.14+PM.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
2)&amp;nbsp; Copy public file to the remote server.&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjm5UsmxwFAJyEbyOgsIU1iS6JbLo43_gbhaxLc-IlCkAwndVaa84VV-VSbb1BsqdtbIdA7-1RDF_aJR0gdBfzyhGBWH3VYADhP3IIAvTDZA__Dr1Fb1I7fNTn17oW8sKGVgzzIWvhQkbXN/s1600/Screen+Shot+2012-04-25+at+1.06.54+PM.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjm5UsmxwFAJyEbyOgsIU1iS6JbLo43_gbhaxLc-IlCkAwndVaa84VV-VSbb1BsqdtbIdA7-1RDF_aJR0gdBfzyhGBWH3VYADhP3IIAvTDZA__Dr1Fb1I7fNTn17oW8sKGVgzzIWvhQkbXN/s1600/Screen+Shot+2012-04-25+at+1.06.54+PM.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
3) Append this key inside this file into authorized_keys. Create authorized_keys file if it does not exist.&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzYOw8NKPsyaFWrQfK0jKSosp2O4xvfeGBOsgRaDxRScFPAIGMzRo09b5LdiwHcNm2yJeaHcARgrgajmmllqFbBNyaNkCP3zbNp9ahHpEK0HVt4MOjb-ZMk-X1R5rdhDWMxsmh6GgbWtH1/s1600/Screen+Shot+2012-04-25+at+1.07.27+PM.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzYOw8NKPsyaFWrQfK0jKSosp2O4xvfeGBOsgRaDxRScFPAIGMzRo09b5LdiwHcNm2yJeaHcARgrgajmmllqFbBNyaNkCP3zbNp9ahHpEK0HVt4MOjb-ZMk-X1R5rdhDWMxsmh6GgbWtH1/s1600/Screen+Shot+2012-04-25+at+1.07.27+PM.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
After above steps, I was expected the setup should work and I tried to login. Surprisingly it failed, as it asked me to enter the password. After spending hour or so trying to figure what could be wrong, I came across this &lt;a href=&quot;http://www.eng.cam.ac.uk/help/jpmg/ssh/authorized_keys_howto.html&quot;&gt;link&lt;/a&gt; which talks about how ssh client and server can be picky. In this page, the author mentions about file permissions. Hence I did quick check and it revealed that the permissions on local and remote files were different:&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_IB0EM94hyphenhyphen7DcGqXM1ErgIkT92nevZv-QzQBmkFykmfCMD96IM1SRPWvazm0AHNypalsuc8soeXp6F-s97mymFgjxo5sRxUTz1s3XY87_yuAEwt5KW3GySpx6yi6msMJWP15BVGLJYtuW/s1600/Screen+Shot+2012-04-25+at+1.57.30+PM.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_IB0EM94hyphenhyphen7DcGqXM1ErgIkT92nevZv-QzQBmkFykmfCMD96IM1SRPWvazm0AHNypalsuc8soeXp6F-s97mymFgjxo5sRxUTz1s3XY87_yuAEwt5KW3GySpx6yi6msMJWP15BVGLJYtuW/s1600/Screen+Shot+2012-04-25+at+1.57.30+PM.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
After changing the permissions, the login attempt succeeded. &lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgR-IM3FNV-pZld8hRJjNtzMBIP8xyxiKGhWZhcqnimrwbxhNoHCswC0tTDXCGk2BOOYaT_ZKleXtnjggt8_C5kcHK7bCmcZDKgH9EXnF4sP_1qgF0Jw8BSFZTuaiaiXMwH3g6M7GDZ4OCU/s1600/Screen+Shot+2012-04-25+at+2.05.57+PM.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgR-IM3FNV-pZld8hRJjNtzMBIP8xyxiKGhWZhcqnimrwbxhNoHCswC0tTDXCGk2BOOYaT_ZKleXtnjggt8_C5kcHK7bCmcZDKgH9EXnF4sP_1qgF0Jw8BSFZTuaiaiXMwH3g6M7GDZ4OCU/s1600/Screen+Shot+2012-04-25+at+2.05.57+PM.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
If file permissions is not the issue in your case, here are few things that can be used to troubleshoot:&lt;br /&gt;
1) Check the public key is actually in authorized_keys remote node file.&lt;br /&gt;
2) Check for compatibility between ssh protocol version, solution is to force them to use version 2 using -2 flag.&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://amrutbudihal.blogspot.com/feeds/4007424693786865894/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/123791467816537526/4007424693786865894' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/4007424693786865894'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/4007424693786865894'/><link rel='alternate' type='text/html' href='http://amrutbudihal.blogspot.com/2012/04/passpharse-less-ssh-troubleshooting-for.html' title='Passpharse less Ssh troubleshooting for Hadoop cluster setup.'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyEz3DgCRLn55-qdYZGagFZqBn03Bwg-3P9jGQV_AIEN6U4SPhaNViXqeaIr3ZA_-EX6JHjp_CRLE9snosA3V7gKX_82bSwjciuYcZe7i3JqbGL829LHS6pZyAg7rV8Sgj_uXzBGHPAP3E/s72-c/Screen+Shot+2012-04-25+at+1.06.14+PM.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-123791467816537526.post-1189159112306883350</id><published>2012-04-17T17:12:00.001-07:00</published><updated>2012-05-09T12:46:37.722-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="CollectionHelper.EMPTY_LIST"/><category scheme="http://www.blogger.com/atom/ns#" term="framework design flaw"/><category scheme="http://www.blogger.com/atom/ns#" term="hibernate bug"/><category scheme="http://www.blogger.com/atom/ns#" term="how not to design a framework"/><title type='text'>Hibernate: Why cant you return Null...</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
In this post, I rant about a patch the application needs to take care of when the framework is flawed. In this case, the framework happens to be &lt;a href=&quot;http://www.hibernate.org/&quot;&gt;Hibernate&lt;/a&gt;. The jar version used is : 3.6.6.Final&lt;br /&gt;

I was working on database query (removing sub-selects) optimizations and hence the need for refactoring the code. Below is the method which I came up with for the optimization. &lt;br /&gt;

&lt;pre class=&quot;brush: java; title: ; notranslate&quot; title=&quot;&quot;&gt;
protected long getMaxOrderingNumberForTrackingNo(String xoomTrackingNumber){
&amp;nbsp;List&amp;lt;Long&amp;gt; orderNumberList = getHibernateTemplate().find(HQL_GET_MAX_ORDER_NUMBER_FOR_TRACKING_NUMBER, xoomTrackingNumber);
&amp;nbsp;long orderNumber=0;
&amp;nbsp;if (!CollectionUtils.isEmpty(orderNumberList)) {
&amp;nbsp;&amp;nbsp;orderNumber=orderNumberList.get(0);
&amp;nbsp;}
&amp;nbsp;return orderNumber;
}
&lt;/pre&gt;

After making my changes, I quickly ran the unit and integration tests. These tests to my surprise failed and I started the quest for finding the culprit! It turns out the issue was in the&amp;nbsp; getHibernateTemplate().find() method which returned an instantiated object with size equals 1 and all elements in the list to be null. The above code hence threw a Null pointer exception. Below debugger screenshot from Idea confirms this claim.&lt;br /&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUNYEw9NA4FJRsH00iL7zkywYdqX6TJpvR7R9xdlvogM8_qZ6zgYswEKIH4Yhk2zgMZ0rdqq7tS4bnCpFeYYLVhmR5sap1IA0ZMOzjpiXi5aJFWEn7QJ6cg4Y-zNoupQiIYXezvIvGp_sY/s1600/Screen+Shot+2012-04-17+at+2.30.34+PM.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUNYEw9NA4FJRsH00iL7zkywYdqX6TJpvR7R9xdlvogM8_qZ6zgYswEKIH4Yhk2zgMZ0rdqq7tS4bnCpFeYYLVhmR5sap1IA0ZMOzjpiXi5aJFWEn7QJ6cg4Y-zNoupQiIYXezvIvGp_sY/s1600/Screen+Shot+2012-04-17+at+2.30.34+PM.png&quot; /&gt;&lt;/a&gt; &lt;br /&gt;
Initially, I thought the CollectionUtils.isEmpty() method does not handle Collections.EMPTY_LIST.&lt;br /&gt;
Hence the method was changed to following:&lt;br /&gt;

&lt;pre class=&quot;brush: java; title: ; notranslate&quot; title=&quot;&quot;&gt;
protected long getMaxOrderingNumberForTrackingNo(String xoomTrackingNumber){
&amp;nbsp;List&amp;lt;Long&amp;gt; orderNumberList = getHibernateTemplate().find(HQL_GET_MAX_ORDER_NUMBER_FOR_TRACKING_NUMBER, xoomTrackingNumber);
&amp;nbsp;long orderNumber=0;
&amp;nbsp;if (orderNumberList!=null &amp;amp;&amp;amp; !orderNumberList.isEmpty()) {
&amp;nbsp;&amp;nbsp; orderNumber =&amp;nbsp; orderNumberList.get(0);
&amp;nbsp;}
&amp;nbsp;return orderNumber;
}
&lt;/pre&gt;
&lt;br /&gt;
Still the test cases failed! Also my assumption about CollectionUtils.isEmpty method not handling Collections.EMPTY_LIST was wrong&lt;br /&gt;
Try out:&lt;br /&gt;
&lt;pre class=&quot;brush: java; title: ; notranslate&quot; title=&quot;&quot;&gt;
assertEquals(true, CollectionUtils.isEmpty(Collections.EMPTY_LIST));
&lt;/pre&gt;
&lt;br /&gt;
So finally confirming that this is a flaw, did the following as a work-around, had to add in a try catch block and document this issue. &lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: java; title: ; notranslate&quot; title=&quot;&quot;&gt;
protected long getMaxOrderingNumberForTrackingNo(String xoomTrackingNumber){
&amp;nbsp;List&amp;lt;Long&amp;gt; orderNumberList = getHibernateTemplate().find(HQL_GET_MAX_ORDER_NUMBER_FOR_TRACKING_NUMBER, xoomTrackingNumber);
&amp;nbsp;long orderNumber=0;
&amp;nbsp;//Need to encapsulate below statement in a try-catch.
&amp;nbsp;//because stupid hibernate instantiates the List object with size=1 and its elements are empty.
&amp;nbsp;//This makes condition check !org.apache.commons.collections.CollectionUtils.isEmpty on the Collection useless.
&amp;nbsp;try {
&amp;nbsp;&amp;nbsp; orderNumber=orderNumberList.get(0);
&amp;nbsp;} catch (NullPointerException npe) {
&amp;nbsp;&amp;nbsp; orderNumber = 0;
&amp;nbsp;&amp;nbsp; logger.debug(&quot;order number list is empty.&quot;);
&amp;nbsp;}
&amp;nbsp;return orderNumber;
} 
&lt;/pre&gt;
&lt;br /&gt;
In this case, it seems to make sense for the method to return in null so that commons utils can check for its emptiness. At this point, I will consider this as a hibernate framework bug.&lt;br /&gt;
&lt;br /&gt;
Found where in hibernate, it is being initialized: &lt;br /&gt;
In SessionImpl.java, method list(String query, QueryParameters queryParameters),&amp;nbsp; line 1263:&lt;br /&gt;
&lt;pre class=&quot;brush: java; title: ; notranslate&quot; title=&quot;&quot;&gt;List results = CollectionHelper.EMPTY_LIST;&lt;/pre&gt;
&lt;br /&gt;
CollectionHelper&#39;s EMPTY_LIST constant is initialized as follows:&lt;br /&gt;
&lt;pre class=&quot;brush: java; title: ; notranslate&quot; title=&quot;&quot;&gt;public static final List EMPTY_LIST = Collections.unmodifiableList( new ArrayList(0) );&lt;/pre&gt; &lt;br /&gt;
From the screenshot above, note the size is 1 and List.isEmpty method checks on size == 0 which explains why these methods are failing! Below is the source code snippet of isEmpty method used in ArrayList.java&lt;br /&gt;
&lt;pre class=&quot;brush: java; title: ; notranslate&quot; title=&quot;&quot;&gt;
/**
 * Returns &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt; if this list contains no elements.
 * @return &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt; if this list contains no elements
 */
&amp;nbsp;public boolean isEmpty() {
&amp;nbsp;&amp;nbsp;return size == 0;
&amp;nbsp;}
&lt;/pre&gt;

As expected, a quick unit test does also confirm that when a null element is added to List, the size is not 0. Following code fails on assertEquals.
&lt;pre class=&quot;brush: java; title: ; notranslate&quot; title=&quot;&quot;&gt;
List results = new ArrayList();
results.add(null);
assertEquals(0, results.size());
&lt;/pre&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://amrutbudihal.blogspot.com/feeds/1189159112306883350/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/123791467816537526/1189159112306883350' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/1189159112306883350'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/1189159112306883350'/><link rel='alternate' type='text/html' href='http://amrutbudihal.blogspot.com/2012/04/hibernate-why-cant-you-return-null.html' title='Hibernate: Why cant you return Null...'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUNYEw9NA4FJRsH00iL7zkywYdqX6TJpvR7R9xdlvogM8_qZ6zgYswEKIH4Yhk2zgMZ0rdqq7tS4bnCpFeYYLVhmR5sap1IA0ZMOzjpiXi5aJFWEn7QJ6cg4Y-zNoupQiIYXezvIvGp_sY/s72-c/Screen+Shot+2012-04-17+at+2.30.34+PM.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-123791467816537526.post-2126580282754327090</id><published>2012-04-07T13:51:00.004-07:00</published><updated>2012-04-07T13:57:23.369-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="deadlock"/><category scheme="http://www.blogger.com/atom/ns#" term="java"/><category scheme="http://www.blogger.com/atom/ns#" term="livelock"/><category scheme="http://www.blogger.com/atom/ns#" term="starvation"/><title type='text'>Deadlock, Starvation and Livelock.</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
In this post, I will talk about an interesting deadlock and immediate steps taken to resolve it.&lt;br /&gt;
&lt;br /&gt;
The system I work on is distributed in nature where instances communicate among themselves so they don&#39;t have to do the same work. We encountered an issue where batching was not being done.&lt;br /&gt;
&lt;br /&gt;
On log analysis, it seemed the batch file was not generated because the thread couldn&#39;t acquire the necessary lock. This lock (a database lock) was held by another thread.&lt;br /&gt;
&lt;br /&gt;
Being a self proclaimed concurrency expert in my team, I went straight into the machine where the webapp was running and executed jstack command to get the thread dump.&lt;br /&gt;
&lt;br /&gt;
Following is the snippet of the thread dump which had acquired the lock:
&lt;br /&gt;
&lt;pre class=&quot;brush: java; title: ; notranslate&quot; title=&quot;&quot;&gt;&quot;http-bio-41080-exec-10&quot; daemon prio=10 tid=0x000000004d387000 nid=0x6620 runnable [0x0000000043e12000]
   java.lang.Thread.State: RUNNABLE
    at com.mycompany.integration.partner.CLASSNAME.createPartnerTransaction(CLASSNAME.java:72)
....
&lt;/pre&gt;
&lt;br /&gt;
And following is the snippet of the thread dump which was waiting for the lock to be released:
&lt;br /&gt;
&lt;pre class=&quot;brush: java; title: ; notranslate&quot; title=&quot;&quot;&gt;&quot;scheduler_Worker-8&quot; prio=10 tid=0x00002aaab05c9000 nid=0x653d waiting for monitor entry [0x0000000044d25000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at com.mycompany.integration.service.impl.CLASSNAME1.acquireNonBlockingLock(CLASSNAME1.java:35)
    - waiting to lock &amp;lt;0x00000000c7bc4b60&amp;gt; (a java.lang.String)
....
&lt;/pre&gt;
&lt;br /&gt;
Taking several thread dumps at regular intervals revealed that http-bio-41080-exec-10 thread was doing something fishy line 72 in CLASSNAME.createPartnerTransaction.&lt;br /&gt;
The line number 72 surrounding the block is as follows: 
&lt;br /&gt;
&lt;pre class=&quot;brush: java; title: ; notranslate&quot; title=&quot;&quot;&gt;String filler = &quot;&quot;;
while (accountNumber.length() + filler.length() &amp;lt; 9) {
     filler.concat(&quot; &quot;);
}
transaction.setPayeeAccountNumber(filler.concat(accountNumber));
&lt;/pre&gt;
&lt;br /&gt;
Initial glance at the above block seemed fine, I asked myself what could be wrong with above while of code.... The issue was lurking which would only be executed when the account number was less than 9. Noticed it? On second look, the bug seemed obvious...&lt;br /&gt;
&lt;br /&gt;
But just to reconfirm the issue, I took the jar file this class was present in and used a jad de-compiler to get back the java file.&lt;br /&gt;
&lt;br /&gt;
The above block of code was converted into following line by compiler.

&lt;br /&gt;
&lt;pre class=&quot;brush: java; title: ; notranslate&quot; title=&quot;&quot;&gt;for(filler = &quot;&quot;; accountNumber.length() + filler.length() &amp;lt; 9; filler.concat(&quot; &quot;));
&lt;/pre&gt;
&lt;br /&gt;
Noticed it??&lt;br /&gt;
Strings in java are immutable and result of concat method needs to be appended to the original variable (in this case, filler) to get the new value.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Immediate step to resolve this issue:&lt;/b&gt; &lt;br /&gt;
As it turns out, an infinite loop was present and would be executed if length of accountNumber+filler (always 0) would be less than 9. On detecting this issue, resolution was as simple as replacing the above block of code with apache commons &lt;a href=&quot;http://commons.apache.org/lang/api-2.5/org/apache/commons/lang/StringUtils.html#leftPad%28java.lang.String,%20int%29&quot;&gt;StringUtils.padLeft&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Thoughts on improving this code on a long term basis:&lt;/b&gt;&lt;br /&gt;
The major issue here is not the infinite loop (thats a symptom), but I think its the combination of Starvation (couldn&#39;t acquire the lock held in database, which is the resource) and Livelock (retrying the operation of getting the resource which will always fail because it is acquired by another thread). &lt;br /&gt;
&lt;br /&gt;
Was there a need to hold this lock when executing this method operation (or the nested methods for that matter)? This is one of the important question that we need to answer by revisiting its architecture....&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://amrutbudihal.blogspot.com/feeds/2126580282754327090/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/123791467816537526/2126580282754327090' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/2126580282754327090'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/2126580282754327090'/><link rel='alternate' type='text/html' href='http://amrutbudihal.blogspot.com/2012/04/deadlock-starvation-and-livelock.html' title='Deadlock, Starvation and Livelock.'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-123791467816537526.post-419781214846699838</id><published>2011-11-02T22:47:00.000-07:00</published><updated>2011-11-02T22:47:22.753-07:00</updated><title type='text'>Java&#39;s BigDecimal.</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Where to use Java&#39;s &lt;a href=&quot;http://download.oracle.com/javase/1,5.0/docs/api/java/math/BigDecimal.html&quot;&gt;BigDecimal&lt;/a&gt;?&lt;br /&gt;
A example where it can be used is in applications which needs floating points with built in rounding facility.&lt;br /&gt;
&lt;br /&gt;
Consider a case, where money is stored as a decimal in database. Sql mapping &lt;a href=&quot;http://download.oracle.com/javase/1.4.2/docs/guide/jdbc/getstart/mapping.html&quot;&gt;link&lt;/a&gt; states: &quot;The recommended Java mapping for the &lt;code&gt;DECIMAL&lt;/code&gt; and &lt;code&gt;NUMERIC&lt;/code&gt; types is &lt;code&gt;java.math.BigDecimal&lt;/code&gt;. The &lt;code&gt;java.math.BigDecimal&lt;/code&gt; type provides math operations to allow &lt;code&gt;BigDecimal&lt;/code&gt; types to be added, subtracted, multiplied, and divided with other &lt;code&gt;BigDecimal&lt;/code&gt; types, with integer types, and with floating point types.&lt;a href=&quot;http://www.blogger.com/blogger.g?blogID=123791467816537526&quot; name=&quot;1007578&quot;&gt;&lt;/a&gt;&quot;&lt;br /&gt;
&lt;br /&gt;
Lets say we want to compute a currency of another country. With currency conversion rate, a currency can be converted into required one. Using BigDecimal&#39;s divide method one can achieve it by mentioning the scale factor and rounding strategy.&lt;br /&gt;
&lt;br /&gt;
2 is the scale (decides the number of digits after decimal point) and ROUND_HALF_EVEN strategy is banker&#39;s rounding.&lt;br /&gt;
&lt;pre class=&quot;brush: java; title: ; notranslate&quot;&gt;
BigDecimal amount = new BigDecimal(1);
BigDecimal rate = new BigDecimal(3);
BigDecimal destinationAmount = amount.divide(rate, 2, BigDecimal.ROUND_HALF_EVEN);&lt;/pre&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://amrutbudihal.blogspot.com/feeds/419781214846699838/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/123791467816537526/419781214846699838' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/419781214846699838'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/419781214846699838'/><link rel='alternate' type='text/html' href='http://amrutbudihal.blogspot.com/2011/11/javas-bigdecimal.html' title='Java&#39;s BigDecimal.'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-123791467816537526.post-5498053681611230733</id><published>2011-10-24T16:44:00.000-07:00</published><updated>2011-10-24T18:36:19.002-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="computing next business day in sql."/><category scheme="http://www.blogger.com/atom/ns#" term="mysql"/><category scheme="http://www.blogger.com/atom/ns#" term="stored procedure"/><title type='text'>Mysql Stored Procedure for Computing Next Business day.</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
In this post, I will explain how to compute next business day using stored procedure. To compute next business day, the code has to take into account Saturday, Sunday and any other bank holidays. Store the public holidays into bank_holiday table with column holiday_date of type DATETIME&lt;br /&gt;
&lt;br /&gt;
&lt;u&gt;&lt;i&gt;&lt;b&gt;Explanation&lt;/b&gt;&lt;/i&gt;&lt;/u&gt;: The first step is to get next day and then check if its a Saturday/Sunday, then increment accordingly (if Saturday increment by 2 days, if Sunday increment by 1 day). Next step involves checking this computed date exists in the bank_holiday table and then recursive calling itself for a match. Following is the code snippet.
&lt;br /&gt;
&lt;pre class=&quot;brush: sql; title: ; notranslate&quot; title=&quot;&quot;&gt;DROP PROCEDURE IF EXISTS NextBusinessDay;

CREATE PROCEDURE NextBusinessDay (IN startDate DATETIME, OUT nextBDay DATETIME)

BEGIN
 DECLARE dayOfWeek INT DEFAULT 0;
 DECLARE rowCount INT DEFAULT 0;
 
 SELECT DATE_ADD(startDate, INTERVAL 1 DAY) INTO nextBDay;
 SELECT DAYOFWEEK(nextBDay) INTO dayOfWeek;
  
 IF dayOfWeek = 7 --Saturday then working day is Monday.
  THEN SELECT DATE_ADD(nextBDay, INTERVAL 2 DAY) INTO nextBDay;
 END IF;

 IF dayOfWeek = 1 --Sunday then working day is Monday.
  THEN SELECT DATE_ADD(nextBDay, INTERVAL 1 DAY) INTO nextBDay;
 END IF;

-- Check for holiday by querying the bank_holiday table.
 SELECT count(*) from bank_holiday where holiday_date = nextBDay INTO rowCount;

 IF rowCount &amp;gt; 0
  THEN CALL NextBusinessDay(nextBDay,nextBDay);
 END IF;
END &lt;/pre&gt;
&lt;br /&gt;
For detailed explaination on built-in DATE_ADD, DAYOFWEEK mysql functions, refer to &lt;a href=&quot;http://dev.mysql.com/doc/refman/5.0/en/date-and-time-functions.html&quot;&gt;link&lt;/a&gt;. For a short tutorial on writing stored procedure, refer to &lt;a href=&quot;http://forums.mysql.com/read.php?98,358569,358569&quot;&gt;link&lt;/a&gt;.&lt;br /&gt;
&lt;u&gt;&lt;i&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/i&gt;&lt;/u&gt;&lt;br /&gt;
&lt;u&gt;&lt;i&gt;&lt;b&gt;Enabling recursive stored procedure:&lt;/b&gt;&lt;/i&gt;&lt;/u&gt; On executing this stored procedure, you will get an error. Following is the trace:&lt;br /&gt;
&lt;pre class=&quot;brush: sql; title: ; notranslate&quot; title=&quot;&quot;&gt;mysql&amp;gt; CALL NextBusinessDay(&#39;2010-07-02 00:00:00.0&#39;, @nextBDay);
ERROR 1456 (HY000): Recursive limit 0 (as set by the max_sp_recursion_depth variable) was exceeded for routine NextBusinessDay&lt;/pre&gt;
&lt;br /&gt;
Enable recursion by passing a int value greater than 0 on mysql server startup. Following is one way:&lt;br /&gt;
&lt;pre class=&quot;brush: sql; title: ; notranslate&quot; title=&quot;&quot;&gt;sudo -b /usr/local/mysql/bin/mysqld_safe --max_sp_recursion_depth=3
&lt;/pre&gt;
More at &lt;a href=&quot;http://dev.mysql.com/doc/refman/5.0/en/server-system-variables.html#sysvar_max_sp_recursion_depth&quot;&gt;link&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;u&gt;&lt;i&gt;Executing stored procedure&lt;/i&gt;&lt;/u&gt;&lt;/b&gt;: Using &lt;a href=&quot;http://dev.mysql.com/doc/refman/5.0/en/call.html&quot;&gt;CALL&lt;/a&gt;. In this case, it will be CALL NextBusinessDay(&#39;2011-10-24&#39;, @nextBDay); To retrieve the result, do select @nextBDay; (i.e. Note this is the same variable passed into Stored procedure call)

Following screenshot shows tests of this procedure over some dates. (Note in US: 5th Sept is observed as Memorial day and hence its a public holiday. Also tested for Leap year 2012.)&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipUwyW6bILJIn38wtNIJrFXI9B8rjxWW0azAOPSpM30vZsRuKts2hxGioeeFNz7CBvxxwYgBSBpEK-VFFXixQa3u_QliuewZIFHfoZldGby9WhgkqfUI2szGBMT0owI-ZOqvAV3zuS3DAu/s1600/Screen+shot+2011-10-24+at+4.40.34+PM.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipUwyW6bILJIn38wtNIJrFXI9B8rjxWW0azAOPSpM30vZsRuKts2hxGioeeFNz7CBvxxwYgBSBpEK-VFFXixQa3u_QliuewZIFHfoZldGby9WhgkqfUI2szGBMT0owI-ZOqvAV3zuS3DAu/s1600/Screen+shot+2011-10-24+at+4.40.34+PM.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://amrutbudihal.blogspot.com/feeds/5498053681611230733/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/123791467816537526/5498053681611230733' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/5498053681611230733'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/5498053681611230733'/><link rel='alternate' type='text/html' href='http://amrutbudihal.blogspot.com/2011/10/mysql-stored-procedure-for-computing.html' title='Mysql Stored Procedure for Computing Next Business day.'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipUwyW6bILJIn38wtNIJrFXI9B8rjxWW0azAOPSpM30vZsRuKts2hxGioeeFNz7CBvxxwYgBSBpEK-VFFXixQa3u_QliuewZIFHfoZldGby9WhgkqfUI2szGBMT0owI-ZOqvAV3zuS3DAu/s72-c/Screen+shot+2011-10-24+at+4.40.34+PM.png" height="72" width="72"/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-123791467816537526.post-2344963150621265679</id><published>2011-10-17T14:06:00.001-07:00</published><updated>2011-10-21T13:36:29.951-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="hibernate"/><category scheme="http://www.blogger.com/atom/ns#" term="mysql"/><category scheme="http://www.blogger.com/atom/ns#" term="mysql query optimization"/><title type='text'>Proper selection of Hibernate Inheritance herirachy.</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
In this post, I will explain a scenario which could have been avoided if proper hibernate inheritance strategy was selected in the first place. To begin with it was not even identified as Hibernate inheritance strategy issue. Infact IT noticed site slowness and reported about it to Engineering that a query was executing slowly (over 40 seconds). From the query which looked as below:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: sql; title: ; notranslate&quot; title=&quot;&quot;&gt;select column1,....,column50
from (
select .. from table1
union
select .. from table2
union
select .. from table3 ) where xid=1;
&lt;/pre&gt;
&lt;br /&gt;
Can above query be optimized? Lets observer the query first. The above queries three different tables, then unions them and then filters the result using where clause. As you guessed it, querying individual table, union them and then filtering doesn&#39;t seem right. What happens if each table has 10K records, wont we be gathering 30K records before filtering on xid! Think what happens if there are millions of records. Besides that the query would be inefficient if queried on un-indexed column. This behavior was being observed in production where we have lots of records, queried on un-indexed column and hence cause for the query to be slow. This was confirmed on explaining the query. Below is the screen shot which confirms the behavior.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3RnNEI4CG4Wl_aaMwLIVu6l_TmZANo19Y4mthd5VQqNYY3GH_KW4FLgTOjV1L-H-pPh8fN6VwcLHa9qhdDpyHes18BNtqXFQplvUQjXJlLJMYlmfWtXbMEw3stt8WbHIdEFhD-d3qC5mK/s1600/Screen+shot+2011-10-20+at+7.00.20+PM.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;99&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3RnNEI4CG4Wl_aaMwLIVu6l_TmZANo19Y4mthd5VQqNYY3GH_KW4FLgTOjV1L-H-pPh8fN6VwcLHa9qhdDpyHes18BNtqXFQplvUQjXJlLJMYlmfWtXbMEw3stt8WbHIdEFhD-d3qC5mK/s640/Screen+shot+2011-10-20+at+7.00.20+PM.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
Ideally we would want to have a query (also indexing the column being queried) which is as follows:&lt;br /&gt;
&lt;pre class=&quot;brush: sql; title: ; notranslate&quot; title=&quot;&quot;&gt;select column1,....,column50
from (
select .. from table1 where&amp;nbsp; xid=1
union
select .. from table2 where&amp;nbsp; xid=1
union
select .. from table3 where&amp;nbsp; xid=1
);
&lt;/pre&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
This improves the query. Can be confirmed by explain on the query. From the below screenshot, we can confirm that lot less records are fetched before union. Also this time, column is indexed and hence the less time! :)&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQ1NhMXw8i8IdvHJsr5oz33yqP2P8Xp895NTbVBopqPaG4kcYeRZ62105Wu4y3J1dUZAj6jK_dNgvxwyaZvSg6k9QsPv5f0-98LjDhy5IiNn5H0X-M32PLDaZvgjGB0TxZHzt_v-Lu67dS/s1600/Screen+shot+2011-10-20+at+7.14.45+PM.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;124&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQ1NhMXw8i8IdvHJsr5oz33yqP2P8Xp895NTbVBopqPaG4kcYeRZ62105Wu4y3J1dUZAj6jK_dNgvxwyaZvSg6k9QsPv5f0-98LjDhy5IiNn5H0X-M32PLDaZvgjGB0TxZHzt_v-Lu67dS/s640/Screen+shot+2011-10-20+at+7.14.45+PM.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
Again, this is was all Hibernate doing this stuff due to the fact that inheritance strategy selected was TABLE_PER_CLASS. As mentioned &lt;a href=&quot;http://en.wikibooks.org/wiki/Java_Persistence/Inheritance#Common_Problems_3&quot;&gt;here&lt;/a&gt;, this strategy runs into join issue and hence the cause for being slow.&lt;br /&gt;
&lt;br /&gt;
Solution adopted: Changed the strategy to SINGLE_TABLE (combined the data from three tables into one. The records are differentiated by a discriminator column). The only trade-off adopting SINGLE_TABLE strategy is that records can be sparse (i.e. not all columns will be populated and hence cannot enforce null constraint). Luckily that was not the case for us! :)&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://amrutbudihal.blogspot.com/feeds/2344963150621265679/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/123791467816537526/2344963150621265679' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/2344963150621265679'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/2344963150621265679'/><link rel='alternate' type='text/html' href='http://amrutbudihal.blogspot.com/2011/10/hibernate-inheritance-herirachy.html' title='Proper selection of Hibernate Inheritance herirachy.'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3RnNEI4CG4Wl_aaMwLIVu6l_TmZANo19Y4mthd5VQqNYY3GH_KW4FLgTOjV1L-H-pPh8fN6VwcLHa9qhdDpyHes18BNtqXFQplvUQjXJlLJMYlmfWtXbMEw3stt8WbHIdEFhD-d3qC5mK/s72-c/Screen+shot+2011-10-20+at+7.00.20+PM.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-123791467816537526.post-2189486355410672456</id><published>2011-10-10T18:45:00.000-07:00</published><updated>2011-10-10T18:45:52.396-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="mysql remote connection"/><category scheme="http://www.blogger.com/atom/ns#" term="mysql tcp/ip connection."/><title type='text'>Configuring Mysql for remote tcp/ip connections.</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
&lt;b&gt;Scenario&lt;/b&gt;:&lt;br /&gt;
When a mysql server is running on a different machine than the required, following instructions can be of help. Not sure if these same instructions can be used for production. As most of my development is on mac, I have my mysql server running on it. There was recent feature configuration enhancement using &lt;a href=&quot;http://www-01.ibm.com/software/integration/cast-iron-cloud-integration/&quot;&gt;IBM&#39;s CastIron tool&lt;/a&gt;. This tool apparently is only built for Windows environment. So I ended up having to install Virtual machine (VMware Fusion) with Windows Xp on it. For this feature enhancement, the tool needed to communicate to database (the very same db on which development is done on.) So there needed a way for this tool running on Windows needed to talk to database server running on Mac OSX.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Solution:&lt;/b&gt;&lt;br /&gt;
The solution is two folds. One create the user and second grant the user with appropriate permissions.&lt;br /&gt;
1) Create the user:&lt;br /&gt;
On mac, open the terminal and login into mysql. (mysql -u &amp;lt;user-name&amp;gt; -h &amp;lt;hostname&amp;gt; -p mysql). On successful mysql login,&amp;nbsp; execute the command: create user &#39;&amp;lt;username&amp;gt;&#39;@&#39;%&#39; identified by &#39;password&#39;; More info at &lt;a href=&quot;http://dev.mysql.com/doc/refman/5.1/en/create-user.html&quot;&gt;link&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
2) Grant the user: &lt;br /&gt;
Once above user is created, give the user grant (read, write or both). For example to give a less restrictive grant on this newly created user, execute the following command: grant all on *.* to &#39;&amp;lt;username&amp;gt;&#39;@&#39;%&#39;. More info on various parameters at &lt;a href=&quot;http://dev.mysql.com/doc/refman/5.1/en/grant.html&quot;&gt;link&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Test it using a &lt;a href=&quot;http://www.mysql.com/products/workbench/&quot;&gt;mysql workbench&lt;/a&gt; on windows or the machine which you are trying to connect from.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Troubleshooting&lt;/b&gt;:&lt;br /&gt;
If for some reason, connection is not successful, check the following:&lt;br /&gt;
&lt;ol style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;Check the my.cnf has line &quot;skip-networking&quot; commented.&lt;/li&gt;
&lt;li&gt;Disable any firewall which might prevent the connection.&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://amrutbudihal.blogspot.com/feeds/2189486355410672456/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/123791467816537526/2189486355410672456' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/2189486355410672456'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/2189486355410672456'/><link rel='alternate' type='text/html' href='http://amrutbudihal.blogspot.com/2011/10/configuring-mysql-for-remote-tcpip.html' title='Configuring Mysql for remote tcp/ip connections.'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-123791467816537526.post-1207113528610776969</id><published>2011-08-29T23:11:00.000-07:00</published><updated>2011-08-29T23:11:44.347-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="data import"/><category scheme="http://www.blogger.com/atom/ns#" term="relational database import in solr"/><category scheme="http://www.blogger.com/atom/ns#" term="solr tomcat setup"/><title type='text'>Apache Solr: Initial Encounter.</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;In this post, I will try to explain how to setup tomcat with solr and working with import data from relational databases (oracle in my case). This guide is more of cookbook thing, more details can be found at&amp;nbsp;&lt;a href=&quot;http://wiki.apache.org/solr/SolrTomcat&quot;&gt;solr-tomcat link&lt;/a&gt;&amp;nbsp;and &lt;a href=&quot;http://wiki.apache.org/solr/DataImportHandler&quot;&gt;data import handler link&lt;/a&gt;.&amp;nbsp;I am assuming tomcat is setup and running and hence wont go into details of setup but the link can be found &lt;a href=&quot;http://tomcat.apache.org/&quot;&gt;here&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Configuring Solr.&lt;/b&gt;&lt;br /&gt;
1) Setup solr directory and setup variable $SOLR_HOME.&lt;br /&gt;
&amp;nbsp; a) Download the apache solr source from &lt;a href=&quot;http://lucene.apache.org/solr/&quot;&gt;link&lt;/a&gt;&amp;nbsp;(click on Resources at left hand side tab and download the latest source code, apache-solr-3.3 at the time of writing this post).&lt;br /&gt;
&amp;nbsp; b) Unzip/ untar the source code. Copy the following into separate directory (I copied it to /home/amrut/solr)&lt;br /&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;&amp;lt;SRC_DIR&amp;gt;/dist/apache-solr-3.3.war to /home/amrut/solr (cp ~/Downloads/apache-solr-3.3/dist/apache-solr.3.3.war /home/amrut/solr/solr.war)&lt;/li&gt;
&lt;li&gt;Contents of &amp;lt;SRC_DIR&amp;gt;/example/solr/ to /home/amrut/solr&amp;nbsp;(cp -r ~/Downloads/apache-solr-3.3/solr/ /home/amrut/solr/)&lt;/li&gt;
&lt;/ul&gt;&lt;div&gt;&amp;nbsp; c) Setup environment variable $SOLR_HOME into .bashrc (for unix)/ .profile (for mac). export SOLR_HOME=/home/amrut/solr&lt;/div&gt;&lt;br /&gt;
2) Setup tomcat context fragment.&lt;br /&gt;
vi $CATALINA_HOME/conf/Catalina/localhost/solr.war&lt;/div&gt;Paste the following into solr.war&lt;br /&gt;
&lt;pre class=&quot;brush: java; title: ; notranslate&quot; title=&quot;&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
&amp;nbsp;&amp;lt;Context docBase=&quot;/home/amrut/solr/solr.war&quot; debug=&quot;0&quot; crossContext=&quot;true&quot;&amp;gt;
&amp;nbsp;&amp;lt;Environment name=&quot;solr/home&quot; type=&quot;java.lang.String&quot; value=&quot;/home/amrut/solr/&quot; override=&quot;true&quot;/&amp;gt;
&amp;lt;/Context&amp;gt;
&lt;/pre&gt;&lt;br /&gt;
3) Start tomcat and monitor the logs for any error.&lt;br /&gt;
Type the following at the command prompt:&amp;nbsp;$CATALINA_HOME/bin/shutdown.sh; $CATALINA_HOME/bin/startup.sh; tail -f $CATALINA_HOME/logs/catalina.out&lt;br /&gt;
&lt;br /&gt;
4) Solr should be available at: http://localhost:8080/solr.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Importing data from Relational database.&lt;/b&gt;&lt;br /&gt;
Consider a scenario where a table in mysql is to be imported into Solr. For people who want to try out, download the torrent dataset at &lt;a href=&quot;http://www.csg.uzh.ch/publications/data/piratebay.html&quot;&gt;link&lt;/a&gt;&amp;nbsp;and insert into a table called torrent which has fields idtorrent, category, size, seeders, leechers. More information on how to load csv file into database can be found at &lt;a href=&quot;http://dev.mysql.com/doc/refman/5.1/en/load-data.html&quot;&gt;link.&lt;/a&gt;&amp;nbsp;&lt;span id=&quot;goog_1494558293&quot;&gt;&lt;/span&gt;&lt;span id=&quot;goog_1494558294&quot;&gt;&lt;/span&gt;&lt;a href=&quot;http://www.blogger.com/&quot;&gt;&lt;/a&gt;&lt;br /&gt;
After loading the data into database table torrent, we must modify schema.xml and solrconfig.xml files.&lt;br /&gt;
1) schema.xml: Configure fields in Solr.&lt;br /&gt;
&lt;pre class=&quot;brush: xml; title: ; notranslate&quot; title=&quot;&quot;&gt;&lt;field indexed=&quot;true&quot; name=&quot;size&quot; stored=&quot;true&quot; type=&quot;string&quot;&gt;
&lt;/field&gt;&amp;lt;field indexed=&quot;true&quot; name=&quot;seeders&quot; stored=&quot;true&quot; type=&quot;string&quot; /&amp;gt;
&amp;lt;field indexed=&quot;true&quot; name=&quot;lechers&quot; stored=&quot;true&quot; type=&quot;string&quot; /&amp;gt;
&amp;lt;field indexed=&quot;true&quot; name=&quot;category&quot; stored=&quot;true&quot; type=&quot;text_general&quot; /&amp;gt;
&amp;lt;field indexed=&quot;true&quot; name=&quot;id&quot; required=&quot;true&quot; stored=&quot;true&quot; type=&quot;string&quot; /&amp;gt;
&lt;/pre&gt;&lt;br /&gt;
2) solrconfig.xml: Map data import handler and corresponding source it should look at (data-config.xml)&lt;br /&gt;
&lt;pre class=&quot;brush: xml; title: ; notranslate&quot; title=&quot;&quot;&gt;&amp;lt;requestHandler name=&quot;/dataimport&quot; class=&quot;org.apache.solr.handler.dataimport.DataImportHandler&quot;&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;lst name=&quot;defaults&quot;&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;str name=&quot;config&quot;&amp;gt;/opt/solr/example/torrent-data-config.xml&amp;lt;/str&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;/lst&amp;gt;
&amp;nbsp; &amp;lt;/requestHandler&amp;gt;
&lt;/pre&gt;&lt;br /&gt;
3) torrent-data-config.xml: contains the source information (in our case, the torrent table information)&lt;br /&gt;
&lt;pre class=&quot;brush: xml; title: ; notranslate&quot; title=&quot;&quot;&gt;&lt;dataconfig&gt;
&amp;lt;datasource driver=&quot;com.mysql.jdbc.Driver&quot; password=&quot;mysql&quot; url=&quot;jdbc:mysql://localhost:3306/torrent_schema&quot; user=&quot;mysql&quot;&amp;gt;
&amp;lt;document name=&quot;torrent&quot;&amp;gt;
&amp;lt;entity name=&quot;item&quot; query=&quot;select idtorrent as id, category, size, seeders, lechers from torrent&quot; /&amp;gt;
&amp;lt;field column=&quot;ID&quot; name=&quot;id&quot; /&amp;gt;
&amp;lt;field column=&quot;CATEGORY&quot; name=&quot;category&quot; /&amp;gt;
&amp;lt;field column=&quot;SIZE&quot; name=&quot;size&quot; /&amp;gt;
&amp;lt;field column=&quot;SEEDERS&quot; name=&quot;seeders&quot; /&amp;gt;
&amp;lt;field column=&quot;LECHERS&quot; name=&quot;lechers&quot; /&amp;gt;
&amp;lt;/document&amp;gt;
&amp;lt;/datasource&amp;gt;&lt;/dataconfig&gt;&lt;/pre&gt;&lt;br /&gt;
Finally, import data using going to this link: http://localhost:8080/solr/dataimport?command=full-import . Monitor the catalina.out and also status can be checked at&amp;nbsp;http://localhost:8080/solr/dataimport?command=status.&lt;br /&gt;
&lt;br /&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://amrutbudihal.blogspot.com/feeds/1207113528610776969/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/123791467816537526/1207113528610776969' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/1207113528610776969'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/1207113528610776969'/><link rel='alternate' type='text/html' href='http://amrutbudihal.blogspot.com/2011/08/apache-solr-initial-encounter.html' title='Apache Solr: Initial Encounter.'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-123791467816537526.post-1253919801421007639</id><published>2011-08-08T14:46:00.001-07:00</published><updated>2011-08-08T14:46:38.891-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="junit"/><category scheme="http://www.blogger.com/atom/ns#" term="TDD"/><category scheme="http://www.blogger.com/atom/ns#" term="test"/><category scheme="http://www.blogger.com/atom/ns#" term="test private methods in java"/><title type='text'>Junit: Testing Private Methods.</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;I was researching on how to test private methods in junit and &lt;a href=&quot;http://www.google.com/search?sourceid=chrome&amp;amp;ie=UTF-8&amp;amp;q=how+to+test+a+private+methods+in+junit&quot;&gt;did a quick query&lt;/a&gt;. Few notable search results pointed me to a &lt;a href=&quot;http://junit-addons.sourceforge.net/junitx/util/PrivateAccessor.html&quot;&gt;PrivateAccessor&lt;/a&gt; class and in particular invoke method. This class is&amp;nbsp;available&amp;nbsp;inside junit-addon jar available at the &lt;a href=&quot;http://javacio.us/search?q=junitx%2Futil%2FPrivateAccessor+more%3Ajar_files&amp;amp;inline=true&quot;&gt;link&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
So the basic idea is that this class bypasses Java modifier&amp;nbsp;security/access specifier on methods and fields. &lt;br /&gt;
Following are the required input parameters:&lt;br /&gt;
1) Object which contains private methods.&lt;br /&gt;
2) Private method to be tested.&lt;br /&gt;
3) Parameter&amp;nbsp;argument&amp;nbsp;type array passed to that private method.&lt;br /&gt;
4) Values array passed to that private method.&lt;br /&gt;
&lt;br /&gt;
The invoke method returns an Object, which I found that it contains the return value of the private method. Straightforward,&amp;nbsp;isn&#39;t!&lt;br /&gt;
&lt;br /&gt;
Following is the snippet of code, I used this class as follows:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: java; title: ; notranslate&quot; title=&quot;&quot;&gt;package mycompany;

import org.junit.Test;
import static org.junit.Assert.assertTrue;
import junitx.util.PrivateAccessor;

import java.io.File;

public class MCExtractorTest {

 @Test
 public void testMerge() throws Exception {
  MCExtractor extractor = new MCExtractor(&quot;/prod/medlineMatch.xml&quot;, &quot;/home/amrut/els/medline/cip&quot;);
  extractor.merge();
 }

 @Test
 public void testGetCollecIdFromDatabase() throws Throwable {
  MCExtractor extractor = new MCExtractor(&quot;/prod/medlineMatch.xml&quot;, &quot;/home/amrut/els/medline/cip&quot;);
  Object object = PrivateAccessor.invoke(extractor, &quot;getCollecIdFromDatabase&quot;, new Class[]{String.class}, new String[]{&quot;S00090X&quot;});
  assertTrue(object.toString().equals(&quot;JL-1s20S000071-1s20S00090X&quot;));
 }
}
&lt;/pre&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://amrutbudihal.blogspot.com/feeds/1253919801421007639/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/123791467816537526/1253919801421007639' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/1253919801421007639'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/1253919801421007639'/><link rel='alternate' type='text/html' href='http://amrutbudihal.blogspot.com/2011/08/junit-testing-private-methods_8528.html' title='Junit: Testing Private Methods.'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-123791467816537526.post-1639510274005715778</id><published>2011-07-31T23:18:00.000-07:00</published><updated>2011-08-04T13:08:00.476-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Data Structure"/><category scheme="http://www.blogger.com/atom/ns#" term="extending LinkedHashMap"/><category scheme="http://www.blogger.com/atom/ns#" term="java"/><category scheme="http://www.blogger.com/atom/ns#" term="LRU Cache"/><title type='text'>LRU Cache: Another way: By Extending LinkedHashMap</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;In my &lt;a href=&quot;http://amrutbudihal.blogspot.com/2011/07/lru-cache-using-priorityqueue.html&quot;&gt;previous post&lt;/a&gt; where I use PriorityQueue to construct a LRU cache. I was aware of LinkedHashMap data structure, but never provided great&amp;nbsp;attention to its documentation. It turns out there is a much simpler way to construct an LRU cache using it.&lt;br /&gt;
&lt;br /&gt;
The documentation clearly states,&lt;br /&gt;
&lt;br /&gt;
&quot;A special&amp;nbsp;&lt;a href=&quot;http://download.oracle.com/javase/6/docs/api/java/util/LinkedHashMap.html#LinkedHashMap(int, float, boolean)&quot;&gt;&lt;code&gt;constructor&lt;/code&gt;&lt;/a&gt;&amp;nbsp;is provided to create a linked hash map whose order of iteration is the order in which its entries were last accessed, from least-recently accessed to most-recently (&lt;i&gt;access-order&lt;/i&gt;). This kind of map is well-suited to building LRU caches. Invoking the&amp;nbsp;&lt;tt&gt;put&lt;/tt&gt;&amp;nbsp;or&amp;nbsp;&lt;tt&gt;get&lt;/tt&gt;&amp;nbsp;method results in an access to the corresponding entry (assuming it exists after the invocation completes). The&amp;nbsp;&lt;tt&gt;putAll&lt;/tt&gt;&amp;nbsp;method generates one entry access for each mapping in the specified map, in the order that key-value mappings are provided by the specified map&#39;s entry set iterator.&amp;nbsp;&lt;i&gt;No other methods generate entry accesses.&lt;/i&gt;&amp;nbsp;In particular, operations on collection-views do&amp;nbsp;&lt;i&gt;not&lt;/i&gt;&amp;nbsp;affect the order of iteration of the backing map&lt;br /&gt;
&lt;br /&gt;
The&amp;nbsp;&lt;a href=&quot;http://download.oracle.com/javase/6/docs/api/java/util/LinkedHashMap.html#removeEldestEntry(java.util.Map.Entry)&quot;&gt;&lt;code&gt;removeEldestEntry(Map.Entry)&lt;/code&gt;&lt;/a&gt;&amp;nbsp;method may be overridden to impose a policy for removing stale mappings automatically when new mappings are added to the map.&quot;&lt;br /&gt;
&lt;br /&gt;
With this new discovery (at-least&amp;nbsp;I became aware of the fact :) ), I wanted to try this out. Within few minutes, I was ready to test my code and as expected the code works. For purpose of testing, I have printed the eldest entry that will be removed once the size is reached. As you can see, I extended LinkedHashMap and overridden the method:&amp;nbsp;removeEldestEntry.&lt;br /&gt;
&lt;br /&gt;
Following is the code if someone wishes to try out.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: java; title: ; notranslate&quot; title=&quot;&quot;&gt;package lru;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

public class Cache extends LinkedHashMap {
 
 private static int CACHE_SIZE=5;
 private static float LOAD_FACTOR = 0.75f;
 private static boolean ACCESS_ORDER = true;
 
 
 public Cache(int initialCapacity, float loadFactor, boolean accessOrder) {
  super(initialCapacity,loadFactor, accessOrder);
 }

 protected boolean removeEldestEntry(Map.Entry eldest) {
  if (size() &amp;gt; CACHE_SIZE) {
   System.out.println(&quot;Size limit reached, removing eldest entry:&quot;+eldest.getKey());
   return true;
  } else {
   return false;
  }
 }
 
 public static void main (String[] args) {
  Cache cache = new Cache(CACHE_SIZE, LOAD_FACTOR , ACCESS_ORDER);
  cache.put(&quot;a&quot;,&quot;a&quot;);cache.put(&quot;b&quot;,&quot;b&quot;);
  cache.put(&quot;c&quot;,&quot;c&quot;);cache.put(&quot;d&quot;,&quot;d&quot;);
  cache.put(&quot;e&quot;,&quot;e&quot;);cache.put(&quot;b&quot;,&quot;b&quot;);
  cache.put(&quot;d&quot;,&quot;d&quot;);cache.put(&quot;b&quot;,&quot;b&quot;);
  cache.put(&quot;c&quot;,&quot;c&quot;);cache.put(&quot;b&quot;,&quot;b&quot;);
  cache.put(&quot;a&quot;,&quot;a&quot;);cache.put(&quot;f&quot;,&quot;f&quot;);
  cache.get(&quot;a&quot;);
  System.out.println(&quot;size:&quot;+cache.size());
  Set&lt;string&gt; keys = cache.keySet();
  for (String key : keys) {
   System.out.println(&quot;Cache contains:&quot;+key);
  }
 }

}
&lt;/string&gt;&lt;/pre&gt;&lt;br /&gt;
Output produces is:&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp; Size limit reached, removing eldest entry:e&lt;br /&gt;
&amp;nbsp; size:5&lt;br /&gt;
&amp;nbsp; Cache contains:d&lt;br /&gt;
&amp;nbsp; Cache contains:c&lt;br /&gt;
&amp;nbsp; Cache contains:b&lt;br /&gt;
&amp;nbsp; Cache contains:f&lt;br /&gt;
&amp;nbsp; Cache contains:a&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;i&gt;&lt;u&gt;Conclusion/Advantages&lt;/u&gt;&lt;/i&gt;&lt;/b&gt;:&lt;br /&gt;
1) Code is very much terse.&lt;br /&gt;
2) Reusing the code which is much stable, instead of re-inventing the world.&lt;br /&gt;
&lt;br /&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://amrutbudihal.blogspot.com/feeds/1639510274005715778/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/123791467816537526/1639510274005715778' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/1639510274005715778'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/1639510274005715778'/><link rel='alternate' type='text/html' href='http://amrutbudihal.blogspot.com/2011/07/lru-cache-another-way-by-extending.html' title='LRU Cache: Another way: By Extending LinkedHashMap'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-123791467816537526.post-2267783937894577643</id><published>2011-07-31T22:18:00.000-07:00</published><updated>2011-07-31T22:18:26.095-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="custom plugin"/><category scheme="http://www.blogger.com/atom/ns#" term="Nutch"/><category scheme="http://www.blogger.com/atom/ns#" term="plugin"/><title type='text'>Nutch Custom Plugin: Initial Experience.</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;I wanted to get familiar with plugin based model of Nutch and hence sharing my experience by writing custom plugin. In another post, I note my experience on &lt;a href=&quot;http://amrutbudihal.blogspot.com/2011/07/debugging-nutch-parse.html&quot;&gt;debugging Nutch&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
I followed &lt;a href=&quot;http://wiki.apache.org/nutch/WritingPluginExample&quot;&gt;tutorial&lt;/a&gt; which has been updated to 1.3 (latest at the time of writing this post). Plugins are to be created inside src/plugin directory.&lt;br /&gt;
&lt;br /&gt;
Custom plugin needs following things:&lt;br /&gt;
1) Configuration files like build.xml, ivy.xml, plugin.xml.&lt;br /&gt;
2) Java classes: which execute business logic.&lt;br /&gt;
&lt;br /&gt;
Following notes should be helpful in setting Nutch quickly.&lt;br /&gt;
1) Directory struture.&lt;br /&gt;
&amp;nbsp; a) The main custom plugin directory should be defined inside &amp;lt;NUTCH_HOME_DIR&amp;gt;/src/plugin.&lt;br /&gt;
&amp;nbsp; b) Inside this directory create build.xml and plugin.xml.&lt;br /&gt;
&amp;nbsp; c) Now create directory src/java/&amp;lt;package-path&amp;gt;, in my case: src/java/com/mycompany/parserfilter/&lt;br /&gt;
&amp;nbsp; d) In this directory, create a lib directory which store any external jars.&lt;br /&gt;
&lt;br /&gt;
2) Make sure build.xml is present. It looks like this, replace &amp;lt;UR_PLUGIN_NAME&amp;gt; with plugin name.&lt;br /&gt;
&lt;pre class=&quot;brush: csharp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot;?&amp;gt;
&amp;lt;project name=&quot;&amp;lt;UR_PLUGIN_NAME&amp;gt;&quot; default=&quot;jar-core&quot;&amp;gt;
&amp;nbsp; &amp;lt;import file=&quot;../build-plugin.xml&quot; /&amp;gt;
&amp;lt;/project&amp;gt;
&lt;/pre&gt;&lt;br /&gt;
3) Plugin.xml: This makes aware of custom code to Nutch system. Any other libraries which your custom code depends on can be defined inside libray tag. I wanted to extract certain section of the crawled page. Hence I am extending HtmlParseFilter. For every custom code, declaration is needed in the form of extension tag. I have defined TagExtractorParseFilter.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: csharp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;plugin id=&quot;food&quot; name=&quot;&amp;lt;UR_PLUGIN_NAME&amp;gt;&quot; version=&quot;0.0.1&quot; provider-name=&quot;mycompany&quot;&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;lt;runtime&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;library name=&quot;&amp;lt;UR_PLUGIN_NAME&amp;gt;&quot;&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;lt;export name=&quot;*&quot;/&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/library&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;library name=&quot;jsoup.jar&quot; /&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;lt;/runtime&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;lt;requires&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;import plugin=&quot;nutch-extensionpoints&quot; /&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;/requires&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;lt;extension id=&quot;com.mycompany.parsefilter.TagExtractorParseFilter&quot;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; name=&quot;&amp;lt;UR_PLUGIN_NAME&amp;gt;&quot;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; point=&quot;org.apache.nutch.parse.HtmlParseFilter&quot;&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;implementation id=&quot;TagExtractorParseFilter&quot;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; class=&quot;com.mycompany.parsefilter.TagExtractorParseFilter&quot;/&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;lt;/extension&amp;gt;
&amp;lt;/plugin&amp;gt;
&lt;/pre&gt;&lt;br /&gt;
4) Remember to register this plugin in build.xml defined inside &amp;lt;NUTCH_HOME_DIR&amp;gt;/src/plugin/ . It can be registered : &amp;nbsp; &amp;nbsp; &amp;lt;ant dir=&quot;&amp;lt;UR_PLUGIN_NAME&amp;gt;&quot; target=&quot;deploy&quot; /&amp;gt; inside target tag. Failure to do so would result in your code not being packaged (into jar file). As of 1.3, the custom code is packaged into jar file and it resides in &amp;lt;NUTCH_HOME-DIR&amp;gt;/runtime/local/plugin/.&lt;br /&gt;
&lt;br /&gt;
5) Lastly write your custom code:&lt;br /&gt;
&lt;pre class=&quot;brush: csharp&quot;&gt;package com.mycompany.parsefilter;

import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.nutch.parse.HTMLMetaTags;
import org.apache.nutch.parse.HtmlParseFilter;
import org.apache.nutch.parse.ParseResult;
import org.apache.nutch.protocol.Content;
import org.w3c.dom.*;


import org.apache.commons.logging.Log;
import org.jsoup.*;
import org.jsoup.select.Elements;

public class TagExtractorParseFilter implements HtmlParseFilter {

&amp;nbsp;private Configuration conf;
&amp;nbsp;public static final Log LOG = LogFactory.getLog(TagExtractorParseFilter.class);

&amp;nbsp;@Override
&amp;nbsp;public Configuration getConf() {
&amp;nbsp; return conf;
&amp;nbsp;}

&amp;nbsp;@Override
&amp;nbsp;public void setConf(Configuration arg0) {
&amp;nbsp; conf = arg0;
&amp;nbsp;}

&amp;nbsp;@Override
&amp;nbsp;public ParseResult filter(Content content, ParseResult parseResult,
&amp;nbsp; &amp;nbsp;HTMLMetaTags metaTags, DocumentFragment doc) {
&amp;nbsp; org.jsoup.nodes.Document document =Jsoup.parse(content.toString());
&amp;nbsp; LOG.warn(&quot;document:&quot;+document.text());
&amp;nbsp; Elements e = document.select(&quot;#rcpinglist&quot;);
&amp;nbsp; if (e != null)
&amp;nbsp; &amp;nbsp;LOG.warn(&quot;ingredients:&quot;+e.text());
&amp;nbsp; else
&amp;nbsp; &amp;nbsp;LOG.warn(&quot;rcpinglist empty.&quot;);
&amp;nbsp; return parseResult;
&amp;nbsp;}
}
&lt;/pre&gt;&lt;br /&gt;
6) Running nutch which invokes custom code:&lt;br /&gt;
&amp;nbsp; &amp;nbsp;a) Create a seed list (the initial urls to fetch) :&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;mkdir urls&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;echo &quot;http://www.YOURWEBSITE.com/&quot; &amp;gt; urls/seed.txt&lt;br /&gt;
&amp;nbsp; &amp;nbsp;b) Inject seed url(s) to nutch crawldb (execute in &amp;lt;NUTCH_HOME_DIR&amp;gt;/runtime/local)&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;bin/nutch inject crawl/crawldb urls&lt;br /&gt;
&amp;nbsp; &amp;nbsp;c) Generate fetch list, fetch and parse content&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;bin/nutch generate crawl/crawldb crawl/segments&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;The above command will generate a new segment directory under crawl/segments that at this point contains files that store the url(s) to be fetched.&lt;br /&gt;
&amp;nbsp; &amp;nbsp;d) In the following commands we need the latest segment dir as parameter so we’ll store it in an environment variable:&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;export SEGMENT=crawl/segments/`ls -tr crawl/segments|tail -1`&lt;br /&gt;
&amp;nbsp; &amp;nbsp; e) Launch the fetcher that actually goes to get the content:&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;bin/nutch fetch $SEGMENT -noParsing&lt;br /&gt;
&amp;nbsp; &amp;nbsp; f) Next I parse the content:&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;bin/nutch parse $SEGMENT&lt;br /&gt;
&amp;nbsp; &amp;nbsp; g)&amp;nbsp;Update the Nutch crawldb. The updatedb command wil store all new urls discovered during the fetch and parse of the previous segment into Nutch database so they can be fetched later. Nutch also stores information about the pages that were fetched so the same urls won’t be fetched again and again.&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;bin/nutch updatedb crawl/crawldb $SEGMENT -filter -normalize&lt;br /&gt;
&amp;nbsp; &amp;nbsp; h)&amp;nbsp;Now a full Fetch cycle is completed. Next you can repeat steps (b-g) couple of times.&lt;br /&gt;
&lt;br /&gt;
Notice in the logs, custom code will be invoked.&lt;br /&gt;
&lt;br /&gt;
7) I would suggest reading my &lt;a href=&quot;http://amrutbudihal.blogspot.com/2011/07/debugging-nutch-parse.html&quot;&gt;debug&lt;/a&gt; experience to get better understanding of the system, just in case you are stuck.&lt;br /&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://amrutbudihal.blogspot.com/feeds/2267783937894577643/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/123791467816537526/2267783937894577643' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/2267783937894577643'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/2267783937894577643'/><link rel='alternate' type='text/html' href='http://amrutbudihal.blogspot.com/2011/07/nutch-custom-plugin-initial-experience.html' title='Nutch Custom Plugin: Initial Experience.'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-123791467816537526.post-4579418450024309542</id><published>2011-07-23T23:19:00.000-07:00</published><updated>2011-08-03T14:12:52.439-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="debug"/><category scheme="http://www.blogger.com/atom/ns#" term="Nutch"/><category scheme="http://www.blogger.com/atom/ns#" term="Nutch Parsing"/><category scheme="http://www.blogger.com/atom/ns#" term="Parser"/><title type='text'>Debugging Nutch Parse Filter.</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;&lt;b&gt;&lt;i&gt;&lt;u&gt;Introduction:&lt;/u&gt;&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;
I heard of Apache nutch, an open source crawler (well, actually it can do more than just crawling. It can index/search on the stored result, more info available at &lt;a href=&quot;http://nutch.apache.org/&quot;&gt;link&lt;/a&gt;).&amp;nbsp;My curiosity to try this out led me through its code internals which I would like to share in this post.&amp;nbsp;Particularly in this post I will share my debugging experience gained by stepping through its Parse segment internals.&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;&lt;u&gt;&lt;b&gt;Background:&lt;/b&gt;&lt;/u&gt;&lt;/i&gt;&lt;br /&gt;
I downloaded the latest stable code (at the time of this post, 1.3 being latest) from &lt;a href=&quot;http://www.apache.org/dyn/closer.cgi/nutch/&quot;&gt;link&lt;/a&gt;.&amp;nbsp;Custom plugin wiki is available at&amp;nbsp;&lt;a href=&quot;http://wiki.apache.org/nutch/PluginCentral&quot;&gt;link&lt;/a&gt;. At the time of writing this post, the tutorial is yet to update for nutch 1.3. Also one can integrate nutch in eclispe using instructions at&amp;nbsp;&lt;a href=&quot;http://wiki.apache.org/nutch/RunNutchInEclipse&quot;&gt;link&lt;/a&gt;, which is not yet complete.With little help available, I went the logging route, where I traced from the logs, what code was invoked. As of 1.3, Nutch can be run in two modes namely local and deploy. I tried the code in local, i.e. invoking nutch commands through command line inside dir &amp;lt;NUTCH_HOME_DIR&amp;gt;/runtime/local.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;i&gt;&lt;u&gt;What was I trying to achieve?&lt;/u&gt;&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;
I was trying to crawl/parse an Indian recipe &lt;a href=&quot;http://tarladalal.com/&quot;&gt;website&lt;/a&gt;. From each of the recipe pages on this website, I wanted to extract&amp;nbsp;Ingredients. Now one has two ways of going about this extraction, one which implements &lt;a href=&quot;http://nutch.apache.org/apidocs-1.3/org/apache/nutch/parse/Parser.html&quot;&gt;Parser&lt;/a&gt;&amp;nbsp;or&amp;nbsp;&lt;a href=&quot;http://nutch.apache.org/apidocs-1.3/org/apache/nutch/parse/HtmlParseFilter.html&quot;&gt;HtmlParseFilter&lt;/a&gt;. I choose HtmlParseFilter as Parser option is already taken care in &lt;a href=&quot;http://nutch.apache.org/apidocs-1.2/org/apache/nutch/parse/html/HtmlParser.html&quot;&gt;HtmlParser&lt;/a&gt; plugin.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;i&gt;&lt;u&gt;Approach:&lt;/u&gt;&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;
From the wiki, I found I needed three things to get my code to work. One being the java class which actually did the parsing (TagExtractorParseFilter), plugin.xml and build.xml. More details on how to write a custom plugin can be found at link (will put the link as soon as I complete the post).&lt;br /&gt;
My first and main road block was my custom code was not invoked. I saw following in hadoop.log (which is located at NUTCH_HOME_DIR/runtime/local/logs/hadoop.log) Changing the log level to DEBUG to get more log information.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;2011-07-23 00:33:37,592 WARN &amp;nbsp;parse.ParserFactory - ParserFactory:Plugin: org.apache.nutch.parse.html.HtmlParser mapped to contentType application/xhtml+xml via parse-plugins.xml, but its plugin.xml file does not claim to support contentType: application/xhtml+xml&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;1962011-07-23 11:21:53,547 WARN &amp;nbsp;parse.ParseUtil - Unable to successfully parse content http://www.tarladalal.com/ of type application/xhtml+xml&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
For the first comment, it turned out in my parse-plugins.xml had mapping of content type &quot;application/xhtml+xml&quot; but parse-html custom plugin&#39;s plugin.xml didnt state it. Had to make the following change, in order to make first error go away (in my case, it was an error as I need content type xhtml to be invoking HtmlParser code). First log is pretty intuitive and was resolved easily.&lt;br /&gt;
&lt;br /&gt;
For Second log, I had to put log statements into code starting from &lt;b&gt;ParseUtil -&amp;gt; HtmlParser -&amp;gt; HtmlParserFilters -&amp;gt; TagExtractorParseFilter&lt;/b&gt; (my custom code). Note my entry into ParseUtil was from the fact log&amp;nbsp;statement in hadoop.log&amp;nbsp;mentioned about it.&lt;br /&gt;
&lt;br /&gt;
ParseUtil internally uses ParserFactory (based on Factory design pattern) to get the appropriate parser based on content type (note this content type is mapped in parse-plugins.xml file and it should match the content type in plugins plugin.xml). Once ParseUtil get hold of appropriate parser (in my case, it is HtmlParser), it calls&lt;br /&gt;
&lt;pre class=&quot;brush: csharp&quot;&gt;parseResult = p.getParse(content)
&lt;/pre&gt;HtmlParser.getParse does a bunch of stuff before calling filters, invoked by&lt;br /&gt;
&lt;pre class=&quot;brush: csharp&quot;&gt;ParseResult filteredParse = this.htmlParseFilters.filter(content, parseResult, metaTags, root);&lt;/pre&gt;&lt;br /&gt;
As these Filters, they are programmed to the interface, in my case, TagExtractorParseFilter.filter is invoked. On further investigation, it turned out there was a NPE thrown. Following is the code snippet where NPE was thrown.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: csharp&quot;&gt;@Override
public ParseResult filter(Content content, ParseResult parseResult, HTMLMetaTags metaTags, DocumentFragment doc) {
 Document document = doc.getOwnerDocument();
 NodeList nodeList = e.getChildNodes();
 Element e = document.getElementById(&quot;rcpinglist&quot;);
 int size = nodeList.getLength();
 for (int i=0; i&amp;lt;size; i++) {
  Node nodeItem = nodeList.item(i);
  LOG.warn(&quot;Node Value:&quot;+nodeItem.getNodeValue());
 }
 return parseResult;
}
&lt;/pre&gt;&lt;br /&gt;
&lt;div class=&quot;p2&quot;&gt;Note error thrown was a NPE, but the stack trace was never visible in either logs or stdout. As it turns out, the exception was being swallowed and parseResult.isSuccess() was set to false, indicating parsing had failed. Following code snippet from HtmlParseFilters, which shows how each filter is called and parseResult is obtained from each. Also on error during filter, original parseResult is returned.&lt;/div&gt;&lt;br /&gt;
&lt;div class=&quot;p1&quot;&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;color: black; font-family: inherit; font-size: small;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: csharp&quot;&gt;/** Run all defined filters. */
public ParseResult filter(Content content, ParseResult parseResult, HTMLMetaTags metaTags, DocumentFragment doc) {
  // loop on each filter
  for (int i = 0 ; i &amp;lt; this.htmlParseFilters.length; i++) {
   // call filter interface
   parseResult = htmlParseFilters[i].filter(content, parseResult, metaTags, doc);
   // any failure on parse obj, return
   if (!parseResult.isSuccess()) {
   // TODO: What happens when parseResult.isEmpty() ?
   // Maybe clone parseResult and use parseResult as backup...
   // remove failed parse before return
   parseResult.filter();
   return parseResult;
  }
 }
 return parseResult;
}
&lt;/pre&gt;&lt;br /&gt;
&lt;div&gt;Once code execution returned to ParseUtils, second log message was printed due to this error.Thus I modified my code to handle the NPE and that resolved my second log.&lt;/div&gt;&lt;br /&gt;
&lt;pre class=&quot;brush: csharp&quot;&gt;@Override
public ParseResult filter(Content content, ParseResult parseResult, HTMLMetaTags metaTags, DocumentFragment doc) {
 Document document = doc.getOwnerDocument();
 NodeList nodeList = e.getChildNodes();
 Element e = document.getElementById(&quot;rcpinglist&quot;);
 if (e != null) {
  int size = nodeList.getLength();
  for (int i=0; i&amp;lt;size; i++) {
   Node nodeItem = nodeList.item(i);
   LOG.warn(&quot;Node Value:&quot;+nodeItem.getNodeValue());
  }
 } else {
  LOG.warn(&quot;no rcpinglist found.&quot;);
 }
 return parseResult;
}
&lt;/pre&gt;&lt;br /&gt;
&lt;b&gt;&lt;u&gt;&lt;i&gt;Conclusion:&lt;/i&gt;&lt;/u&gt;&lt;/b&gt;&lt;br /&gt;
Log statements are very useful and give good insights on debugging the program. Using log as my starting point, I was able to understand the code in a much better way. The only con I feel is it takes little extra time. I would recommend this route if one needs to understand a system internals.&lt;br /&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://amrutbudihal.blogspot.com/feeds/4579418450024309542/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/123791467816537526/4579418450024309542' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/4579418450024309542'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/4579418450024309542'/><link rel='alternate' type='text/html' href='http://amrutbudihal.blogspot.com/2011/07/debugging-nutch-parse.html' title='Debugging Nutch Parse Filter.'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-123791467816537526.post-4747263547138978684</id><published>2011-07-11T16:56:00.000-07:00</published><updated>2011-08-06T21:12:33.407-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Data Structure"/><category scheme="http://www.blogger.com/atom/ns#" term="Heap"/><category scheme="http://www.blogger.com/atom/ns#" term="java"/><category scheme="http://www.blogger.com/atom/ns#" term="LRU Cache"/><category scheme="http://www.blogger.com/atom/ns#" term="PriorityQueue"/><title type='text'>LRU Cache using PriorityQueue</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;&lt;br /&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;&lt;b&gt;&lt;i&gt;&lt;u&gt;Background&lt;/u&gt;&lt;/i&gt;&lt;/b&gt;:&lt;br /&gt;
LRU (least recently used) cache is a good way of implementing cache because it frees up precious memory based on the last time an element was used. The criteria here is to purge items based on when the last time a particular element was last accessed.&lt;br /&gt;
&lt;br /&gt;
In this post, I tried to implement a LRU cache using PriorityQueue. My rationale for choosing this combination: &lt;a href=&quot;http://download.oracle.com/javase/6/docs/api/java/util/PriorityQueue.html&quot;&gt;PriorityQueue&lt;/a&gt; does the sorting internally based on Comparator passed to it and I can concentrate on creating the cache, rather than worrying about maintaining the sort order.&lt;br /&gt;
&lt;br /&gt;
&lt;b style=&quot;font-style: italic; text-decoration: underline;&quot;&gt;Update:&lt;/b&gt;&amp;nbsp;Check another way of implementing lru which is more terse found at &lt;a href=&quot;http://amrutbudihal.blogspot.com/2011/07/lru-cache-another-way-by-extending.html&quot;&gt;link&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;i&gt;&lt;u&gt;Implementation details&lt;/u&gt;&lt;/i&gt;&lt;/b&gt;:&lt;br /&gt;
I mainly have two Classes:&lt;br /&gt;
1) Element.java: This is the place holder for values in PriorityQueue (PQ) which has timestamp and value attributes. timestamp is to be updated when a element is accessed (put or get operations inside LRUCache)&lt;br /&gt;
2) LRUCache.java: This class uses PQ, elements are ordered in this object based on timestamp (the least timestamp would the first to be popped out). There is a cap SIZE of 5 on the cache. HashMap stores the actual value. LRUCache provides two public methods: get and put. Each of which is explained below:&lt;br /&gt;
&amp;nbsp; &amp;nbsp;a) get(String key) : Idea is as soon as the key is&amp;nbsp;retrieved&amp;nbsp;from the map, element key (this key matches to that of HashMap, key) in PriorityQueue should be updated with current timestamp.&lt;br /&gt;
&amp;nbsp; &amp;nbsp;b)&amp;nbsp;put(String key, String value) : This method is responsible for checking if the key entry exist in map (in this case, it is a cache hit and hence update the PQ element with new timestamp). If the key does not exist in map, check if the map size. If greater than the cap set, remove the least recently used entry. If not greater, simply add this key to PQ.&lt;br /&gt;
&amp;nbsp; &amp;nbsp;c) There are a set of private methods (insert, remove and update) which operate on PQ. &lt;b&gt;insert&lt;/b&gt; and &lt;b&gt;remove&lt;/b&gt; are&amp;nbsp;pretty&amp;nbsp;intuitive, both methods use offer and poll methods provided by PQ.&lt;br /&gt;
&amp;nbsp; &amp;nbsp;d) &lt;b&gt;Update&lt;/b&gt; operation is interesting. I knew that underlying data structure for PQ is &lt;a href=&quot;http://en.wikipedia.org/wiki/Heap_(data_structure)&quot;&gt;heap&lt;/a&gt;.&amp;nbsp;I was under the impression, that modifying elements (updating the timestamp) in PQ would initiate re-ordering of elements process. My assumption soon proved wrong when I saw the output not matching the required behavior. As it turned out, a heap would update only on inserting or removing operations and hence for my update operation, I had to delete and insert the entry(partially sorted tree) into PQ. Thus update operation is performed in O(n.logn) times (find a element takes linear time and then inserting that element takes logn time).&lt;br /&gt;
&lt;br /&gt;
Following is the code snippet:&lt;br /&gt;
&lt;pre class=&quot;brush: csharp&quot;&gt;package lru;

import java.util.*;
import org.apache.commons.lang.StringUtils;

public class LRUCache {
private static final int SIZE = 5;
private Map&amp;lt;String, String&amp;gt; map = new HashMap&amp;lt;String, String&amp;gt;();

private PriorityQueue&amp;lt;Element&amp;gt; pq = new PriorityQueue&amp;lt;Element&amp;gt;(SIZE, new Comparator(){
 @Override
 public int compare(Object arg0, Object arg1) {
 //Ordering the elements as per timestamp.
 if (! (arg0 instanceof Element) || !(arg1 instanceof Element))
   return 0;
 Element e1 = (Element) arg0;
 Element e2 = (Element) arg1;
 return e1.getTimestamp().compareTo(e2.getTimestamp());
 }
});

private void insert(Element e) {
 System.out.println(&quot;Received Element: &quot;+e.getValue());
 pq.offer(e);
}

private String remove() {
 Element leastUsed = pq.poll();
 if (leastUsed != null) {
  System.out.println(&quot;Removing least used element:&quot;+leastUsed.getValue());
  System.out.println(&quot;This element was last used:&quot;+leastUsed.getTimestamp());
  return leastUsed.getValue();
 }
 return &quot;&quot;;
}

private void update(String mostRecentEleKey) {
 //update priority queue with most recent access.
 //Internal datastructure on PriorityQueue is Heap and it is partially sorted.
 //This means, any update on elements means to delete them and add them again.

 Iterator&amp;lt;Element&amp;gt; pqIterator = pq.iterator();
 while(pqIterator.hasNext()) {
 Element e = pqIterator.next();
  if(e.getValue().equals(mostRecentEleKey)) {
   pqIterator.remove();
   break;
  }
 }
 Element mostRecent = new Element();
 mostRecent.setTimestamp(new Date());
 mostRecent.setValue(mostRecentEleKey);
 insert(mostRecent);
}

public String get(String key) {
 String value = map.get(key);
 if (StringUtils.isNotEmpty(value)) {
  System.out.println(&quot;Updating &quot;+key+&quot; with current timestamp.&quot;);
  update(key);
 }
 return value;
}

public void put(String key, String value) {
 System.out.println(&quot;Before put opertaion, map size:&quot;+map.size());
 if (map.containsKey(key)) {
  System.out.println(&quot;Cache hit on key:&quot;+key+&quot;, nothing to insert!&quot;);
  update(key);
 } else {
  if(map.size() &amp;gt;= SIZE) {
   String leastUsedKey = remove();
   map.remove(leastUsedKey);
  }
  System.out.println(&quot;Element not present in Cache: &quot;+key);
  Element e = new Element();
  e.setValue(key);
  e.setTimestamp(new Date());
  insert(e);
  map.put(key,value);
 }
 System.out.println(&quot;After put operation, following stats are generated:&quot;);
 System.out.println(&quot;Least used element:&quot;+pq.peek().getValue()+&quot;, last used at:&quot;+pq.peek().getTimestamp());
 System.out.println(&quot;map size:&quot;+map.size());
}

public static void main(String[] args) {
 LRUCache cache = new LRUCache();
 cache.put(&quot;a&quot;,&quot;a&quot;);cache.put(&quot;b&quot;,&quot;b&quot;);
 cache.put(&quot;c&quot;,&quot;c&quot;);cache.put(&quot;d&quot;,&quot;d&quot;);
 cache.put(&quot;e&quot;,&quot;e&quot;);cache.put(&quot;b&quot;,&quot;b&quot;);
 cache.put(&quot;d&quot;,&quot;d&quot;);cache.put(&quot;b&quot;,&quot;b&quot;);
 cache.put(&quot;c&quot;,&quot;c&quot;);cache.put(&quot;b&quot;,&quot;b&quot;);
 cache.put(&quot;a&quot;,&quot;a&quot;);cache.put(&quot;f&quot;,&quot;f&quot;);
 cache.get(&quot;a&quot;);
}
}


package lru;

import java.util.Date;
import org.apache.commons.lang.builder.*;

public class Element {
private String value;
private Date timestamp;

public String getValue() {
 return value;
}

public void setValue(String value) {
 this.value = value;
}

public Date getTimestamp() {
 return timestamp;
}

public void setTimestamp(Date timestamp) {
 this.timestamp = timestamp;
}

public boolean equals(Element e) {
 return value.equals(e.getValue());
}

public int hashCode(){
 return new HashCodeBuilder(17,37).append(value).toHashCode();
}
}
&lt;/pre&gt;&lt;br /&gt;
This produces &lt;b&gt;output&lt;/b&gt;:&lt;br /&gt;
&lt;br /&gt;
Before put opertaion, map size:0&lt;br /&gt;
Element not present in Cache: a&lt;br /&gt;
Received Element: a&lt;br /&gt;
After put operation, following stats are generated:&lt;br /&gt;
Least used element:a, last used at: Mon Jul 11 17:03:29 PDT 2011&lt;br /&gt;
map size:1&lt;br /&gt;
Before put opertaion, map size:1&lt;br /&gt;
Element not present in Cache: b&lt;br /&gt;
Received Element: b&lt;br /&gt;
After put operation, following stats are generated:&lt;br /&gt;
Least used element:a, last used at: Mon Jul 11 17:03:29 PDT 2011&lt;br /&gt;
map size:2&lt;br /&gt;
Before put opertaion, map size:2&lt;br /&gt;
Element not present in Cache: c&lt;br /&gt;
Received Element: c&lt;br /&gt;
After put operation, following stats are generated:&lt;br /&gt;
Least used element:a, last used at: Mon Jul 11 17:03:29 PDT 2011&lt;br /&gt;
map size:3&lt;br /&gt;
Before put opertaion, map size:3&lt;br /&gt;
Element not present in Cache: d&lt;br /&gt;
Received Element: d&lt;br /&gt;
After put operation, following stats are generated:&lt;br /&gt;
Least used element:a, last used at: Mon Jul 11 17:03:29 PDT 2011&lt;br /&gt;
map size:4&lt;br /&gt;
Before put opertaion, map size:4&lt;br /&gt;
Element not present in Cache: e&lt;br /&gt;
Received Element: e&lt;br /&gt;
After put operation, following stats are generated:&lt;br /&gt;
Least used element:a, last used at: Mon Jul 11 17:03:29 PDT 2011&lt;br /&gt;
map size:5&lt;br /&gt;
Before put opertaion, map size:5&lt;br /&gt;
Cache hit on key:b, nothing to insert!&lt;br /&gt;
Received Element: b&lt;br /&gt;
After put operation, following stats are generated:&lt;br /&gt;
Least used element:a, last used at: Mon Jul 11 17:03:29 PDT 2011&lt;br /&gt;
map size:5&lt;br /&gt;
Before put opertaion, map size:5&lt;br /&gt;
Cache hit on key:d, nothing to insert!&lt;br /&gt;
Received Element: d&lt;br /&gt;
After put operation, following stats are generated:&lt;br /&gt;
Least used element:a, last used at: Mon Jul 11 17:03:29 PDT 2011&lt;br /&gt;
map size:5&lt;br /&gt;
Before put opertaion, map size:5&lt;br /&gt;
Cache hit on key:b, nothing to insert!&lt;br /&gt;
Received Element: b&lt;br /&gt;
After put operation, following stats are generated:&lt;br /&gt;
Least used element:a, last used at: Mon Jul 11 17:03:29 PDT 2011&lt;br /&gt;
map size:5&lt;br /&gt;
Before put opertaion, map size:5&lt;br /&gt;
Cache hit on key:c, nothing to insert!&lt;br /&gt;
Received Element: c&lt;br /&gt;
After put operation, following stats are generated:&lt;br /&gt;
Least used element:a, last used at: Mon Jul 11 17:03:29 PDT 2011&lt;br /&gt;
map size:5&lt;br /&gt;
Before put opertaion, map size:5&lt;br /&gt;
Cache hit on key:b, nothing to insert!&lt;br /&gt;
Received Element: b&lt;br /&gt;
After put operation, following stats are generated:&lt;br /&gt;
Least used element:a, last used at: Mon Jul 11 17:03:29 PDT 2011&lt;br /&gt;
map size:5&lt;br /&gt;
Before put opertaion, map size:5&lt;br /&gt;
Cache hit on key:a, nothing to insert!&lt;br /&gt;
Received Element: a&lt;br /&gt;
After put operation, following stats are generated:&lt;br /&gt;
Least used element:e, last used at: Mon Jul 11 17:03:29 PDT 2011&lt;br /&gt;
map size:5&lt;br /&gt;
Before put opertaion, map size:5&lt;br /&gt;
Removing least used element:e&lt;br /&gt;
This element was last used:Mon Jul 11 17:03:29 PDT 2011&lt;br /&gt;
Element not present in Cache: f&lt;br /&gt;
Received Element: f&lt;br /&gt;
After put operation, following stats are generated:&lt;br /&gt;
Least used element:d, last used at: Mon Jul 11 17:03:29 PDT 2011&lt;br /&gt;
map size:5&lt;br /&gt;
Updating a with current timestamp.&lt;br /&gt;
Received Element: a&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Further work:&lt;/b&gt;&lt;br /&gt;
1) Need to make it &lt;a href=&quot;http://amrutbudihal.blogspot.com/2011/06/java-generics.html&quot;&gt;generic&lt;/a&gt;.&lt;br /&gt;
2) Handling memory leak.&lt;/div&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://amrutbudihal.blogspot.com/feeds/4747263547138978684/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/123791467816537526/4747263547138978684' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/4747263547138978684'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/123791467816537526/posts/default/4747263547138978684'/><link rel='alternate' type='text/html' href='http://amrutbudihal.blogspot.com/2011/07/lru-cache-using-priorityqueue.html' title='LRU Cache using PriorityQueue'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>