<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-6719192018427942027</id><updated>2012-01-30T15:47:30.201-08:00</updated><category term='hibernate'/><category term='ehcache'/><title type='text'>Code Smart</title><subtitle type='html'>Just a youngster trying to learn more about software development.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>36</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-1342278816553717329</id><published>2011-10-07T13:07:00.000-07:00</published><updated>2011-10-07T19:34:10.032-07:00</updated><title type='text'>Ruby script to list process info in Windows</title><content type='html'>Below is a Ruby script to list process id and its command line in Windows.&lt;br/&gt;&lt;p&gt;To run it:&lt;br/&gt;ruby pid.rb &amp;lt;process-name&amp;gt;&lt;br/&gt;ie: ruby pid.rb java:  will list java.exe and javaw.exe processes&lt;/p&gt;&lt;br/&gt;&lt;pre class="text" name="code"&gt;&lt;br /&gt;class ProcessInfo&lt;br /&gt;  attr_accessor :cmdLine, :pid&lt;br /&gt;  &lt;br /&gt;  def self.parseWmic(cmd)&lt;br /&gt;    result = `#{cmd}`&lt;br /&gt;    raise("Error: " + result) unless $? == 0&lt;br /&gt;    processes = []&lt;br /&gt;    pinfo = nil&lt;br /&gt;    result.split(/\r?\n/).each do |line|&lt;br /&gt;      next if line =~ /^\s*$/&lt;br /&gt;      if line =~ /CommandLine=(.*)/i&lt;br /&gt;        pinfo = ProcessInfo.new&lt;br /&gt;        pinfo.cmdLine = $1&lt;br /&gt;      elsif line =~ /ProcessId=(\d+)/i&lt;br /&gt;        pinfo.pid = $1&lt;br /&gt;        processes &lt;&lt; pinfo unless pinfo.pid.to_i == $$.to_i&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;    return processes&lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  def self.queryProcess(processName)&lt;br /&gt;    return self.parseWmic("wmic process where \"name like '" + processName + &lt;br /&gt;                "%'\" get ProcessID,Commandline /format:list")&lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  def self.queryJavaProcess&lt;br /&gt;    return self.queryProcess('java')&lt;br /&gt;  end  &lt;br /&gt;  &lt;br /&gt;  def to_s&lt;br /&gt;    @pid.to_s + " " + @cmdLine&lt;br /&gt;  end	&lt;br /&gt;end	&lt;br /&gt;&lt;br /&gt;puts ProcessInfo.queryProcess(ARGV[0])&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-1342278816553717329?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/1342278816553717329/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=1342278816553717329' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/1342278816553717329'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/1342278816553717329'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2011/10/ruby-script-to-list-process-info-in.html' title='Ruby script to list process info in Windows'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-8171921157529003289</id><published>2011-10-06T16:54:00.000-07:00</published><updated>2011-10-06T16:54:20.962-07:00</updated><title type='text'>Equivalent of ps -ef | grep java in Windows</title><content type='html'>If you want to list a process and its command line, process id in Windows, this is the command:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;wmic process where "name='java.exe'" get ProcessID, Commandline /format:list&lt;/div&gt;&lt;br /&gt;That is the equivalent of "ps -ef | grep java" in Linux. Then you could use taskkill /f /pid &lt;processid&gt;&amp;lt;PID&amp;gt; to kill it.&lt;/processid&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-8171921157529003289?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/8171921157529003289/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=8171921157529003289' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/8171921157529003289'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/8171921157529003289'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2011/10/equivalent-of-ps-ef-grep-java-in.html' title='Equivalent of ps -ef | grep java in Windows'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-3370234279885299114</id><published>2011-09-23T16:46:00.000-07:00</published><updated>2011-09-23T16:50:02.086-07:00</updated><title type='text'>Install JDK on Windows without a UI</title><content type='html'>in CMD.exe shell:&lt;br /&gt;&lt;br /&gt;./jdk-6u27-windows-x64.exe /s INSTALLDIR=\c:\jdk\jdk1.6.0_27-x64&lt;br /&gt;&lt;br /&gt;This will allow you to install the JDK automatically without any GUI prompt, ideal for automation script.Note the forward slash before the c: drive is necessary. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-3370234279885299114?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/3370234279885299114/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=3370234279885299114' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/3370234279885299114'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/3370234279885299114'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2011/09/install-jdk-on-windows-without-ui.html' title='Install JDK on Windows without a UI'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-8784832601452759322</id><published>2011-04-07T11:51:00.000-07:00</published><updated>2011-09-08T18:49:48.460-07:00</updated><title type='text'>Force JBoss to load your lib jars</title><content type='html'>If you're running JBoss and you want it to favor librarires that go with your WAR instead of using what JBoss bundled with. Add this jboss-web.xml to your WEB-INF dir&lt;br /&gt;&lt;br /&gt;&lt;pre class="xml" name="code"&gt;&lt;br /&gt;&amp;lt;jboss-web&amp;gt;&lt;br /&gt;  &amp;lt;class-loading java2classloadingcompliance="false"&amp;gt;&lt;br /&gt;  &amp;lt;loader-repository&amp;gt;&lt;br /&gt;      org.terracotta:archive=all-in-one.war&lt;br /&gt;      &amp;lt;loader-repository-config&amp;gt;&lt;br /&gt;          java2ParentDelegation=false&lt;br /&gt;      &amp;lt;/loader-repository-config&amp;gt;&lt;br /&gt;  &amp;lt;/loader-repository&amp;gt;&lt;br /&gt;  &amp;lt;/class-loading&amp;gt;&lt;br /&gt;&amp;lt;/jboss-web&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Substitute "org.terracotta" for your package name and "all-in-one.war" for your WAR&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-8784832601452759322?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/8784832601452759322/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=8784832601452759322' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/8784832601452759322'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/8784832601452759322'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2011/04/force-jboss-to-load-your-lib-jars.html' title='Force JBoss to load your lib jars'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-3610085271078828684</id><published>2010-11-04T10:51:00.000-07:00</published><updated>2010-11-04T10:54:53.111-07:00</updated><title type='text'>QA Engineer needed at Terracotta</title><content type='html'>If you're like testing softwares, writing applications to find bugs and figuring out performances, &lt;a href="http://terracotta.org"&gt;Terracotta &lt;/a&gt;is looking for QA engineers.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-3610085271078828684?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/3610085271078828684/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=3610085271078828684' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/3610085271078828684'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/3610085271078828684'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2010/11/qa-engineer-needed-at-terracotta.html' title='QA Engineer needed at Terracotta'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-4521199514136767555</id><published>2010-07-15T16:47:00.001-07:00</published><updated>2010-07-15T16:47:58.069-07:00</updated><title type='text'>Very bizarre problem when running JDK 32 bit on a 64 bit Linux box</title><content type='html'>I just noticed a very weird problem if you use a 32bit Sun JDK on a 64bit RedHat Linux.&lt;br /&gt;&lt;br /&gt;The "user.home" system properties, returned by System.getProperty("user.home"), has the value of "?". Same thing happens with "user.name" property.&lt;br /&gt;&lt;br /&gt;I'm not sure why this is but seems like a bug too me.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;-bash-3.00$ /shares/jdk/hotspot1.6.0_16/bin/java ShowSystemProps | grep "user"&lt;br /&gt;user.country = US&lt;br /&gt;user.dir = /home/hhuynh&lt;br /&gt;user.home = ?&lt;br /&gt;user.timezone = &lt;br /&gt;user.name = ?&lt;br /&gt;user.language = en&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Correct values if I used 64bit JDK&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;-bash-3.00$ /shares/jdk/hotspot1.6.0_16_x64/bin/java ShowSystemProps | grep "user"&lt;br /&gt;user.country = US&lt;br /&gt;user.dir = /home/hhuynh&lt;br /&gt;user.home = /export2/homes/hhuynh&lt;br /&gt;user.timezone = &lt;br /&gt;user.name = hhuynh&lt;br /&gt;user.language = en&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This bug leads to Maven creating the local repository under the current working dir: "./?/.m2/repository" and leads to many confusing problems down the road.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-4521199514136767555?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/4521199514136767555/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=4521199514136767555' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/4521199514136767555'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/4521199514136767555'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2010/07/very-bizarre-problem-when-running-jdk.html' title='Very bizarre problem when running JDK 32 bit on a 64 bit Linux box'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-8487565462343196920</id><published>2010-07-14T16:04:00.000-07:00</published><updated>2010-07-14T16:13:20.417-07:00</updated><title type='text'>Simple Bash script to rotate log</title><content type='html'>If you have some program that doesn't handle its own log rotation, you could use the below script to rotate the log. Ideally, you would call this script in a daily crob job.&lt;br /&gt;&lt;br /&gt;&lt;pre class="ruby" name="code"&gt;&lt;br /&gt;#!/bin/bash&lt;br /&gt;logfile=$1&lt;br /&gt;if [ ! -f $logfile ]; then&lt;br /&gt;  echo "log file not found $logfile"&lt;br /&gt;  exit 1&lt;br /&gt;fi&lt;br /&gt;timestamp=`date +%Y%m%d`&lt;br /&gt;newlogfile=$logfile.$timestamp&lt;br /&gt;cp $logfile $newlogfile&lt;br /&gt;cat /dev/null &gt; $logfile&lt;br /&gt;gzip -f -9 $newlogfile&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-8487565462343196920?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/8487565462343196920/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=8487565462343196920' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/8487565462343196920'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/8487565462343196920'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2010/07/simple-bash-script-to-do-log-rotation.html' title='Simple Bash script to rotate log'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-5331412210396059026</id><published>2009-08-24T18:36:00.000-07:00</published><updated>2009-08-24T18:51:09.926-07:00</updated><title type='text'>Maven gotcha: "post-integration-test" phase</title><content type='html'>In Maven, "post-integration-test" phase can be used to do test clean up, for example, to shut down Cargo after the tests finished.&lt;br /&gt;&lt;br /&gt;If you run "maven install" then the phases would be in this order:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;pre-integration-test:  start Cargo (web server)&lt;br /&gt;integration-test:      start tests&lt;br /&gt;post-integratino-test: stop Cargo &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;However, if you just run "maven integration-test" to run the tests directly, "post-integration-test" phase is never called, which might lead to your cleanup process not being run.&lt;br /&gt;&lt;br /&gt;So the workaround is to bind your clean up tasks to "integration-test" itself which will be run right after the tests run during that phase.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-5331412210396059026?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/5331412210396059026/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=5331412210396059026' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/5331412210396059026'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/5331412210396059026'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2009/08/maven-gotcha-post-integration-test.html' title='Maven gotcha: &quot;post-integration-test&quot; phase'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-625921436440801736</id><published>2009-08-08T21:53:00.000-07:00</published><updated>2009-08-08T22:02:47.541-07:00</updated><title type='text'>Maven Javadoc workaround: Unable to find package java.lang in classpath or bootclasspath</title><content type='html'>Seen this before?&lt;br /&gt;&lt;br /&gt;&lt;pre class="java" name="code"&gt;&lt;br /&gt;Embedded error: Error rendering Maven report: Exit code: 1 - com.sun.tools.javac.util.FatalError: Fatal Error: Unable to find package java.lang in classpath or bootclasspath&lt;br /&gt; at com.sun.tools.javac.comp.MemberEnter.importAll(MemberEnter.java:123)&lt;br /&gt; at com.sun.tools.javac.comp.MemberEnter.visitTopLevel(MemberEnter.java:509)&lt;br /&gt; at com.sun.tools.javac.tree.JCTree$JCCompilationUnit.accept(JCTree.java:446)&lt;br /&gt; at com.sun.tools.javac.comp.MemberEnter.memberEnter(MemberEnter.java:387)&lt;br /&gt; at com.sun.tools.javac.comp.MemberEnter.complete(MemberEnter.java:819)&lt;br /&gt; at com.sun.tools.javac.code.Symbol.complete(Symbol.java:386)&lt;br /&gt; at com.sun.tools.javac.code.Symbol$ClassSymbol.complete(Symbol.java:758)&lt;br /&gt; at com.sun.tools.javac.comp.Enter.complete(Enter.java:451)&lt;br /&gt; at com.sun.tools.javac.comp.Enter.main(Enter.java:429)&lt;br /&gt; at com.sun.tools.javadoc.JavadocEnter.main(JavadocEnter.java:53)&lt;br /&gt; at com.sun.tools.javadoc.JavadocTool.getRootDocImpl(JavadocTool.java:152)&lt;br /&gt; at com.sun.tools.javadoc.Start.parseAndExecute(Start.java:330)&lt;br /&gt; at com.sun.tools.javadoc.Start.begin(Start.java:128)&lt;br /&gt; at com.sun.tools.javadoc.Main.execute(Main.java:41)&lt;br /&gt; at com.sun.tools.javadoc.Main.main(Main.java:31)&lt;br /&gt;javadoc: error - fatal error&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This error has baffled me for the last time. Though I don't understand why the plugin or javadoc would trigger this error, I found a workaround: adding rt.jar to the "bootclasspath":&lt;br /&gt;&lt;br /&gt;&amp;lt;bootclasspath&amp;gt;${java.home}/lib/rt.jar&amp;lt;/bootclasspath&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="xml" name="code"&gt;&lt;br /&gt;         &amp;lt;plugin&amp;gt;&lt;br /&gt;            &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;&lt;br /&gt;            &amp;lt;artifactId&amp;gt;maven-javadoc-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;            &amp;lt;version&amp;gt;2.6&amp;lt;/version&amp;gt;&lt;br /&gt;            &amp;lt;configuration&amp;gt;&lt;br /&gt;               &amp;lt;minmemory&amp;gt;128m&amp;lt;/minmemory&amp;gt;&lt;br /&gt;               &amp;lt;maxmemory&amp;gt;512m&amp;lt;/maxmemory&amp;gt;&lt;br /&gt;               &amp;lt;!-- fix error can't find java.lang package --&amp;gt;&lt;br /&gt;               &amp;lt;bootclasspath&amp;gt;${java.home}/lib/rt.jar&amp;lt;/bootclasspath&amp;gt;&lt;br /&gt;            &amp;lt;/configuration&amp;gt;&lt;br /&gt;            &amp;lt;reportSets&amp;gt;&lt;br /&gt;               &amp;lt;reportSet&amp;gt;&lt;br /&gt;                  &amp;lt;reports&amp;gt;&lt;br /&gt;                     &amp;lt;report&amp;gt;aggregate&amp;lt;/report&amp;gt;&lt;br /&gt;                  &amp;lt;/reports&amp;gt;&lt;br /&gt;               &amp;lt;/reportSet&amp;gt;&lt;br /&gt;            &amp;lt;/reportSets&amp;gt;&lt;br /&gt;         &amp;lt;/plugin&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-625921436440801736?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/625921436440801736/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=625921436440801736' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/625921436440801736'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/625921436440801736'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2009/08/maven-javadoc-workaround-unable-to-find.html' title='Maven Javadoc workaround: Unable to find package java.lang in classpath or bootclasspath'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-4717484189618952621</id><published>2009-06-23T09:11:00.000-07:00</published><updated>2009-06-23T09:45:23.566-07:00</updated><title type='text'>Starting Terracotta Server as a Windows service</title><content type='html'>There's an easy way to set up Terracotta server as Windows service using the open source &lt;a href="http://wrapper.tanukisoftware.org"&gt;Java Service Wrapper&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;I've used their &lt;a href="http://wrapper.tanukisoftware.org/doc/english/integrate.html"&gt;Integration Method 1&lt;/a&gt;, which uses a wrapper &lt;span style="font-weight:bold;"&gt;WrapperSimpleApp &lt;/span&gt;to run Terracotta server main class &lt;span style="font-weight:bold;"&gt;com.tc.server.TCServerMain&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;First, just download and install the latest version wrapper-windows-x86-32-3.3.5. Then make copy of conf/wrapper.conf and name it TerracottaServer.conf.&lt;br /&gt;&lt;br /&gt;Fill out these needed properties:&lt;br /&gt;&lt;pre name="code"&gt;&lt;br /&gt;# for logging&lt;br /&gt;wrapper.java.command.loglevel=INFO&lt;br /&gt;&lt;br /&gt;# Method 1 main class&lt;br /&gt;wrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperSimpleApp&lt;br /&gt;&lt;br /&gt;# classpath&lt;br /&gt;# (1) is for WrapperSimpleApp&lt;br /&gt;# (2) is for Terracotta Server - You'll need to fix up the path for your own use&lt;br /&gt;wrapper.java.classpath.1=../lib/wrapper.jar&lt;br /&gt;wrapper.java.classpath.2=d:/work/builds/terracotta-3.0.1/lib/tc.jar&lt;br /&gt;&lt;br /&gt;# Java Additional Parameters&lt;br /&gt;## NOTE: -server option only works with a JDK, not with JRE&lt;br /&gt;wrapper.java.additional.1=-server &lt;br /&gt;wrapper.java.additional.2=-XX:+HeapDumpOnOutOfMemoryError&lt;br /&gt;wrapper.java.additional.3=-Dcom.sun.management.jmxremote&lt;br /&gt;wrapper.java.additional.4=-Dtc.install-root=d:/work/builds/terracotta-3.0.1&lt;br /&gt;&lt;br /&gt;# Initial Java Heap Size (in MB)&lt;br /&gt;wrapper.java.initmemory=512&lt;br /&gt;&lt;br /&gt;# Maximum Java Heap Size (in MB)&lt;br /&gt;wrapper.java.maxmemory=512&lt;br /&gt;&lt;br /&gt;# Application parameters. This is where you specify TC server main class&lt;br /&gt;wrapper.app.parameter.1=com.tc.server.TCServerMain&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That's pretty much all you need. There are Batch scripts in the "bin" folder of the wrapper installation where you can install/uninstall your service. Just make sure you modify those scripts to point to TerracottaServer.conf file you made earlier.&lt;br /&gt;&lt;br /&gt;Once you have the service install, you can start/stop the service by using Windows services manager or by using the scripts. There will be a log of the run under "&lt;wrapper_home&gt;/logs"&lt;br /&gt;&lt;br /&gt;I've made a &lt;a href="http://hhuynh.googlepages.com/TerracottaServerServiceWrapper.zip"&gt;copy&lt;/a&gt; of the scripts and the TerracottaServer.conf for easy tryout.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-4717484189618952621?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/4717484189618952621/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=4717484189618952621' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/4717484189618952621'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/4717484189618952621'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2009/06/starting-terracotta-server-as-windows.html' title='Starting Terracotta Server as a Windows service'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-5807912854515210810</id><published>2009-06-03T22:44:00.000-07:00</published><updated>2009-06-03T23:25:14.440-07:00</updated><title type='text'>Executing a Maven plugin from a Maven plugin</title><content type='html'>I've searched for a way to call a Maven plugin from inside another plugin but couldn't find any. So I've put on my hacker hat and made it happen. It might not be the best way to do it or even orthodox but here goes.&lt;br /&gt;&lt;br /&gt;For example, Terracotta has a Maven plugin, namely "tc" and I want to add a 'help' goal to it which works exactly like the &lt;a href="http://maven.apache.org/plugins/maven-help-plugin/"&gt;help plugin&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;With the &lt;span style="font-weight:bold;"&gt;help plugin&lt;/span&gt;, you can do this:&lt;br /&gt;&lt;pre name="code"&gt;&lt;br /&gt;mvn help:describe org.terracotta.maven.plugins:tc-maven-plugin -Ddetail&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;But it's a little verbose. I want to do&lt;br /&gt;&lt;pre name="code"&gt; &lt;br /&gt;mvn tc:help&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;which would accomplish the same thing. To be able to do that, I have my "tc" plugin depended on "help" by declaring it as a dependency in the pom.&lt;br /&gt;&lt;br /&gt;&lt;pre class="xml" name="code"&gt;&lt;br /&gt;    &amp;lt;dependency&amp;gt;&lt;br /&gt;     &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;&lt;br /&gt;     &amp;lt;artifactId&amp;gt;maven-help-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;     &amp;lt;version&amp;gt;2.1&amp;lt;/version&amp;gt;&lt;br /&gt;    &amp;lt;/dependency&amp;gt; &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Then I looked in the source of the "help" plugin, specifically &lt;span style="font-style:italic;"&gt;&lt;a href="https://svn.apache.org/repos/asf/maven/plugins/trunk/maven-help-plugin/src/main/java/org/apache/maven/plugins/help/DescribeMojo.java"&gt;DescribedMojo.java&lt;/a&gt;&lt;/span&gt; which handles to goal "help:describe" as shown above. So basically, if I can construct a DescribeMojo instance and fill in the needed info, you'll be able to execute it just like your own mojo. The trick is, DescribeMojo has all of it fields "private" and no setters.&lt;br /&gt;So I hacked the hell out of it and dug into its privates by using reflection. This is a no-no in so many books but I feel I'm working on a known version (2.1) of the "help" plugin so the chance of its API change is slim.&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;/**&lt;br /&gt; * Print help for all known goals&lt;br /&gt; * &lt;br /&gt; * @author hhuynh&lt;br /&gt; * &lt;br /&gt; * @goal help&lt;br /&gt; */&lt;br /&gt;public class HelpMojo extends AbstractMojo {&lt;br /&gt;  .....&lt;br /&gt;&lt;br /&gt;  /**&lt;br /&gt;   * @parameter expression="${project.remoteArtifactRepositories}"&lt;br /&gt;   * @required&lt;br /&gt;   * @readonly&lt;br /&gt;   */&lt;br /&gt;  private List remoteRepositories;&lt;br /&gt;&lt;br /&gt;  /**&lt;br /&gt;   * The goal you want to see help. By default help prints for all goals&lt;br /&gt;   * &lt;br /&gt;   * @parameter expression="${goal}"&lt;br /&gt;   */&lt;br /&gt;  private String goal;&lt;br /&gt;&lt;br /&gt;  public void execute() throws MojoExecutionException, MojoFailureException {&lt;br /&gt;    DescribeMojo describeMojo = new DescribeMojo();&lt;br /&gt;    setValue(describeMojo, "artifactFactory", artifactFactory);&lt;br /&gt;    setValue(describeMojo, "pluginManager", pluginManager);&lt;br /&gt;    setValue(describeMojo, "projectBuilder", projectBuilder);&lt;br /&gt;    setValue(describeMojo, "project", project);&lt;br /&gt;    setValue(describeMojo, "settings", settings);&lt;br /&gt;    setValue(describeMojo, "session", session);&lt;br /&gt;    setValue(describeMojo, "localRepository", localRepository);&lt;br /&gt;    setValue(describeMojo, "remoteRepositories", remoteRepositories);&lt;br /&gt;&lt;br /&gt;    setValue(describeMojo, "plugin", "org.terracotta.maven.plugins:tc-maven-plugin");&lt;br /&gt;    setValue(describeMojo, "detail", true);&lt;br /&gt;    setValue(describeMojo, "goal", goal);&lt;br /&gt;    &lt;br /&gt;    describeMojo.execute();&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  private void setValue(Object o, String field, Object value) throws MojoFailureException {&lt;br /&gt;    Class c = o.getClass();&lt;br /&gt;    Field _field;&lt;br /&gt;    try {&lt;br /&gt;      _field = c.getDeclaredField(field);&lt;br /&gt;      _field.setAccessible(true);&lt;br /&gt;      _field.set(o, value);&lt;br /&gt;    } catch (Exception e) {&lt;br /&gt;      throw new MojoFailureException(e.getMessage());&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The 3 important fields I had to fill out are:&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;  &lt;li&gt;plugin: full name of the plugin you want to print help&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;detail: I want full description by default&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;goal:   If specified, it only prints help for that goal&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;There are other component fields that Maven will normally inject them into your own mojo automatically. For example, the field "remoteRepositories" is needed by DescribeMojo and since we construct it by hand, we have to inject the value of this field ourselves. But you can get the value of it for free by Maven so all I need to do is just declaring it and passing it along.&lt;br /&gt;&lt;br /&gt;Voila, you got a fully functional DescribeMojo instance and all that left is to call "execute()" on it.&lt;br /&gt;&lt;br /&gt;So that's how the hack is done.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-5807912854515210810?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/5807912854515210810/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=5807912854515210810' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/5807912854515210810'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/5807912854515210810'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2009/06/executing-maven-plugin-from-maven.html' title='Executing a Maven plugin from a Maven plugin'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-31304385251813241</id><published>2009-04-27T00:28:00.000-07:00</published><updated>2009-04-27T01:38:52.152-07:00</updated><title type='text'>Sharing data of Spring beans with Terracotta</title><content type='html'>This is a quick demo showing how you would share data of Spring beans between different JVMs using &lt;a href="http://www.terracotta.org"&gt;Terracotta&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The example is based on the &lt;a href="http://www.springbyexample.org/examples/core-concepts.html"&gt;Voting Booth&lt;/a&gt; of &lt;a href="http://www.springbyexample.org/examples/core-concepts-our-example-in-spring-ioc.html "&gt;Spring By Example&lt;/a&gt; website (a great website to learn Spring by the way)&lt;br /&gt;&lt;br /&gt;In this example, I have 2 beans defined in &lt;a href="http://svn.terracotta.org/svn/forge/projects/TerracottaSpringDemo/src/main/resources/application-context.xml"&gt;application-context.xml&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="java" name="code"&gt;&lt;br /&gt; &amp;lt;bean id="recorder" class="org.terracotta.demo.LocalVoteRecorder" /&amp;gt;&lt;br /&gt;&lt;br /&gt; &amp;lt;bean id="votingBooth" class="org.terracotta.demo.VotingBooth"&amp;gt;&lt;br /&gt;  &amp;lt;property name="voteRecorder" ref="recorder" /&amp;gt;&lt;br /&gt; &amp;lt;/bean&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So simply, the 'votingBooth' bean has a reference to the 'recorder' bean which has a map of candidates and their votes. &lt;br /&gt;&lt;br /&gt;&lt;pre class="java" name="code"&gt;&lt;br /&gt;public class VotingBooth {&lt;br /&gt;  VoteRecorder recorder = null;&lt;br /&gt;&lt;br /&gt;  public void setVoteRecorder(VoteRecorder recorder) {&lt;br /&gt;    this.recorder = recorder;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public void vote(Candidate candidate) {&lt;br /&gt;    recorder.record(candidate);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  public void printResult() {&lt;br /&gt;    recorder.tallyVotes();&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* ************* */&lt;br /&gt;public class LocalVoteRecorder implements VoteRecorder {&lt;br /&gt;  final static Log                      logger      = LogFactory&lt;br /&gt;                                                        .getLog(LocalVoteRecorder.class);&lt;br /&gt;  private final Map&amp;lt;Candidate, Integer&amp;gt; votesRecord = new ConcurrentHashMap&amp;lt;Candidate, Integer&amp;gt;();&lt;br /&gt;&lt;br /&gt;  public void record(Candidate candidate) {&lt;br /&gt;    int count = 0;&lt;br /&gt;&lt;br /&gt;    if (!votesRecord.containsKey(candidate)) {&lt;br /&gt;      votesRecord.put(candidate, count);&lt;br /&gt;    } else {&lt;br /&gt;      count = votesRecord.get(candidate);&lt;br /&gt;    }&lt;br /&gt;    count++;&lt;br /&gt;    votesRecord.put(candidate, count);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public void tallyVotes() {&lt;br /&gt;    for (Candidate candidate : votesRecord.keySet()) {&lt;br /&gt;      logger.info("Candidate '" + candidate + "' has "&lt;br /&gt;          + votesRecord.get(candidate) + " votes.");&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Spring will do its magic and inject the right data to into main program at runtime. To demo, I have the main function vote 5 times between 2 known candidates then print out the result in the end. &lt;br /&gt;&lt;br /&gt;&lt;pre class="java" name="code"&gt;&lt;br /&gt;public class Main {&lt;br /&gt;&lt;br /&gt;  final static Log         logger     = LogFactory.getLog(Main.class);&lt;br /&gt;  final static Random      rand       = new Random();&lt;br /&gt;  final static Candidate[] candidates = new Candidate[] {&lt;br /&gt;      new Candidate("Hung"), new Candidate("Nick") };&lt;br /&gt;&lt;br /&gt;  public static void main(String[] args) {&lt;br /&gt;    ApplicationContext applicationContext = new ClassPathXmlApplicationContext(&lt;br /&gt;        "/application-context.xml");&lt;br /&gt;&lt;br /&gt;    VotingBooth votingBooth = (VotingBooth) applicationContext&lt;br /&gt;        .getBean("votingBooth");&lt;br /&gt;&lt;br /&gt;    // vote 5 times&lt;br /&gt;    for (int i = 0; i &amp;lt; 5; i++) {&lt;br /&gt;      Candidate luckyCandidate = candidates[rand.nextInt(2)];&lt;br /&gt;      votingBooth.vote(luckyCandidate);&lt;br /&gt;      logger.info("voted for candidate " + luckyCandidate);&lt;br /&gt;    }&lt;br /&gt;  &lt;br /&gt;    votingBooth.printResult();&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So if you run this Spring app as is, it will print the result of that run and exit. Quite simple.&lt;br /&gt;&lt;br /&gt;Now let say we want to maintain a voting record that would last beyond the life cycle of that run. And also, any subsequent runs should add to the vote tally. In other words, the data is shared between the nodes (JVMs) and persisted.&lt;br /&gt;&lt;br /&gt;This is where Terracotta comes in. The data we're interested in sharing will be marked as "ROOT". In this case, it's the "votesRecord" field of the 'recorder' bean. This field will be transparently clustered by Terracotta, its value will be saved in Terracotta server and it will be faulted back into a new node as needed. There's no thirdparty function calls nor networking code, just plain Java. Here is what needed in Terracotta configuration file &lt;a href="http://svn.terracotta.org/svn/forge/projects/TerracottaSpringDemo/tc-config.xml"&gt;tc-config.xml&lt;/a&gt;:&lt;br /&gt;&lt;pre class="xml" name="code"&gt;&lt;br /&gt;  &amp;lt;application&amp;gt;&lt;br /&gt;    &amp;lt;dso&amp;gt;&lt;br /&gt;      &amp;lt;instrumented-classes&amp;gt;&lt;br /&gt;        &amp;lt;include&amp;gt;&lt;br /&gt;          &amp;lt;class-expression&amp;gt;org.terracotta.demo.*&amp;lt;/class-expression&amp;gt;&lt;br /&gt;        &amp;lt;/include&amp;gt;&lt;br /&gt;      &amp;lt;/instrumented-classes&amp;gt;&lt;br /&gt;      &amp;lt;roots&amp;gt;&lt;br /&gt;        &amp;lt;root&amp;gt;&lt;br /&gt;          &amp;lt;field-name&amp;gt;org.terracotta.demo.LocalVoteRecorder.votesRecord&amp;lt;/field-name&amp;gt;&lt;br /&gt;        &amp;lt;/root&amp;gt;&lt;br /&gt;      &amp;lt;/roots&amp;gt;&lt;br /&gt;    &amp;lt;/dso&amp;gt;&lt;br /&gt;  &amp;lt;/application&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That piece of config tells Terracotta to instrument all classes under the org.terracotta.demo package and make "votesRecord" a root.&lt;br /&gt;&lt;br /&gt;Since Terracotta has Maven plugin, I just use its plugin and run the example. The &lt;a href="http://svn.terracotta.org/svn/forge/projects/TerracottaSpringDemo/pom.xml"&gt;pom&lt;/a&gt; is here if you're interested. First, you need to start Terracotta server:&lt;br /&gt;&lt;br /&gt;% mvn tc:start&lt;br /&gt;&lt;br /&gt;Then you can run the example with Terracotta enable simply by:&lt;br /&gt;&lt;br /&gt;% mvn tc:run&lt;br /&gt;&lt;br /&gt;On first run, I got this result: Nick got voted twice&lt;br /&gt;&lt;br /&gt;&lt;pre class="text" name="code"&gt;&lt;br /&gt;[INFO] [vote] INFO  [main] voted for candidate Nick&lt;br /&gt;[INFO] [vote] INFO  [main] voted for candidate Hung&lt;br /&gt;[INFO] [vote] INFO  [main] voted for candidate Hung&lt;br /&gt;[INFO] [vote] INFO  [main] voted for candidate Nick&lt;br /&gt;[INFO] [vote] INFO  [main] voted for candidate Hung&lt;br /&gt;[INFO] [vote] INFO  [main] Candidate 'Hung' has 3 votes.&lt;br /&gt;[INFO] [vote] INFO  [main] Candidate 'Nick' has 2 votes.&lt;br /&gt;[INFO] Finished node vote&lt;br /&gt;[INFO] DSO processes finished&lt;br /&gt;[INFO] Skipping stopping DSO Server&lt;br /&gt;[INFO] ------------------------------------------------------------------------&lt;br /&gt;[INFO] BUILD SUCCESSFUL&lt;br /&gt;[INFO] ------------------------------------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;On second run: Nick got voted one more time brings his total votes to 3&lt;br /&gt;&lt;br /&gt;&lt;pre class="text" name="code"&gt;&lt;br /&gt;[INFO] [vote] INFO  [main] voted for candidate Hung&lt;br /&gt;[INFO] [vote] INFO  [main] voted for candidate Hung&lt;br /&gt;[INFO] [vote] INFO  [main] voted for candidate Nick&lt;br /&gt;[INFO] [vote] INFO  [main] voted for candidate Hung&lt;br /&gt;[INFO] [vote] INFO  [main] voted for candidate Hung&lt;br /&gt;[INFO] [vote] INFO  [main] Candidate 'Hung' has 7 votes.&lt;br /&gt;[INFO] [vote] INFO  [main] Candidate 'Nick' has 3 votes.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This shows that the votes are indeed shared between 2 separate runs. To see the shared data live, you can use &lt;a href="http://www.terracotta.org/web/display/orgsite/Terracotta+Developer+Console"&gt;Terracotta Developer Console&lt;/a&gt; to look at the roots:&lt;br /&gt;&lt;br /&gt;Run this Maven command to start the console:&lt;br /&gt;&lt;br /&gt;% mvn tc:dev-console&lt;br /&gt;&lt;br /&gt;&lt;img src="http://hhuynh.googlepages.com/devconsole.jpg"/&gt;&lt;br /&gt;&lt;br /&gt;You can check out the whole project from Subversion: &lt;a href="http://svn.terracotta.org/svn/forge/projects/TerracottaSpringDemo/"&gt;http://svn.terracotta.org/svn/forge/projects/TerracottaSpringDemo &lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-31304385251813241?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/31304385251813241/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=31304385251813241' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/31304385251813241'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/31304385251813241'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2009/04/sharing-data-of-spring-beans-with.html' title='Sharing data of Spring beans with Terracotta'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-6822121389931329963</id><published>2009-04-14T14:45:00.001-07:00</published><updated>2009-04-14T14:52:31.170-07:00</updated><title type='text'>Create unique temp filename with Batch script</title><content type='html'>For Windows batch scripts, you could use %RANDOM% to generate a random number and use that as part of the temp file name but that doesn't guarantee uniqueness since random number can be repeated. By adding another variable to the mix, seconds and fraction of seconds, the file name would be unique: &lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="batch"&gt;&lt;br /&gt;&lt;br /&gt;call :GETTEMPNAME&lt;br /&gt;echo "Temp file name is %TMPFILE%"&lt;br /&gt;goto :EOF&lt;br /&gt;&lt;br /&gt;:GETTEMPNAME&lt;br /&gt;set TMPFILE=%TMP%\mytempfile-%RANDOM%-%TIME:~6,5%.tmp&lt;br /&gt;if exist "%TMPFILE%" GOTO :GETTEMPNAME &lt;br /&gt;&lt;br /&gt;:EOF&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-6822121389931329963?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/6822121389931329963/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=6822121389931329963' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/6822121389931329963'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/6822121389931329963'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2009/04/create-unique-temp-filename-with-batch.html' title='Create unique temp filename with Batch script'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-6234040663900964890</id><published>2009-01-19T19:01:00.000-08:00</published><updated>2009-01-19T19:22:27.440-08:00</updated><title type='text'>Using Ant as a library</title><content type='html'>Ant has a lot of predefined tasks that could be a great help. You can use them directly from your Java code. Here are some examples using Ant to download a file, unzip a package and exec some shell command.&lt;br /&gt;&lt;br /&gt;You would need to have &lt;br /&gt; + ant-1.7.1.jar &lt;br /&gt; + ant-launcher-1.7.1.jar &lt;br /&gt; + commons-io-1.3.2.jar (for exec example, found at &lt;a href="http://commons.apache.org/io/"&gt;http://commons.apache.org/io&lt;/a&gt;) &lt;br /&gt;&lt;br /&gt;in your classpath.&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;public class AntDemo {&lt;br /&gt;  &lt;br /&gt;  /**&lt;br /&gt;   * Download a file at sourceUrl to dest&lt;br /&gt;   *&lt;br /&gt;   */&lt;br /&gt;  public static void download(URL sourceUrl, File dest) {&lt;br /&gt;    Project dummyProject = new Project();&lt;br /&gt;    dummyProject.init();&lt;br /&gt;    &lt;br /&gt;    Get antGet = new Get();&lt;br /&gt;    antGet.setProject(dummyProject);&lt;br /&gt;    antGet.setVerbose(true);&lt;br /&gt;    antGet.setSrc(sourceUrl);&lt;br /&gt;    antGet.setDest(dest);&lt;br /&gt;    antGet.execute();&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  /**&lt;br /&gt;   * Unzip a zip file&lt;br /&gt;   *&lt;br /&gt;   */&lt;br /&gt;  public static void unzip(File src, File dest) {&lt;br /&gt;    Project dummyProject = new Project();&lt;br /&gt;    dummyProject.init();&lt;br /&gt;&lt;br /&gt;    Expand antUnzip = new Expand();&lt;br /&gt;    antUnzip.setProject(dummyProject);&lt;br /&gt;    antUnzip.setSrc(src);&lt;br /&gt;    antUnzip.setDest(dest);&lt;br /&gt;    antUnzip.execute();&lt;br /&gt;&lt;br /&gt;    /* ant doesn't preserve permission bits&lt;br /&gt;       need to restore them manually */&lt;br /&gt;&lt;br /&gt;    Chmod chmod = new Chmod();&lt;br /&gt;    chmod.setProject(dummyProject);&lt;br /&gt;    chmod.setDir(new File(src.getAbsolutePath().replaceAll(".zip", "")));&lt;br /&gt;    chmod.setPerm("a+rx");&lt;br /&gt;    chmod.setIncludes("**/**");&lt;br /&gt;    chmod.execute();&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  &lt;br /&gt;  /**&lt;br /&gt;   * Run a shell command and return the output as String&lt;br /&gt;   *  &lt;br /&gt;   */&lt;br /&gt;  public static String exec(String command, List&lt;String&gt; params, File workDir) {&lt;br /&gt;    File outputFile;&lt;br /&gt;    try {&lt;br /&gt;      outputFile = File.createTempFile("exec", ".out");&lt;br /&gt;    } catch (IOException e) {&lt;br /&gt;      throw new RuntimeException(e);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    Project dummyProject = new Project();&lt;br /&gt;    dummyProject.init();&lt;br /&gt;&lt;br /&gt;    ExecTask execTask = new ExecTask();&lt;br /&gt;    execTask.setProject(dummyProject);&lt;br /&gt;    execTask.setOutput(outputFile);&lt;br /&gt;    execTask.setDir(workDir != null ? workDir : new File(System&lt;br /&gt;        .getProperty("user.dir")));&lt;br /&gt;    execTask.setExecutable(command);&lt;br /&gt;    if (params != null) {&lt;br /&gt;      for (String param : params) {&lt;br /&gt;        execTask.createArg().setValue(param);&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    execTask.execute();&lt;br /&gt;&lt;br /&gt;    FileReader reader = null;&lt;br /&gt;    try {&lt;br /&gt;      reader = new FileReader(outputFile);&lt;br /&gt;      return IOUtils.toString(reader);&lt;br /&gt;    } catch (Exception e) {&lt;br /&gt;      throw new RuntimeException(e);&lt;br /&gt;    } finally {&lt;br /&gt;      IOUtils.closeQuietly(reader);&lt;br /&gt;      outputFile.delete();&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public static void main(String[] args) {&lt;br /&gt;    List&lt;String&gt; params = Arrays.asList(new String[] { "Hello", "World" });&lt;br /&gt;    System.out.println(exec("echo", params, null));&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Just note that you'll need a dummy project for the Ant task. Otherwise you'll get an NullPointerException.&lt;br /&gt;&lt;br /&gt;There are a lot of tasks that you could use. The list is here &lt;a href="http://ant.apache.org/manual/tasksoverview.html"&gt;http://ant.apache.org/manual/tasksoverview.html&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-6234040663900964890?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/6234040663900964890/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=6234040663900964890' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/6234040663900964890'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/6234040663900964890'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2009/01/using-ant-as-library.html' title='Using Ant as a library'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-7739709017709112033</id><published>2009-01-04T23:57:00.001-08:00</published><updated>2009-01-05T12:14:39.579-08:00</updated><title type='text'>Java Webstart Demos with Terracotta enabled</title><content type='html'>For easy deployment, &lt;a href="http://terracotta.org"&gt;Terracotta&lt;/a&gt; enabled Java applications can also be launched via web start. There are certain issues to look out for but it's certainly possible. I wrote a small demo to demonstrate it.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Issue 1&lt;/span&gt;: &lt;span style="font-style:italic;color:blue"&gt;Terracotta installation required on user's computer before running.&lt;/span&gt; &lt;br /&gt;&lt;br /&gt;  This is solved easily by downloading a Terracotta zip file and extract it to a known location on user computer. In my demo, I install it to $HOME/terracotta_ws.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Issue 2&lt;/span&gt;: &lt;span style="font-style:italic;color:blue"&gt;Java Webstart applications don't allow modification of boot classpath&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;  Since you can't append or pre-append to boot classpath of the webstart VM, which Terracotta requires, you are forced to spawn an external JVM for your app. This is solvable by signing your jars with a keystore, that will allow your webstart app to spawn external process. This is a &lt;a href="http://weblogs.java.net/blog/kirillcool/archive/2005/05/signing_jars_fo.html"&gt;good blog&lt;/a&gt; showing how to sign your jars.&lt;br /&gt;&lt;br /&gt;  Note that only jars that are part of your main webstart GUI frame and its libraries are required to be signed. Jars that are part of your Terracotta applications are not required.&lt;br /&gt;&lt;br /&gt;  When I use "webstart app", I mean the control app that is downloaded and run by Java webstart.&lt;br /&gt;&lt;br /&gt;  Wehn I use "main app" or "TC enabled app", I mean the Terracotta enabled applications that are spawned in external processes by the "webstart app"&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Issue 3&lt;/span&gt;: &lt;span style="font-style:italic;color:blue"&gt;You can't declare your main application jars as part of resources to download in JNLP file.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  The main reason is that Java Webstart obfuscates those jars and stores them in a cache with variable folder name. So your webstart app has to handle the downloading and installing the main app's binaries (zip file) to a known location. This is also doable. Take a look at the JNLP file I have:&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;&amp;lt;jnlp spec="1.0+" codebase="http://localhost:8080/webstartdemo"&lt;br /&gt; href="demo.jnlp"&amp;gt;&lt;br /&gt; &amp;lt;information&amp;gt;&lt;br /&gt;  &amp;lt;title&amp;gt;TC webstart demo&amp;lt;/title&amp;gt;&lt;br /&gt;  &amp;lt;vendor&amp;gt;Terracotta&amp;lt;/vendor&amp;gt;&lt;br /&gt;  &amp;lt;description&amp;gt;Demo of how to start Terracotta from webstart&lt;br /&gt;  &amp;lt;/description&amp;gt;&lt;br /&gt;  &amp;lt;description kind="short"&amp;gt;TC Webstart example&amp;lt;/description&amp;gt;&lt;br /&gt;  &amp;lt;offline-allowed /&amp;gt;&lt;br /&gt; &amp;lt;/information&amp;gt;&lt;br /&gt; &amp;lt;resources&amp;gt;&lt;br /&gt;  &amp;lt;j2se version="1.5+" /&amp;gt;&lt;br /&gt;  &amp;lt;jar href="TCWebStartDemo.jar" download="eager" /&amp;gt;&lt;br /&gt;  &amp;lt;jar href="ant-1.7.1.jar" /&amp;gt;&lt;br /&gt;  &amp;lt;jar href="ant-launcher-1.7.1.jar" /&amp;gt;&lt;br /&gt;  &amp;lt;jar href="miglayout-3.6.2-swing.jar" /&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;property name="tcws.codebase" value="http://localhost:8080/webstartdemo" /&amp;gt;&lt;br /&gt;  &amp;lt;property name="tcws.terracotta.zip" value="terracotta-trunk-nightly-rev11190.zip" /&amp;gt;&lt;br /&gt;  &amp;lt;property name="tcws.demo.list" value="sharededitor,mandelbrot" /&amp;gt;&lt;br /&gt;  &amp;lt;property name="tcws.sharededitor.mainClass" value="demo.sharededitor.Main" /&amp;gt;&lt;br /&gt;  &amp;lt;property name="tcws.mandelbrot.mainClass" value="mandelbrot.Main" /&amp;gt;&lt;br /&gt;&lt;br /&gt; &amp;lt;/resources&amp;gt;&lt;br /&gt; &amp;lt;application-desc main-class="demo.MainFrame" /&amp;gt;&lt;br /&gt; &amp;lt;security&amp;gt;&lt;br /&gt;  &amp;lt;all-permissions /&amp;gt;&lt;br /&gt; &amp;lt;/security&amp;gt;&lt;br /&gt;&amp;lt;/jnlp&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;From line 13 to 16, those jars are for the webstart app which will be downloaded automatically by java webstart. And those are the ones that need to be signed.&lt;br /&gt;Line 25 indicates the main class of the webstart app.&lt;br /&gt;&lt;br /&gt;From line 18 to 22, that is how I communicate to my webstart app (via system properies) about where to download Terracotta and the demos (Terracotta enabled apps). Each demo is a zip file which contains a tc-config.xml, a "lib" folder with all the jars it requires.&lt;br /&gt;&lt;br /&gt;You can play with &lt;a href="http://download.terracotta.org/webstartdemo/index.html"&gt;the demo here&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;The webstart app Eclipse project can be checked out at SVN repo &lt;a href="http://svn.terracotta.org/svn/forge/projects/labs/TCWebStartDemo"&gt;http://svn.terracotta.org/svn/forge/projects/labs/TCWebStartDemo&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Your main interests should be the 2 classes:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;  &lt;li&gt;&lt;a href="http://svn.terracotta.org/svn/forge/projects/labs/TCWebStartDemo/src/demo/DemoManager.java"&gt;DemoManager&lt;/a&gt;: download and install TC and demos&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;&lt;a href="http://svn.terracotta.org/svn/forge/projects/labs/TCWebStartDemo/src/demo/MainFrame.java"&gt;MainFrame&lt;/a&gt;: webstart app that spawns processes&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Enjoy the demos :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-7739709017709112033?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/7739709017709112033/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=7739709017709112033' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/7739709017709112033'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/7739709017709112033'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2009/01/java-webstart-demos-with-terracotta.html' title='Java Webstart Demos with Terracotta enabled'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-8433213061137693104</id><published>2009-01-02T20:34:00.000-08:00</published><updated>2009-01-04T09:37:23.852-08:00</updated><title type='text'>Redirecting System.out and System.err to JTextPane or JTextArea</title><content type='html'>In Swing, if you want to redirect System.err and System.out to a JTextPane or a JTextArea, you only need to override the write() methods of OutputStream to append the text to the text pane instead.&lt;br /&gt;&lt;br /&gt;The example below shown how to do it with JTextPane. For JTextArea, it's quite simpler, just call JTextArea.append()&lt;br /&gt;&lt;br /&gt;This is useful when you spawn external process from a Swing application and want to see its output in your own text component.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Jan 04 2009: Revised regarding Eric Burke's comment below. &lt;/span&gt;&lt;br /&gt;JTextPane version: &lt;pre name="code" class="java"&gt;&lt;br /&gt;  private void updateTextPane(final String text) {&lt;br /&gt;    SwingUtilities.invokeLater(new Runnable() {&lt;br /&gt;      public void run() {&lt;br /&gt;        Document doc = textPane.getDocument();&lt;br /&gt;        try {&lt;br /&gt;          doc.insertString(doc.getLength(), text, null);&lt;br /&gt;        } catch (BadLocationException e) {&lt;br /&gt;          throw new RuntimeException(e);&lt;br /&gt;        }&lt;br /&gt;        textPane.setCaretPosition(doc.getLength() - 1);&lt;br /&gt;      }&lt;br /&gt;    });&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  private void redirectSystemStreams() {&lt;br /&gt;    OutputStream out = new OutputStream() {&lt;br /&gt;      @Override&lt;br /&gt;      public void write(final int b) throws IOException {&lt;br /&gt;        updateTextPane(String.valueOf((char) b));&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      @Override&lt;br /&gt;      public void write(byte[] b, int off, int len) throws IOException {&lt;br /&gt;        updateTextPane(new String(b, off, len));&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      @Override&lt;br /&gt;      public void write(byte[] b) throws IOException {&lt;br /&gt;        write(b, 0, b.length);&lt;br /&gt;      }&lt;br /&gt;    };&lt;br /&gt;&lt;br /&gt;    System.setOut(new PrintStream(out, true));&lt;br /&gt;    System.setErr(new PrintStream(out, true));&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt; JTextArea version:&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;  private void updateTextArea(final String text) {&lt;br /&gt;    SwingUtilities.invokeLater(new Runnable() {&lt;br /&gt;      public void run() {&lt;br /&gt;        textArea.append(text);&lt;br /&gt;      }&lt;br /&gt;    });&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  private void redirectSystemStreams() {&lt;br /&gt;    OutputStream out = new OutputStream() {&lt;br /&gt;      @Override&lt;br /&gt;      public void write(int b) throws IOException {&lt;br /&gt;        updateTextArea(String.valueOf((char) b));&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      @Override&lt;br /&gt;      public void write(byte[] b, int off, int len) throws IOException {&lt;br /&gt;        updateTextArea(new String(b, off, len));&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      @Override&lt;br /&gt;      public void write(byte[] b) throws IOException {&lt;br /&gt;        write(b, 0, b.length);&lt;br /&gt;      }&lt;br /&gt;    };&lt;br /&gt;&lt;br /&gt;    System.setOut(new PrintStream(out, true));&lt;br /&gt;    System.setErr(new PrintStream(out, true));&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a href="javascript:void(0)" onclick="toggle('old1')"&gt;[+] Old versions&lt;/a&gt;&lt;br /&gt;&lt;div id="old1" style="display:none"&gt;&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;  private void redirectSystemStreams() {&lt;br /&gt;    OutputStream out = new OutputStream() {&lt;br /&gt;      @Override&lt;br /&gt;      public void write(int b) throws IOException {&lt;br /&gt;        Document doc = textPane.getDocument();&lt;br /&gt;        try {&lt;br /&gt;          doc.insertString(doc.getLength(), String.valueOf((char) b), null);&lt;br /&gt;        } catch (BadLocationException e) {&lt;br /&gt;          throw new IOException(e);&lt;br /&gt;        }&lt;br /&gt;        textPane.setCaretPosition(doc.getLength() - 1);&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      @Override&lt;br /&gt;      public void write(byte[] b, int off, int len) throws IOException {&lt;br /&gt;        Document doc = textPane.getDocument();&lt;br /&gt;        try {&lt;br /&gt;          doc.insertString(doc.getLength(), new String(b, off, len), null);&lt;br /&gt;        } catch (BadLocationException e) {&lt;br /&gt;          throw new IOException(e);&lt;br /&gt;        }&lt;br /&gt;        textPane.setCaretPosition(doc.getLength() - 1);&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      @Override&lt;br /&gt;      public void write(byte[] b) throws IOException {&lt;br /&gt;        write(b, 0, b.length);&lt;br /&gt;      }&lt;br /&gt;    };&lt;br /&gt;&lt;br /&gt;    System.setOut(new PrintStream(out, true));&lt;br /&gt;    System.setErr(new PrintStream(out, true));&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt; JTextArea version:&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;  private void redirectSystemStreams() {&lt;br /&gt;    OutputStream out = new OutputStream() {&lt;br /&gt;      @Override&lt;br /&gt;      public void write(int b) throws IOException {&lt;br /&gt;        textArea.append(String.valueOf((char) b));&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      @Override&lt;br /&gt;      public void write(byte[] b, int off, int len) throws IOException {&lt;br /&gt;        textArea.append(new String(b, off, len));&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      @Override&lt;br /&gt;      public void write(byte[] b) throws IOException {&lt;br /&gt;        write(b, 0, b.length);&lt;br /&gt;      }&lt;br /&gt;    };&lt;br /&gt;&lt;br /&gt;    System.setOut(new PrintStream(out, true));&lt;br /&gt;    System.setErr(new PrintStream(out, true));&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-8433213061137693104?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/8433213061137693104/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=8433213061137693104' title='15 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/8433213061137693104'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/8433213061137693104'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2009/01/redirecting-systemout-and-systemerr-to.html' title='Redirecting System.out and System.err to JTextPane or JTextArea'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><thr:total>15</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-6442223227178660829</id><published>2008-12-18T14:56:00.000-08:00</published><updated>2011-06-16T09:57:51.337-07:00</updated><title type='text'>Link checker with Java - better version</title><content type='html'>I posted an &lt;a href="http://unserializableone.blogspot.com/2007/10/check-for-broken-links-in-ruby-bash.html"&gt;old blog&lt;/a&gt; regarding checking for broken links with Java. However, that version uses 3rd party library httpunit and doesn't handle forwarding case. If a url forwards to another url, http response code would be 302 and the old version would consider that link is broken.&lt;br /&gt;&lt;br /&gt;The new version would follow the forwarding url and check it. If the forwarded url is dead, it would be detected.&lt;br /&gt;&lt;br /&gt;So here is the new code, without any extra 3rd party API:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;  private static boolean isLive(String link) {&lt;br /&gt;    HttpURLConnection urlConnection = null;&lt;br /&gt;    try {&lt;br /&gt;      URL url = new URL(link);&lt;br /&gt;      urlConnection = (HttpURLConnection) url.openConnection();&lt;br /&gt;      urlConnection.setRequestMethod("HEAD");&lt;br /&gt;      urlConnection.setConnectTimeout(5000); /* timeout after 5s if can't connect */&lt;br /&gt;      urlConnection.setReadTimeout(5000); /* timeout after 5s if the page is too slow */&lt;br /&gt;      urlConnection.connect();&lt;br /&gt;      String redirectLink = urlConnection.getHeaderField("Location");&lt;br /&gt;      if (redirectLink != null &amp;&amp; !link.equals(redirectLink)) {&lt;br /&gt;        return isLive(redirectLink);&lt;br /&gt;      } else {&lt;br /&gt;        return urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK;&lt;br /&gt;      }&lt;br /&gt;    } catch (Exception e) {&lt;br /&gt;      return false;&lt;br /&gt;    } finally {&lt;br /&gt;      if (urlConnection != null) {&lt;br /&gt;        urlConnection.disconnect();&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  public static void main(String[] args) {&lt;br /&gt;    System.out.println(isLive("http://google.com"));&lt;br /&gt;    System.out.println(isLive("http://somefakelink.com"));&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-6442223227178660829?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/6442223227178660829/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=6442223227178660829' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/6442223227178660829'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/6442223227178660829'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2008/12/link-checker-with-java-better-version.html' title='Link checker with Java - better version'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-6453671350661063355</id><published>2008-12-18T00:49:00.000-08:00</published><updated>2009-12-21T13:04:50.644-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ehcache'/><title type='text'>Simple demo of clustering Ehcache</title><content type='html'>To cluster a cache between 2 or more JVM's, it's quite simple if you use &lt;a href="http://terracotta.org"&gt;Terracotta&lt;/a&gt; underneath. It will handle the networking, the shared memory, transparently for you. There's no extra API but a configuration file.&lt;br /&gt;&lt;br /&gt;Terracotta also has maven plugin support so you could grab the demo project below and run without downloading anything.&lt;br /&gt;&lt;br /&gt;The demo is only one Java source file. It is meant to be run in 2 VM's.&lt;br /&gt;&lt;br /&gt;I used a cyclic barrier to synchronize between the 2 VM's. It will block until both VM hit the "await()" call. The first node will create a cache and populate it with 2 elements. The second node then queries out of the cache to demonstrate that it could get to the shared data.&lt;br /&gt;&lt;br /&gt;&lt;pre name='code' class='java'&gt;package demo;&lt;br /&gt;&lt;br /&gt;import java.util.concurrent.CyclicBarrier;&lt;br /&gt;&lt;br /&gt;import net.sf.ehcache.Cache;&lt;br /&gt;import net.sf.ehcache.CacheManager;&lt;br /&gt;import net.sf.ehcache.Element;&lt;br /&gt;import net.sf.ehcache.store.MemoryStoreEvictionPolicy;&lt;br /&gt;&lt;br /&gt;public class EhcacheDemo {&lt;br /&gt;&lt;br /&gt;  private CacheManager  manager = CacheManager.create();&lt;br /&gt;  private CyclicBarrier barrier = new CyclicBarrier(2);&lt;br /&gt;&lt;br /&gt;  public void run() throws Exception {&lt;br /&gt;    Cache testCache;&lt;br /&gt;&lt;br /&gt;    System.out.println("Waiting for the other node...");&lt;br /&gt;    if (barrier.await() == 0) {&lt;br /&gt;      System.out.println("Creating the testCache...");&lt;br /&gt;      testCache = new Cache("test", 100, MemoryStoreEvictionPolicy.LFU, true,&lt;br /&gt;          null, false, 60, 30, false, 0, null);&lt;br /&gt;      manager.addCache(testCache);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    barrier.await();&lt;br /&gt;    testCache = manager.getCache("test");&lt;br /&gt;&lt;br /&gt;    if (barrier.await() == 0) {&lt;br /&gt;      System.out.println("First node: adding 2 elements...");&lt;br /&gt;      testCache.put(new Element("k1", "v1"));&lt;br /&gt;      testCache.put(new Element("k2", "v2"));&lt;br /&gt;    } else {&lt;br /&gt;      System.out.println("Second node: querying 3 elements...");&lt;br /&gt;      System.out.println(testCache.get("k1"));&lt;br /&gt;      System.out.println(testCache.get("k2"));&lt;br /&gt;      System.out.println(testCache.get("k3"));&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    System.out.println("Done!");&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public static void main(String[] args) throws Exception {&lt;br /&gt;    new EhcacheDemo().run();&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Note that second node is also querying for non-existed element "k3" as a sanity check.&lt;br /&gt;&lt;br /&gt;So how does Terracotta know what I want to share? From the above code, I tell Terracotta to share the cache "manager" and "barrier" by marking them as root in Terracotta configuration file tc-config.xml&lt;br /&gt;&lt;pre name='code' class='xml'&gt;&lt;br /&gt;  &amp;lt;application&amp;gt;&lt;br /&gt;    &amp;lt;dso&amp;gt;&lt;br /&gt;      &amp;lt;instrumented-classes/&amp;gt;&lt;br /&gt;      &amp;lt;roots&amp;gt;&lt;br /&gt;        &amp;lt;root&amp;gt;&lt;br /&gt;          &amp;lt;field-name&amp;gt;demo.EhcacheDemo.manager&amp;lt;/field-name&amp;gt;&lt;br /&gt;        &amp;lt;/root&amp;gt;&lt;br /&gt;        &amp;lt;root&amp;gt;&lt;br /&gt;          &amp;lt;field-name&amp;gt;demo.EhcacheDemo.barrier&amp;lt;/field-name&amp;gt;&lt;br /&gt;        &amp;lt;/root&amp;gt;&lt;br /&gt;      &amp;lt;/roots&amp;gt;&lt;br /&gt;    &amp;lt;/dso&amp;gt;&lt;br /&gt;  &amp;lt;/application&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;After you &lt;a href="http://www.terracotta.org/confluence/download/attachments/8257635/EhcacheDemo.zip"&gt;download the project&lt;/a&gt;, just run this Maven commands:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;mvn install&lt;br /&gt;mvn tc:run&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The output would be something like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[INFO] [node1] Waiting for the other node...&lt;br /&gt;[INFO] [node0] Waiting for the other node...&lt;br /&gt;[INFO] [node0] Creating the testCache...&lt;br /&gt;[INFO] [node1] First node: adding 2 elements...&lt;br /&gt;[INFO] [node0] Second node: querying 3 elements...&lt;br /&gt;[INFO] [node1] Done!&lt;br /&gt;[INFO] [node0] [ key = k1, value=v1, version=1, hitCount=2, CreationTime = 1229592169906, LastAccessTime = 1229592170081 ]&lt;br /&gt;[INFO] [node0] [ key = k2, value=v2, version=1, hitCount=2, CreationTime = 1229592170070, LastAccessTime = 1229592170145 ]&lt;br /&gt;[INFO] [node0] null&lt;br /&gt;[INFO] [node0] Done!&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-6453671350661063355?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/6453671350661063355/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=6453671350661063355' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/6453671350661063355'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/6453671350661063355'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2008/12/simple-demo-of-clustering-ehcache.html' title='Simple demo of clustering Ehcache'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-1683564794968401323</id><published>2008-09-21T22:38:00.001-07:00</published><updated>2008-09-22T10:14:49.896-07:00</updated><title type='text'>Compile and test with different JDK with a Maven project</title><content type='html'>If you ever have the need to compile and run tests of your Maven project with different JDK, here are some tips:&lt;br /&gt;&lt;br /&gt;&lt;h1&gt;1. The cheapest way&lt;/h1&gt;&lt;br /&gt;&lt;br /&gt;Maven by default use your JAVA_HOME to compile the project and then run the tests.&lt;br /&gt;So if want to run your tests with different jdk, just set your JAVA_HOME to the jdk of your desire. Here's a neat treat in Bash to set an env variable locally to the current process.&lt;br /&gt;&lt;br /&gt;Assume you have environment variables JAVA_HOME_15 and JAVA_HOME_16 pointing to their respective jdk1.5 and jdk1.6, you can then do this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;%&gt; JAVA_HOME=$JAVA_HOME_15 mvn install   # run with 1.5&lt;br /&gt;%&gt; JAVA_HOME=$JAVA_HOME_16 mvn install   # run with 1.6&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h1&gt;2. The Maven way&lt;/h1&gt; (hairy verbose and messy pom.xml profiles)&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;This only works with Maven-2.1.0-M1. Failed with 2.0.9 for me&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;You have to specify which JDK you want for 2 of these plugins: maven-compiler-plugin and maven-surefire-plugin. &lt;br /&gt;&lt;br /&gt;&lt;pre name='code' class='xml:nogutter'&gt;&lt;br /&gt;  &amp;lt;build&amp;gt;&lt;br /&gt;    &amp;lt;plugins&amp;gt;&lt;br /&gt;      &amp;lt;plugin&amp;gt;&lt;br /&gt;        &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;&lt;br /&gt;        &amp;lt;artifactId&amp;gt;maven-compiler-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;        &amp;lt;configuration&amp;gt;&lt;br /&gt;          &amp;lt;verbose&amp;gt;true&amp;lt;/verbose&amp;gt;&lt;br /&gt;          &amp;lt;fork&amp;gt;true&amp;lt;/fork&amp;gt;&lt;br /&gt;          &amp;lt;executable&amp;gt;${jdk}/bin/javac&amp;lt;/executable&amp;gt;&lt;br /&gt;          &amp;lt;compilerVersion&amp;gt;1.5&amp;lt;/compilerVersion&amp;gt;&lt;br /&gt;        &amp;lt;/configuration&amp;gt;&lt;br /&gt;      &amp;lt;/plugin&amp;gt;&lt;br /&gt;      &amp;lt;plugin&amp;gt;&lt;br /&gt;        &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;&lt;br /&gt;        &amp;lt;artifactId&amp;gt;maven-surefire-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;        &amp;lt;configuration&amp;gt;&lt;br /&gt;          &amp;lt;jvm&amp;gt;${jdk}/bin/java&amp;lt;/jvm&amp;gt;&lt;br /&gt;          &amp;lt;forkMode&amp;gt;once&amp;lt;/forkMode&amp;gt;&lt;br /&gt;        &amp;lt;/configuration&amp;gt;&lt;br /&gt;      &amp;lt;/plugin&amp;gt;&lt;br /&gt;    &amp;lt;/plugins&amp;gt;&lt;br /&gt;  &amp;lt;/build&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So you see I use the property ${jdk} Now we just need to define a profile for each of the JDK we want and set the ${jdk} appropriately. &lt;br /&gt;&lt;br /&gt;&lt;pre name='code' class='xml:nogutter'&gt;&lt;br /&gt;  &amp;lt;profiles&amp;gt;&lt;br /&gt;      &amp;lt;profile&amp;gt;&lt;br /&gt;      &amp;lt;id&amp;gt;default_jdk&amp;lt;/id&amp;gt;&lt;br /&gt;      &amp;lt;activation&amp;gt;&lt;br /&gt;          &amp;lt;activeByDefault&amp;gt;true&amp;lt;/activeByDefault&amp;gt;&lt;br /&gt;      &amp;lt;/activation&amp;gt;&lt;br /&gt;      &amp;lt;properties&amp;gt;&lt;br /&gt;          &amp;lt;jdk&amp;gt;${env.JAVA_HOME}&amp;lt;/jdk&amp;gt;&lt;br /&gt;      &amp;lt;/properties&amp;gt;&lt;br /&gt;    &amp;lt;/profile&amp;gt;&lt;br /&gt;    &amp;lt;profile&amp;gt;&lt;br /&gt;      &amp;lt;id&amp;gt;jdk15&amp;lt;/id&amp;gt;&lt;br /&gt;      &amp;lt;activation&amp;gt;&lt;br /&gt;          &amp;lt;activeByDefault&amp;gt;false&amp;lt;/activeByDefault&amp;gt;&lt;br /&gt;      &amp;lt;/activation&amp;gt;&lt;br /&gt;      &amp;lt;properties&amp;gt;&lt;br /&gt;          &amp;lt;jdk&amp;gt;${env.JAVA_HOME_15}&amp;lt;/jdk&amp;gt;&lt;br /&gt;      &amp;lt;/properties&amp;gt;&lt;br /&gt;    &amp;lt;/profile&amp;gt;&lt;br /&gt;    &amp;lt;profile&amp;gt;&lt;br /&gt;      &amp;lt;id&amp;gt;jdk16&amp;lt;/id&amp;gt;&lt;br /&gt;      &amp;lt;activation&amp;gt;&lt;br /&gt;          &amp;lt;activeByDefault&amp;gt;false&amp;lt;/activeByDefault&amp;gt;&lt;br /&gt;      &amp;lt;/activation&amp;gt;&lt;br /&gt;      &amp;lt;properties&amp;gt;&lt;br /&gt;          &amp;lt;jdk&amp;gt;${env.JAVA_HOME_16}&amp;lt;/jdk&amp;gt;&lt;br /&gt;      &amp;lt;/properties&amp;gt;&lt;br /&gt;    &amp;lt;/profile&amp;gt;    &lt;br /&gt;  &amp;lt;/profiles&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Above I defined 3 profiles. The first one is the default one. It's a trick to set ${jdk} to $JAVA_HOME so that Maven knows what to use if you don't specify any profile to run. &lt;br /&gt;&lt;br /&gt;Here how you would activate each profile:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;%&gt; mvn install          # no profile so by default run tests with JAVA_HOME&lt;br /&gt;%&gt; mvn install -Pjdk15  # run with jdk1.5&lt;br /&gt;%&gt; mvn install -Pjdk16  # run with jdk1.6&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Note that in the profile I use env variables JAVA_HOME_15 and JAVA_HOME_16 also. You can of course fill in an absolute path to your JDK.&lt;br /&gt;&lt;br /&gt;So there you have it. Took me awhile to figure out.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-1683564794968401323?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/1683564794968401323/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=1683564794968401323' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/1683564794968401323'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/1683564794968401323'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2008/09/compile-and-test-with-different-jdk.html' title='Compile and test with different JDK with a Maven project'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-8787172115540037556</id><published>2008-03-26T15:56:00.000-07:00</published><updated>2008-04-20T12:48:16.979-07:00</updated><title type='text'>Bug bite</title><content type='html'>It's a very subtle bug but results in great headache. I didn't notice my bug until someone pointed it out. Good lesson learned: Always have tests when you refactor.&lt;br /&gt;&lt;br /&gt;See if you could spot it.&lt;br /&gt;&lt;br /&gt;&lt;pre name='code' class='java:nogutter'&gt;&lt;br /&gt;// BEFORE&lt;br /&gt;private LockID[] getAllLockIDs(LockID id) {&lt;br /&gt;  LockID[] lids = new LockID[transactionStack.size() + 1];&lt;br /&gt;  lids[0] = id;&lt;br /&gt;  for (int i = 1; i &amp;lt; lids.length; i++) {&lt;br /&gt;    TransactionContext tc = (TransactionContext) transactionStack.get(i - 1);&lt;br /&gt;    lids[i] = tc.getLockID();&lt;br /&gt;  }&lt;br /&gt;  return lids;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;   &lt;br /&gt;// AFTER&lt;br /&gt;private List getAllLockIDs(LockID id) {&lt;br /&gt;  List lids = new ArrayList();&lt;br /&gt;  lids.add(id);&lt;br /&gt;  for (int i = 1; i &amp;lt; transactionStack.size(); i++) {&lt;br /&gt;    TransactionContext tc = (TransactionContext) transactionStack.get(i - 1);&lt;br /&gt;    lids.add(tc.getLockID());&lt;br /&gt;  }&lt;br /&gt;  return lids;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-8787172115540037556?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/8787172115540037556/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=8787172115540037556' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/8787172115540037556'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/8787172115540037556'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2008/03/bug-bite.html' title='Bug bite'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-4895210484961720491</id><published>2008-02-07T09:56:00.001-08:00</published><updated>2009-01-15T14:09:23.419-08:00</updated><title type='text'>Pod Racing: How to synchronize threads across multiple JVMs</title><content type='html'>Sometimes, you want to start the job in multiple JVMs only when all the VM has started. This scenario often comes up with multiplayer games. Let's take a pod racing game for example. The players need to start at the same time and each game server handles 1 player (for simplicity's sake)&lt;br /&gt;&lt;br /&gt;Starting from Java 5, there's a &lt;a href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/CyclicBarrier.html"&gt;CyclicBarrier&lt;/a&gt; to synchronize threads and is a perfect tool for this. However, it only handles threads in 1 VM. D'oh! Here's where &lt;a href="http://www.terracotta.org"&gt;Terracotta&lt;/a&gt; comes in. If you make that CyclicBarrier a shared object, Terracotta will handle the clustering for you, transparently (no API). All of a sudden, your CyclicBarrier object will be seen across all the VMs participated in the game. &lt;br /&gt;&lt;br /&gt;Here's an example of how to do it. First, let's take a look at the PodRacer class&lt;br /&gt;&lt;pre name="code" class="java"&gt;package demo;&lt;br /&gt;&lt;br /&gt;import java.util.concurrent.CyclicBarrier;&lt;br /&gt;&lt;br /&gt;public class PodRacer {&lt;br /&gt;  public final static int     COUNT   = 2;&lt;br /&gt;  private final CyclicBarrier barrier = new CyclicBarrier(COUNT);&lt;br /&gt;  private String              name;&lt;br /&gt;&lt;br /&gt;  public PodRacer(String name) {&lt;br /&gt;    this.name = name;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public void ready() {&lt;br /&gt;    System.out.println(name + ": ready");&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public void set() throws Exception {&lt;br /&gt;    /* in Terracotta world, all threads in different VMs will block here */&lt;br /&gt;    barrier.await();&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public void go() throws Exception {&lt;br /&gt;    System.out.println(name + ": go");&lt;br /&gt;    Thread.sleep((int) (Math.random() * 5000) + 10);&lt;br /&gt;    System.out.println(name + ": arrived at " + System.currentTimeMillis());&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public static void main(String[] args) throws Exception {&lt;br /&gt;    PodRacer racer = new PodRacer(System.getProperty("racer.name", "unknown"));&lt;br /&gt;    racer.ready();&lt;br /&gt;    racer.set();&lt;br /&gt;    racer.go();&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Notice I hardcoded number of racers here to 2 but it can be made dynamic. If you run this class just like a normal Java program, it will block at racer.set() call because there's only 1 thread arriving at the barrier whereas it requires 2 for the barrier to be lifted. And if you don't use Terracotta underneath, it doesn't matter how many VMs you start, it will just block there.&lt;br /&gt;&lt;br /&gt;Next, I'll add Terracotta into the mix and share the &lt;i&gt;barrier&lt;/i&gt; object. That can be achieved by marking it in Terracotta configuration file &lt;b&gt;tc-config.xml&lt;/b&gt;&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;&amp;lt;con:tc-config xmlns:con=&amp;quot;http://www.terracotta.org/config&amp;quot;&amp;gt;&lt;br /&gt;  &amp;lt;servers&amp;gt;&lt;br /&gt;    &amp;lt;server host=&amp;quot;%i&amp;quot; name=&amp;quot;localhost&amp;quot;&amp;gt;&lt;br /&gt;      &amp;lt;dso-port&amp;gt;9510&amp;lt;/dso-port&amp;gt;&lt;br /&gt;      &amp;lt;jmx-port&amp;gt;9520&amp;lt;/jmx-port&amp;gt;&lt;br /&gt;      &amp;lt;data&amp;gt;terracotta/server-data&amp;lt;/data&amp;gt;&lt;br /&gt;      &amp;lt;logs&amp;gt;terracotta/server-logs&amp;lt;/logs&amp;gt;&lt;br /&gt;    &amp;lt;/server&amp;gt;&lt;br /&gt;  &amp;lt;/servers&amp;gt;&lt;br /&gt;  &amp;lt;clients&amp;gt;&lt;br /&gt;    &amp;lt;logs&amp;gt;terracotta/client-logs&amp;lt;/logs&amp;gt;&lt;br /&gt;  &amp;lt;/clients&amp;gt;&lt;br /&gt;  &amp;lt;application&amp;gt;&lt;br /&gt;    &amp;lt;dso&amp;gt;&lt;br /&gt;      &amp;lt;instrumented-classes/&amp;gt;&lt;br /&gt;      &amp;lt;roots&amp;gt;&lt;br /&gt;        &amp;lt;root&amp;gt;&lt;br /&gt;          &amp;lt;field-name&amp;gt;demo.PodRacer.barrier&amp;lt;/field-name&amp;gt;&lt;br /&gt;        &amp;lt;/root&amp;gt;&lt;br /&gt;      &amp;lt;/roots&amp;gt;&lt;br /&gt;    &amp;lt;/dso&amp;gt;&lt;br /&gt;  &amp;lt;/application&amp;gt;&lt;br /&gt;&amp;lt;/con:tc-config&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And that's it. When I start 2 JVMs running PodRacer class with Terracotta, I will efficiently have 2 threads calling &lt;i&gt;racer.set()&lt;/i&gt; on the &lt;b&gt;&lt;i&gt;shared barrier&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;To make it even easier to try out with Terracotta, there's a Maven 2 plugin that will handle starting up your project with Terracotta enabled. Here's the pom.xml of my project:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;&amp;lt;project xmlns=&amp;quot;http://maven.apache.org/POM/4.0.0&amp;quot; xmlns:xsi=&amp;quot;http://www.w3.org/2001/XMLSchema-instance&amp;quot; xsi:schemaLocation=&amp;quot;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd&amp;quot;&amp;gt;&lt;br /&gt;  &amp;lt;modelVersion&amp;gt;4.0.0&amp;lt;/modelVersion&amp;gt;&lt;br /&gt;  &amp;lt;groupId&amp;gt;PodRacing&amp;lt;/groupId&amp;gt;&lt;br /&gt;  &amp;lt;artifactId&amp;gt;PodRacing&amp;lt;/artifactId&amp;gt;&lt;br /&gt;  &amp;lt;version&amp;gt;0.0.1&amp;lt;/version&amp;gt;&lt;br /&gt;  &amp;lt;build&amp;gt;&lt;br /&gt;    &amp;lt;plugins&amp;gt;&lt;br /&gt;      &amp;lt;plugin&amp;gt;&lt;br /&gt;        &amp;lt;artifactId&amp;gt;maven-compiler-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;        &amp;lt;configuration&amp;gt;&lt;br /&gt;          &amp;lt;source&amp;gt;1.5&amp;lt;/source&amp;gt;&lt;br /&gt;          &amp;lt;target&amp;gt;1.5&amp;lt;/target&amp;gt;&lt;br /&gt;        &amp;lt;/configuration&amp;gt;&lt;br /&gt;      &amp;lt;/plugin&amp;gt;&lt;br /&gt;      &amp;lt;plugin&amp;gt;&lt;br /&gt;        &amp;lt;groupId&amp;gt;org.terracotta.maven.plugins&amp;lt;/groupId&amp;gt;&lt;br /&gt;        &amp;lt;artifactId&amp;gt;tc-maven-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;        &amp;lt;version&amp;gt;1.0.3&amp;lt;/version&amp;gt;&lt;br /&gt;        &amp;lt;executions&amp;gt;&lt;br /&gt;          &amp;lt;execution&amp;gt;&lt;br /&gt;            &amp;lt;phase&amp;gt;package&amp;lt;/phase&amp;gt;&lt;br /&gt;            &amp;lt;goals&amp;gt;&lt;br /&gt;              &amp;lt;goal&amp;gt;bootjar&amp;lt;/goal&amp;gt;&lt;br /&gt;            &amp;lt;/goals&amp;gt;&lt;br /&gt;          &amp;lt;/execution&amp;gt;&lt;br /&gt;        &amp;lt;/executions&amp;gt;&lt;br /&gt;        &lt;br /&gt;        &amp;lt;configuration&amp;gt;&lt;br /&gt;          &amp;lt;processes&amp;gt;&lt;br /&gt;            &amp;lt;process nodeName=&amp;quot;racer1&amp;quot; &lt;br /&gt;                     className=&amp;quot;demo.PodRacer&amp;quot;&lt;br /&gt;                     jvmargs=&amp;quot;-Dracer.name=Anakin&amp;quot;/&amp;gt;&lt;br /&gt;            &amp;lt;process nodeName=&amp;quot;racer2&amp;quot; &lt;br /&gt;                     className=&amp;quot;demo.PodRacer&amp;quot;&lt;br /&gt;                     jvmargs=&amp;quot;-Dracer.name=Sebulba&amp;quot;/&amp;gt;&lt;br /&gt;          &amp;lt;/processes&amp;gt;&lt;br /&gt;          &lt;br /&gt;        &amp;lt;/configuration&amp;gt;&lt;br /&gt;      &amp;lt;/plugin&amp;gt;&lt;br /&gt;    &amp;lt;/plugins&amp;gt;&lt;br /&gt;  &amp;lt;/build&amp;gt;&lt;br /&gt;  &amp;lt;pluginRepositories&amp;gt;&lt;br /&gt;    &amp;lt;pluginRepository&amp;gt;&lt;br /&gt;      &amp;lt;releases /&amp;gt;&lt;br /&gt;      &amp;lt;snapshots /&amp;gt;&lt;br /&gt;      &amp;lt;id&amp;gt;terracotta&amp;lt;/id&amp;gt;&lt;br /&gt;      &amp;lt;url&amp;gt;http://download.terracotta.org/maven2&amp;lt;/url&amp;gt;&lt;br /&gt;    &amp;lt;/pluginRepository&amp;gt;&lt;br /&gt;  &amp;lt;/pluginRepositories&amp;gt;&lt;br /&gt;&amp;lt;/project&amp;gt;&lt;br /&gt;&lt;/pre&gt; &lt;br /&gt;&lt;br /&gt;See how I defined 2 racers through the &lt;i&gt;process&lt;/i&gt; element in the plugin configuration. That will start 2 distinct VMs with Terracotta enabled. The output looks something like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[INFO] Starting DSO nodes &lt;br /&gt;[INFO] Starting node racer1: c:\jdk\jdk1.6.0_02\jre/bin/java.exe -Dcom.tc.l1.modules.repositories=file:/C:/Users/hhuynh/.m2/repository/ -Dtc.nodeName=racer1 -Dtc.numberOfNodes=2 -Dtc.config=d:\work\workspace\projects\PodRacing\tc-config.xml -Dtc.classpath=file:/c:/Users/hhuynh/AppData/Local/Temp/tc-classpath37736.tmp -Dtc.session.classpath=/C:/Users/hhuynh/.m2/repository/org/terracotta/tc-session/2.5.0/tc-session-2.5.0.jar -Dcom.tc.l1.modules.repositories=file:/C:/Users/hhuynh/.m2/repository/ -Xbootclasspath/p:d:\work\workspace\projects\PodRacing\target\dso-boot.jar -Dracer.name=Anakin -cp d:\work\workspace\projects\PodRacing\target\classes; demo.PodRacer&lt;br /&gt;[INFO] Starting node racer2: c:\jdk\jdk1.6.0_02\jre/bin/java.exe -Dcom.tc.l1.modules.repositories=file:/C:/Users/hhuynh/.m2/repository/ -Dtc.nodeName=racer2 -Dtc.numberOfNodes=2 -Dtc.config=d:\work\workspace\projects\PodRacing\tc-config.xml -Dtc.classpath=file:/c:/Users/hhuynh/AppData/Local/Temp/tc-classpath37737.tmp -Dtc.session.classpath=/C:/Users/hhuynh/.m2/repository/org/terracotta/tc-session/2.5.0/tc-session-2.5.0.jar -Dcom.tc.l1.modules.repositories=file:/C:/Users/hhuynh/.m2/repository/ -Xbootclasspath/p:d:\work\workspace\projects\PodRacing\target\dso-boot.jar -Dracer.name=Sebulba -cp d:\work\workspace\projects\PodRacing\target\classes; demo.PodRacer&lt;br /&gt;[INFO] ------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;[INFO] [racer2] Sebulba: ready&lt;br /&gt;[INFO] [racer1] Anakin: ready&lt;br /&gt;[INFO] [racer1] Anakin: go&lt;br /&gt;[INFO] [racer2] Sebulba: go&lt;br /&gt;[INFO] [racer2] Sebulba: arrived at 1202406914274&lt;br /&gt;[INFO] Finished node racer2&lt;br /&gt;[INFO] [racer1] Anakin: arrived at 1202406915125&lt;br /&gt;[INFO] Finished node racer1&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Ouch, Anakin sucked!&lt;br /&gt;&lt;br /&gt;Download the Maven &lt;a href="http://www.terracotta.org/confluence/display/labs/Pod+Racing+-+How+to+synchronize+threads+across+multiple+JVMs"&gt;project &lt;/a&gt; and try it out. Have fun.&lt;br /&gt;&lt;br /&gt;P.S. More about sharing memory between JVMs here &lt;a href="http://unserializableone.blogspot.com/2006/10/share-precious-heap-memory-accross.html"&gt;http://unserializableone.blogspot.com/2006/10/share-precious-heap-memory-accross.html&lt;/a&gt; or visit &lt;a href="http://terracotta.org"&gt;Terracotta&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-4895210484961720491?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/4895210484961720491/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=4895210484961720491' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/4895210484961720491'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/4895210484961720491'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2008/02/pod-racing-how-to-synchronize-threads.html' title='Pod Racing: How to synchronize threads across multiple JVMs'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-8709488040194098789</id><published>2007-11-27T07:59:00.000-08:00</published><updated>2007-11-27T08:59:33.890-08:00</updated><title type='text'>Monkey tales: Continuous Integration at Terracotta</title><content type='html'>As a platform to cluster and scale Java applications, &lt;a href="http://www.terracotta.org"&gt;Terracotta&lt;/a&gt; usages are greatly varied and resourceful. Further more, Terracotta supports Linux, Windows, and Solaris (also OSX for dev), compiles and runs with Sun JDKs (1.4, 1.5, 1.6) and IBM. As for application servers, we support Tomcat, JBoss, Jetty, Websphere, Weblogic, etc. The list goes on and on. That puts a lot of burden on testings and build infrastructure and we can't think of a better way than continuous integration.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://cruisecontrol.sourceforge.net/"&gt;CruiseControl&lt;/a&gt; (CC) comes in handy and works really well for us. It basically checks out new changesets from the repo periodically then compile and run all tests. Our custom built system, tcbuild, is JRuby + Ant combo. It has the ability to run defined group of tests such as system tests, unit tests, container tests, crash tests, etc.&lt;br /&gt;&lt;br /&gt;We create a CC project for each group of tests, namely check_unit, check_system, check_container, etc that varies in jdk, appserver, test mode. There will be 57 projects with all the permutations.&lt;br /&gt;&lt;br /&gt;And each CC project will do:&lt;br /&gt;- check out latest revisions&lt;br /&gt;- compile&lt;br /&gt;- run tests in that project&lt;br /&gt;- report&lt;br /&gt;....&lt;br /&gt;&lt;br /&gt;And we do this on multiple boxes that run Redhat, Suse 10, Windows 2003, Solaris 9, 10. We call these monkey machines.&lt;br /&gt;&lt;br /&gt;So what happens when there's a bad build (compiling error), or tests fail? CC will email a specific team depends on which group of tests that failed. There's also a mailing list that will keep track of every failure.&lt;br /&gt;&lt;br /&gt;This works great but there's a drawback. Whenever there's a compile error, we got spammed and we got it bad. It will fail on all 57 CC runs on each monkey box.&lt;br /&gt;&lt;br /&gt;To solve this problem, we devised a two-tier monkey scheme. We would have a monkey box, called "monkey police" that would check out the latest revision(s) every 5 minute. It will compile and run a group crucial system tests. If the build fails to compile or any of the test fails, that means that build is sorely broken. In this case, the police will email the last person(s) and the engineer mailing list about the error. If everything works out fine, the police will mark the latest revision as good and save it onto a shared file. The monkey troops (other test machines) will read from that shared file and only do "svn update" up to the latest known good revision before they run their tests.&lt;br /&gt;&lt;br /&gt;So before, the troops will do "svn update" and it will pull down every changes. With this scheme, the troops will do "svn update -r 2333" where "2333" is a good revision that the monkey police has tested.&lt;br /&gt;&lt;br /&gt;And we have one monkey police per branch. So when there's build error or fatal test failure, we'll know about it right away and it won't disturb our test machines in the mean time.&lt;br /&gt;&lt;br /&gt;Why bother with a bad checkin? :)&lt;br /&gt;&lt;br /&gt;This has worked great for us and I thought I'd share the experience.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-8709488040194098789?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/8709488040194098789/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=8709488040194098789' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/8709488040194098789'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/8709488040194098789'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2007/11/monkey-tales-continuous-integration-at.html' title='Monkey tales: Continuous Integration at Terracotta'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-9094022318300326226</id><published>2007-10-15T10:59:00.000-07:00</published><updated>2007-10-16T10:36:01.075-07:00</updated><title type='text'>Solution to classpath too long (aka input line too long) problem in Windows</title><content type='html'>If you use Java in Windows, you're bound to run into classpath too long problem when your classpath grows. Windows has a limit (1KB - 2KB) of characters you can have on one single command line. The infamous "Input line is too long" is very annoying. Here's a trick to get around it.&lt;br /&gt;&lt;br /&gt;"java.exe" command also scan for classes from the environment variable "CLASSPATH". If you can break your classpath into separate folders and jars, you can concatenate them like this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;setlocal&lt;br /&gt;&lt;br /&gt;set CLASSPATH=c:/my/first/jar/somejar.jar;%CLASSPATH%&lt;br /&gt;set CLASSPATH=c:/my/second/jar/someotherjar.jar;%CLASSPATH%&lt;br /&gt;set CLASSPATH=c:/path/to/a/folder;%CLASSPATH%&lt;br /&gt;.......&lt;br /&gt;.......&lt;br /&gt;&lt;br /&gt;java com.mycompany.Main&lt;br /&gt;&lt;br /&gt;endlocal&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;By using CLASSPATH env variable, you don't need to pass it in to "java" command. The setlocal/endlocal pair ensures the CLASSPATH is "local" to this process and won't pollute the systemwise value.&lt;br /&gt;&lt;br /&gt;--------&lt;br /&gt;Updated:&lt;br /&gt;&lt;br /&gt;Travis has pointed out that there is a limit on environment variable in Windows. I found it limited to 8K. So this isn't an absolute solution but it should sustain you for awhile. As suggested by D, you could shorten the path by using virtual drive. Further more, JDK6 has supported wildcard (*) in classpath.&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;Virtual drive:&lt;br /&gt;Say you have bunch of jars under c:/path/to/lib&lt;br /&gt;&lt;br /&gt;subst z: c:/my/path/to/lib&lt;br /&gt;set CLASSPATH=z:/jar1.jar:%CLASSPATH%&lt;br /&gt;&lt;br /&gt;Or with JDK6:&lt;br /&gt;&lt;br /&gt;set CLASSPATH=c:/path/to/lib/*:%CLASSPATH%&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-9094022318300326226?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/9094022318300326226/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=9094022318300326226' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/9094022318300326226'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/9094022318300326226'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2007/10/solution-to-classpath-too-long-aka.html' title='Solution to classpath too long (aka input line too long) problem in Windows'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-841692037299427681</id><published>2007-10-08T19:38:00.000-07:00</published><updated>2009-01-05T16:21:26.696-08:00</updated><title type='text'>Check for broken links in Ruby, Bash script and Java</title><content type='html'>I found myself writing 3 different versions of a function to check for broken links in Ruby, Bash and Java. Just want to document here in case anyone interested. Note that these functions use HEAD method (as opposed to GET/POST). It won't download a big file just to see if it's live.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Ruby&lt;/b&gt;&lt;br /&gt;&lt;pre class="ruby"&gt;require 'net/http'&lt;br /&gt;require 'uri'&lt;br /&gt;&lt;br /&gt;def isLive?(url)&lt;br /&gt;  uri = URI.parse(url)&lt;br /&gt;  response = nil&lt;br /&gt;  Net::HTTP.start(uri.host, uri.port) { |http|&lt;br /&gt;    response = http.head(uri.path.size &gt; 0 ? uri.path : "/")&lt;br /&gt;  }  &lt;br /&gt;  return response.code == "200"&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;puts isLive?("http://google.com")&lt;br /&gt;puts isLive?("http://asdfasdf.com")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Bash&lt;/b&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;#!/bin/bash&lt;br /&gt;&lt;br /&gt;function isLive {&lt;br /&gt;  wget -q --spider $1&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;isLive "http://google.com/somefakelink"&lt;br /&gt;&lt;br /&gt;if [ $? -eq 0 ]; then&lt;br /&gt;  echo "Good link"&lt;br /&gt;else &lt;br /&gt;  echo "Broken link"&lt;br /&gt;fi&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Java&lt;/b&gt;: &lt;span style="font-weight:bold;color:red"&gt;Edited (December 18 2008)&lt;/span&gt;: I've written a better Java version that handles link forwarding and doesn't use 3rd party API &lt;a href="http://unserializableone.blogspot.com/2008/12/link-checker-with-java-better-version.html"&gt;here&lt;/a&gt;&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;/* need httpunit-1.6.jar http://httpunit.sourceforge.net */&lt;br /&gt;&lt;br /&gt;private boolean isLive(String link) {&lt;br /&gt;  try {&lt;br /&gt;    WebRequest request = new HeadMethodWebRequest(link);&lt;br /&gt;    WebConversation wc = new WebConversation();&lt;br /&gt;    WebResponse response = wc.getResource(request);&lt;br /&gt;    return response.getResponseCode() == 200&lt;br /&gt;  } catch (Exception e)&lt;br /&gt;    return false;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-841692037299427681?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/841692037299427681/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=841692037299427681' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/841692037299427681'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/841692037299427681'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2007/10/check-for-broken-links-in-ruby-bash.html' title='Check for broken links in Ruby, Bash script and Java'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-1464060337034433365</id><published>2007-09-18T00:47:00.001-07:00</published><updated>2007-10-14T13:39:15.965-07:00</updated><title type='text'>Toggle code portion in your blog</title><content type='html'>I'm just testing out my javascript to toggle code sections in my blog. It's a useful feature to help keep your blog appears short while still provides enough details.&lt;br /&gt;&lt;br /&gt;&lt;a href="javascript:void(0)" onclick="toggle('thecode')"&gt;Show code&lt;/a&gt;&lt;br /&gt;&lt;div id='thecode' style="display:none"&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;The javascript I use:&lt;br/&gt;&lt;br /&gt;function toggle(id) {&lt;br /&gt;  var e = document.getElementById(id);&lt;br /&gt;  e.style.display = e.style.display == "none" ? "block" : "none";&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;Then create an anchor to control the toggling:&lt;br /&gt;&amp;lt;a href="javascript:void(0)" onclick="toggle('thecode')"&amp;gt;Show code&amp;lt;/a&amp;gt;&lt;br /&gt;&amp;lt;div id="thecode" style="display:none"&amp;gt; my code goes here &amp;lt;div&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Hope this works :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-1464060337034433365?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/1464060337034433365/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=1464060337034433365' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/1464060337034433365'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/1464060337034433365'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2007/09/toggle-code-portion-in-your-blog.html' title='Toggle code portion in your blog'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-5711850864135368837</id><published>2007-09-17T18:05:00.000-07:00</published><updated>2009-12-21T13:04:26.860-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ehcache'/><title type='text'>Distributed EhCache as second level cache under Hibernate</title><content type='html'>EhCache is a one of the great options for Hibernate second level cache. By making it distributed, multiple web applications will be able to share the same cache thus enhance your overall performance and availability. To enable the distributed cache, &lt;a href="http://www.terracotta.org/"&gt;Terracotta 2.4.3&lt;/a&gt; has a built in support for EhCache 1.3.0 and 1.2.4. I will go through an example of how this be done.&lt;br /&gt;&lt;br /&gt;The stack could be visualized like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;-----------        -----------&lt;br /&gt;Tomcat 1            Tomcat 2&lt;br /&gt;-----------        -----------&lt;br /&gt;Hibernate           Hibernate&lt;br /&gt;------------------------------&lt;br /&gt;        EhCache&lt;br /&gt;       TERRACOTTA&lt;br /&gt;------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Terracotta is the driving force though its presence is transparent to your web app thanks to bytecode instrumentation. First, let take a look at enabling EhCache in Hibernate configuration file&lt;br /&gt;&lt;a href="javascript:void(0)" onclick="toggle('c1')"&gt;[+]hibernate.cfg.xml&lt;/a&gt;&lt;br /&gt;&lt;pre id="c1" style="display: none;"&gt;... database setting ...&lt;br /&gt;&amp;lt;property name="cache.use_query_cache"&amp;gt;true&amp;lt;/property&amp;gt;&lt;br /&gt;&amp;lt;property name="cache.provider_configuration_file_resource_path"&amp;gt;ehcache.xml&amp;lt;/property&amp;gt;&lt;br /&gt;&amp;lt;property name="cache.use_second_level_cache"&amp;gt;true&amp;lt;/property&amp;gt;&lt;br /&gt;&amp;lt;property name="cache.provider_class"&amp;gt;org.hibernate.cache.EhCacheProvider&amp;lt;/property&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;mapping resource="Event.hbm.xml" /&amp;gt;&lt;br /&gt;&amp;lt;mapping resource="Person.hbm.xml" /&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It's just the same standard setting that you would use for non-distributed case. You want to turn on query cache mode, point Hibernate to ehcache.xml and finally specify a provider. The ehcache.xml can be as simple as this:&lt;br /&gt;&lt;a href="javascript:void(0)" onclick="toggle('c2')"&gt;[+]ehcache.xml&lt;/a&gt;&lt;br /&gt;&lt;pre id="c2" style="display: none;"&gt;&amp;lt;ehcache&amp;gt;&lt;br /&gt;  &amp;lt;diskStore path="user.dir"/&amp;gt;&lt;br /&gt;  &amp;lt;defaultCache&lt;br /&gt;      maxElementsInMemory="10000"&lt;br /&gt;      eternal="false"&lt;br /&gt;      overflowToDisk="false"&lt;br /&gt;      timeToIdleSeconds="300"&lt;br /&gt;      timeToLiveSeconds="300"&lt;br /&gt;      diskPersistent="false"&lt;br /&gt;      diskExpiryThreadIntervalSeconds="120"&lt;br /&gt;      memoryStoreEvictionPolicy="LRU"/&amp;gt;&lt;br /&gt;  &lt;br /&gt;  &amp;lt;cache name="org.hibernate.cache.StandardQueryCache"&lt;br /&gt;      maxElementsInMemory="100"&lt;br /&gt;      eternal="false"&lt;br /&gt;      timeToIdleSeconds="120"&lt;br /&gt;      timeToLiveSeconds="120"&lt;br /&gt;      overflowToDisk="false"/&amp;gt;&lt;br /&gt;  &lt;br /&gt;  &amp;lt;cache name="org.hibernate.cache.UpdateTimestampsCache"&lt;br /&gt;      maxElementsInMemory="5000"&lt;br /&gt;      timeToIdleSeconds="120"&lt;br /&gt;      timeToLiveSeconds="120"&lt;br /&gt;      eternal="true"/&amp;gt;&lt;br /&gt;&amp;lt;/ehcache&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;One thing I'd like to point out is that Terracotta persists heap memory to disk efficiently (and fault them in as needed) so "overflowToDisk" becomes redundant in StandardQueryCache. As a matter of fact, Terracotta doesn't honor this option. Now that the cache is set up, in our entity mapping files, we need to let Hibernate know which entities we'd like to cache during runtime. In my example, I have an Event table (title, date) and a Person table (firstname, lastname) that have a many-to-many relationship through a join table PERSON_EVENT. With that in mind, let's examine Event.hbm.xml and Person.hbm.xml:&lt;br /&gt;&lt;a href="javascript:void(0)" onclick="toggle('c3')"&gt;[+]Mapping files&lt;/a&gt;&lt;br /&gt;&lt;pre id="c3" style="display: none;"&gt;&lt;br /&gt;&lt;u&gt;Event.hbm.xml&lt;/u&gt;:&lt;br /&gt;&amp;lt;class name="events.Event" table="EVENTS"&amp;gt;&lt;br /&gt;&lt;b&gt;&amp;lt;cache usage="read-write" /&amp;gt;&lt;/b&gt;&lt;br /&gt;&amp;lt;id name="id" column="EVENT_ID"&amp;gt;&lt;br /&gt;  &amp;lt;generator class="native" /&amp;gt;&lt;br /&gt;&amp;lt;/id&amp;gt;&lt;br /&gt;&amp;lt;property name="date" column="EVENT_DATE" /&amp;gt;&lt;br /&gt;&amp;lt;property name="title" /&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;set name="participants" table="PERSON_EVENT" lazy="true"&lt;br /&gt;  inverse="true" cascade="lock"&amp;gt;&lt;br /&gt;  &lt;b&gt;&amp;lt;cache usage="read-write" /&amp;gt;&lt;/b&gt;&lt;br /&gt;  &amp;lt;key column="EVENT_ID" /&amp;gt;&lt;br /&gt;  &amp;lt;many-to-many column="PERSON_ID"&lt;br /&gt;    class="events.Person" /&amp;gt;&lt;br /&gt;&amp;lt;/set&amp;gt;&lt;br /&gt;&amp;lt;/class&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;u&gt;Person.hbm.xml&lt;/u&gt;:&lt;br /&gt;&amp;lt;class name="events.Person" table="PERSON"&amp;gt;&lt;br /&gt;   &lt;b&gt;&amp;lt;cache usage="read-write" /&amp;gt;&lt;/b&gt;&lt;br /&gt;   &amp;lt;id name="id" column="PERSON_ID"&amp;gt;&lt;br /&gt;       &amp;lt;generator class="native"/&amp;gt;&lt;br /&gt;   &amp;lt;/id&amp;gt;&lt;br /&gt;&lt;br /&gt;   &amp;lt;property name="firstname"/&amp;gt;&lt;br /&gt;   &amp;lt;property name="lastname"/&amp;gt;&lt;br /&gt;  &lt;br /&gt;   &amp;lt;set name="events" table="PERSON_EVENT"&amp;gt;&lt;br /&gt;       &lt;b&gt;&amp;lt;cache usage="read-write" /&amp;gt;&lt;/b&gt;&lt;br /&gt;       &amp;lt;key column="PERSON_ID"/&amp;gt;&lt;br /&gt;       &amp;lt;many-to-many column="EVENT_ID" class="events.Event"/&amp;gt;&lt;br /&gt;   &amp;lt;/set&amp;gt; &lt;br /&gt;&lt;br /&gt;&amp;lt;/class&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;u&gt;Event.java&lt;/u&gt;:&lt;br /&gt;public class Event {&lt;br /&gt; private Long   id;&lt;br /&gt; private String title;&lt;br /&gt; private String date;&lt;br /&gt; private Set    participants = new HashSet();&lt;br /&gt;&lt;br /&gt; /* getters and setters */&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;u&gt;Person.java&lt;/u&gt;:&lt;br /&gt;public class Person {&lt;br /&gt; private Long   id;&lt;br /&gt; private String firstname;&lt;br /&gt; private String lastname;&lt;br /&gt; private Set    events = new HashSet();&lt;br /&gt;&lt;br /&gt; /* getters and setters */&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The details might be distracting but if you're familiar with Hibernate, this should be as simple as it gets :)&lt;br /&gt;&lt;br /&gt;Now we get past all the settings, the fun stuff begins with our servlets. I've created 2 servlets, one called CreateEvents that will populate data into our table. The other, QueryEvents, will query and display the cache hit statistic.&lt;br /&gt;&lt;a href="javascript:void(0)" onclick="toggle('c5')"&gt;[+]CreateEvents.java&lt;/a&gt;&lt;br /&gt;&lt;pre id="c5" class="prettyprint" style="display: none;"&gt;&lt;br /&gt;public class CreateEvents extends HttpServlet {&lt;br /&gt;&lt;br /&gt; /* (1) */&lt;br /&gt; public void init() throws ServletException {&lt;br /&gt;   super.init();&lt;br /&gt;   generateData();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; protected void doGet(HttpServletRequest req, HttpServletResponse resp)&lt;br /&gt;     throws ServletException, IOException {&lt;br /&gt;   /* (2) */&lt;br /&gt;   if (req.getParameter("fn") != null &amp;amp;&amp;amp; req.getParameter("ln") != null) {&lt;br /&gt;     EventManager.createAndStorePerson(req.getParameter("fn"), req&lt;br /&gt;         .getParameter("ln"));&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   resp.setContentType("text/html");&lt;br /&gt;   resp.getWriter().println("&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;");&lt;br /&gt;  &lt;br /&gt;   /* (3) */&lt;br /&gt;   Statistics stats = HibernateUtil.getSessionFactory().getStatistics();&lt;br /&gt;   stats.setStatisticsEnabled(true);&lt;br /&gt;   QueryStatistics queryStats = stats.getQueryStatistics("from Event");&lt;br /&gt;  &lt;br /&gt;   /* (4) */&lt;br /&gt;   resp.getWriter().println(&lt;br /&gt;       "Events created: " + EventManager.query("Event") + "&amp;lt;br/&amp;gt;");&lt;br /&gt;   resp.getWriter().println(&lt;br /&gt;       "Event query cache miss: " + queryStats.getCacheMissCount() + "&amp;lt;br/&amp;gt;");&lt;br /&gt;   resp.getWriter().println(&lt;br /&gt;       "Event query cache hit: " + queryStats.getCacheHitCount() + "&amp;lt;br/&amp;gt;");&lt;br /&gt;   resp.getWriter().println("&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;");&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private void generateData() {&lt;br /&gt;   HibernateUtil.dropAndCreateDatabaseSchema();&lt;br /&gt;&lt;br /&gt;   /* people */&lt;br /&gt;   Long p1 = EventManager.createAndStorePerson("Ichigo", "Kurosaki");&lt;br /&gt;   Long p2 = EventManager.createAndStorePerson("Abarai", "Renji");&lt;br /&gt;   Long p3 = EventManager.createAndStorePerson("Ishida", "Uryu");&lt;br /&gt;&lt;br /&gt;   /* events */&lt;br /&gt;   Long e1 = EventManager.createAndStoreEvent("Event 1", "2007-09-30");&lt;br /&gt;   Long e2 = EventManager.createAndStoreEvent("Event 2", "2007-12-01");&lt;br /&gt;&lt;br /&gt;   /* participants in e1 */&lt;br /&gt;   EventManager.addPersonToEvent(p1, e1);&lt;br /&gt;   EventManager.addPersonToEvent(p2, e1);&lt;br /&gt;   EventManager.addPersonToEvent(p3, e1);&lt;br /&gt;&lt;br /&gt;   /* participants in e2 */&lt;br /&gt;   EventManager.addPersonToEvent(p2, e2);&lt;br /&gt;   EventManager.addPersonToEvent(p3, e2);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Some default data (3 persons and 2 events) are created during init() phase. With (2),  I added an option to add additional Person to the database so later we can use it to demonstrate cache invalidating. To be able to get statistic of cache hit and miss, (3), a query statistic object is created. It will give us the hit/miss count in (4), for the query "select * from Event".&lt;br /&gt;With QueryEvents.java, we will ask Hibernate for list of persons, and events, which would prove to us whether the cache is used or database is used:&lt;br /&gt;&lt;a href="javascript:void(0)" onclick="toggle('c6')"&gt;[+]QueryEvents.java&lt;/a&gt;&lt;br /&gt;&lt;pre id="c6" class="prettyprint" style="display: none;"&gt;&lt;br /&gt;public class QueryEvents extends HttpServlet {&lt;br /&gt;&lt;br /&gt; protected void doGet(HttpServletRequest req, HttpServletResponse resp)&lt;br /&gt;     throws ServletException, IOException {&lt;br /&gt;&lt;br /&gt;   resp.setContentType("text/html");&lt;br /&gt;   resp.getWriter().println("&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;");&lt;br /&gt;&lt;br /&gt;   Statistics stats = HibernateUtil.getSessionFactory().getStatistics();&lt;br /&gt;   stats.setStatisticsEnabled(true);&lt;br /&gt;   QueryStatistics queryStats = stats.getQueryStatistics("from Event");&lt;br /&gt;&lt;br /&gt;   /* (1) query Event */&lt;br /&gt;   List events = EventManager.query("Event");&lt;br /&gt;   resp.getWriter().println(&lt;br /&gt;       "Events found: " +  events + "&amp;lt;br/&amp;gt;");&lt;br /&gt;   resp.getWriter().println(&lt;br /&gt;       "Event query cache miss: " + queryStats.getCacheMissCount() + "&amp;lt;br/&amp;gt;");&lt;br /&gt;   resp.getWriter().println(&lt;br /&gt;       "Event query cache hit: " + queryStats.getCacheHitCount() + "&amp;lt;br/&amp;gt;");&lt;br /&gt;&lt;br /&gt;   /* (2) query People */&lt;br /&gt;   queryStats = stats.getQueryStatistics("from Person");&lt;br /&gt;   resp.getWriter().println("&amp;lt;br/&amp;gt;" +&lt;br /&gt;       "People found: " + EventManager.query("Person") + "&amp;lt;br/&amp;gt;");&lt;br /&gt;   resp.getWriter().println(&lt;br /&gt;       "Person query cache miss: " + queryStats.getCacheMissCount() + "&amp;lt;br/&amp;gt;");&lt;br /&gt;   resp.getWriter().println(&lt;br /&gt;       "Person query cache hit: " + queryStats.getCacheHitCount() + "&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;");&lt;br /&gt;&lt;br /&gt;   /* (3) reassociate transient Event objects to this hibernate session */&lt;br /&gt;   /* so we can query for participants */&lt;br /&gt;   Session session = HibernateUtil.getSessionFactory().getCurrentSession();&lt;br /&gt;   session.beginTransaction();&lt;br /&gt;   for (Iterator it = events.iterator(); it.hasNext();) {&lt;br /&gt;     session.lock((Event) it.next(), LockMode.NONE);&lt;br /&gt;   }&lt;br /&gt;  &lt;br /&gt;   resp.getWriter().println("Participants of Even 1: " + ((Event)events.get(0)).getParticipants() + "&amp;lt;br/&amp;gt;");&lt;br /&gt;   resp.getWriter().println("Participants of Even 2: " + ((Event)events.get(1)).getParticipants() + "&amp;lt;br/&amp;gt;");&lt;br /&gt;  &lt;br /&gt;   session.close();   &lt;br /&gt;   resp.getWriter().println("&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;");&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I ran these two servlets in 1 Tomcat to make sure everything working correctly:&lt;br /&gt;By hitting http://localhost:8080/Events/create (mapped to CreateEvents servlet)&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Events created: [Event 1: 2007-09-30, Event 2: 2007-12-01]&lt;br /&gt;Event query cache miss: 1&lt;br /&gt;Event query cache hit: 0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Since it's the first time we query for events, that's why we have one cache miss. Hibernate went to db to get the data.&lt;br /&gt;Now we hit http://localhost:8080/Events/query (mapped to QueryEvents servlet), the result is:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Events found: [Event 1: 2007-09-30, Event 2: 2007-12-01]&lt;br /&gt;Event query cache miss: 1&lt;br /&gt;Event query cache hit: 1&lt;br /&gt;&lt;br /&gt;People found: [Ichigo Kurosaki, Abarai Renji, Ishida Uryu]&lt;br /&gt;Person query cache miss: 1&lt;br /&gt;Person query cache hit: 0&lt;br /&gt;&lt;br /&gt;Participants of Even 1: [Abarai Renji, Ishida Uryu, Ichigo Kurosaki]&lt;br /&gt;Participants of Even 2: [Abarai Renji, Ishida Uryu]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As expected, the Event query is now hit the cache, raising the hit count to 1. And since it's the first time we query for Person, the cache miss is 1. The participants list proves that the event pojos coming from the cache are valid and can be re-associated to this Hibernate session. Now, if we run CreateEvents servlet on Tomcat 1 and QueryEvents on Tomcat 2, the distributed cache should give us the same result.&lt;br /&gt;This is where Terracotta comes in. There is no change in settings needed in any of the Hibernate mappings files, nor ehcache.xml. There is no code change either. What you need to do is to run Tomcat with Terracotta enabled. The process involved setting 3 java system properties to Tomcat jvm&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;-Xbootclasspath/p:"path/to/Terracotta/bootjar"&lt;br /&gt;-Dtc.install-root=/path/to/Terracotta/install&lt;br /&gt;-Dtc.config=/path/to/tc-config.xml&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Detailed instructions can be found &lt;a href="http://www.terracotta.org/confluence/display/docs1/Setting+Up+a+Tomcat+Web+Cluster"&gt;here&lt;/a&gt;&lt;br /&gt;Luckily, Terracotta has a nice Session configuration tool that will help you set up 2 Tomcats (or Weblogic) cluster. All you need is to import your WAR file. I created a Evetns.war file that contains both of my serlvets and all the needed jars. I need to configure tc-config.xml to let Terracotta knows that I'm using Hibernate, EhCache by adding those modules (1). Also, classes that will be shared need to be instrumented (2). Terracotta also supports sharing of session by declaring your webapp name (3). However, I'm not clustering any session in this example.&lt;br /&gt;&lt;a href="javascript:void(0)" onclick="toggle('c7')"&gt;[+]tc-config.xml&lt;/a&gt;&lt;br /&gt;&lt;pre id="c7" style="display: none;"&gt;&lt;br /&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;br /&gt;&amp;lt;con:tc-config xmlns:con="http://www.terracotta.org/config"&amp;gt;&lt;br /&gt; &amp;lt;servers&amp;gt;&lt;br /&gt;   &amp;lt;server host="%i" name="localhost"&amp;gt;&lt;br /&gt;     &amp;lt;dso-port&amp;gt;9510&amp;lt;/dso-port&amp;gt;&lt;br /&gt;     &amp;lt;jmx-port&amp;gt;9520&amp;lt;/jmx-port&amp;gt;&lt;br /&gt;     &amp;lt;data&amp;gt;terracotta/server-data&amp;lt;/data&amp;gt;&lt;br /&gt;     &amp;lt;logs&amp;gt;terracotta/server-logs&amp;lt;/logs&amp;gt;&lt;br /&gt;   &amp;lt;/server&amp;gt;&lt;br /&gt; &amp;lt;/servers&amp;gt;&lt;br /&gt; &amp;lt;clients&amp;gt;&lt;br /&gt;   &amp;lt;logs&amp;gt;terracotta/client-logs&amp;lt;/logs&amp;gt;&lt;br /&gt;   ( 1 )&lt;br /&gt;   &amp;lt;modules&amp;gt;&lt;br /&gt; &amp;lt;module name="clustered-hibernate-3.1.2" version="1.0.0" /&amp;gt;&lt;br /&gt; &amp;lt;module name="clustered-ehcache-1.3" version="1.0.0" /&amp;gt;&lt;br /&gt;   &amp;lt;/modules&amp;gt;&lt;br /&gt; &amp;lt;/clients&amp;gt;&lt;br /&gt; &amp;lt;application&amp;gt;&lt;br /&gt;   &amp;lt;dso&amp;gt;&lt;br /&gt;     ( 2 )&lt;br /&gt;     &amp;lt;instrumented-classes&amp;gt;&lt;br /&gt;       &amp;lt;include&amp;gt;&lt;br /&gt;         &amp;lt;class-expression&amp;gt;events.Event&amp;lt;/class-expression&amp;gt;&lt;br /&gt;       &amp;lt;/include&amp;gt;&lt;br /&gt;       &amp;lt;include&amp;gt;&lt;br /&gt;         &amp;lt;class-expression&amp;gt;events.Person&amp;lt;/class-expression&amp;gt;&lt;br /&gt;       &amp;lt;/include&amp;gt;&lt;br /&gt;       &amp;lt;include&amp;gt;&lt;br /&gt;         &amp;lt;class-expression&amp;gt;events.EventManager&amp;lt;/class-expression&amp;gt;&lt;br /&gt;       &amp;lt;/include&amp;gt;       &lt;br /&gt;     &amp;lt;/instrumented-classes&amp;gt;&lt;br /&gt;     ( 3 )&lt;br /&gt;     &amp;lt;web-applications&amp;gt;&lt;br /&gt;       &amp;lt;web-application&amp;gt;Events&amp;lt;/web-application&amp;gt;&lt;br /&gt;     &amp;lt;/web-applications&amp;gt;&lt;br /&gt;   &amp;lt;/dso&amp;gt;&lt;br /&gt; &amp;lt;/application&amp;gt;&lt;br /&gt; &amp;lt;system&amp;gt;&lt;br /&gt;   &amp;lt;configuration-model&amp;gt;development&amp;lt;/configuration-model&amp;gt;&lt;br /&gt; &amp;lt;/system&amp;gt;&lt;br /&gt;&amp;lt;/con:tc-config&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The session configurator will start up Terracotta server and 2 Tomcats. We can now access CreateEvents servlet on the first Tomcat at port 9081 by hitting http://localhost:9081/Events/create and hit QueryEvents on the second Tomcat at http://localhost:9082/Events/query.&lt;br /&gt;&lt;br /&gt;The result I got is:&lt;br /&gt;&lt;pre&gt; Events/create:&lt;br /&gt;Events created: [Event 1: 2007-09-30, Event 2: 2007-12-01]&lt;br /&gt;Event query cache miss: 1&lt;br /&gt;Event query cache hit: 0&lt;br /&gt;&lt;br /&gt;Events/query:&lt;br /&gt;Events found: [Event 1: 2007-09-30, Event 2: 2007-12-01]&lt;br /&gt;Event query cache miss: 0&lt;br /&gt;Event query cache hit: 1&lt;br /&gt;&lt;br /&gt;People found: [Ichigo Kurosaki, Abarai Renji, Ishida Uryu]&lt;br /&gt;Person query cache miss: 1&lt;br /&gt;Person query cache hit: 0&lt;br /&gt;&lt;br /&gt;Participants of Even 1: [Abarai Renji, Ishida Uryu, Ichigo Kurosaki]&lt;br /&gt;Participants of Even 2: [Abarai Renji, Ishida Uryu]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If I reload Events/query, the statistic is as expected:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Event query cache miss: 0&lt;br /&gt;Event query cache hit: 2&lt;br /&gt;&lt;br /&gt;Person query cache miss: 1&lt;br /&gt;Person query cache hit: 1&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;To test that the cache is invalidated, after hitting Events/query, in the cache we now have a list of 3 persons. If we create one new person, by hitting http://localhost:9081/Events/create?fn=John&amp;amp;ln=Smith, what we have in the cache now is stale data. Of course, thanks to Terracotta, the second Tomcat + Hibernate is aware of this situation and stale data will be invalidated. Which leads to a cache miss (instead of a hit) when we reload http://localhost:9082/Events/query&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Event query cache miss: 0&lt;br /&gt;Event query cache hit: 3&lt;br /&gt;&lt;br /&gt;People found: [Ichigo Kurosaki, Abarai Renji, Ishida Uryu, John Smith]&lt;br /&gt;Person query cache miss: 2&lt;br /&gt;Person query cache hit: 1&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As you can see, Event query cache hit continues to rise, when we now have a cache miss in Person query since the cached data is made invalid. Hibernate had to hit the database for new fresh data.&lt;br /&gt;&lt;br /&gt;I hope I didn't bore you with too much details but I think it's important to each steps. &lt;a href="http://www.terracotta.org/"&gt;Terracotta&lt;/a&gt; is greatly beneficial if you choose to use EhCache as distributed cache with Hibernate.&lt;br /&gt;&lt;br /&gt;You can download the project &lt;a href="http://www.terracotta.org/confluence/display/labs/Distributed+EhCache+as+second+level+cache+under+Hibernate"&gt; here&lt;/a&gt; and give it a try.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-5711850864135368837?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/5711850864135368837/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=5711850864135368837' title='17 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/5711850864135368837'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/5711850864135368837'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2007/09/distributed-ehcache-as-second-level.html' title='Distributed EhCache as second level cache under Hibernate'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><thr:total>17</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-2264350230696784065</id><published>2007-09-06T13:12:00.000-07:00</published><updated>2007-09-06T13:30:01.388-07:00</updated><title type='text'>Simple directory browser for Amazon S3</title><content type='html'>If you have used &lt;a href="http://www.amazon.com/gp/browse.html?node=16427261"&gt;Amazon S3&lt;/a&gt;, you might be annoyed at the lack of support for directory browsing. If you hit your repo url, all you get is an XML file listing your content. So I wrote this small javascript to read that XML file, then display as a directory tree just like you're browsing an FTP site.&lt;br /&gt;&lt;br /&gt;For example, say &lt;a href="http://www.terracotta.org"&gt;Terracotta&lt;/a&gt; has a S3 repo at &lt;a href="http://download.terracotta.org"&gt;http://download.terracotta.org&lt;/a&gt;. If you click on that link, you'll get an XML file. Now I want to be able to see what we have under &lt;a href="http://download.terracotta.org/maven2"&gt;http://download.terracotta.org/maven2&lt;/a&gt;, you'll get nothing but an error trying to go there.&lt;br /&gt;&lt;br /&gt;My javascript is under the page &lt;a href="http://download.terracotta.org/maven2/index.html"&gt;http://download.terracotta.org/maven2/index.html&lt;/a&gt; &lt;br /&gt;&lt;br /&gt;Just right click on that page and read the source, the javascript is pretty simple. Could have been made better or fancier but I'm no javascript guru :)&lt;br /&gt;&lt;br /&gt;Maybe it could be of use for you.&lt;br /&gt;&lt;br /&gt;Note: S3 doesn't serve index.html file automatically (after all, it's not a web server) so you have to hit the index.html explicitly.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-2264350230696784065?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/2264350230696784065/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=2264350230696784065' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/2264350230696784065'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/2264350230696784065'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2007/09/simple-directory-browser-for-amazon-s3.html' title='Simple directory browser for Amazon S3'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-3959861784544265370</id><published>2007-08-26T21:25:00.000-07:00</published><updated>2007-08-26T22:14:57.071-07:00</updated><title type='text'>Lightweight Google Geocoder with Java</title><content type='html'>I often look for tools before deciding to build them myself for obvious reasons: saving time and labor. I was looking for a Java implementation of Google geocoder but there seems to be only this one &lt;a href="http://geo-google.sourceforge.net/"&gt;GeoGoogle&lt;/a&gt;. My first impression is that the tool is a little heavy for its job. Further more, it chooses to deal with XML format and all the parsing and schema validation is a bit too much for my taste. To use &lt;a href="http://www.google.com/apis/maps/documentation/#Geocoding_HTTP_Request"&gt;Google geocode&lt;/a&gt; service, all you need is to send an HTTP request along with your address. The response will be in either XML or JSON format. It will include details about the address you sent such as city, state, zipcode, etc, but the most important things are longitude and latitude. So I decided to go with JSON and wrote this tool. Hopefully, someone else might find it useful. Many thanks to the folks that wrote &lt;a href="http://json-lib.sourceforge.net/"&gt;JSON-LIB&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;public class GGcoder {&lt;br /&gt;  private static final String URL         = "http://maps.google.com/maps/geo?output=json";&lt;br /&gt;  private static final String DEFAULT_KEY = "YOUR_GOOGLE_API_KEY"; &lt;br /&gt;&lt;br /&gt;  public static GAddress geocode(String address, String key) throws Exception {&lt;br /&gt;    URL url = new URL(URL + "&amp;q=" + URLEncoder.encode(address, "UTF-8")&lt;br /&gt;        + "&amp;key=" + key);&lt;br /&gt;    URLConnection conn = url.openConnection();&lt;br /&gt;    ByteArrayOutputStream output = new ByteArrayOutputStream(1024);&lt;br /&gt;    IOUtils.copy(conn.getInputStream(), output);&lt;br /&gt;    output.close();&lt;br /&gt;&lt;br /&gt;    GAddress gaddr = new GAddress();&lt;br /&gt;    JSONObject json = JSONObject.fromString(output.toString());&lt;br /&gt;    JSONObject placemark = (JSONObject) query(json, "Placemark[0]");&lt;br /&gt;&lt;br /&gt;    final String commonId = "AddressDetails.Country.AdministrativeArea";&lt;br /&gt;&lt;br /&gt;    gaddr.setFullAddress(query(placemark, "address").toString());&lt;br /&gt;    gaddr.setZipCode(query(placemark,&lt;br /&gt;        commonId + ".SubAdministrativeArea.Locality.PostalCode.PostalCodeNumber")&lt;br /&gt;        .toString());&lt;br /&gt;    gaddr.setAddress(query(placemark,&lt;br /&gt;        commonId + ".SubAdministrativeArea.Locality.Thoroughfare.ThoroughfareName")&lt;br /&gt;        .toString());&lt;br /&gt;    gaddr.setCity(query(placemark,&lt;br /&gt;        commonId + ".SubAdministrativeArea.SubAdministrativeAreaName").toString());&lt;br /&gt;    gaddr.setState(query(placemark, commonId + ".AdministrativeAreaName").toString());&lt;br /&gt;    gaddr.setLat(Double.parseDouble(query(placemark, "Point.coordinates[1]")&lt;br /&gt;        .toString()));&lt;br /&gt;    gaddr.setLng(Double.parseDouble(query(placemark, "Point.coordinates[0]")&lt;br /&gt;        .toString()));&lt;br /&gt;    return gaddr;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public static GAddress geocode(String address) throws Exception {&lt;br /&gt;    return geocode(address, DEFAULT_KEY);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  /* allow query for json nested objects, ie. Placemark[0].address */&lt;br /&gt;  private static Object query(JSONObject jo, String query) {&lt;br /&gt;    try {&lt;br /&gt;      String[] keys = query.split("\\.");&lt;br /&gt;      Object r = queryHelper(jo, keys[0]);&lt;br /&gt;      for (int i = 1; i &lt; keys.length; i++) {&lt;br /&gt;        r = queryHelper(jo.fromObject(r), keys[i]);&lt;br /&gt;      }&lt;br /&gt;      return r;&lt;br /&gt;    } catch (JSONException e) {&lt;br /&gt;      return "";&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  /* help in query array objects: Placemark[0] */&lt;br /&gt;  private static Object queryHelper(JSONObject jo, String query) {&lt;br /&gt;    int openIndex = query.indexOf('[');&lt;br /&gt;    int endIndex = query.indexOf(']');&lt;br /&gt;    if (openIndex &gt; 0) {&lt;br /&gt;      String key = query.substring(0, openIndex);&lt;br /&gt;      int index = Integer.parseInt(query.substring(openIndex + 1, endIndex));&lt;br /&gt;      return jo.getJSONArray(key).get(index);&lt;br /&gt;    }&lt;br /&gt;    return jo.get(query);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public static void main(String[] args) throws Exception {&lt;br /&gt;    System.out.println(GGcoder.geocode("650 Townsend st, San Francsico, CA"));&lt;br /&gt;    System.out.println(GGcoder.geocode("94103"));&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;/* Class to hold geocode result */&lt;br /&gt;public class GAddress {&lt;br /&gt;  public String address;&lt;br /&gt;  public String fullAddress;&lt;br /&gt;  public String zipCode;&lt;br /&gt;  public String city;&lt;br /&gt;  public String state;&lt;br /&gt;  public double lat;&lt;br /&gt;  public double lng;&lt;br /&gt;&lt;br /&gt;  /* getters and setters */&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-3959861784544265370?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/3959861784544265370/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=3959861784544265370' title='14 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/3959861784544265370'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/3959861784544265370'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2007/08/lightweight-google-geocoder-with-java.html' title='Lightweight Google Geocoder with Java'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><thr:total>14</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-8473032169338311669</id><published>2007-08-16T08:56:00.000-07:00</published><updated>2009-01-21T17:55:51.620-08:00</updated><title type='text'>Make use of Java dynamic proxy</title><content type='html'>I didn't know about Java dynamic &lt;a href="http://java.sun.com/j2se/1.5.0/docs/guide/reflection/proxy.html"&gt;proxy&lt;/a&gt; before but finally got a chance to learn and found a good use for it. The test I was writing had the smell of repeating code:&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;private void testPutWithoutSynch(params...) throws Exception {&lt;br /&gt;  ...&lt;br /&gt;  &lt;br /&gt;  map.put("k1", "v1");&lt;br /&gt;  &lt;br /&gt;  assertions...&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;private void testPutWithSynch(params...) throws Exception {&lt;br /&gt;  ...&lt;br /&gt;  &lt;br /&gt;  synchronized(map) {&lt;br /&gt;    map.put("k1", "v1");&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  assertions...&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The test pattern repeated with other operations that you can do with Map like puttAll, remove, etc... Do_set_up() is actually some pre setup steps that I need to perform according to parameters list. As you can see, I have to repeat the test method twice for each Map operation I want to test, with and without the synchronize block.&lt;br /&gt;&lt;br /&gt;So there is this code smell and I didn't know get rid of it. Then Tim Eck, my coworker, showed me how to use Java proxy. The idea is to wrap my map in a proxy, and use reflection to invoke methods of the map. By using a invocation handler, I can have a variable to control whether  or not I want to use synchronize for that particular operation.&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;class Handler implements InvocationHandler {&lt;br /&gt;  private final Map map;&lt;br /&gt;  private boolean useSynch = false;&lt;br /&gt;&lt;br /&gt;  public Handler(Map map) {&lt;br /&gt;    this.map = map;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public void setUseSynch(boolean flag) {&lt;br /&gt;    this.useSynch = flag;&lt;br /&gt;  } &lt;br /&gt;&lt;br /&gt;  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {&lt;br /&gt;    try {&lt;br /&gt;      if (useSynch) {&lt;br /&gt;        return invokeWithSynch(method, args);&lt;br /&gt;      } else {&lt;br /&gt;        return method.invoke(map, args);&lt;br /&gt;      }&lt;br /&gt;    } catch (InvocationTargetException e) {&lt;br /&gt;      throw e.getTargetException();&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  private Object invokeWithSynch(Method method, Object[] args) throws Throwable {&lt;br /&gt;    synchronized (map) {&lt;br /&gt;      return method.invoke(map, args);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt; &lt;br /&gt;&lt;br /&gt;Then the proxy can be created like this:&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;Map map = new HashMap();&lt;br /&gt;Handler handler = new Handler(map);&lt;br /&gt;Map mapProxy = (Map)Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] { Map.class }, handler);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;With that my test methods can be reduced to:&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;private void testPut(params..., boolean useSynch) throws Exception {&lt;br /&gt;  do_set_up(params)&lt;br /&gt;  &lt;br /&gt;  handler.setUseSynch(useSynch);&lt;br /&gt;  mapProxy.put("k1", "v1");&lt;br /&gt;&lt;br /&gt;  assertions depends on useSynch&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Then I just need to call this method twice and accomplish the same thing.&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;testPut(params..., false);&lt;br /&gt;testPut(params..., true);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Pretty neat huh? Java proxy is nice tool to know.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-8473032169338311669?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/8473032169338311669/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=8473032169338311669' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/8473032169338311669'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/8473032169338311669'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2007/08/make-use-of-java-dynamic-proxy.html' title='Make use of Java dynamic proxy'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-7981133283076682449</id><published>2007-07-26T09:07:00.000-07:00</published><updated>2008-12-18T11:53:04.850-08:00</updated><title type='text'>Cluster a standalone Spring app to calculate Mandelbrot set</title><content type='html'>I've heard about Spring for awhile and have made little attempt to check out what it was. Most of the descriptions about Spring gave me the impressions that only big enterprise applications would use Spring. Man I was wrong :) As I dug down into &lt;a title="Pro Spring" href="http://www.amazon.com/Pro-Spring-Rob-Harrop/dp/1590594614/ref=pd_bbs_sr_1/105-2548607-1400420?ie=UTF8&amp;amp;s=books&amp;amp;qid=1185465692&amp;amp;sr=8-1"&gt;Pro Spring&lt;/a&gt; book and read the &lt;a title="Spring reference" href="http://www.springframework.org/docs/reference/index.html"&gt;Spring reference&lt;/a&gt;, I was able to convert one of my stand alone apps into Spring and reaped the benefit that Spring has to offer. Not only that, I was able to cluster my bean to share it among JVMs with little effort, thanks to &lt;a title="Terracotta" href="http://www.terracotta.org"&gt;Terracotta&lt;/a&gt;.&lt;br&gt;&lt;br&gt;My application is a simple demo of calculating the &lt;a title="Mandelbrot set" href="http://en.wikipedia.org/wiki/Mandelbrot_set"&gt;Mandelbrot set&lt;/a&gt;. It is multithreaded, using a queue task and workers. Here is a rough sketch (I'm UML illiterate):&lt;br /&gt;&lt;pre class="note-box"&gt;&lt;br /&gt;-------------------------------------            -----------------------------&lt;br /&gt; MandelbrotModel:                                  CalculateNode : Runnable&lt;br /&gt;   rawData: int[][]                                 model: MandelbrotModel&lt;br /&gt;   workLoad: BlockingQueue&lt;Segment&gt;&lt;br /&gt; ------------------------------------  1 -----&gt;* -----------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Each CaculateNode has a reference to the model, thus the workLoad queue. It will take a task (a Segment) out of workLoad, process it, and put back the findings in "rawData". Spring comes into the picture when it allows me to wire the dependency between nodes and model, like so:&lt;br /&gt;&lt;pre class="note-box"&gt;&lt;br /&gt;   &amp;lt;bean id="model" class="mandelbrot.MandelbrotModel" scope="singleton"&amp;gt;&lt;br&gt;        &amp;lt;property name="length"&amp;gt;&lt;br&gt;            &amp;lt;value&amp;gt;600&amp;lt;/value&amp;gt;&lt;br&gt;        &amp;lt;/property&amp;gt;    &lt;br&gt;        &amp;lt;property name="numNodes"&amp;gt;&lt;br&gt;            &amp;lt;value&amp;gt;2&amp;lt;/value&amp;gt;&lt;br&gt;        &amp;lt;/property&amp;gt;&lt;br&gt;        &amp;lt;property name="numTasks"&amp;gt;&lt;br&gt;            &amp;lt;value&amp;gt;20&amp;lt;/value&amp;gt;&lt;br&gt;        &amp;lt;/property&amp;gt;        &lt;br&gt;    &amp;lt;/bean&amp;gt;&lt;br&gt;&lt;br&gt;    &amp;lt;bean id="node" class="mandelbrot.CalculateNode" scope="singleton"&amp;gt;&lt;br&gt;        &amp;lt;property name="model" ref="model"&amp;gt;&lt;br&gt;        &amp;lt;/property&amp;gt;&lt;br&gt;    &amp;lt;/bean&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The above snippet describes 2 beans, a "model" and a "node". Properties are 1 on 1 matching of fields in your class. Say the field "private int length" in the model is set to 600 pixel, and so on. Spring "magic" is in the "node" bean, it says, point node's field of "model" to the model bean above, hence the dependency injection. &lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;public class CalculateNode implements Runnable {&lt;br /&gt;  private MandelbrotModel        model;&lt;br /&gt;&lt;br /&gt;  public void setModel(MandelbrotModel model) {&lt;br /&gt;    this.model = model;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Notice I have a setModel() method in the CalculateNode but I don't have to call it explicitly in my program. Spring will call it and set it for me. My node can do all it work, pretending that it has the model reference. The main method is really simple. &lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;  public static void main(String[] args) {&lt;br /&gt;    ApplicationContext ctx = new ClassPathXmlApplicationContext(&lt;br /&gt;        "mandelbrot.xml");&lt;br /&gt;    &lt;br /&gt;    CalculateNode node1 = (CalculateNode) ctx.getBean("node");&lt;br /&gt;    new Thread(node1).start();&lt;br /&gt;    &lt;br /&gt;    CalculateNode node2 = (CalculateNode) ctx.getBean("node");&lt;br /&gt;    new Thread(node2).start();&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I just need to query out 2 nodes and start them in 2 threads, no need to worry where is my model and how the nodes got a reference to it. &lt;br&gt;&lt;br&gt;But wait, there's more. Spring also offers to publish application events for you for cheap. Say now that I have data of the Mandelbrot, I can display it on a Swing application. I don't want to wait for all the nodes to finish calculation and display the graphic in the end. I want to paint it in real time, as soon as a node finishes with a segment, I'll paint it. To pull that off, I need to know when a segment is finished, the right source would be to ask the model. It knows that information when a node reports back with data. The "ghetto" way would be implement a listener list, traverse that list and and invoke callback function for each listener. This was the old implementation that I had. It does the job but it aint neat. In real world, you might see how this is not desirable sometimes because it couples your components. Spring allows you to solve this problem nicely: if you want to publish events, use the application context and call publishEvent() with your message! That's all you have to do. &lt;br&gt;&lt;br&gt;Let examine this function in the MandelbrotModel. It's invoked by a node to report result&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;  public synchronized void addSegmentData(int[][] data, Segment segment) {&lt;br /&gt;    for (int row = segment.getStart(); row &amp;lt; segment.getEnd(); row++) {&lt;br /&gt;      System.arraycopy(data[row - segment.getStart()], 0, rawData[row], 0,&lt;br /&gt;          length);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    ctx.publishEvent(new SegmentEvent(this, segment));&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;"ctx" is a reference to the context of the application, automatically given to anyone if you implement a Spring interface "ApplicationContextAware".&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;  @Override&lt;br /&gt;  public void setApplicationContext(ApplicationContext applicationcontext)&lt;br /&gt;      throws BeansException {&lt;br /&gt;    ctx = applicationcontext;&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As for someone who wants to listen to events, implements ApplicationListener interface and catch your events:&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;  @Override&lt;br /&gt;  public void onApplicationEvent(ApplicationEvent event) {&lt;br /&gt;    if (event instanceof SegmentEvent) {&lt;br /&gt;      final SegmentEvent segEvent = (SegmentEvent) event;&lt;br /&gt;      processSegment(segEvent.getSegment());&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I have my ViewerFrame (extends from JFrame) to act as listener, declared in my XML bean definition file and also has a reference to the model:&lt;br /&gt;&lt;pre class="note-box"&gt;&lt;br /&gt;      &amp;lt;bean id="frame" class="mandelbrot.ViewerFrame" scope="singleton"&amp;gt;&lt;br&gt;        &amp;lt;property name="model" ref="model"&amp;gt;&lt;br&gt;        &amp;lt;/property&amp;gt;&lt;br&gt;    &amp;lt;/bean&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;There is no change in the main method. As Spring initializes the "frame" bean, it will automatically show up. This is pretty easy because I made itself aware, knowing that it has been created by implementing InitialzingBean interface&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;  @Override&lt;br /&gt;  public void afterPropertiesSet() throws Exception {&lt;br /&gt;    int length = model.getLength();&lt;br /&gt;    this.image = new BufferedImage(length, length, BufferedImage.TYPE_INT_RGB);&lt;br /&gt;&lt;br /&gt;    this.setSize(length, length);&lt;br /&gt;    SwingUtilities.invokeLater(new Runnable() {&lt;br /&gt;      public void run() {&lt;br /&gt;        setVisible(true);&lt;br /&gt;      }&lt;br /&gt;    });&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The fun doesn't end here though. Now I want to have another JVM to join in the calculation, making it go faster (a little bit of drama here since the Mandelbrot set is pretty fast to calculate). The ideal scenario is the second JVM also has the same "model" in memory then its minions of "nodes" can just pound on the work. This second JVM can be on a totally separate machine even. How the heck anyone is gonna pull this off without changing the code, worrying about networking, sharing heap, etc...? This is where the power of Terracotta comes in. I just mark the "model" bean as shared and the SegmentEvent as distributed. That will enable any JVM in the cluster having reference to this model, and events published from model will be cluster-wise. &lt;br&gt;&lt;br&gt;Terracotta is fully Spring aware, and the snippet below is how I wired it to my Spring app.&lt;br /&gt;&lt;pre class="note-box"&gt;&lt;br /&gt;  &amp;lt;spring&amp;gt;&lt;br&gt;      &amp;lt;jee-application name="*"&amp;gt;&lt;br&gt;        &amp;lt;application-contexts&amp;gt;&lt;br&gt;          &amp;lt;application-context&amp;gt;&lt;br&gt;            &amp;lt;paths&amp;gt;&lt;br&gt;              &amp;lt;path&amp;gt;mandelbrot.xml&amp;lt;/path&amp;gt;&lt;br&gt;            &amp;lt;/paths&amp;gt;&lt;br&gt;            &amp;lt;beans&amp;gt;&lt;br&gt;              &amp;lt;bean name="model"&amp;gt;&lt;br&gt;              &amp;lt;/bean&amp;gt;&lt;br&gt;            &amp;lt;/beans&amp;gt;&lt;br&gt;            &amp;lt;distributed-events&amp;gt;&lt;br&gt;             &amp;lt;distributed-event&amp;gt;mandelbrot.SegmentEvent&amp;lt;/distributed-event&amp;gt;&lt;br&gt;            &amp;lt;/distributed-events&amp;gt;&lt;br&gt;          &amp;lt;/application-context&amp;gt;&lt;br&gt;        &amp;lt;/application-contexts&amp;gt;&lt;br&gt;      &amp;lt;/jee-application&amp;gt;&lt;br&gt;&amp;lt;/spring&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;When I use Terracotta eclipse plugin, after starting a Terracotta server (used to hold the shared objects), starting two instances of my applications that shared the "model" is as easy as hitting the Run button twice. Now I have 2 JVMs, each has 2 nodes working on the same model. Neato. As my bird would say "oh wow" when something excites him.&lt;br&gt;&lt;br&gt;So there you have it. I'm a Spring novice now but I'm sure there are many cools things from Spring waiting to be discovered. And with the added boost from &lt;a title="Terracotta" href="http://www.terracotta.org"&gt;Terracotta&lt;/a&gt;, the fun is multiplied.&lt;br&gt;&lt;br&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp1.blogger.com/_AUFkgevfdTc/RqjMExOQBxI/AAAAAAAABEs/nF745IrztkQ/s1600-h/mandelbrot.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp1.blogger.com/_AUFkgevfdTc/RqjMExOQBxI/AAAAAAAABEs/nF745IrztkQ/s320/mandelbrot.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5091543761046996754" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Here is the link to the &lt;a href="http://www.terracotta.org/confluence/download/attachments/22958/SpringMandelbrot.tar.gz"&gt;source&lt;/a&gt; code&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-7981133283076682449?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/7981133283076682449/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=7981133283076682449' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/7981133283076682449'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/7981133283076682449'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2007/07/ive-heard-about-spring-for-awhile-and.html' title='Cluster a standalone Spring app to calculate Mandelbrot set'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp1.blogger.com/_AUFkgevfdTc/RqjMExOQBxI/AAAAAAAABEs/nF745IrztkQ/s72-c/mandelbrot.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-857732482438088304</id><published>2007-06-27T09:52:00.000-07:00</published><updated>2007-06-27T10:26:47.695-07:00</updated><title type='text'>Alternative to properties file: YAML beans</title><content type='html'>Plain ol properties files are great for setting configurations for your apps. But if your configuration requires structured data and you prefer not use XML, there is a great alternative: YAML format and beans.&lt;br /&gt;&lt;br /&gt;Let assume we have a config as below. One client and multiple servers.&lt;br /&gt;&lt;br /&gt;&lt;pre class="note-box"&gt;&lt;br /&gt;client:&lt;br /&gt; log: /tmp/client/log&lt;br /&gt; debug: on&lt;br /&gt;&lt;br /&gt;servers:&lt;br /&gt; - host: zeus&lt;br /&gt;   port: 9510&lt;br /&gt;&lt;br /&gt; - host: apollo&lt;br /&gt;   port: 9511&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Yaml treats the whole config file as a hash map, keys are "client" and "servers". Subsequently, it treats "client" as a another hash map, and "servers" is an array of maps. Sounds complicated but it's straight forward once you're getting a hang of it.&lt;br /&gt;&lt;br /&gt;To parse this config, &lt;a href="http://jyaml.sourceforge.net/"&gt;JYaml&lt;/a&gt; makes it easier than fixing cereal:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;   Map config = (Map) YAML.load(new FileReader("config.yml"));&lt;br /&gt;&lt;br /&gt;   Map client = (Map) config.get("client");&lt;br /&gt;   System.out.println("Client: " + client);&lt;br /&gt;      &lt;br /&gt;   List servers = (ArrayList) config.get("servers");&lt;br /&gt;   System.out.println("Servers: " + servers);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And output is:&lt;br /&gt;&lt;pre class="note-box"&gt;&lt;br /&gt;Client: {debug=true, log=/tmp/client/log}&lt;br /&gt;Servers: [{host=zeus, port=9510}, {host=apollo, port=9511}]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You would access client's and server's properties like this:&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;  client.get("log");&lt;br /&gt;  Map firstServer = (Map)servers.get(0);&lt;br /&gt;  firstServer.get("host");&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;If you want the config being mapped to Java beans, JYaml can populate the beans for you. This is my preferred way since it's much nicer.&lt;br /&gt;&lt;br /&gt;The beans for your config will be as simple as:&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;/* Top level bean for the whole config */&lt;br /&gt;public class TcConfig {&lt;br /&gt;  private List servers;&lt;br /&gt;  private Client client;&lt;br /&gt;  &lt;br /&gt;  public List getServers() {&lt;br /&gt;    return servers;&lt;br /&gt;  }&lt;br /&gt;  public void setServers(List servers) {&lt;br /&gt;    this.servers = servers;&lt;br /&gt;  }&lt;br /&gt;  public Client getClient() {&lt;br /&gt;    return client;&lt;br /&gt;  }&lt;br /&gt;  public void setClient(Client client) {&lt;br /&gt;    this.client = client;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* Client */&lt;br /&gt;public class Client {&lt;br /&gt;  private String log;&lt;br /&gt;  private boolean debug;&lt;br /&gt;  &lt;br /&gt;  /* getters and setters */&lt;br /&gt;  &lt;br /&gt;  public String toString() {&lt;br /&gt;    return "log=" + log + ", debug=" + debug;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* Server */&lt;br /&gt;public class Server {&lt;br /&gt;  private String host;&lt;br /&gt;  private int port;&lt;br /&gt;  private String log;&lt;br /&gt;  &lt;br /&gt;  /* getters and setters */&lt;br /&gt;  &lt;br /&gt;  public String toString() {&lt;br /&gt;    return "host=" + host + ", port=" + port + ", log=" + log;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And to populate your beans, just 1 line of code:&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;  TcConfig config = (TcConfig)Yaml.loadType(new File("config.yml"), TcConfig.class);&lt;br /&gt;      &lt;br /&gt;  System.out.println("Servers : " + config.getServers());&lt;br /&gt;  System.out.println("Client : " + config.getClient());&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;XMLBeans can accomplish the same thing with other features like autogenerated Java beans, enforce schema, etc, but for simple things, I prefer Yaml.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-857732482438088304?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/857732482438088304/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=857732482438088304' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/857732482438088304'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/857732482438088304'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2007/06/alternative-to-properties-file-yaml.html' title='Alternative to properties file: YAML beans'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-5820777948344121340</id><published>2007-06-14T11:11:00.000-07:00</published><updated>2009-12-21T13:05:11.811-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='hibernate'/><category scheme='http://www.blogger.com/atom/ns#' term='ehcache'/><title type='text'>Share that POJO - Hibernate clustered and empowered</title><content type='html'>I haven't had much experience with OR mapping in Java apps so I wanted to study Hibernate tutorial and tried to cluster it at the same time. Starting with &lt;a href="http://www.terracotta.org/"&gt;Terracotta&lt;/a&gt; 2.4 (currently at stable0), Hibernate is supported.&lt;br /&gt;&lt;br /&gt;My first question to the engineer who worked on the feature is "What is being shared in Hibernate?" It turns out, the plain old Java objects (POJOs) that Hibernate constructs from the database are the ones that we're interested in sharing, in this case, across multiple JVMs. I told myself, that's pretty handy, so we don't have to hit the database again for the same information on another node when those objects have already been loaded and shared thanks to Terracotta DSO server. He told me that's not the only cool feature though. With those shared objects from Hibernate, one JVM can just reassociate them to its Hibernate session and start to access colletions that are mapped as one-to-many, many-to-many, that don't exist yet in memory due to lazy initalization. Wow. (Of course, that Hibernate session will have to hit the database for that new info you request. You don't always get free beer!)&lt;br /&gt;&lt;br /&gt;With that peaked interest, I worked on this cool and thorough &lt;a href="http://www.hibernate.org/hib_docs/v3/reference/en/html/tutorial.html"&gt;tutorial&lt;/a&gt; from Hibernate.org&lt;br /&gt;&lt;br /&gt;The schema for this tutorial as follow:&lt;br /&gt;&lt;pre class="note-box"&gt;&lt;br /&gt;  EVENTS           PERSON_EVENT       PERSON&lt;br /&gt;  ----------       -------------      ----------&lt;br /&gt;  *EVENT_ID        *EVENT_ID          *PERSON_ID&lt;br /&gt;   EVENT_DATE      *PERSON_ID          FIRSTNAME&lt;br /&gt;   TITLE                               LASTNAME&lt;br /&gt;  &lt;br /&gt;    &lt;br /&gt;  PERSON_EMAIL_ADDR&lt;br /&gt;  ------------------&lt;br /&gt;  *PERSON_ID&lt;br /&gt;  *EMAIL_ADDR&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;For each table, there is a Javabean to represents it. Each javabean instance will be mapped to a row in the database. Well, that's Hibernate in a nutshell for me (speaking with my own ignorance and limited experience)&lt;br /&gt;&lt;br /&gt;The xml mapping for Events table as follow:&lt;br /&gt;&lt;pre class="note-box"&gt;&lt;br /&gt;&amp;lt;hibernate-mapping&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;class name="events.Event" table="EVENTS"&amp;gt;&lt;br /&gt;  &amp;lt;id name="id" column="EVENT_ID"&amp;gt;&lt;br /&gt;      &amp;lt;generator class="native"/&amp;gt;&lt;br /&gt;  &amp;lt;/id&amp;gt;&lt;br /&gt;  &amp;lt;property name="date" type="timestamp" column="EVENT_DATE"/&amp;gt;&lt;br /&gt;  &amp;lt;property name="title"/&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;set name="participants" table="PERSON_EVENT" lazy="true" inverse="true" cascade="lock"&amp;gt;&lt;br /&gt;      &amp;lt;key column="EVENT_ID"/&amp;gt;&lt;br /&gt;      &amp;lt;many-to-many column="PERSON_ID" class="events.Person"/&amp;gt;&lt;br /&gt;  &amp;lt;/set&amp;gt;&lt;br /&gt;&amp;lt;/class&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/hibernate-mapping&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;It's a many-to-many relationship between Events and Person. Notice I set the lazy loading to true, it is needed to demonstrate the point I mentioned earlier.&lt;br /&gt;&lt;br /&gt;Here is Person's map file.&lt;br /&gt;&lt;br /&gt;&lt;pre class="note-box"&gt;&lt;br /&gt;&amp;lt;class name="events.Person" table="PERSON"&amp;gt;&lt;br /&gt;  &amp;lt;id name="id" column="PERSON_ID"&amp;gt;&lt;br /&gt;      &amp;lt;generator class="native"/&amp;gt;&lt;br /&gt;  &amp;lt;/id&amp;gt;&lt;br /&gt;  &amp;lt;property name="age"/&amp;gt;&lt;br /&gt;  &amp;lt;property name="firstname"/&amp;gt;&lt;br /&gt;  &amp;lt;property name="lastname"/&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;set name="events" table="PERSON_EVENT"&amp;gt;&lt;br /&gt;      &amp;lt;key column="PERSON_ID"/&amp;gt;&lt;br /&gt;      &amp;lt;many-to-many column="EVENT_ID" class="events.Event"/&amp;gt;&lt;br /&gt;  &amp;lt;/set&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;set name="emailAddresses" table="PERSON_EMAIL_ADDR"&amp;gt;&lt;br /&gt;    &amp;lt;key column="PERSON_ID"/&amp;gt;&lt;br /&gt;    &amp;lt;element type="string" column="EMAIL_ADDR"/&amp;gt;&lt;br /&gt;  &amp;lt;/set&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/class&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;For hibernate.cfg.xml file, I changed the config from the tutorial a little bit to use Derby database. It is run as server mode. Here is the change:&lt;br /&gt;&lt;br /&gt;&lt;pre class="note-box"&gt;&lt;br /&gt;&amp;lt;property name='connection.driver_class'&amp;gt;org.apache.derby.jdbc.ClientDriver&amp;lt;/property&amp;gt;&lt;br /&gt;&amp;lt;property name='connection.url'&amp;gt;jdbc:derby://localhost:1527/MyDbTest&amp;lt;/property&amp;gt;&lt;br /&gt;&amp;lt;property name='connection.username'&amp;gt;user1&amp;lt;/property&amp;gt;&lt;br /&gt;&amp;lt;property name='connection.password'&amp;gt;user1&amp;lt;/property&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Derby is shipped with jdk1.6.0_01, under java-home/db/lib. To start it,&lt;br /&gt;&lt;br /&gt;&lt;pre class="node-box"&gt;&lt;br /&gt;% java -jar derbyrun.jar server start&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Then I created "MyDbTest" database by running this once:&lt;br /&gt;&lt;pre class="node-box"&gt;&lt;br /&gt;% java -jar derbyrun.jar dblook -d "jdbc:derby://localhost:1527/MyDbTest;create=true"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now on to the EventManager.java of the tutorial. I didn't follow it fully. I created 2 events, and 3 persons. First event is "Engineer meeting", second one is a "Document Meeting" contain these participants&lt;br /&gt;&lt;br /&gt;// engMeeting = {steve, orion, tim}&lt;br /&gt;// docMeeting = {steve, orion}&lt;br /&gt;&lt;br /&gt;Here is a snippet of EventManager.java.&lt;br /&gt;&lt;br /&gt;&lt;pre class="note-box"&gt;&lt;br /&gt;public class EventManager {&lt;br /&gt;&lt;br /&gt;// shared object - declared as a root in tc-config.xml&lt;br /&gt;private static final List events = new ArrayList();&lt;br /&gt;&lt;br /&gt;public static void main(String[] args) {&lt;br /&gt;  EventManager mgr = new EventManager();&lt;br /&gt;&lt;br /&gt;  // create 3 persons Steve, Orion, Tim&lt;br /&gt;  Long steveId = mgr.createAndStorePerson("Steve", "Harris");&lt;br /&gt;  mgr.addEmailToPerson(steveId, "steve@terracottatech.com");&lt;br /&gt;&lt;br /&gt;  ...&lt;br /&gt;&lt;br /&gt;  // create 2 events&lt;br /&gt;  Long engMeetingId = mgr.createAndStoreEvent("Eng Meeting", new Date());&lt;br /&gt;  mgr.addPersonToEvent(steveId, engMeetingId);&lt;br /&gt;  mgr.addPersonToEvent(orionId, engMeetingId);&lt;br /&gt;  mgr.addPersonToEvent(timId, engMeetingId);&lt;br /&gt;&lt;br /&gt;  Long docMeetingId = mgr.createAndStoreEvent("Doc Meeting", new Date());&lt;br /&gt;&lt;br /&gt;  ....&lt;br /&gt;&lt;br /&gt;  // store our events into a shared object&lt;br /&gt;  synchronized (events) {&lt;br /&gt;    events.addAll(mgr.listEvents());&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  HibernateUtil.getSessionFactory().close();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;private List listEvents() {&lt;br /&gt;&lt;br /&gt;  Session session = HibernateUtil.getSessionFactory().getCurrentSession();&lt;br /&gt;  session.beginTransaction();&lt;br /&gt;&lt;br /&gt;  List result = session.createQuery("from Event").list();&lt;br /&gt;&lt;br /&gt;  session.getTransaction().commit();&lt;br /&gt;  return result;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Most of it is straight forward and copied from the Hibernate tutorial. Terracotta comes into the picture with the presence of the shared-object "events". It is declared as a root in Terracotta config and it will hold the list of events that Hibernate loaded from database. Here is how declare it in tc-config.xml:&lt;br /&gt;&lt;pre class="note-box"&gt;&lt;br /&gt;&amp;lt;root&amp;gt;&lt;br /&gt;&amp;lt;field-name&amp;gt;events.EventManager.events&amp;lt;/field-name&amp;gt;&lt;br /&gt;&amp;lt;root-name&amp;gt;tcEvents&amp;lt;/root-name&amp;gt;&lt;br /&gt;&amp;lt;/root&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The synchronized block of shared object "events" is necessary. It's in a context of multithreads and multi-JVM.&lt;br /&gt;&lt;br /&gt;So that's enough for us to start the first JVM. I turned on SQL log from log4j so we can see the SQL being generated by Hibernate. There are a lot of sql statements printed out, both "insert" and "select" as you would expect Hibernate to&lt;br /&gt;generate. After it's finished, our "events" list will contain events object, and since it's set with "lazy=true", their participant set will be empty. We can confirm this by looking at Terracotta Admin console:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp0.blogger.com/_AUFkgevfdTc/RnGMn-tWU0I/AAAAAAAABA4/Jmum9Is2I9Q/s1600-h/pic1.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp0.blogger.com/_AUFkgevfdTc/RnGMn-tWU0I/AAAAAAAABA4/Jmum9Is2I9Q/s400/pic1.png" alt="" id="BLOGGER_PHOTO_ID_5075992873499251522" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Under root, we have 1 root, namely "tcEvents" that has 2 objects. Expanding them, and we'll see the participant set is empty. Hibernate internal set is null.&lt;br /&gt;&lt;br /&gt;org.hibernate.collection.PersistentSet.set=null&lt;br /&gt;&lt;br /&gt;So that is my first app. Here comes my second app that run in a totally seperate VM. I caled it EventChecker and it will list emails of people in the first meeting.&lt;br /&gt;&lt;br /&gt;First, it has to know about the same shared object "tcEvents" that we put onto Terracotta DSO Server. We can do this easily by specify it in tc-config.xml:&lt;br /&gt;&lt;br /&gt;&lt;pre class="note-box"&gt;&lt;br /&gt;&amp;lt;roots&amp;gt;&lt;br /&gt;  &amp;lt;root&amp;gt;&lt;br /&gt;    &amp;lt;field-name&amp;gt;events.EventManager.events&amp;lt;/field-name&amp;gt;&lt;br /&gt;    &amp;lt;root-name&amp;gt;tcEvents&amp;lt;/root-name&amp;gt;&lt;br /&gt;  &amp;lt;/root&amp;gt;&lt;br /&gt;  &amp;lt;root&amp;gt;&lt;br /&gt;    &amp;lt;field-name&amp;gt;events.EventChecker.events&amp;lt;/field-name&amp;gt;&lt;br /&gt;    &amp;lt;root-name&amp;gt;tcEvents&amp;lt;/root-name&amp;gt;&lt;br /&gt;  &amp;lt;/root&amp;gt;&lt;br /&gt;&amp;lt;/roots&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;By using the same root-name, it's mapped to the same list of EventManger. Its code is pretty short so I list all of it here:&lt;br /&gt;&lt;br /&gt;&lt;pre class="note-box"&gt;&lt;br /&gt;public class EventChecker {&lt;br /&gt;  // shared object&lt;br /&gt;  private static final List events = new ArrayList();&lt;br /&gt;&lt;br /&gt;  public static void main(String[] args) {&lt;br /&gt;&lt;br /&gt;  synchronized (events) {&lt;br /&gt;    // list events before even opening a Hibernate session&lt;br /&gt;    System.out.println("** events: " + events);&lt;br /&gt;&lt;br /&gt;    Session session = HibernateUtil.getSessionFactory().getCurrentSession();&lt;br /&gt;    session.beginTransaction();&lt;br /&gt;&lt;br /&gt;    // reassociate transient pojos to this session&lt;br /&gt;    for (Iterator it = events.iterator(); it.hasNext(); ) {&lt;br /&gt;      session.lock((Event)it.next(), LockMode.NONE);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // list people in first event&lt;br /&gt;    Event event = (Event)events.get(0);&lt;br /&gt;    Set people = event.getParticipants();&lt;br /&gt;    System.out.println("** people: " + people);&lt;br /&gt;&lt;br /&gt;    // list emails of people from first event&lt;br /&gt;    Set emails = new HashSet();&lt;br /&gt;    for (Iterator it = people.iterator(); it.hasNext(); ) {&lt;br /&gt;      Person person = (Person)it.next();          &lt;br /&gt;      emails.addAll(person.getEmailAddresses());&lt;br /&gt;    }&lt;br /&gt;    System.out.println("** emails: " + emails);&lt;br /&gt;&lt;br /&gt;    session.getTransaction().commit();&lt;br /&gt;    HibernateUtil.getSessionFactory().close();&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Here it is you see the usage of synchorized block on the shared object. We're accessing it in brand new JVM. Without Terracotta, the events list will be empty. With Terracotta, the events list contains events from EventManager, faulting by DSO server transparently.&lt;br /&gt;I then reassociated the pojos to my newly created Hibernate session. This is always needed when you work with Hibernate. I didn't know about this until I got an exception saying I don't have a session or session might have been closed by Hibernate.&lt;br /&gt;&lt;br /&gt;After that, I can list the pariticipants and their emails using Hibernate. This is all Hibernate doing. I love it :)&lt;br /&gt;&lt;br /&gt;Here is the output, it will show that Hibernate didn't generate SQL to load in the events since it's already loaded and stored by Terracotta. It only loaded from Person and Person_Email_Addr:&lt;br /&gt;&lt;pre class="note-box"&gt;&lt;br /&gt;** events: [Eng Meeting: 2007-06-14 11:06:12.0, Doc Meeting: 2007-06-14 11:06:12.0]&lt;br /&gt;&lt;br /&gt;Hibernate: select participan0_.EVENT_ID as EVENT1_1_, participan0_.PERSON_ID as PERSON2_1_, person1_.PERSON_ID as PERSON1_2_0_, person1_.age as age2_0_, person1_.firstname as firstname2_0_, person1_.lastname as lastname2_0_ from PERSON_EVENT participan0_ left outer join PERSON person1_ on participan0_.PERSON_ID=person1_.PERSON_ID where participan0_.EVENT_ID=?&lt;br /&gt;&lt;br /&gt;** people: [Orion Letizi, Steve Harris, Tim Eck]&lt;br /&gt;&lt;br /&gt;Hibernate: select emailaddre0_.PERSON_ID as PERSON1_0_, emailaddre0_.EMAIL_ADDR as EMAIL2_0_ from PERSON_EMAIL_ADDR emailaddre0_ where emailaddre0_.PERSON_ID=?&lt;br /&gt;Hibernate: select emailaddre0_.PERSON_ID as PERSON1_0_, emailaddre0_.EMAIL_ADDR as EMAIL2_0_ from PERSON_EMAIL_ADDR emailaddre0_ where emailaddre0_.PERSON_ID=?&lt;br /&gt;Hibernate: select emailaddre0_.PERSON_ID as PERSON1_0_, emailaddre0_.EMAIL_ADDR as EMAIL2_0_ from PERSON_EMAIL_ADDR emailaddre0_ where emailaddre0_.PERSON_ID=?&lt;br /&gt;&lt;br /&gt;** emails: [teck@terracottatech.com, steve@terracottatech.com, orion@terracottatech.com]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Looking at the Admin console again, we see that the participant set is now loaded in by Hibernate and shared by Terracotta.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_AUFkgevfdTc/RnGMsutWU1I/AAAAAAAABBA/pRwqKH-3rZw/s1600-h/pic2.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp3.blogger.com/_AUFkgevfdTc/RnGMsutWU1I/AAAAAAAABBA/pRwqKH-3rZw/s400/pic2.png" alt="" id="BLOGGER_PHOTO_ID_5075992955103630162" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;There is one gotcha that I ran into that I thought I should mention. In hibernate.cfg.xml, there is an option to clean out and create the schema every time Hibernate start.&lt;br /&gt;&lt;pre class="note-box"&gt;&lt;br /&gt;&amp;lt;!-- Drop and re-create the database schema on startup --&amp;gt;&lt;br /&gt;&amp;lt;property name=\"hbm2ddl.auto\"&amp;gt;create&amp;lt;/property&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;When you run EventManger, you need it to create the schema for you. But when you run EventChecker, you don't want Hibernate to wipe out your database. You should comment out that line before running it.&lt;br /&gt;&lt;br /&gt;I had fun working on Hibernate tutorial and playing it Terracotta. I don't have much experience with J2EE and Hibernate so I can't really comment on a practical use case. Maybe you will have better idea how to use it :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-5820777948344121340?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/5820777948344121340/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=5820777948344121340' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/5820777948344121340'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/5820777948344121340'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2007/06/share-that-pojo-hibernate-cluserted-and.html' title='Share that POJO - Hibernate clustered and empowered'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp0.blogger.com/_AUFkgevfdTc/RnGMn-tWU0I/AAAAAAAABA4/Jmum9Is2I9Q/s72-c/pic1.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-1139730711725847949</id><published>2007-04-13T11:22:00.000-07:00</published><updated>2007-06-16T09:01:22.733-07:00</updated><title type='text'>Performance comparision between ConcurrentHashMap and synchronized HashMap in Terracotta</title><content type='html'>Since the introduction of ConcurrentHashMap in Java 5, it has been the better choice over HashMap in highly threaded applications. To see how much better, Brian Goetz from his book "Java Concurrency in Practice" wrote a test to compare performance between the two maps. The test sceanario is this:&lt;br /&gt;&lt;br /&gt;For N threads concurrently execute a loop that chooses a random key and looks up value corresponding to that key. If the value is found, it is removed with a probability of 0.02. If not, it is added to the map with a probability of 0.6.&lt;br /&gt;&lt;br /&gt;The result is ConcurrentHashMap performs much better when number of threads increases. Below is the graph taken from the book with the author's permission.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp0.blogger.com/_AUFkgevfdTc/Rh_NyWKOUhI/AAAAAAAAA7U/uj658EH2REY/s1600-h/figure11.3.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp0.blogger.com/_AUFkgevfdTc/Rh_NyWKOUhI/AAAAAAAAA7U/uj658EH2REY/s400/figure11.3.png" alt="" id="BLOGGER_PHOTO_ID_5052983571758600722" border="0" /&gt;&lt;/a&gt;Since &lt;a href="http://www.terracotta.org/"&gt;Open Terracotta&lt;/a&gt; supports ConcurrentHashMap, I wanted so see if the performance advantage is still there (and how much) in distributed environment. My test scenario is modeled after Brian's. However, the number of threads are spread over 4 nodes (Linux RH4). Here is my test code:&lt;br /&gt;&lt;br /&gt;&lt;pre class="note-box" style="margin: 0px; font-family: Courier New,Courier; font-size: 10pt;"&gt;&lt;br /&gt;package tc.qa;&lt;br /&gt;&lt;br /&gt;import java.util.Map;&lt;br /&gt;import java.util.Random;&lt;br /&gt;import java.util.concurrent.ConcurrentHashMap;&lt;br /&gt;import java.util.concurrent.CyclicBarrier;&lt;br /&gt;import java.util.concurrent.atomic.AtomicInteger;&lt;br /&gt;&lt;br /&gt;public class ConcurrentHashMapLoadTest extends Thread {&lt;br /&gt;  private static double WRITE_PROBABILITY  = 0.6;&lt;br /&gt;  private static double REMOVE_PROBABILITY = 0.02;&lt;br /&gt;  private static int    THREAD_COUNT       = 4;&lt;br /&gt;  private static int    VM_COUNT           = 4;&lt;br /&gt;  private static int    RUNTIME            = 5 * 60 * 1000;&lt;br /&gt;  private static int    KEY_RANGE          = 100000;&lt;br /&gt;&lt;br /&gt;  // roots - in TC world, these are static finals and accessible across JVMs&lt;br /&gt;  private Map           map                = new ConcurrentHashMap();&lt;br /&gt;  private AtomicInteger throughput         = new AtomicInteger(0);&lt;br /&gt;  private CyclicBarrier barrier;&lt;br /&gt;&lt;br /&gt;  private Random        random;&lt;br /&gt;  private int           op;&lt;br /&gt;&lt;br /&gt;  public ConcurrentHashMapLoadTest() {&lt;br /&gt;    random = new Random();&lt;br /&gt;    barrier = new CyclicBarrier(THREAD_COUNT);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public void run() {&lt;br /&gt;&lt;br /&gt;    // ready&lt;br /&gt;    if (barrier() == 0) {&lt;br /&gt;      System.out.println("Started...");&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // go&lt;br /&gt;    long start = System.currentTimeMillis();&lt;br /&gt;    while (System.currentTimeMillis() - start &lt; RUNTIME) {&lt;br /&gt;      Integer key = new Integer(random.nextInt(KEY_RANGE));&lt;br /&gt;      if (get(map, key) != null) {&lt;br /&gt;        if (random.nextDouble() &lt; REMOVE_PROBABILITY) {&lt;br /&gt;          remove(map, key);&lt;br /&gt;          op++;&lt;br /&gt;        }&lt;br /&gt;      } else {&lt;br /&gt;        if (random.nextDouble() &lt; WRITE_PROBABILITY) {&lt;br /&gt;          put(map, key, key);&lt;br /&gt;          op++;&lt;br /&gt;        }&lt;br /&gt;      }&lt;br /&gt;      op++;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    throughput.addAndGet(op);&lt;br /&gt;&lt;br /&gt;    if (barrier() == 0) {&lt;br /&gt;      System.out.println("Map type:           "&lt;br /&gt;          + map.getClass().getSimpleName());&lt;br /&gt;      System.out.println("Runtime:            " + RUNTIME);&lt;br /&gt;      System.out.println("Number of threads:  " + THREAD_COUNT);&lt;br /&gt;      System.out.println("Write probability:  " + WRITE_PROBABILITY);&lt;br /&gt;      System.out.println("Remove probability: " + REMOVE_PROBABILITY);&lt;br /&gt;      System.out.println("Ops per second:     "&lt;br /&gt;          + (throughput.intValue() * 1000.0 / RUNTIME));&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  private int barrier() {&lt;br /&gt;    try {&lt;br /&gt;      return barrier.await();&lt;br /&gt;    } catch (Exception e) {&lt;br /&gt;      e.printStackTrace();&lt;br /&gt;    }&lt;br /&gt;    return -1;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  private Object get(Map map, Object key) {&lt;br /&gt;    if (map instanceof ConcurrentHashMap) {&lt;br /&gt;      return map.get(key);&lt;br /&gt;    } else {&lt;br /&gt;      synchronized (map) {&lt;br /&gt;        return map.get(key);&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  private void put(Map map, Object key, Object value) {&lt;br /&gt;    if (map instanceof ConcurrentHashMap) {&lt;br /&gt;      map.put(key, value);&lt;br /&gt;    } else {&lt;br /&gt;      synchronized (map) {&lt;br /&gt;        map.put(key, value);&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  private void remove(Map map, Object key) {&lt;br /&gt;    if (map instanceof ConcurrentHashMap) {&lt;br /&gt;      map.remove(key);&lt;br /&gt;    } else {&lt;br /&gt;      synchronized (map) {&lt;br /&gt;        map.remove(key);&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public static void getParams() {&lt;br /&gt;    WRITE_PROBABILITY = Double.parseDouble(System.getProperty("wp", "0.6"));&lt;br /&gt;    REMOVE_PROBABILITY = Double.parseDouble(System.getProperty("rmp", "0.02"));&lt;br /&gt;    THREAD_COUNT = Integer.parseInt(System.getProperty("thread", "4"));&lt;br /&gt;    VM_COUNT = Integer.parseInt(System.getProperty("vm", "4"));&lt;br /&gt;    RUNTIME = Integer.parseInt(System.getProperty("runtime", "300000"));&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public static void main(String[] args) {&lt;br /&gt;    getParams();&lt;br /&gt;    int threads_per_vm = THREAD_COUNT / VM_COUNT;&lt;br /&gt;    for (int t = 0; t &lt; threads_per_vm; t++) {&lt;br /&gt;      ConcurrentHashMapLoadTest thread = new ConcurrentHashMapLoadTest();&lt;br /&gt;      thread.start();&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I wrapped the put(), get() methods to easily switch the map types. After running this test on 4 nodes with jdk1.6.0_01, I got result as below:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp0.blogger.com/_AUFkgevfdTc/RiLTGmKOUjI/AAAAAAAAA7k/exTHrx0AyPY/s1600-h/tcload.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp0.blogger.com/_AUFkgevfdTc/RiLTGmKOUjI/AAAAAAAAA7k/exTHrx0AyPY/s400/tcload.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5053833842139222578" /&gt;&lt;/a&gt;&lt;br /&gt;The Y axis is throughput, normalized to throughput of 4 threads in ConcurrentHashMap.&lt;br /&gt;With network overhead and locks contention in Terracotta, ConcurrentHashMap still far outweights HashMap. Notice how performance of HashMap doesn't degrade much when number of threads increases. This all thanks to the way Terracotta replicates only the delta changes in the map to other nodes. No serialization involved.&lt;br /&gt;&lt;br /&gt;The gap between ConcurrentHashMap and HashMap in Terracotta isn't big as it was in the case of 1 JVM like Brian's test. I'm not sure I'm comparing oranges with apples here because obviously it's not the same test, just the same idea. We're always striving for better performance overall and each release has proven that.&lt;br /&gt;&lt;br /&gt;Check us out and let us know what you think. &lt;a href="http://www.terracotta.org/"&gt;http://www.terracotta.org&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Hung-&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-1139730711725847949?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/1139730711725847949/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=1139730711725847949' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/1139730711725847949'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/1139730711725847949'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2007/04/performance-comparision-between.html' title='Performance comparision between ConcurrentHashMap and synchronized HashMap in Terracotta'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp0.blogger.com/_AUFkgevfdTc/Rh_NyWKOUhI/AAAAAAAAA7U/uj658EH2REY/s72-c/figure11.3.png' height='72' width='72'/><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-8200201815732994222</id><published>2007-02-11T19:05:00.000-08:00</published><updated>2006-12-14T02:01:36.127-08:00</updated><title type='text'>Using POSTGIS to find points of interest within a radius</title><content type='html'>I've recently looked into using &lt;a href="http://postgis.refractions.net"&gt;PostGIS&lt;/a&gt;, an extension that enables support for spatial objects in PostgresSQL database.&lt;br /&gt;&lt;br /&gt;The problem I was trying to solve is, given a point with (longitude, latitude), how can I find all other points within a certain radius. I've solved it before using &lt;a href="http://www.meridianworlddata.com/Distance-Calculation.asp"&gt;formula&lt;/a&gt; to calculate distances between 2 points but it's pretty slow if your table has a lot of records. PostGIS can help solve this problem faster and more elegant. &lt;br /&gt;&lt;br /&gt;First thing first, let's get us some data to play around with. I found this excellent &lt;a href="http://www.bostongis.com/?content_name=postgis_tut03"&gt;tutorial&lt;/a&gt; that teaches you how to get zipcode data from the US Census. I'll skim through it here, but you should definitely check out that article.&lt;br /&gt;&lt;br /&gt;Assume you have a zipcode table with these columns:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;state    zip     latitude        longitude&lt;br /&gt;-------------------------------------------&lt;br /&gt;CA 92230 33.911404 -116.768347&lt;br /&gt;CA 92234 33.807761 -116.464731&lt;br /&gt;CA 92236 33.679872 -116.176562&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now you need to add a column to this table to hold the geometric data represents by [longitude, latitude] pair. &lt;br /&gt;&lt;code&gt;&lt;br /&gt;SELECT AddGeometryColumn( 'public', 'zipcode', 'geom', 32661, 'POINT', 2 );&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;* add column 'geom' into table 'zipcode' under schema 'public'&lt;br /&gt;* the column holds a point with 2 dimensions and has spherical reference id (srid) 32661&lt;br /&gt;&lt;br /&gt;* now we populate the table with data converted from srid 4269 (longitude/latitude measurement) to srid 32661 (WGS 84 system)&lt;br /&gt;&lt;code&gt;&lt;br /&gt;UPDATE zipcode&lt;br /&gt;SET geom = transform(PointFromText('POINT(' || longitude || ' ' || latitude || ')',4269),32661) ;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;The transformation from one spherical reference to another is needed for calculating the surface distance. &lt;a href="http://www.ngs.noaa.gov/faq.shtml#WGS84"&gt;WGS84&lt;/a&gt; is measured in meters by the way.&lt;br /&gt;&lt;br /&gt;Now your table will look like this&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;state zip  latitude   longitude    geom&lt;br /&gt;-----------------------------------------------&lt;br /&gt;CA  92230  33.911404  -116.768347  0101000020957F0000B7FB4ED5F2C44EC166AFB20D1A3D5341&lt;br /&gt;CA  92234  33.807761  -116.464731  0101000020957F0000AA8102ECECFD4EC18284302439245341&lt;br /&gt;CA  92236  33.679872  -116.176562  0101000020957F000067A4B44D2D3B4FC1596E974B370E5341&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now we only need to write a query, say, find me all the zipcode within 10 mile radius of "92230". (or 16093 meters)&lt;br /&gt;&lt;br /&gt;&lt;div style="font-size: 10pt"&gt;&lt;br /&gt;&lt;span style="color:blue"&gt;SELECT&lt;/span&gt; state, zip&lt;br /&gt;&lt;span style="color:blue"&gt;FROM&lt;/span&gt; zipcode&lt;br /&gt;&lt;span style="color:blue"&gt;WHERE&lt;/span&gt;&lt;br /&gt;&lt;b&gt;distance&lt;/b&gt;(&lt;br /&gt;&amp;nbsp;&amp;nbsp;   &lt;b&gt;transform&lt;/b&gt;(&lt;b&gt;PointFromText&lt;/b&gt;('POINT(-116.768347 33.911404)', 4269),32661),&lt;br /&gt;&amp;nbsp;&amp;nbsp;   geom) &lt; 16093&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Here function &lt;b&gt;distance(here_geom, there_geom)&lt;/b&gt; returns meters. Notice how you would read in a point(long/lat) from string using PointFromText() then do the transform.&lt;br /&gt;&lt;br /&gt;However, this query isn't optimal. It calculates distances from zip 92230 to all other zipcodes. We would rather have a bounding box of 10 miles around our interested point, filtering out the points that interact with this box then calculate the exact distances to the center. Sounds like a lot of work but in reality, it can be achieve by adding another condition:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-size: 10pt"&gt;&lt;br /&gt;&lt;span style="color:blue"&gt;SELECT&lt;/span&gt; state, zip&lt;br /&gt;&lt;span style="color:blue"&gt;FROM&lt;/span&gt; zipcode&lt;br /&gt;&lt;span style="color:blue"&gt;WHERE&lt;/span&gt;&lt;br /&gt;geom &amp;&amp; expand(&lt;b&gt;transform&lt;/b&gt;(&lt;b&gt;PointFromText&lt;/b&gt;('POINT(-116.768347 33.911404)', 4269),32661), 16093)&lt;br /&gt;&lt;span style="color:blue"&gt;AND&lt;/span&gt;&lt;br /&gt;&lt;b&gt;distance&lt;/b&gt;(&lt;br /&gt;&amp;nbsp;&amp;nbsp;   &lt;b&gt;transform&lt;/b&gt;(&lt;b&gt;PointFromText&lt;/b&gt;('POINT(-116.768347 33.911404)', 4269),32661),&lt;br /&gt;&amp;nbsp;&amp;nbsp;   geom) &lt; 16093&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;The new query is much much faster. My benchmark shows 200% in speed compared to the first query. Pretty sweet!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-8200201815732994222?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/8200201815732994222/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=8200201815732994222' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/8200201815732994222'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/8200201815732994222'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2007/02/using-postgis-to-find-points-of.html' title='Using POSTGIS to find points of interest within a radius'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-4512485722144263937</id><published>2006-12-12T14:33:00.000-08:00</published><updated>2006-12-12T14:57:41.368-08:00</updated><title type='text'>Using Java bytecode in clustering technique</title><content type='html'>When I first heard of clustering Java objects, I immediately thought about object serialization. As someone just got out of school, it's an educated deduction :) I soon learned it's not always the case when speed and scalability are taken into account. Serialization of a plain ol' java object is rather slow when you have lots of transactions, and lots of objects. So how else then you would maintain the object integrity across cluster?&lt;br /&gt;&lt;br /&gt;For &lt;a href="http://www.terracotta.org" onClick="javascript:urchinTracker('index');"&gt;Terracotta DSO&lt;/a&gt;, we choose to go one level down, the JVM bytecode (as opposed to the application level) There's like a whole new world of Java when you decide to look into it. As it turns out, every time you set or get values to variables, the opcodes for them are "getfield" and "putfield". DSO looks for these opcodes and record the mutation of an object in one JVM, then replay that "tape" in another JVM on the same clustered object. The final effect is that we can replicate object data in multiple JVMs on the field level.&lt;br /&gt;&lt;br /&gt;Let take a look at a simple class: Person.java&lt;br /&gt;To create the byte code, run these commands:&lt;br /&gt;&lt;br /&gt;$&gt; javac Person.java&lt;br /&gt;$&gt; javap -c Person &gt; Person.bc&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Person.java&lt;/h4&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class Person {&lt;br /&gt;   private String name;&lt;br /&gt;  &lt;br /&gt;   public Person(String aName) {&lt;br /&gt;       name = aName;&lt;br /&gt;   }&lt;br /&gt;  &lt;br /&gt;   public String getName() {&lt;br /&gt;       return name;&lt;br /&gt;   }&lt;br /&gt;  &lt;br /&gt;   public void setName(String aName) {&lt;br /&gt;       name = aName;&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Person.bc&lt;/h4&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Compiled from "Person.java"&lt;br /&gt;public class Person extends java.lang.Object{&lt;br /&gt;public Person(java.lang.String);&lt;br /&gt; Code:&lt;br /&gt;  0:    aload_0&lt;br /&gt;  1:    invokespecial    #1; //Method java/lang/Object."&lt;init&gt;":()V&lt;br /&gt;  4:    aload_0&lt;br /&gt;  5:    aload_1&lt;br /&gt;  6:    putfield    #2; //Field name:Ljava/lang/String;&lt;br /&gt;  9:    return&lt;br /&gt;&lt;br /&gt;public java.lang.String getName();&lt;br /&gt; Code:&lt;br /&gt;  0:    aload_0&lt;br /&gt;  1:    getfield    #2; //Field name:Ljava/lang/String;&lt;br /&gt;  4:    areturn&lt;br /&gt;&lt;br /&gt;public void setName(java.lang.String);&lt;br /&gt; Code:&lt;br /&gt;  0:    aload_0&lt;br /&gt;  1:    aload_1&lt;br /&gt;  2:    putfield    #2; //Field name:Ljava/lang/String;&lt;br /&gt;  5:    return&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Voila, the gut of our class is exposed. For DSO, this is what we work on. So as you can see, the DSO claim about only sending the deltas (the changes) of an object across network, makes sense. The magic is in the bytecode instrumentation, using ASM framework.&lt;br /&gt;&lt;br /&gt;Similarly, locking by using "synchronized" blocks can be observed by the tell-tales "monitorenter", "monitorexit" bytecodes. Don't take my words for it, try it out yourself. I've learned a great deal since I start studying Java bytecode.&lt;br /&gt;&lt;br /&gt;One good source I found is: &lt;a href="http://www-128.ibm.com/developerworks/ibm/library/it-haggar_bytecode/"&gt;Java bytecode&lt;/a&gt;:&lt;br /&gt;Understanding bytecode makes you a better programmer &lt;br /&gt;&lt;br /&gt;Ari Zilka &lt;a href="http://video.google.com/videoplay?docid=7660457673499305140&amp;amp;q=Google+engEDU" onClick="javascript:urchinTracker('index');"&gt;talk at Google&lt;/a&gt; is another great source.&lt;br /&gt;&lt;br /&gt;Have fun-&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-4512485722144263937?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/4512485722144263937/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=4512485722144263937' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/4512485722144263937'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/4512485722144263937'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2006/12/using-java-bytecode-in-clustering.html' title='Using Java bytecode in clustering technique'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6719192018427942027.post-376849647101601563</id><published>2006-10-08T23:56:00.000-07:00</published><updated>2008-05-10T10:39:55.005-07:00</updated><title type='text'>Share precious heap memory across multiple JVM's</title><content type='html'>I've written some puzzle solvers for fun during college. Among them was for &lt;a href="http://en.wikipedia.org/wiki/Boggle"&gt;Boggle&lt;/a&gt; board. One annoying thing about it is the startup time. It took a while to load up a 4 MB English dictionary into memory. Some of my other puzzle solvers also use the same dictionary. Wouldn't it be cool if you only need to load this dictionary once and all of the apps could use it? It would stay resident in the heap and ready to be used in different applications and between runs.&lt;br /&gt;&lt;br /&gt;One other solution is to use a database but this is not always preferable, mostly due to speed, and of course it is not as elegant. I wanted to use the dictionary object as a native Java object. The idea was shelved for awhile until I came to work with &lt;a href="http://www.terracotta.org/"&gt;Terracotta&lt;/a&gt;, whose distributed shared objects (&lt;a href="http://www.terracotta.org/confluence/display/explore/POJO+Clustering"&gt;DSO&lt;/a&gt;) might just pull off the resident-heap-memory idea nicely.&lt;br /&gt;&lt;br /&gt;First, let's take look at the puzzle solver. Given a 4 by 4 board, the main objective is to find as many words as possible that can be constructed from the letters of sequentially adjacent cubes. The board I’m solving in the game has the content as follows:&lt;br /&gt;&lt;pre&gt;c  a  t  d&lt;br /&gt;l  i  n  e&lt;br /&gt;m  a  r  o&lt;br /&gt;p  e  t  s&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Some obvious words are: calm, line, mine, and dent.&lt;br /&gt;&lt;br /&gt;The program will use the dictionary to scan for all possibilities. A recursive algorithm works great in solving this puzzle. For every possible path, it will look up the current word in dictionary. So let's see how we get the dictionary loaded into memory.&lt;br /&gt;&lt;br /&gt;In the Terracotta world, that would mean loading the data into a root (shared object). That shared object will be transparently clustered and made available to any other JVM in the cluster.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger2/5259/820213869437004/1600/figure1.0.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px;" src="http://photos1.blogger.com/blogger2/5259/820213869437004/1600/figure1.0.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;The dictionary is implemented as a &lt;a href="http://en.wikipedia.org/wiki/Trie"&gt;Trie&lt;/a&gt; for fast retrieving. The dictionary loader will read words from a file, one word per line and insert into the trie’s structure. For efficiency, we'll batch the inserts. Here I choose the batch size to be 50. Adjusting this number will affect the load time.&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;package dictionary;&lt;br /&gt;&lt;br /&gt;import java.io.BufferedReader;&lt;br /&gt;import java.io.File;&lt;br /&gt;import java.io.FileReader;&lt;br /&gt;import java.io.IOException;&lt;br /&gt;import java.util.List;&lt;br /&gt;import java.util.Vector;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;public class DictionaryLoader {&lt;br /&gt;&lt;br /&gt;private Dictionary dictionary = new Dictionary();&lt;br /&gt;&lt;br /&gt;public void load(File dictFile) {&lt;br /&gt;  int batchSize = 50;&lt;br /&gt;  List&amp;lt;String&amp;gt; batchOfWords = new Vector&amp;lt;String&amp;gt;();&lt;br /&gt;&lt;br /&gt;  BufferedReader reader = null;&lt;br /&gt;  try {&lt;br /&gt;      reader = new BufferedReader(new FileReader(dictFile));&lt;br /&gt;      String word;&lt;br /&gt;      while ((word = reader.readLine()) != null) {&lt;br /&gt;          batchOfWords.add(word);&lt;br /&gt;&lt;br /&gt;          if (batchOfWords.size() == batchSize) {&lt;br /&gt;              synchronized (dictionary) {&lt;br /&gt;                  dictionary.add(batchOfWords);&lt;br /&gt;                  batchOfWords.clear();&lt;br /&gt;              }&lt;br /&gt;          }&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      /* add the rest of the words left in the batch */&lt;br /&gt;      if (batchOfWords.size() &amp;gt; 0) {&lt;br /&gt;          synchronized (dictionary) {&lt;br /&gt;              dictionary.add(batchOfWords);&lt;br /&gt;          }&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      System.out.println("size = " + dictionary.size());&lt;br /&gt;&lt;br /&gt;  } catch (Exception e) {&lt;br /&gt;      e.printStackTrace();&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  finally {&lt;br /&gt;      try {&lt;br /&gt;          if (reader != null) reader.close();&lt;br /&gt;      } catch (IOException e) {&lt;br /&gt;          e.printStackTrace();&lt;br /&gt;      }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public static void main(String[] args) {&lt;br /&gt;  DictionaryLoader loader = new DictionaryLoader();&lt;br /&gt;  long elapsed = System.currentTimeMillis();&lt;br /&gt;  loader.load(new File("dict.txt"));&lt;br /&gt;  elapsed = System.currentTimeMillis() - elapsed;&lt;br /&gt;  System.out.println("Time took to load the dictionary (ms): "&lt;br /&gt;                      + elapsed);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;In the Terracotta configuration file, tc-config.xml, we declare the dictionary field to be a shared "root." The entire graph of objects referenceable by this root automatically becomes clustered by Terracotta. We also give this root field a cluster-wide name ("dictionary") with the root-name tag. This is very important because this is how other applications get access to this same root.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&amp;lt;root&amp;gt;&lt;br /&gt;&amp;lt;field-name&amp;gt;dictionary.DictionaryLoader.dictionary&amp;lt;/field-name&amp;gt;&lt;br /&gt;&amp;lt;root-name&amp;gt;dictionary&amp;lt;/root-name&amp;gt;&lt;br /&gt;&amp;lt;/root&amp;gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;To test our little dictionary, I wrote the DictionaryLookup app. It also declares a dictionary object, but names it differently.&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;package dictionary;&lt;br /&gt;&lt;br /&gt;public class DictionaryLookup {&lt;br /&gt;&lt;br /&gt;private static final Dictionary dict = new Dictionary();&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;public void checkWord(String word) {&lt;br /&gt;  if (dict.isWord(word)) {&lt;br /&gt;      System.out.println("'" + word + "' is a word" );&lt;br /&gt;  }&lt;br /&gt;  else if (dict.isPrefix(word)) {&lt;br /&gt;      System.out.println("'" + word + "' is a prefix");&lt;br /&gt;  }&lt;br /&gt;  else {&lt;br /&gt;      System.out.println("'" + word + "' is NOT found");&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;public static void main(String[] args) {&lt;br /&gt;&lt;br /&gt;  DictionaryLookup dl = new DictionaryLookup();&lt;br /&gt;  String[] words = {"eat", "my", "shorts", "homer", "crapola", "dict",&lt;br /&gt;          "config", "configuration", "sweet", "abracadabra"};&lt;br /&gt;&lt;br /&gt;  for (int i = 0; i &amp;lt; words.length; i++) {&lt;br /&gt;       dl.checkWord(words[i]);&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The field "dict" is declared as root in tc-config.xml but that doesn’t mean the server will have 2 roots (namely two clustered versions of dictionary objects). The trick is the root-name tag. It tells the server to treat these two roots as one, interchangeably.&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&amp;lt;root&amp;gt;&lt;br /&gt;&amp;lt;field-name&amp;gt;dictionary.DictionaryLoader.dictionary&amp;lt;/field-name&amp;gt;&lt;br /&gt;&amp;lt;root-name&amp;gt;dictionary&amp;lt;/root-name&amp;gt;&lt;br /&gt;&amp;lt;/root&amp;gt;&lt;br /&gt;&amp;lt;root&amp;gt;&lt;br /&gt;&amp;lt;field-name&amp;gt;dictionary.DictionaryLookup.dict&amp;lt;/field-name&amp;gt;&lt;br /&gt;&amp;lt;root-name&amp;gt;dictionary&amp;lt;/root-name&amp;gt;&lt;br /&gt;&amp;lt;/root&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The interesting thing about the DictionaryLookup is how it assumes the dictionary content is already there and available for use. All it does is to declare a Dictionary field and Terracotta automatically assigns the same “dictionary” shared root to that field.&lt;br /&gt;&lt;br /&gt;DictionaryLookup has proven that this approach works correctly and as I wanted. I can go ahead and attach the dictionary to our puzzle solver:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&amp;lt;root&amp;gt;&lt;br /&gt;&amp;lt;field-name&amp;gt;dictionary.DictionaryLoader.dictionary&amp;lt;/field-name&amp;gt;&lt;br /&gt;&amp;lt;root-name&amp;gt;dictionary&amp;lt;/root-name&amp;gt;&lt;br /&gt;&amp;lt;/root&amp;gt;&lt;br /&gt;&amp;lt;root&amp;gt;&lt;br /&gt;&amp;lt;field-name&amp;gt;dictionary.DictionaryLookup.dict&amp;lt;/field-name&amp;gt;&lt;br /&gt;&amp;lt;root-name&amp;gt;dictionary&amp;lt;/root-name&amp;gt;&lt;br /&gt;&amp;lt;/root&amp;gt;&lt;br /&gt;&amp;lt;root&gt;&lt;br /&gt;&amp;lt;field-name&amp;gt;games.boggle.Board.theDictionary&amp;lt;/field-name&amp;gt;&lt;br /&gt;&amp;lt;root-name&amp;gt;dictionary&amp;lt;/root-name&amp;gt;&lt;br /&gt;&amp;lt;/root&amp;gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;If you look at Board.java, you'll see how the game can focus on its main logic without having to worry about loading up the dictionary in memory. This eliminates huge amount of time if you choose to use a really big size dictionary. And not only just one application can use dictionary at a time; multiple applications from multiple networks can take advantage of this network attached memory.&lt;br /&gt;&lt;br /&gt;Source code available &lt;a href="http://www.terracotta.org/confluence/display/labs/Shared+objects+between+different+applications"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6719192018427942027-376849647101601563?l=unserializableone.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://unserializableone.blogspot.com/feeds/376849647101601563/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6719192018427942027&amp;postID=376849647101601563' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/376849647101601563'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6719192018427942027/posts/default/376849647101601563'/><link rel='alternate' type='text/html' href='http://unserializableone.blogspot.com/2006/10/share-precious-heap-memory-accross.html' title='Share precious heap memory across multiple JVM&apos;s'/><author><name>Hung Huynh</name><uri>http://www.blogger.com/profile/07622166829380787185</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://1.bp.blogspot.com/_AUFkgevfdTc/SNcusQ2CpKI/AAAAAAAACS8/tiu1wT0TULE/S220/hung.png'/></author><thr:total>8</thr:total></entry></feed>
