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

<channel>
	<title>Boni Gopalan's Blog</title>
	<atom:link href="http://whiteboardjunkie.wordpress.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://whiteboardjunkie.wordpress.com</link>
	<description>Tales from my recent tech travels</description>
	<lastBuildDate>Thu, 26 Jan 2012 02:23:38 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='whiteboardjunkie.wordpress.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://s2.wp.com/i/buttonw-com.png</url>
		<title>Boni Gopalan's Blog</title>
		<link>http://whiteboardjunkie.wordpress.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://whiteboardjunkie.wordpress.com/osd.xml" title="Boni Gopalan&#039;s Blog" />
	<atom:link rel='hub' href='http://whiteboardjunkie.wordpress.com/?pushpress=hub'/>
		<item>
		<title>FolderListener for JDK7 &#8211; Watch folder events more elegantly.</title>
		<link>http://whiteboardjunkie.wordpress.com/2012/01/26/folderlistener-for-jdk7-watch-folder-events-more-elegantly/</link>
		<comments>http://whiteboardjunkie.wordpress.com/2012/01/26/folderlistener-for-jdk7-watch-folder-events-more-elegantly/#comments</comments>
		<pubDate>Thu, 26 Jan 2012 02:15:13 +0000</pubDate>
		<dc:creator>bonigv</dc:creator>
				<category><![CDATA[Concurrency]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[JDK7]]></category>
		<category><![CDATA[File Change Notification]]></category>
		<category><![CDATA[Folder Change Listener]]></category>
		<category><![CDATA[FolderListener]]></category>
		<category><![CDATA[WatchService]]></category>

		<guid isPermaLink="false">http://whiteboardjunkie.wordpress.com/?p=193</guid>
		<description><![CDATA[Source code discussed in this post can be freely downloaded and used in whatever way or form you want.  SVN URL is, svn checkout http://whiteboardjunkie.googlecode.com/svn/trunk/parallelly parallelly-read-only JDK7 provides a library solution through java.nio.file.WatchService  to the classic folder watching dilemma Java developers went through for a decade.  It defines an interface closely matching the Observable.  JDK7 also provides concrete [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=whiteboardjunkie.wordpress.com&amp;blog=4302397&amp;post=193&amp;subd=whiteboardjunkie&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<blockquote><p>Source code discussed in this post can be freely downloaded and used in whatever way or form you want.  SVN URL is, svn checkout <a href="http://whiteboardjunkie.googlecode.com/svn/trunk/parallelly/" target="_blank"><strong><em>http</em></strong>://whiteboardjunkie.googlecode.com/svn/trunk/parallelly</a> parallelly-read-only</p></blockquote>
<p>JDK7 provides a library solution through <a href="http://docs.oracle.com/javase/7/docs/api/java/nio/file/WatchService.html" target="_blank">java.nio.file.WatchService</a>  to the classic folder watching dilemma Java developers went through for a decade.  It defines an interface closely matching the <a href="http://docs.oracle.com/javase/7/docs/api/java/util/Observable.html" target="_blank">Observable</a>.  JDK7 also provides <a href="http://docs.oracle.com/javase/7/docs/api/java/nio/file/FileSystem.html#newWatchService()" target="_blank">concrete implementation classes</a> implementing this interface to watch for folder changes.  The key performance boosts from this implementation is drawn from it using the OS interrupts to watch a folder (where ever those are available for consumption).  This approach frees up unnecessary cycle time and complexity in code for a &#8216;Watcher&#8217; thread.  I liked it.  A good discussion of how to go about implementing a FolderWatcher is discussed here by Venish Joe in his post titled <a href="http://www.venishjoe.net/2009/10/monitor-directory-for-changes-using.html" target="_blank">Monitor a Directory for Changes using Java</a>.</p>
<p>When experimenting with this feature for one of the products we are building it annoyed the hell out of me that there is no listener paradigm out of the box for this feature.  I would have loved to register a  listener for particular events on a folder and then execute code only when these events occur.  That way my code will be free from the boilerplate instructions of polling for events and more annoyingly that infinite loop to poll for these events.   How I wished something like this was available.</p>
<p><pre class="brush: java;">
		FolderWatchers.getInstance().addFolderListener(Paths.get(&quot;c:/boni/&quot;),
				new ChangeListener(ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY) {
					@Override
					public void onEvent(WatchEvent&lt;Path&gt; anEvent) {
						System.out.println(&quot;LISTENER 1 &quot; + anEvent.kind().name().toString()
								+ &quot; &quot; + anEvent.context());
					}
				});

</pre></p>
<p>Here I am registering a ChangeListener to be notified whenever there is any CREATE, DELETE or modify operations on path &#8220;c:/boni&#8221; (or it&#8217;s subfolders).  The meat of execution I am interested in is coded inside the &#8216;onEvent&#8217;.   This is a working code. What is done here is to take away all the mundane tasks of creating a watch service for a &#8216;Path&#8217; and then polling through the events away.  The underlying constructs are completely thread safe.  Furthermore, &#8216;addFolderListener&#8217; returns a &#8216;FolderWatcherFuture&#8217; through which you can cancel the task.   Simple and elegant.  Without much further ado I present to you FolderWatchers (and its test case)</p>
<p><a href="http://whiteboardjunkie.googlecode.com/svn/trunk/parallelly/src/test/java/org/boni/parallely/service/FolderWatchers.java" target="_blank">FolderWatchers.java</a></p>
<p><pre class="brush: java;">
package org.boni.parallely.service;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class FolderWatchers {

	private static FolderWatchers watchers = null;
	private static WatchService watchService = null;
	private static final ExecutorService executor = Executors.newCachedThreadPool();
	private FolderWatchers() throws IOException{
		watchService = FileSystems.getDefault().newWatchService();
	}
	public static FolderWatchers getInstance() throws IOException {
		if (null == watchers){
			watchers = new FolderWatchers();
		}
		return watchers;
	}
	public FolderWatcherFuture addFolderListener(Path path, ChangeListener changeListener) throws IOException {
		FolderWatcher aWatcher = new FolderWatcher(path,changeListener,watchService);
		Future f = executor.submit(aWatcher);
		FolderWatcherFuture future = new FolderWatcherFuture(aWatcher, f);
		return future;
	}

	public void cancelFolderWatching(FolderWatcherFuture folderWatcherFuture) {

	}
}
class FolderWatcher implements Runnable{
	private WatchKey watchKey = null;
	private ChangeListener listener = null;
	protected FolderWatcher(Path path, ChangeListener changeListener, WatchService watchService) throws IOException {
		watchKey = path.register(watchService, changeListener.getEventTypes());
		listener = changeListener;
	}
	@Override
	public void run() {
		while(!cancel){
			for (WatchEvent anEvent : watchKey.pollEvents()){
				listener.onEvent((WatchEvent&lt;Path&gt;)anEvent);
			}
		}
	}
	private boolean cancel = false;
	protected void cancel(){
		this.cancel = true;
	}
}

class FolderWatcherFuture implements Future{
	private FolderWatcher watcher = null;
	private Future future = null;
	public FolderWatcherFuture(FolderWatcher watcher, Future future){
		this.watcher = watcher;
		this.future = future;
	}
	@Override
	public boolean cancel(boolean mayInterruptIfRunning) {
		watcher.cancel();
		return future.cancel(mayInterruptIfRunning);
	}
	@Override
	public Object get() throws InterruptedException, ExecutionException {
		return future.get();
	}
	@Override
	public Object get(long timeout, TimeUnit unit) throws InterruptedException,
			ExecutionException, TimeoutException {
		return future.get(timeout, unit);
	}
	@Override
	public boolean isCancelled() {
		return future.isCancelled();
	}
	@Override
	public boolean isDone() {
		return future.isDone();
	}
}
</pre></p>
<p><a href="http://whiteboardjunkie.googlecode.com/svn/trunk/parallelly/src/test/java/org/boni/parallely/service/TestFolderWatcher.java" target="_blank">TestFolderWatcher.java</a></p>
<p><pre class="brush: java;">
package org.boni.parallely.service;




import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;




import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.WatchEvent;




import org.junit.Test;




public class TestFolderWatcher {
	@Test
	public void testFutureWatchService() throws IOException {
		FolderWatchers.getInstance().addFolderListener(Paths.get(&quot;c:/boni/&quot;),
				new ChangeListener(ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY) {
					@Override
					public void onEvent(WatchEvent&lt;Path&gt; anEvent) {
						System.out.println(&quot;LISTENER 1 &quot; + anEvent.kind().name().toString()
								+ &quot; &quot; + anEvent.context());
					}
				});
		FolderWatchers.getInstance().addFolderListener(Paths.get(&quot;C:/boni/installs&quot;),
				new ChangeListener(ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY) {
					@Override
					public void onEvent(WatchEvent&lt;Path&gt; anEvent) {
						System.out.println(&quot;LISTENER 2 &quot; + anEvent.kind().name().toString()
								+ &quot; &quot; + anEvent.context());
					}
				});
		while (true) {
			;
		}
	}
}
</pre></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/whiteboardjunkie.wordpress.com/193/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/whiteboardjunkie.wordpress.com/193/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/whiteboardjunkie.wordpress.com/193/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/whiteboardjunkie.wordpress.com/193/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/whiteboardjunkie.wordpress.com/193/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/whiteboardjunkie.wordpress.com/193/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/whiteboardjunkie.wordpress.com/193/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/whiteboardjunkie.wordpress.com/193/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/whiteboardjunkie.wordpress.com/193/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/whiteboardjunkie.wordpress.com/193/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/whiteboardjunkie.wordpress.com/193/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/whiteboardjunkie.wordpress.com/193/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/whiteboardjunkie.wordpress.com/193/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/whiteboardjunkie.wordpress.com/193/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=whiteboardjunkie.wordpress.com&amp;blog=4302397&amp;post=193&amp;subd=whiteboardjunkie&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://whiteboardjunkie.wordpress.com/2012/01/26/folderlistener-for-jdk7-watch-folder-events-more-elegantly/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/e2a5bbed8e1b94c20be75e9da955120e?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">boni</media:title>
		</media:content>
	</item>
		<item>
		<title>Unit Testing Concurrent Execution</title>
		<link>http://whiteboardjunkie.wordpress.com/2012/01/16/unit-testing-concurrent-execution/</link>
		<comments>http://whiteboardjunkie.wordpress.com/2012/01/16/unit-testing-concurrent-execution/#comments</comments>
		<pubDate>Mon, 16 Jan 2012 04:36:13 +0000</pubDate>
		<dc:creator>bonigv</dc:creator>
				<category><![CDATA[Concurrency]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Test Driven Development]]></category>
		<category><![CDATA[ExecutionCompletionService]]></category>
		<category><![CDATA[Junit]]></category>
		<category><![CDATA[Testing]]></category>

		<guid isPermaLink="false">http://whiteboardjunkie.wordpress.com/?p=186</guid>
		<description><![CDATA[Source code discussed in this post can be freely downloaded and used in whatever way or form you want.  SVN URL is, svn checkout http://whiteboardjunkie.googlecode.com/svn/trunk/parallelly parallelly-read-only I put together couple of classes to ease testing pieces of code under concurrency load.  It is generic enough and accommodates a certain level of flexibility.  It is reusable for your specific purpose [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=whiteboardjunkie.wordpress.com&amp;blog=4302397&amp;post=186&amp;subd=whiteboardjunkie&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<blockquote><p>Source code discussed in this post can be freely downloaded and used in whatever way or form you want.  SVN URL is, svn checkout <strong><em>http</em></strong>://whiteboardjunkie.googlecode.com/svn/trunk/parallelly parallelly-read-only</p></blockquote>
<p>I put together couple of classes to ease testing pieces of code under concurrency load.  It is generic enough and accommodates a certain level of flexibility.  It is reusable for your specific purpose and completely eliminates the need to write plumbing code to manage concurrent execution. I found it way more simpler than some of the nonstandard &#8216;frameworks&#8217; solving the same problem.   What it does not do currently is to watch for thread locks.  A bit more hacking will add that feature.  Another day.</p>
<p><pre class="brush: java;">
	@Test
	public void testServerUnderLoad(){
		final JumbleServer jumbleServer = new JumbleServer();
		CompletionServiceForConcurrencyTest&lt;String&gt; resultService = new CompletionServiceForConcurrencyTest&lt;String&gt;(Executors.newCachedThreadPool());
		for (int i = 0 ; i &lt; MAX_THREAD_COUNT; i++){
			resultService.submit(
				new ConcurrentTestCallable&lt;String&gt;(new Object[]{
														UUID.randomUUID().toString()
													}) {
					@Override
					String businessLogic() throws Exception{
						return jumbleServer.jumbleString((String)(parameters[0]));
					}
					@Override
					boolean isResultValid(String output) throws Exception {
						String input = (String)(parameters[0]);
						char[] inputChars = input.toCharArray();
						char[] outputChars = output.toCharArray();
						Arrays.sort(inputChars);
						Arrays.sort(outputChars);
						//Once sorted both char[] should be the same :)
						if (new String(inputChars).equals(new String(outputChars)))
							return true;
						return false;
					}
			});
		}
		Assert.assertTrue(resultService.test());
	}

</pre></p>
<p>That is all.  This tests, instantiates the class to test and calls the method it wants to test in  MAX_THREAD_COUNT number of threads.  Execution is managed through a cached thread pool.  It collects back results from all threads and validates each of the threads worked as expected.  You need to implement two methods specific to your test case viz.</p>
<p><pre class="brush: java;">
abstract T businessLogic() throws Exception;
abstract boolean isResultValid(T output) throws Exception;
</pre></p>
<p>Finally it prints out a summary of test execution.  The results look something like this.  For ease of verification I have added random failure as a feature to simulate Exception conditions for some threads.</p>
<p><pre class="brush: java;">
-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running org.boni.parallely.service.ConcurrencyTestJig
Jan 12, 2012 5:53:46 PM org.boni.parallely.service.CompletionServiceForConcurrencyTest test
SEVERE: Exception while executing thread
java.lang.Exception: This is not a good time to Jumble
        at org.boni.parallely.service.ConcurrencyTestJig$JumbleServer.jumbleString(ConcurrencyTestJig.java:28)
        at org.boni.parallely.service.ConcurrencyTestJig$1.businessLogic(ConcurrencyTestJig.java:63)
        at org.boni.parallely.service.ConcurrencyTestJig$1.businessLogic(ConcurrencyTestJig.java:60)
        at org.boni.parallely.service.ConcurrentTestCallable.call(ConcurrentTestCallable.java:14)
        at org.boni.parallely.service.ConcurrentTestCallable.call(ConcurrentTestCallable.java:5)
        at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
        at java.util.concurrent.FutureTask.run(FutureTask.java:138)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441)
        at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
        at java.util.concurrent.FutureTask.run(FutureTask.java:138)
        at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
Concurrency test summary : TOTAL_THREADS:26, SUCCESS:24, FAILURE:2
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
        at java.lang.Thread.run(Thread.java:619)
Jan 12, 2012 5:53:46 PM org.boni.parallely.service.CompletionServiceForConcurrencyTest test
SEVERE: Exception while executing thread
java.lang.Exception: This is not a good time to Jumble
        at org.boni.parallely.service.ConcurrencyTestJig$JumbleServer.jumbleString(ConcurrencyTestJig.java:28)
        at org.boni.parallely.service.ConcurrencyTestJig$1.businessLogic(ConcurrencyTestJig.java:63)
        at org.boni.parallely.service.ConcurrencyTestJig$1.businessLogic(ConcurrencyTestJig.java:60)
        at org.boni.parallely.service.ConcurrentTestCallable.call(ConcurrentTestCallable.java:14)
        at org.boni.parallely.service.ConcurrentTestCallable.call(ConcurrentTestCallable.java:5)
        at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
        at java.util.concurrent.FutureTask.run(FutureTask.java:138)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441)
        at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
        at java.util.concurrent.FutureTask.run(FutureTask.java:138)
        at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
        at java.lang.Thread.run(Thread.java:619)
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.207 sec &lt;&lt;&lt; FAILURE!

Results :

Failed tests:
  testServerUnderLoad(org.boni.parallely.service.ConcurrencyTestJig)

Tests run: 1, Failures: 1, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[ERROR] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
</pre></p>
<p>This test uses a simple Jig (it will be laughable if I call it a framework) to make such tests look trivial by eliminating thread management code completely.  Let us take a look.</p>
<p>Main outer container of execution of the test jig is a custom <a href="http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/ExecutorCompletionService.html" target="_blank">ExecutorCompletionService</a>  that neatly wraps the <a href="http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/Executor.html" target="_blank">executor </a>of your choice.  It has a mildly overidden <a href="http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/ExecutorCompletionService.html#submit(java.util.concurrent.Callable)" target="_blank">submit(Callable&lt;T&gt;) </a>and a completely deprecated <a href="http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/ExecutorCompletionService.html#submit(java.lang.Runnable, V)" target="_blank">submit(Runnable)</a>.  Feel free to modify the code to implement s<a href="http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/ExecutorCompletionService.html#submit(java.lang.Runnable, V)" target="_blank">ubmit(Runnable) </a>if that fits your specific test scenario better.  After submitting all your Callables (it is a special type of Callable and we will get to it in a bit), you call test().  This will use its &#8216;<a href="http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/ExecutorCompletionService.html" target="_blank">ExecutorCompletionService</a> &#8217; capabilities to collect all the results from threads and generate a test result summary.  How cool is that!</p>
<p><em>CompletionServiceForConcurrencyTest.java</em></p>
<p><pre class="brush: java;">
class CompletionServiceForConcurrencyTest&lt;T&gt; extends ExecutorCompletionService&lt;Result&lt;T&gt;&gt;{
	private final Log logger = LogFactory.getLog(CompletionServiceForConcurrencyTest.class);
	private int count = 0;
	public CompletionServiceForConcurrencyTest(Executor executor){
		super(executor);
	}
	@Override
	public Future&lt;Result&lt;T&gt;&gt; submit(Callable&lt;Result&lt;T&gt;&gt; task) {
		count = count+1;
		return super.submit(task);
	}
	@Override
	public Future&lt;Result&lt;T&gt;&gt; submit(Runnable task, Result&lt;T&gt; result) {
		throw new RuntimeException(&quot;Testing through runnable submission not supported currently&quot;);
	}
	/**
	 * Packs all the ran test results neatly by providing summary information
	 * and exception logs if any.
	 * @return boolean : TRUE -&gt; All threads ran as expected.
	 */
	public boolean test(){
		List&lt;Result&lt;T&gt;&gt; results = new ArrayList&lt;Result&lt;T&gt;&gt;();
		for (int i = 0 ; i &lt; count; i++){
			try {
				//waits on the CompletionService to make available results
				//as and when a thread completes.  The results are added
				//to a List for verification in the next step.
				results.add(take().get());
			} catch (InterruptedException e) {
			} catch (ExecutionException e) {
			}
		}
		int failedRsults = 0;
		for (Result&lt;T&gt; aResult : results){
			if (aResult.result == null){
				logger.error(aResult, aResult.e);
				failedRsults = failedRsults + 1;
			}
		}
		System.out.println(&quot;Concurrency test summary : TOTAL_THREADS:&quot; + results.size() + &quot;, SUCCESS:&quot; + (results.size() - failedRsults) + &quot;, FAILURE:&quot; + failedRsults );
		return (failedRsults == 0) ? true : false;
	}
}
</pre></p>
<p>Next let me introduce you to my <a href="http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/Callable.html" target="_blank">Callable</a>.  It is based on the abstract class ConcurrentTestCallable.  It is Generic to accommodate different return types from Callable execution.  It is overiding the method &#8216;call&#8217;  and provides a Result Value.  The Result data object wraps result, exception or a message back from the callable.  Unfortunately in my jig I am making &#8216;call&#8217; final.  Feel free to change it.</p>
<p><em>ConcurrentTestCallable.java</em></p>
<p><pre class="brush: java;">
abstract class ConcurrentTestCallable&lt;T&gt; implements Callable&lt;Result&lt;T&gt;&gt;{
	Object[] parameters = null;
	public ConcurrentTestCallable(Object[] testInputParameters){
		this.parameters = testInputParameters;
	}
	/**
	 * Implement your execution logic here.  It is supposed to either return
	 * a result of type T or throw an appropriate exception in case of error
	 * scenario.
	 * @return T result of exection
	 * @throws Exception appropriate exception wrapping error condition.
	 */
	abstract T businessLogic() throws Exception;
	/**
	 * Logic to check whether the returned result is as expected.  You have access to the
	 * Input parameters through Object[] parameters.
	 * @param output Result from {@link businessLogic}
	 * @return boolean confirming accuracy of the result or not.
	 * @throws Exception
	 */
	abstract boolean isResultValid(T output) throws Exception;
	public final Result&lt;T&gt; call() throws Exception {
		try {
			T result = businessLogic();
			if (isResultValid(result)){
				return new Result&lt;T&gt;(result, null, &quot;PERFECT MATCH&quot;,parameters);
			}
			return new Result&lt;T&gt;(result,null,&quot;UNEXPECTED RESULTS&quot;,parameters);
		} catch (Exception e) {
			return new Result&lt;T&gt;(null,e,&quot;Exception while executing thread&quot;,parameters);
		}
	}
}
</pre></p>
<p>Input parameters for execution of thread is passed in through an Object[] on the constructor.  There are two abstract methods that needs implementation.  The inputParameters are stored in a &#8216;protected&#8217; local variables.  So those can be accessed from either of these methods.</p>
<ol>
<li>abstract T businessLogic() throws Exception</li>
<li>abstract boolean isResultValid(T output) throws Exception</li>
</ol>
<p>When we submit the thread, method &#8216;call&#8217; will be invoked.  call simply delegates the execution first to &#8216;T businessLogic()&#8217;.  It uses &#8216;isResultValid(T)&#8217; to validate.  Depending on the results an appropriate &#8216;Result&#8217; object is returned.  If Result.result is null your thread did not execute as planned.</p>
<p><em>Result.java</em></p>
<p><pre class="brush: java;">
//Generic result carrier to return both
//results and any possible exceptions from Callable (Thread)
class Result&lt;T&gt;{
	T result = null;
	Exception e = null;
	String message = null;
	Object[] inputParameters = null;
	Result(T aResult, Exception e, String message, Object[] inputParameters){
		this.result = aResult;
		this.e = e;
		this.message = message;
		this.inputParameters = inputParameters;
	}
	@Override
	public String toString() {
		return message;
	}
}
</pre></p>
<p>In the example discussed in this post I am using a server <a href="http://code.google.com/p/whiteboardjunkie/source/browse/trunk/parallelly/src/test/java/org/boni/parallely/service/ConcurrencyTestJig.java" target="_blank">JumbleServer.jumble() </a>that randomly throws an exception to simulate problem scenarios.  You can access the complete source code from subversion @</p>
<blockquote><p>svn checkout <strong><em>http</em></strong>://whiteboardjunkie.googlecode.com/svn/trunk/parallelly parallelly-read-only</p></blockquote>
<p><strong>Edit[1/17/2012]:</strong></p>
<p>To check whether the test jig is flexible as I make it sound like I got down to testing it a bit.  What I did is to write a different piece of code with a different set of responsibilities.  This one is a &#8216;SweatShopFactory&#8217; that churns out nothing less than &#8216;Persons&#8217;.  Wow! Now that is sinister.</p>
<p><pre class="brush: java;">
	class Person implements Serializable{
		String name;
		char sex;
		int age;
	}
	class SweatShopServer{
		private final Random random = new Random(System.currentTimeMillis());
		public List&lt;Person&gt; getPersons(char sex, int ageFrom, int ageTo, int count){
			List&lt;Person&gt; persons = new ArrayList&lt;Person&gt;();
			while(persons.size() &lt; count){
				Person aPerson = new Person();
				aPerson.name = UUID.randomUUID().toString();
				aPerson.sex = random.nextBoolean() ? 'F' : 'M';
				aPerson.age = random.nextInt(ageTo);
				if (aPerson.age &gt; ageFrom &amp;&amp; aPerson.age &lt; ageTo &amp;&amp; aPerson.sex == sex){
					//This is where I am rigging the tests to create
					//some random failures;
					if (aPerson.age == 17) aPerson.age = ageTo + 1;
					persons.add(aPerson);
				}
			}
			return persons;
		}
	}
[sourcecode]

To test this class under load I wrote this (and only this) code.

[sourcecode language=&quot;java&quot;]
	@Test
	public void testPersonsServerUnderLoad(){
		final SweatShopServer sweatshopServer = new SweatShopServer();
		CompletionServiceForConcurrencyTest&lt;List&lt;Person&gt;&gt; resultService = new CompletionServiceForConcurrencyTest&lt;List&lt;Person&gt;&gt;(Executors.newCachedThreadPool());
		final Random random = new Random(System.currentTimeMillis());
		for (int i = 0 ; i &lt; MAX_THREAD_COUNT; i++){
			resultService.submit(
				new ConcurrentTestCallable&lt;List&lt;Person&gt;&gt;(new Object[]{random.nextBoolean() ? 'M' : 'F', 0, random.nextInt(100), random.nextInt(100)}) {
					@Override
					List&lt;Person&gt; businessLogic() throws Exception{
						char sex = ((Character)(parameters[0])).charValue();
						int ageFrom = ((Integer)(parameters[1])).intValue();
						int ageTo = ((Integer)(parameters[2])).intValue();
						int count = ((Integer)(parameters[3])).intValue();
						return
						sweatshopServer.getPersons(	sex, 
													ageFrom, 
													ageTo, 
													count);
					}
					@Override
					boolean isResultValid(List&lt;Person&gt; output) throws Exception {
						char sex = ((Character)(parameters[0])).charValue();
						int ageFrom = ((Integer)(parameters[1])).intValue();
						int ageTo = ((Integer)(parameters[2])).intValue();
						int count = ((Integer)(parameters[3])).intValue();
						Assert.assertNotNull(output);
						Assert.assertTrue(&quot;Expected size:&quot; + count + &quot;, Actual size:&quot; + output.size(), count == output.size());
						for (Person aPerson : output){
							Assert.assertEquals(aPerson.sex,sex);
							Assert.assertTrue(&quot;Expected age &gt; &quot; + ageFrom , aPerson.age &gt; ageFrom);
							Assert.assertTrue(&quot;Expected age &lt; &quot; + ageTo, aPerson.age &lt; ageTo);
						}
						return true;
					}
					
			});
		}
		Assert.assertTrue(resultService.test());
	}

</pre></p>
<p>And it worked just the way I thought it would.  Perfectly!</p>
<p><pre class="brush: java;">
-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running org.boni.parallely.service.ConcurrencyTestJig
Jan 17, 2012 10:50:38 AM org.boni.parallely.service.CompletionServiceForConcurrencyTest test
SEVERE: Exception while executing thread
java.lang.IllegalArgumentException: n must be positive
        at java.util.Random.nextInt(Random.java:250)
Concurrency test summary : TOTAL_THREADS:16, SUCCESS:15, FAILURE:1
        at org.boni.parallely.service.ConcurrencyTestJig$SweatShopServer.getPersons(ConcurrencyTestJig.java:61)
        at org.boni.parallely.service.ConcurrencyTestJig$1.businessLogic(ConcurrencyTestJig.java:87)
        at org.boni.parallely.service.ConcurrencyTestJig$1.businessLogic(ConcurrencyTestJig.java:80)
        at org.boni.parallely.service.ConcurrentTestCallable.call(ConcurrentTestCallable.java:28)
        at org.boni.parallely.service.ConcurrentTestCallable.call(ConcurrentTestCallable.java:5)
        at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.457 sec &lt;&lt;&lt; FAILURE!
        at java.util.concurrent.FutureTask.run(FutureTask.java:138)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441)
        at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
        at java.util.concurrent.FutureTask.run(FutureTask.java:138)
        at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
        at java.lang.Thread.run(Thread.java:619)

Results :

Failed tests:
  testPersonsServerUnderLoad(org.boni.parallely.service.ConcurrencyTestJig)

Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
</pre></p>
<p>Ok, now that is some minimal validation.  Now it is time to use it some real code.  May be to load test our Image Server <img src='http://s0.wp.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/whiteboardjunkie.wordpress.com/186/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/whiteboardjunkie.wordpress.com/186/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/whiteboardjunkie.wordpress.com/186/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/whiteboardjunkie.wordpress.com/186/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/whiteboardjunkie.wordpress.com/186/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/whiteboardjunkie.wordpress.com/186/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/whiteboardjunkie.wordpress.com/186/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/whiteboardjunkie.wordpress.com/186/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/whiteboardjunkie.wordpress.com/186/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/whiteboardjunkie.wordpress.com/186/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/whiteboardjunkie.wordpress.com/186/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/whiteboardjunkie.wordpress.com/186/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/whiteboardjunkie.wordpress.com/186/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/whiteboardjunkie.wordpress.com/186/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=whiteboardjunkie.wordpress.com&amp;blog=4302397&amp;post=186&amp;subd=whiteboardjunkie&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://whiteboardjunkie.wordpress.com/2012/01/16/unit-testing-concurrent-execution/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/e2a5bbed8e1b94c20be75e9da955120e?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">boni</media:title>
		</media:content>
	</item>
		<item>
		<title>Lucene and Hyphens</title>
		<link>http://whiteboardjunkie.wordpress.com/2011/12/16/lucene-and-hyphens/</link>
		<comments>http://whiteboardjunkie.wordpress.com/2011/12/16/lucene-and-hyphens/#comments</comments>
		<pubDate>Fri, 16 Dec 2011 13:33:31 +0000</pubDate>
		<dc:creator>bonigv</dc:creator>
				<category><![CDATA[Lucene]]></category>
		<category><![CDATA[Test Driven Development]]></category>
		<category><![CDATA[Custom Analyzer]]></category>
		<category><![CDATA[Search]]></category>
		<category><![CDATA[Tokenizing]]></category>

		<guid isPermaLink="false">http://whiteboardjunkie.wordpress.com/?p=177</guid>
		<description><![CDATA[I have used Apache Lucene for providing an intelligent intuitive search for my products many times over.  Well that was till yesterday! No, I did not ditch Lucene.  But what threw a monkey in the wrench was the &#8216;intuitive&#8217; nature of the product.  Let me explain. Say I have a string &#8216;abc-def&#8217; to be indexed. [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=whiteboardjunkie.wordpress.com&amp;blog=4302397&amp;post=177&amp;subd=whiteboardjunkie&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>I have used <a href="http://lucene.apache.org/java/docs/index.html" target="_blank">Apache Lucene</a> for providing an intelligent intuitive search for my products many times over.  Well that was till yesterday! No, I did not ditch Lucene.  But what threw a monkey in the wrench was the &#8216;intuitive&#8217; nature of the product.  Let me explain.</p>
<p>Say I have a string &#8216;abc-def&#8217; to be indexed.  The say Lucene works (well actually the StandardAnalyzer that delegates the tokenizing through StandardTokenizer works) is by splitting &#8216;abc-def&#8217; into &#8216;abc&#8217;, &#8216;def&#8217; to index the two words separately.  The problem now is that when someone searched with &#8220;abc-def&#8221; Lucene says there are no matching records.  Well this, even to us the defenders of engineering dignity, was plain unacceptable.  Not much digging later I saw that it was exactly the way in which <a href="http://lucene.apache.org/java/3_0_2/api/all/org/apache/lucene/analysis/standard/StandardAnalyzer.html" target="_blank">StandardAnalyze</a>r is designed to work.  No problem I said.  One of the perks of being a heavily open source shop with awareness of GPL Vs. LGPL pitfalls is that we know we could tweak the platforms we use to make it work the way we want.  So to me the plan was to modify the <a href="http://lucene.apache.org/java/3_0_2/api/all/org/apache/lucene/analysis/standard/StandardAnalyzer.html" target="_blank">StandardAnalyzer</a> to not to tokenize strings containing hyphens (&#8216;-&#8217;).</p>
<p>Now the challenge was that bug was reported at sharp 6PM, right after I tucked in my laptop and told my wife that I will pickup the pasta on my way home.   The techie hubris got the better of me.  Out came the laptop but not the power chord since I figured it is below my dignity to work beyond whatever power is left on the machine for such a trivial fix. Big mistake &#8211; never under estimate a problem, never assume while debugging.</p>
<p>Shortly after I started to dig in I realized that it need more than Java to fix this tiny bug.  <a href="http://lucene.apache.org/java/3_0_2/api/all/org/apache/lucene/analysis/standard/StandardAnalyzer.html" target="_blank">StandardAnalyzer </a>under its hoods delegates most of the heavy lifting to <a href="http://lucene.apache.org/java/3_0_2/api/all/org/apache/lucene/analysis/standard/StandardTokenizer.html" target="_blank">StandardTokenizer </a>- which to my shock was a generated java file.  The source was in <a href="http://en.wikipedia.org/wiki/Flex_lexical_analyser" target="_blank">flex</a>; written specifically for a generator unknown to me yesterday &#8211; <a href="http://jflex.de/manual.html" target="_blank">JFlex</a>.  As we speak we are best of buddies.  Alright then out came the power chord and I got down to <a href="http://jflex.de/manual.html" target="_blank">RTFM </a>of JFlex.  An hour and a few test cases a later I was reasonably confident to perform an invasive surgery on the <a href="http://svn.apache.org/viewvc/lucene/dev/branches/lucene_solr_3_1/lucene/src/java/org/apache/lucene/analysis/standard/StandardTokenizerImpl.jflex?revision=1078587&amp;view=markup" target="_blank">StandardTokenizerImpl.jflex</a>.  Or so I thought.</p>
<p>Though my micro test cases around JFlex worked well my main testcase on the product code base refused to budge.  It was adamant that the tokenizing happened at hyphens irrespective of what I wrote in <a href="http://jflex.de/index.html" target="_blank">JFlex</a>.  By 11PM I accepted defeat and drove home.</p>
<p>There is wisdom in whoever said that sometimes the best solution to a hard problem is sleep.  Today morning I sat down to tackle the gorilla and I saw that the plain problem was hiding in plain sight.  Nice guys at Lucene development team had left a note in the same package where they bundled StandardTokenizerImpl.jflex.  It said, for the love of goo use JDK1.4 for to run Jflex to generate code.  Wait! 1.4?? 1.4?? A decade old JDK?  &#8217;Yes&#8217;; it said.  So I went and got hold of JDK 1.4 (That took the JDK count on my notebook 5).</p>
<blockquote><p>WARNING: if you change StandardTokenizerImpl.jflex and need to regenerate<br />
the tokenizer, only use Java 1.4 !!!<br />
This grammar currently uses constructs (eg :digit:, :letter:) whose<br />
meaning can vary according to the JRE used to run jflex. See<br />
<a href="https://issues.apache.org/jira/browse/LUCENE-1126"> https://issues.apache.org/jira/browse/LUCENE-1126</a> for details.<br />
For current backwards compatibility it is needed to support<br />
only Java 1.4 &#8211; this will change in Lucene 3.1.</p></blockquote>
<table border="1">
<tbody>
<tr>
<td style="text-align:center;"><strong>Original</strong></td>
<td style="text-align:center;"><strong>Modified</strong></td>
</tr>
<tr>
<td><!--Original Flex--><br />
<pre class="brush: plain;">
// floating point, serial, model numbers, ip addresses, etc.
// every other segment must have at least one digit
NUM        = (
		   {HAS_DIGIT} ({P} {ALPHANUM})+
		   | {ALPHANUM} {P} {HAS_DIGIT}
           | {HAS_DIGIT} {P} {ALPHANUM}
           | {ALPHANUM} ({P} {HAS_DIGIT} {P} {ALPHANUM})+
           | {HAS_DIGIT} ({P} {ALPHANUM} {P} {HAS_DIGIT})+
           | {ALPHANUM} {P} {HAS_DIGIT} ({P} {ALPHANUM} {P} {HAS_DIGIT})+
           | {HAS_DIGIT} {P} {ALPHANUM} ({P} {HAS_DIGIT} {P} {ALPHANUM})+)
</pre></td>
<td><!--Modified Flex--><br />
<pre class="brush: plain;">
// floating point, serial, model numbers, ip addresses, etc.
// every other segment must have at least one digit
NUM        = (
			 {ALPHANUM} {P} {ALPHANUM}
		   | {ALPHANUM} ({P} {ALPHANUM})+
		   | {HAS_DIGIT} ({P} {ALPHANUM})+
		   | {ALPHANUM} {P} {HAS_DIGIT}
           | {HAS_DIGIT} {P} {ALPHANUM}
           | {ALPHANUM} ({P} {HAS_DIGIT} {P} {ALPHANUM})+
           | {HAS_DIGIT} ({P} {ALPHANUM} {P} {HAS_DIGIT})+
           | {ALPHANUM} {P} {HAS_DIGIT} ({P} {ALPHANUM} {P} {HAS_DIGIT})+
           | {HAS_DIGIT} {P} {ALPHANUM} ({P} {HAS_DIGIT} {P} {ALPHANUM})+)
</pre></td>
</tr>
</tbody>
</table>
<p>The custom analyzer code is here. The MyTokenizer.java is a generated java file when we compile the modified .jflex grammar file with JFlex.</p>
<p><pre class="brush: java;">
public final class MyCustomAnalyzer extends org.apache.lucene.analysis.Analyzer {
	static Version luceneVersion = null;
	static StandardAnalyzer STANDARD = null;
	public MyCustomAnalyzer(Version version){
		super();
		this.luceneVersion = version;
		STANDARD = new StandardAnalyzer(luceneVersion){
			@Override
			public TokenStream tokenStream(String fieldName, Reader reader) {
			    MyTokenizer tokenStream = new MyTokenizer(luceneVersion, reader);
			    tokenStream.setMaxTokenLength(STANDARD.getMaxTokenLength());
			    TokenStream result = new StandardFilter(tokenStream);
			    result = new LowerCaseFilter(result);
			    result = new StopFilter(StopFilter.getEnablePositionIncrementsVersionDefault(luceneVersion), result, StandardAnalyzer.STOP_WORDS_SET);
			    return result;
			}
		};
	}
	@Override
	public TokenStream tokenStream(String fieldName, Reader reader) {
		return STANDARD.tokenStream(fieldName, reader);
	}
	@Override
	public TokenStream reusableTokenStream(String fieldName, Reader reader)
			throws IOException {
		return STANDARD.reusableTokenStream(fieldName, reader);
	}
}

</pre></p>
<p>Miracle!  It worked as I wanted.  Exactly as I wanted.  My test cases passed with flying colors and I was laughing ear to ear.  Great!</p>
<p>So, for the unlucky or the intrigued who is left to write a custom analyzer to introduce grammar changes in tokenizing &#8211; be prepared to befriend JFlex.  Believe me though it is a nice tool and I can already see a few places in parsing where my team would love to put those muscles into work.</p>
<p>Ciao!</p>
<p><strong><br />
</strong></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/whiteboardjunkie.wordpress.com/177/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/whiteboardjunkie.wordpress.com/177/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/whiteboardjunkie.wordpress.com/177/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/whiteboardjunkie.wordpress.com/177/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/whiteboardjunkie.wordpress.com/177/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/whiteboardjunkie.wordpress.com/177/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/whiteboardjunkie.wordpress.com/177/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/whiteboardjunkie.wordpress.com/177/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/whiteboardjunkie.wordpress.com/177/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/whiteboardjunkie.wordpress.com/177/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/whiteboardjunkie.wordpress.com/177/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/whiteboardjunkie.wordpress.com/177/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/whiteboardjunkie.wordpress.com/177/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/whiteboardjunkie.wordpress.com/177/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=whiteboardjunkie.wordpress.com&amp;blog=4302397&amp;post=177&amp;subd=whiteboardjunkie&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://whiteboardjunkie.wordpress.com/2011/12/16/lucene-and-hyphens/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/e2a5bbed8e1b94c20be75e9da955120e?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">boni</media:title>
		</media:content>
	</item>
		<item>
		<title>What I do when I  leave the code half way?</title>
		<link>http://whiteboardjunkie.wordpress.com/2011/09/14/what-i-do-when-i-need-to-leave-your-code-half-way/</link>
		<comments>http://whiteboardjunkie.wordpress.com/2011/09/14/what-i-do-when-i-need-to-leave-your-code-half-way/#comments</comments>
		<pubDate>Wed, 14 Sep 2011 10:02:19 +0000</pubDate>
		<dc:creator>bonigv</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://whiteboardjunkie.wordpress.com/?p=150</guid>
		<description><![CDATA[In my typical day I juggle around working with half a dozen Eclipse workspaces each organized for a logical group of projects. Some are purely work, some open source projects and some fun learning projects. Work takes priority on a work day. So when it comes calling with a problem I will drop whatever I [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=whiteboardjunkie.wordpress.com&amp;blog=4302397&amp;post=150&amp;subd=whiteboardjunkie&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>In my typical day I juggle around working with half a dozen Eclipse workspaces each organized for a logical group of projects.  Some are purely work,  some open source projects and some fun learning projects.  Work takes priority on a work day.  So when it comes calling with a problem I will drop whatever I am working on midway.  For example now. Now the challenge is how to get back to the programming thought train as I am leaving it now.  One sneaky trick I have perfected is to introduce a compilation error and save.  This way I find it easier to restart from where I left.  What do you do?<br />
<a href="http://whiteboardjunkie.files.wordpress.com/2011/01/compilation_error.png"><img src="http://whiteboardjunkie.files.wordpress.com/2011/01/compilation_error.png?w=720" alt="" title="compilation_error"   class="alignleft size-full wp-image-174" /></a></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/whiteboardjunkie.wordpress.com/150/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/whiteboardjunkie.wordpress.com/150/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/whiteboardjunkie.wordpress.com/150/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/whiteboardjunkie.wordpress.com/150/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/whiteboardjunkie.wordpress.com/150/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/whiteboardjunkie.wordpress.com/150/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/whiteboardjunkie.wordpress.com/150/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/whiteboardjunkie.wordpress.com/150/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/whiteboardjunkie.wordpress.com/150/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/whiteboardjunkie.wordpress.com/150/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/whiteboardjunkie.wordpress.com/150/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/whiteboardjunkie.wordpress.com/150/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/whiteboardjunkie.wordpress.com/150/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/whiteboardjunkie.wordpress.com/150/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=whiteboardjunkie.wordpress.com&amp;blog=4302397&amp;post=150&amp;subd=whiteboardjunkie&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://whiteboardjunkie.wordpress.com/2011/09/14/what-i-do-when-i-need-to-leave-your-code-half-way/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/e2a5bbed8e1b94c20be75e9da955120e?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">boni</media:title>
		</media:content>

		<media:content url="http://whiteboardjunkie.files.wordpress.com/2011/01/compilation_error.png" medium="image">
			<media:title type="html">compilation_error</media:title>
		</media:content>
	</item>
		<item>
		<title>Folder links in subversion (svn)</title>
		<link>http://whiteboardjunkie.wordpress.com/2011/04/01/folder-links-in-subversion-svn/</link>
		<comments>http://whiteboardjunkie.wordpress.com/2011/04/01/folder-links-in-subversion-svn/#comments</comments>
		<pubDate>Fri, 01 Apr 2011 13:47:15 +0000</pubDate>
		<dc:creator>bonigv</dc:creator>
				<category><![CDATA[Subverison]]></category>
		<category><![CDATA[Folder Link]]></category>
		<category><![CDATA[Subversion]]></category>
		<category><![CDATA[SVN]]></category>
		<category><![CDATA[svn:externals]]></category>

		<guid isPermaLink="false">https://whiteboardjunkie.wordpress.com/2011/04/01/folder-links-in-subversion-svn/</guid>
		<description><![CDATA[Subversion provides a natural way to pull together resources from different parts of one or more subversion repository locations into a single working folder.&#160; Once setup the commits are pushed into correct parent locations and updates are pulled from the initially configured urls.&#160; This mechanism works equivalent to the symbolic links.&#160; Here is an example. [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=whiteboardjunkie.wordpress.com&amp;blog=4302397&amp;post=172&amp;subd=whiteboardjunkie&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Subversion provides a natural way to pull together resources from different parts of one or more subversion repository locations into a single working folder.&#160; Once setup the commits are pushed into correct parent locations and updates are pulled from the initially configured urls.&#160; This mechanism works equivalent to the symbolic links.&#160; Here is an example.</p>
<p><strong>Step 1:</strong></p>
<p>Create a folder ./externals</p>
<p><strong>Step 2:</strong></p>
<p>Add the new folder into repository using command,</p>
<div id="codeSnippetWrapper">
<pre style="text-align:left;line-height:12pt;background-color:#f4f4f4;width:100%;font-family:&#039;direction:ltr;color:black;font-size:8pt;overflow:visible;border-style:none;margin:0;padding:0;">svn add ./externals</pre>
<p></div>
<p><strong>Step 3:</strong></p>
<p>Create a file “<em>externals.txt</em>” with these two lines</p>
<div id="codeSnippetWrapper">
<pre style="text-align:left;line-height:12pt;background-color:#f4f4f4;width:100%;font-family:&#039;direction:ltr;color:black;font-size:8pt;overflow:visible;border-style:none;margin:0;padding:0;">tomcat/bin http://svn.apache.org/repos/asf/tomcat/tc6.0.x/trunk/bin
apacheds/resources http://svn.apache.org/repos/asf/directory/apacheds/tags/1.5.7/resources</pre>
<p></div>
<p><strong>Step 4:</strong></p>
<p>From command line run the following command.</p>
<div id="codeSnippetWrapper">
<pre style="text-align:left;line-height:12pt;background-color:#f4f4f4;width:100%;font-family:&#039;direction:ltr;color:black;font-size:8pt;overflow:visible;border-style:none;margin:0;padding:0;">svn propset svn:externals ./externals -F externals.txt</pre>
<p></div>
<p>You are all set.&#160; Any updates on <em>externals</em> folder (after committing the newly created folder) will fetch you the latest from tomcat/trunk and apacheds/tags/1.5.7/resources.&#160; What the command has done is to set a property names ‘svn:externals’ into the folder metadata of folder ‘externals’ that tells svn client that some of its child folders are to be fetched/updated/committed through a different repository location.</p>
<p>&#160; If you change any parts of the “externals” code, you will be able to check it in right into the correct code-base.&#160; This solution avoids potential mess ups involving code base mash-ups.</p>
<p>One flipside of this solution is that while branching or tagging you will need to be extra careful in case your symbolic links also needs to shift.&#160; To this effect it is worth noting that there are other useful commands for managing svn folder properties.&#160; Check out the documentation <a href="http://svnbook.red-bean.com/en/1.0/ch07s02.html#svn-ch-7-sect-2.1" target="_blank">here</a>.</p>
<p>There <a href="http://svnbook.red-bean.com/en/1.0/ch07s03.html" target="_blank">is subverison documentation</a> discussing this feature.&#160; But I did not find it very easy to understand.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/whiteboardjunkie.wordpress.com/172/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/whiteboardjunkie.wordpress.com/172/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/whiteboardjunkie.wordpress.com/172/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/whiteboardjunkie.wordpress.com/172/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/whiteboardjunkie.wordpress.com/172/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/whiteboardjunkie.wordpress.com/172/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/whiteboardjunkie.wordpress.com/172/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/whiteboardjunkie.wordpress.com/172/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/whiteboardjunkie.wordpress.com/172/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/whiteboardjunkie.wordpress.com/172/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/whiteboardjunkie.wordpress.com/172/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/whiteboardjunkie.wordpress.com/172/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/whiteboardjunkie.wordpress.com/172/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/whiteboardjunkie.wordpress.com/172/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=whiteboardjunkie.wordpress.com&amp;blog=4302397&amp;post=172&amp;subd=whiteboardjunkie&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://whiteboardjunkie.wordpress.com/2011/04/01/folder-links-in-subversion-svn/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/e2a5bbed8e1b94c20be75e9da955120e?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">boni</media:title>
		</media:content>
	</item>
		<item>
		<title>Managing Spatial Data</title>
		<link>http://whiteboardjunkie.wordpress.com/2011/04/01/managing-spatial-data/</link>
		<comments>http://whiteboardjunkie.wordpress.com/2011/04/01/managing-spatial-data/#comments</comments>
		<pubDate>Fri, 01 Apr 2011 04:19:53 +0000</pubDate>
		<dc:creator>bonigv</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Geometry]]></category>
		<category><![CDATA[Hibernate Spatial]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Spatial Data]]></category>

		<guid isPermaLink="false">https://whiteboardjunkie.wordpress.com/2011/04/01/managing-spatial-data/</guid>
		<description><![CDATA[Hibernate Spatial makes managing spatial data through java code a breeze.&#160; What hibernate-spatial provides is a standardized non- optimized layer that abstract the spatial data management engine.&#160; Currently there are a few databases that supports spatial data management.&#160; Granddaddy of this group is PostgreSQL.&#160; Not so far behind is Oracle and SQL Server.&#160; Very far [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=whiteboardjunkie.wordpress.com&amp;blog=4302397&amp;post=167&amp;subd=whiteboardjunkie&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.hibernatespatial.org/" target="_blank">Hibernate Spatial</a> makes managing spatial data through java code a breeze.&#160; What hibernate-spatial provides is a standardized non- optimized layer that abstract the spatial data management engine.&#160; Currently there are a few databases that supports spatial data management.&#160; Granddaddy of this group is <a href="http://www.postgresql.org/" target="_blank">PostgreSQL</a>.&#160; Not so far behind is <a href="http://www.oracle.com/technetwork/database/options/spatial/index.html" target="_blank">Oracle</a> and <a href="http://www.microsoft.com/sqlserver/2008/en/us/spatial-data.aspx" target="_blank">SQL Server</a>.&#160; Very far behind but trying hard to catch up is <a href="http://dev.mysql.com/doc/refman/5.0/en/spatial-extensions.html" target="_blank">MySQL</a>.&#160; MySQLs implementation is <a href="http://dev.mysql.com/doc/refman/5.0/en/functions-for-testing-spatial-relations-between-geometric-objects.html#functions-that-test-spatial-relationships-between-geometries" target="_blank">incomplete especially around spatial analysis functions</a>.&#160; </p>
<blockquote><p>Note</p>
<p>Currently, MySQL does not implement these functions according to the specification. Those that are implemented return the same result as the corresponding MBR-based functions.</p>
</blockquote>
<p>Then the obvious question is “What is complete?” In this context complete is what is specified by the guys at <a href="http://www.opengeospatial.org/" target="_blank">Open Geospatial Consortium (OGC).</a>&#160; Hibernate spatial acknowledges that none of these databases are fully OGC compliant.&#160; So it uses a clever mechanism to still provide a uniform API to the underlying spatial databases.&#160; It uses a Provider that lets each databse specific provider to define its own ‘HibernateSpatialXXXXDialect’.&#160; HibernateSpatialDialect invariably extends one of the Dialects provided by Hibernate.&#160; So to a large extent it is hugely dependent on the twists and turns versions of Hibernate goes through.&#160; At the same time it has always tried to keep itself as close to the curve as possible and have updated its versions matching to Hibernate’s.&#160;&#160; Recently we shifted the spatial database from MySQL to Oracle.&#160; This shift was painless because we used this spatial layer in between.&#160; 90% of the spatial code worked out of the box when I changed the provided from mysql to oracle.&#160; The 10% of the code that did not work well was pertaining to data fetch back.&#160;&#160; Investigation into these unearthed problems revealed some serious shortcomings of MySQL as a spatial data store.</p>
<p>It is interesting to note that spatial data management can be creatively used beyond GeoSpatial systems.&#160; Geo spatial systems can be thought of as a specific use case of a family of problems dealing with annotated images with spatial relationship between various annotations.&#160; These images can be of any type – for example a portrait, a scan of an architectural drawing, a digitized high resolution image of the solar system, images from molecular analysis of samples etc.&#160; In one of the future posts I will discuss some ideas on how to creatively use spatial database for innovative solutions.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/whiteboardjunkie.wordpress.com/167/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/whiteboardjunkie.wordpress.com/167/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/whiteboardjunkie.wordpress.com/167/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/whiteboardjunkie.wordpress.com/167/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/whiteboardjunkie.wordpress.com/167/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/whiteboardjunkie.wordpress.com/167/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/whiteboardjunkie.wordpress.com/167/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/whiteboardjunkie.wordpress.com/167/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/whiteboardjunkie.wordpress.com/167/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/whiteboardjunkie.wordpress.com/167/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/whiteboardjunkie.wordpress.com/167/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/whiteboardjunkie.wordpress.com/167/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/whiteboardjunkie.wordpress.com/167/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/whiteboardjunkie.wordpress.com/167/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=whiteboardjunkie.wordpress.com&amp;blog=4302397&amp;post=167&amp;subd=whiteboardjunkie&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://whiteboardjunkie.wordpress.com/2011/04/01/managing-spatial-data/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/e2a5bbed8e1b94c20be75e9da955120e?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">boni</media:title>
		</media:content>
	</item>
		<item>
		<title>@NamedQueries, IS NULL and a hack that saved yet another day.</title>
		<link>http://whiteboardjunkie.wordpress.com/2011/03/31/namedqueries-is-null-and-a-hack-that-saved-yet-another-day/</link>
		<comments>http://whiteboardjunkie.wordpress.com/2011/03/31/namedqueries-is-null-and-a-hack-that-saved-yet-another-day/#comments</comments>
		<pubDate>Thu, 31 Mar 2011 10:10:54 +0000</pubDate>
		<dc:creator>bonigv</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">https://whiteboardjunkie.wordpress.com/2011/03/31/namedqueries-is-null-and-a-hack-that-saved-yet-another-day/</guid>
		<description><![CDATA[Last week we discovered a problem after deploying our perfectly compliant JPA layer that uses hibernate on an Oracle Schema.  The issues concerned behavior of @NamedQueries.  Specifically some named queries with NULLable fields as part of WHERE Condition.  For example consider the following EjbQL Now consider the scenario where the API writer expected the following [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=whiteboardjunkie.wordpress.com&amp;blog=4302397&amp;post=166&amp;subd=whiteboardjunkie&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Last week we discovered a problem after deploying our perfectly compliant JPA layer that uses hibernate on an Oracle Schema.  The issues concerned behavior of @NamedQueries.  Specifically some named queries with NULLable fields as part of WHERE Condition.  For example consider the following EjbQL</p>
<p><pre class="brush: sql;">Select A From Magazine a Where a.month = :month and a.year= :year and a.type= :magazineType</pre></p>
<p>Now consider the scenario where the API writer expected the following behavior when one or more of the parameters in the WHERE CLAUSE is null.</p>
<p>1. year == null; return all records where YEAR IS NULL.</p>
<p>2. type == null; return all records where TYPE IS NULL.</p>
<p>It is not uncommon to come across such situations when the WHERE clause includes some NULLable fields.  The problem here is that SQL would expect the Query to read the following for Scenario 1 &amp; 2</p>
<p><pre class="brush: sql;">1. Select A From Magazine a Where a.month = :month and a.year is NULL and a.type= :magazineType</pre></p>
<p><pre class="brush: sql;">2. Select A From Magazine a Where a.month = :month and a.year= :year and a.type IS NULL</pre></p>
<p>Since we have established that either ‘year’ or ‘type’ can be null it is quite possible that both YEAR and TYPE are NULL.  Then the correct Query is supposed to read:</p>
<p><pre class="brush: sql;">3. Select A From Magazine a Where a.month = :month and a.year IS and a.type IS NULL</pre></p>
<p>This is going to create a problem for application developer since he is expected now to either maintain a bunch of named Queries or construct these EJBQLs dynamically so that the appropriate IS NULL substitutions takes the place of ‘= null’ locations.  Ideally I would have preferred developer handling such scenarios in code.  However this issue missed our thorough testing routines <span style="text-decoration:underline;">since the hibernate implementation of JPA Handler for MySQL seemed intelligent enough to handle the IS NULL conversions internally when one of the parameters passed was null.  However this is not the case with corresponding Oracle Dialect class</span>.  In our product there were close to 200 NamedQueries and we did not have enough time to make all those code changes and run the product through a complete test cycle.  Therefore we chose to introduce a Generic routine to check and handle this scenario.  This way the changes are central and will be well testable.</p>
<p>Expected behavior:</p>
<blockquote><p>Whenever system is asked to execute a named Query an internal routine will check whether any of the passed parameters is a null value.  If it is a null then then an appropriate Query conversion will be done to convert = null or !=null usage if any, to IS NULL or IS NOT NULL.  The resulting Query will then be executed as a dynamic Query.</p></blockquote>
<p>The design had ensured there were only two places in the system through which a @NamedQuery can go for execution.  This part of the code I modified to check for null values in any incoming ‘Named Parameter’.  If one or more found the Query is then translated into a dynamic query with Equivalent IS NULL / IS NOT NULL Conversions.</p>
<p>Problem 1 : Given a NamedQuery’s name, Get the corresponding Ejbql  query string.</p>
<p><pre class="brush: java;">

	public NamedQueries getNamedQueries(){
		NamedQueries queries = persistentClass.getAnnotation(NamedQueries.class);
		return queries;
	}
	
	protected String getQuery(String queryName){
		NamedQueries queries = getNamedQueries();
		if (null == queries){
			return null;
		}
		NamedQuery[] namedQueries = queries.value();
		if (null == namedQueries || namedQueries.length &lt;= 0) return null;
		for (NamedQuery aQuery : namedQueries){
			if (queryName.equals(aQuery.name()))
				return aQuery.query();
		}
		return null;
	}


</pre></p>
<p></span></p>
<p>In this code, instance nariable ‘persistentClass’ points to the Type (Class) of the main Entity.  All my Named Queries for an Entity bean are defined within that Entity bean (as annotations).  This design consistency coupled with a bit of Java Reflection gets me the Query String fairly easily.  Afterwards a risky but effective code scanning ensures correct Query translation.</p>
<p><pre class="brush: java;">
	protected String convertEqualToNullToIsNull(String query, String paramemeterIdentifier) {
		if (StringUtils.indexOf(query, &quot;!= :&quot; + paramemeterIdentifier) != StringUtils.INDEX_NOT_FOUND){
			return StringUtils.replaceOnce(query, &quot;!= :&quot; + paramemeterIdentifier, &quot; IS NOT NULL &quot;);
		}
		else if (StringUtils.indexOf(query, &quot;= :&quot; + paramemeterIdentifier) != StringUtils.INDEX_NOT_FOUND){
			return StringUtils.replaceOnce(query, &quot;= :&quot; + paramemeterIdentifier, &quot; IS NULL &quot;);
		} 
		return query;
		
	}

</pre></p>
<p>The wrapper API call that ties together all these together is this:</p>
<p><pre class="brush: java;">
	protected Query handleNullParameterConversions(final String name, Map&lt;String , ? extends Object&gt; params){
		logger.debug(&quot;IS NULL Conversion For Query : &quot; + name);
		String parameterizedQuery = getQuery(name);
		logger.debug(parameterizedQuery);
		//correct query with necessary IS NULL / IS NOT NULL
		if (null != params){
			Set&lt;String&gt; keys = params.keySet();
			for (String aKey : keys){
				Object value = params.get(aKey);
				if (null == value){
					logger.debug(&quot;IS NULL CONVERSION Required For:&quot; + aKey);
					parameterizedQuery = convertEqualToNullToIsNull(parameterizedQuery, aKey);
					logger.debug(parameterizedQuery);
				}
			}
		}
		logger.debug(&quot;Final Query:&quot;+ parameterizedQuery);
		javax.persistence.Query query = getEntityManager().createQuery(parameterizedQuery);
		
		for (final Map.Entry&lt;String, ? extends Object&gt; param : params
				.entrySet()) {
			if (null == param.getValue()) continue;
			query.setParameter(param.getKey(), param.getValue());
		}
		
		return query;
	}
</pre><br />
The following line ensures that the resulting queries are not malformed.  This check makes the code well testable.</p>
<p><pre class="brush: java;">
javax.persistence.Query query = getEntityManager().createQuery(parameterizedQuery);
</pre></p>
<p>For such hack codes it will be criminal to commit to a large codebase without adequate test case.  I wanted to keep the test case a bit future proof.  So what I needed was a test that can scan and identify all @NamedQueries, then pick all the named parameters in the where clause and then test for each and every parameter combination.  For example in our  original Query,</p>
<p><pre class="brush: sql;">Select A From Magazine a Where a.month = :month and a.year= :year and a.type= :magazineType</pre></p>
<p>The three named parameters creates 7 potential combinations (Any 3 IS NULL [3], Any 2 IS NULL [3], All 3 IS NULL [1]).  For n parameter Query the number of test cases are</p>
<p>N = nC1 + nC2 + nC3 + … + nCn</p>
<p>So first I wrote a trivial package scanner to identify all the DaoImpls that need to be tested.  Then I passed each to the following method to test validity of resulting query for each parameter combinations.</p>
<p><pre class="brush: java;">
	private void testNamedQueriesOfDao(GenericJpaDaoImpl dao){
		logger.debug(&quot;TESTING:&quot; + dao.getClass().getName());
		dao.setEntityManager(getEntityManager());
		NamedQueries queries = dao.getNamedQueries();
		if (null == queries){
			logger.debug(&quot;No Named Queries found in:&quot; + dao.getClass().getName());
			return;
		}
		NamedQuery[] namedQueries = queries.value();
		if (null == namedQueries) fail(&quot;Named Queries are null&quot;);
		for (NamedQuery aQuery : namedQueries){
			String query = aQuery.query();
			logger.debug(&quot;Original Query:&quot; + query);
			String[] variables = dao.getNamedParametersInSequence(query);
			List&lt;String[]&gt; allCombinations = getallCombinationsFor(variables);
			for (String[] aCombination : allCombinations){
				combinations = combinations + 1;
				logger.info(&quot;Parameter(s):&quot; + toString(aCombination));
				Map&lt;String, Object&gt; parameters = new HashMap&lt;String, Object&gt;(); 
				for (String paramemeterIdentifier : aCombination){
					parameters.put(paramemeterIdentifier, null);
				}
				Query convertedQuery = dao.handleNullParameterConversions(aQuery.name(), parameters) ;
			}
			queryCount = queryCount + 1;
		}
		
	}
</pre></p>
<p>I also wrote not so elegant a method to fetch all combinations (sub sets actually) of a given sub array.</p>
<p><pre class="brush: java;">
	private List&lt;String[]&gt; getallCombinationsFor(String[] variables) {
		int length = variables.length;
		List&lt;String[]&gt; stringCombinations = new ArrayList&lt;String[]&gt;();
		if (length &lt;= 1){
			stringCombinations.add(variables);
			return stringCombinations;
		}
		String maxValue = &quot;&quot;;
		for (int i = (length - 1) ; i &gt;=0 ; i--){
			maxValue = maxValue + i;
		}
		logger.debug(&quot;Max Value for Base :&quot; + length + &quot; = &quot; + maxValue);
		int maxIntValue = Integer.parseInt(maxValue,length);
		logger.debug(&quot;Equivalent Max Value in decimal : = &quot; + maxIntValue);
		List&lt;String&gt; combinations = new ArrayList&lt;String&gt;();
		for (int i = 0 ; i &lt;= maxIntValue; i++){
			String aCombination = Integer.toString(i, length);
			if (isUnique(aCombination,maxValue.toCharArray())){
				combinations.add(aCombination);
				if (aCombination.indexOf(&quot;0&quot;) == -1){
					combinations.add(&quot;0&quot; + aCombination);
				}
			}
		}
		Collections.sort(combinations);
		for (String aCombination : combinations){
			logger.debug(aCombination);
			char[] positions = aCombination.toCharArray();
			List&lt;String&gt; aStringCombination = new ArrayList&lt;String&gt;();
			for (char aPosition : positions){
				aStringCombination.add(variables[Integer.valueOf(String.valueOf(aPosition))]);
			}
			String [] justAdded = null;
			stringCombinations.add(justAdded = aStringCombination.toArray(new String[positions.length]));
			String newlyAdded = &quot;&quot;;
			for (String aString : justAdded){
				newlyAdded = newlyAdded + aString + &quot;,&quot;;
			}
			logger.debug(newlyAdded);
		}
		return stringCombinations;
	}

</pre></p>
<p>All these together gave me enough confidence that the code base is sufficiently fool proof.  However as part of the next minor version release we are going to clean up the code base and make appropriate Query handling to incorporate IS NULL / IS NOT NULL deliberately.  Automatic interpretations of logic is trouble in long term maintenance.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/whiteboardjunkie.wordpress.com/166/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/whiteboardjunkie.wordpress.com/166/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/whiteboardjunkie.wordpress.com/166/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/whiteboardjunkie.wordpress.com/166/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/whiteboardjunkie.wordpress.com/166/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/whiteboardjunkie.wordpress.com/166/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/whiteboardjunkie.wordpress.com/166/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/whiteboardjunkie.wordpress.com/166/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/whiteboardjunkie.wordpress.com/166/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/whiteboardjunkie.wordpress.com/166/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/whiteboardjunkie.wordpress.com/166/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/whiteboardjunkie.wordpress.com/166/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/whiteboardjunkie.wordpress.com/166/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/whiteboardjunkie.wordpress.com/166/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=whiteboardjunkie.wordpress.com&amp;blog=4302397&amp;post=166&amp;subd=whiteboardjunkie&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://whiteboardjunkie.wordpress.com/2011/03/31/namedqueries-is-null-and-a-hack-that-saved-yet-another-day/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/e2a5bbed8e1b94c20be75e9da955120e?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">boni</media:title>
		</media:content>
	</item>
		<item>
		<title>Dabbling with Google Chrome Extensions</title>
		<link>http://whiteboardjunkie.wordpress.com/2011/02/06/dabbling-with-google-chrome-extensions/</link>
		<comments>http://whiteboardjunkie.wordpress.com/2011/02/06/dabbling-with-google-chrome-extensions/#comments</comments>
		<pubDate>Sun, 06 Feb 2011 05:39:28 +0000</pubDate>
		<dc:creator>bonigv</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Dailymile]]></category>
		<category><![CDATA[DHTML]]></category>
		<category><![CDATA[Extensions]]></category>
		<category><![CDATA[Google Chrome]]></category>
		<category><![CDATA[JSON]]></category>

		<guid isPermaLink="false">https://whiteboardjunkie.wordpress.com/2011/02/06/dabbling-with-google-chrome-extensions/</guid>
		<description><![CDATA[Four days back I started with a ‘wish’ for an easier access to one of my favorite online communities – Dailymile.&#160; The use case was simple yet powerful.&#160; I make dozens of visit to this community everyday to checkout what my friends are up to with their respective workouts; be it a randonneuring ride of [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=whiteboardjunkie.wordpress.com&amp;blog=4302397&amp;post=164&amp;subd=whiteboardjunkie&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Four days back I started with a ‘wish’ for an easier access to one of my favorite online communities – <a href="http://www.dailymile.com">Dailymile</a>.&#160; The use case was simple yet powerful.&#160; I make dozens of visit to this community everyday to checkout what my friends are up to with their respective workouts; be it a <a href="http://en.wikipedia.org/wiki/Randonneuring" target="_blank">randonneuring</a> ride of 400km, a fast paced 10km run, climbing intervals from Sri Lanka or general random thoughts or pictures my buddies wanted to share.&#160; I also am super curious to know what they have to say about my own workouts.&#160; I must confess that the current process is not so difficult – all I need to do is get to the webpage and read through.&#160; But here I was, four days back, wishing there were a little icon on my browser toolbar clicking which will show a nifty drop down window with clickable snippets of the details I am interested in.&#160; Something like this.</p>
<p><a href="http://whiteboardjunkie.files.wordpress.com/2011/02/image.png"><img style="border-bottom:0;border-left:0;display:inline;margin-left:0;border-top:0;margin-right:0;border-right:0;" title="image" border="0" alt="image" align="left" src="http://whiteboardjunkie.files.wordpress.com/2011/02/image_thumb.png?w=252&#038;h=213" width="252" height="213" /></a> </p>
<p>Yes! Google made its Extension development <a href="http://code.google.com/chrome/extensions/getstarted.html" target="_blank">so easy</a> for an UI layman like me that exactly 4 hours after the idea I had version 1.0 out.&#160; I was able to easily <a href="http://www.dailymile.com/people/bonigv/entries/5159637" target="_blank">distribute it to fellow chrome users</a> in the dailymile community and get their valuable feedback.&#160; As we speak the version that we see on the left is <a href="http://www.dailymile.com/people/bonigv/entries/5203532" target="_blank">1.0.4</a>, with some feature additions and bugs fixed.&#160; Since I was brimming with ideas to make this extension better but not having enough time at hand to sit down and code it through I have decided to open source it through <a href="http://code.google.com/p/whiteboardjunkie/source/browse/#svn%2Ftrunk%2Fdailymile" target="_blank">my regular Google Code channel</a> and see whether someone else can help me out.&#160; Are you listening?</p>
<p> A chrome extension is a powerful application that runs within the security sandbox of the browser.&#160; As part of every chrome browser distribution, Google has packaged in all tools one needs to develop and distribute extensions.&#160; These tools include a powerful packaging mechanism, <a href="http://code.google.com/chrome/extensions/api_index.html" target="_blank">rich set of extensions APIs</a>, a debugger, a resource tracker through which you can inspect the DOM and various HTTP requests.</p>
<p>Chrome is the most comprehensive HTML 5 reference implementation out there with supports for local storage, audio, video, Canvas and many more.&#160; The extensions API empowers you to take advantage of all these services to make your software as useful and intuitive to the end user as you want.&#160; Clearly imagination is the limit in what an extension can do.</p>
<p>In this specific dailymile extension’s case all I am doing it to make an <a href="http://www.w3.org/TR/XMLHttpRequest/" target="_blank">XMLHttpRequest</a> to a hosted <a href="https://api.dailymile.com/" target="_blank">dailymile API</a> that returns the data I want in a JSON format.&#160; Through a JavaScript callback I then parse and dynamically paint the user interface.&#160; If my user wants to see more details of the workout (or a note or an image), a click will open the data in its full glory on a chrome tab.&#160; To achieve all this all I had to do was to write a <a href="http://code.google.com/chrome/extensions/manifest.html" target="_blank">manifest.json</a> file that defines which are all the web resources I am going to access, what all browser permission I would need and finally what is the starting page of my extension.&#160; While developing the extension browser allows me to load, test and debug from my development directory.&#160; Once fully satisfied with the product I can package the extension into a .crx distributable using browser tools itself.&#160; Once I have a CRX file I could either choose to <a href="https://chrome.google.com/webstore/developer/dashboard" target="_blank">host it on google chrome extensions library</a> (to sign up as chrome extensions developer there is a nominal USD5 fee) or host on your own.&#160; I decided to host it on my own through my <a href="http://code.google.com/p/whiteboardjunkie/downloads/list" target="_blank">Google Code page</a>.&#160; The packaging has a powerful addition of <a href="http://code.google.com/chrome/extensions/autoupdate.html" target="_blank">auto updating</a>.&#160; Once enabled all I need to do to get a newer version of my software to my current users is to change a file on an HTTP location that announces a new version is ready for download.&#160; Chrome will check this file periodically and auto update.&#160; I setup an auto updating for this extension as well.&#160; Overall a very neat development, packaging and distribution system.&#160; Very impressed.</p>
<blockquote><p>Source code available @     <br />svn checkout <a href="https://whiteboardjunkie.googlecode.com/svn/trunk/dailymile">https://whiteboardjunkie.googlecode.com/svn/trunk/dailymile</a>      <br />Browse Source code <a href="http://code.google.com/p/whiteboardjunkie/source/browse/#svn/trunk/dailymile">here</a></p>
</blockquote>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/whiteboardjunkie.wordpress.com/164/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/whiteboardjunkie.wordpress.com/164/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/whiteboardjunkie.wordpress.com/164/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/whiteboardjunkie.wordpress.com/164/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/whiteboardjunkie.wordpress.com/164/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/whiteboardjunkie.wordpress.com/164/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/whiteboardjunkie.wordpress.com/164/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/whiteboardjunkie.wordpress.com/164/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/whiteboardjunkie.wordpress.com/164/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/whiteboardjunkie.wordpress.com/164/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/whiteboardjunkie.wordpress.com/164/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/whiteboardjunkie.wordpress.com/164/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/whiteboardjunkie.wordpress.com/164/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/whiteboardjunkie.wordpress.com/164/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=whiteboardjunkie.wordpress.com&amp;blog=4302397&amp;post=164&amp;subd=whiteboardjunkie&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://whiteboardjunkie.wordpress.com/2011/02/06/dabbling-with-google-chrome-extensions/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/e2a5bbed8e1b94c20be75e9da955120e?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">boni</media:title>
		</media:content>

		<media:content url="http://whiteboardjunkie.files.wordpress.com/2011/02/image_thumb.png" medium="image">
			<media:title type="html">image</media:title>
		</media:content>
	</item>
		<item>
		<title>Random Thought #2</title>
		<link>http://whiteboardjunkie.wordpress.com/2011/01/27/random-thought-2/</link>
		<comments>http://whiteboardjunkie.wordpress.com/2011/01/27/random-thought-2/#comments</comments>
		<pubDate>Thu, 27 Jan 2011 11:38:36 +0000</pubDate>
		<dc:creator>bonigv</dc:creator>
				<category><![CDATA[random musings]]></category>

		<guid isPermaLink="false">http://whiteboardjunkie.wordpress.com/2011/01/27/random-thought-2/</guid>
		<description><![CDATA[To everyone who runs online campaigns to &#8216;Save&#8217; stuff I have a word for advice, Ctrl-S.<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=whiteboardjunkie.wordpress.com&amp;blog=4302397&amp;post=155&amp;subd=whiteboardjunkie&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<blockquote><p><em>To everyone who runs online campaigns to &#8216;Save&#8217; stuff I have a word for advice, Ctrl-S.</em></p>
</blockquote>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/whiteboardjunkie.wordpress.com/155/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/whiteboardjunkie.wordpress.com/155/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/whiteboardjunkie.wordpress.com/155/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/whiteboardjunkie.wordpress.com/155/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/whiteboardjunkie.wordpress.com/155/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/whiteboardjunkie.wordpress.com/155/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/whiteboardjunkie.wordpress.com/155/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/whiteboardjunkie.wordpress.com/155/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/whiteboardjunkie.wordpress.com/155/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/whiteboardjunkie.wordpress.com/155/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/whiteboardjunkie.wordpress.com/155/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/whiteboardjunkie.wordpress.com/155/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/whiteboardjunkie.wordpress.com/155/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/whiteboardjunkie.wordpress.com/155/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=whiteboardjunkie.wordpress.com&amp;blog=4302397&amp;post=155&amp;subd=whiteboardjunkie&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://whiteboardjunkie.wordpress.com/2011/01/27/random-thought-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/e2a5bbed8e1b94c20be75e9da955120e?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">boni</media:title>
		</media:content>
	</item>
		<item>
		<title>Random Thought #1</title>
		<link>http://whiteboardjunkie.wordpress.com/2011/01/24/random-thought-1/</link>
		<comments>http://whiteboardjunkie.wordpress.com/2011/01/24/random-thought-1/#comments</comments>
		<pubDate>Mon, 24 Jan 2011 06:24:20 +0000</pubDate>
		<dc:creator>bonigv</dc:creator>
				<category><![CDATA[random musings]]></category>
		<category><![CDATA[facebook]]></category>
		<category><![CDATA[random]]></category>

		<guid isPermaLink="false">http://whiteboardjunkie.wordpress.com/2011/01/24/random-thought-1/</guid>
		<description><![CDATA[World comprises of only two types of events; those that are shared on facebook and those that are unworthy of.<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=whiteboardjunkie.wordpress.com&amp;blog=4302397&amp;post=151&amp;subd=whiteboardjunkie&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<blockquote><p>World comprises of only two types of events; those that are shared on facebook and those that are unworthy of.</p>
</blockquote>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/whiteboardjunkie.wordpress.com/151/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/whiteboardjunkie.wordpress.com/151/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/whiteboardjunkie.wordpress.com/151/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/whiteboardjunkie.wordpress.com/151/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/whiteboardjunkie.wordpress.com/151/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/whiteboardjunkie.wordpress.com/151/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/whiteboardjunkie.wordpress.com/151/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/whiteboardjunkie.wordpress.com/151/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/whiteboardjunkie.wordpress.com/151/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/whiteboardjunkie.wordpress.com/151/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/whiteboardjunkie.wordpress.com/151/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/whiteboardjunkie.wordpress.com/151/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/whiteboardjunkie.wordpress.com/151/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/whiteboardjunkie.wordpress.com/151/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=whiteboardjunkie.wordpress.com&amp;blog=4302397&amp;post=151&amp;subd=whiteboardjunkie&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://whiteboardjunkie.wordpress.com/2011/01/24/random-thought-1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/e2a5bbed8e1b94c20be75e9da955120e?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">boni</media:title>
		</media:content>
	</item>
	</channel>
</rss>
