Learnings from the Past: Hibernate related Best Practices

March 18th, 2010

Back to blogging after a gap of 8 months. I have been working on some cool optimization stuff related to Hibernate. Here is a brief summary of the best practices i have come across on this and past assignments involving Hibernate.

# Practice What? Why? How?
1 Use Lazy Load Don’t load objects until you need to use them. Queries involve lesser joins, so become faster By Using @Lazy annotation for simple attributes and LazyCollection for Collections
2 Use allocationSize>1 for Sequences Use an allocationSize value of 50 or 100 instead of the default allocationSize of 1 Avoids frequent db access to fetch next sequence value By using @allocationSize=50, Hibernate will generate the next sequence values for 50 invocations before hitting the db again.
3 Keep big character/byte fields in a seperate entity Seperate big fields like blob, clob, varchar(500), etc., into a seperate entity called <enityName>Details Avoids time spent in fetching data for these fields on listing screens which do not typically display this information By using a one-to-one mapping with the details object and keeping the direction of association from main->detail
4 Use Hibernate Caching Using Hibernate Second Level cache and Query cache to store frequently used objects/results Avoids hitting the db for frequently used information Turn on Hibernate Second level cache and Quey cache

Learnings from the past: Writing jMock Unit tests in 6 simple steps

July 17th, 2009

Do you ever come across a situation where your Unit test ends up testing more than what a unit is supposed to be?  Does your unit test end up looking more like an Integration test? Does it only run within a container? Does it depend on external things like records in a table, entries in ldap or data in some files? If your answer to any of the above questions is yes, then jMock could be the solution to your unit testing problem.

jMock is a library for testing Java code with mock objects. It sits on top of your existing unit testing framework (eg.Junit). It allows you to mock out interactions between objects hence preventing external dependencies.

You can write a jMock unit test using 6 simple steps:

  1. Initialize input:-  This is where you initialize all static data that will be used for the current unit test.
  2. Initialize mocks:- Mock out your external dependencies here.
  3. Setup expectations:- After you create the mock objects, you need to specify what interactions you are expecting to have with them. If the interactions dont happen as you expected, the test fails.
  4. Initialize subject:- Subject is the object that you are trying to unit test. You will create an instance of the object and initialize it here.
  5. Invoke Subject:- Call the method to be tested with static input.
  6. Assert:- Make sure output is as expected and all expectations set on the mocks have been satisfied.

Here is an example of how to go about using jMock:-
AccountService exposes a “getAccountInformation” method which we want to unti test. This method first validates the input, authenticates credentials with ldap and then fetches the account information from the database. It uses a DB accessor and ldap authenticator to do so. First lets look at the AccountService:

package com.amarphadke.jmockdemo.service;
 
import com.amarphadke.jmockdemo.accessor.Authenticator;
import com.amarphadke.jmockdemo.accessor.DataAccessor;
import com.amarphadke.jmockdemo.domain.Account;
import com.amarphadke.jmockdemo.exception.DomainException;
import com.amarphadke.jmockdemo.exception.ValidationException;
import com.amarphadke.jmockdemo.transport.AccountInformation;
import com.amarphadke.jmockdemo.transport.AccountInformationRequest;
 
public class AccountService {
	private DataAccessor dataAccessor;
	private Authenticator authenticator;
 
	public AccountService(DataAccessor dataAccessor, Authenticator authenticator) {
		super();
		this.dataAccessor = dataAccessor;
		this.authenticator = authenticator;
	}
 
	public AccountInformation getAccountInformation(
			AccountInformationRequest request) throws ValidationException, DomainException {
		validateInput(request);
		Account account = dataAccessor.fetchAccount(request.getAccountId());
		AccountInformation accountInformation = convertDomainObjectToTransportObject(account);
		return accountInformation;
	}
 
	private AccountInformation convertDomainObjectToTransportObject(Account account) {
		AccountInformation accountInformation = new AccountInformation(account
				.getAccountId(), account.getAccountType(), account
				.getLastAccessedDate(), account.getFirstName(), account
				.getLastName(), account.getBalance());
		return accountInformation;
	}
 
	private void validateInput(AccountInformationRequest request)
			throws ValidationException {
		if (request.getAccountId() <= 0) {
			throw new ValidationException(1, "Invalid Account Id");
		}
 
		if (request.getUsername() == null) {
			throw new ValidationException(2, "Invalid Username");
		}
 
		if (request.getPassword() == null) {
			throw new ValidationException(3, "Invalid Password");
		}
 
		if (!authenticator.isValidCredential(request.getAccountId(), request
				.getUsername(), request.getPassword())) {
			throw new ValidationException(4, "Invalid Credentails for Account");
		}
	}
}

Now, lets take a look at the unit test:

package com.amarphadke.jmockdemo.service;
 
import java.util.Calendar;
import java.util.Date;
 
import junit.framework.TestCase;
 
import org.jmock.Expectations;
import org.jmock.Mockery;
 
import com.amarphadke.jmockdemo.accessor.Authenticator;
import com.amarphadke.jmockdemo.accessor.DataAccessor;
import com.amarphadke.jmockdemo.domain.Account;
import com.amarphadke.jmockdemo.exception.DomainException;
import com.amarphadke.jmockdemo.exception.ValidationException;
import com.amarphadke.jmockdemo.service.AccountService;
import com.amarphadke.jmockdemo.transport.AccountInformation;
import com.amarphadke.jmockdemo.transport.AccountInformationRequest;
 
public class AccountServiceTest extends TestCase {
	private Mockery context = new Mockery();
 
	/*
	 * Test for AccountService.getAccountInformation(AccountInformationRequest)
	 * with valid input.
	 */
	public void testGetAccountInformationWithValidInput()
			throws ValidationException, DomainException {
		// Step 1: Initialize input
		final long accountId = 123456789;
		final String username = "jmockDemoUser";
		final String password = "encryptedPassword";
		String accountType = "Checking";
		Double balance = new Double(100000000.99);
		String firstName = "Richie";
		String lastName = "Rich";
		Date lastAccessedDate = Calendar.getInstance().getTime();
 
		final AccountInformationRequest request = new AccountInformationRequest(
				accountId, username, password);
		final Account account = new Account(accountId, accountType,
				lastAccessedDate, firstName, lastName, balance, null, null);
		final AccountInformation accountInformation = new AccountInformation(
				accountId, accountType, lastAccessedDate, firstName, lastName, balance);
 
		// Step 2: Initialize mocks
		final DataAccessor dataAccessor = context.mock(DataAccessor.class);
		final Authenticator authenticator = context.mock(Authenticator.class);
 
		// Step 3: Setup Expectations
		context.checking(new Expectations() {
			{
				oneOf(authenticator).isValidCredential(accountId, username, password);
				will(returnValue(true));
 
				oneOf(dataAccessor).fetchAccount(accountId);
				will(returnValue(account));
			}
		});
 
		// Step 4: Initialize Subject
		AccountService accountService = new AccountService(dataAccessor,
				authenticator);
 
		// Step 5: Invoke Subject
		AccountInformation actualAccountInformation = accountService
				.getAccountInformation(request);
 
		// Step 6: Assert
		assertEquals(accountInformation, actualAccountInformation);
		context.assertIsSatisfied();
	}
}

Complex Programs with Simple code?

April 5th, 2009

Chessboard

After my last post, i was wondering if really complex programs/systems/applications could be written using simple, easy-to-understand code. Code that does not span more than 10 lines a method.

The most complex program that i could think of to experiment with was a Chess server – an app that would evaluate a position and suggest the “next best” move. This app would have a set of rules which would determine how good a position is. All possible “next moves” will be stored in a tree and evaluated on the position they end up with after ‘n’ moves. For more information->http://en.wikipedia.org/wiki/Computer_chess

I have created an open-source project on sourceforge.net for this activity->http://sourceforge.net/projects/javachessserver. What i have in there so far are the Chess domain objects, Rule interface, several skeleton rules and one concrete rule->CheckmateRule

I would be interested in knowing what you think about this project. If you are interested in joining this project and wish to explore this idea, i would be happy to include you.

Code quality and cyclomatic complexity

February 21st, 2009

I recently came across this article on code quality and cyclomatic complexity. Found it interesting and so thought of sharing.
Do the following phrases sound familiar to you?  They surely sounded familiar to me.

Sure, it’s a bit confusing (at first), but look how extensible it is!!

It’s confusing to you because you obviously don’t understand patterns.

Internet Connection monitoring script for the GNOME panel

January 14th, 2009

I was having issues with my Internet connection lately. My connection used to drop after using it for a while and the router used to lose the IP address. I decided to write a simple script that would sit on the GNOME panel and tell me the current state of my Internet connection.

I found a Java-GNOME library that would allow me to program for the GTK using Java. I wrote a simple Java program that would connect to www.google.com and check if it can get the content. It would do this periodically (every 10s) with a connection timeout of 5s. If the site is reachable, the program would show a green icon in the notification area. If not, it will show a red blinking icon.

To use it here is what you need to do:

  1. Extract this archive to a scripts sub-directory in your home folder.
  2. In your .profile, add the following lines at the end
  3. #Invoking Internet connection monitoring script
    $HOME/scripts/networkmonitor.sh

Internet Connection is upInternet Connection is up

Just log in again and you should see a green/red icon in your GNOME panel notification area. In case you face any problems try running $HOME/scripts/network_monitor.sh from a terminal and also check the sh file executable permission.

Web Server’s virtual directory and network drive

January 4th, 2009

I encountered a problem today trying to create an Apache Tomcat virtual directory that pointed to a mapped network drive.
Found out that this was due to the fact that Tomcat was running as a Service with a local account. It was not able to see the network drive since that used to get mapped with the logon scripts.
Solution was to either run Tomcat Service my account or run the startup script from the command line. Tried the command line option and it recognized the network drive.
This is probably true for any web server that runs as a service.

Update on the GreaseMonkey Custom homepage script

December 31st, 2008

As mentioned at the end of my last post, i continued exploring the idea of having a custom homepage with content aggregated from different sites on the client side. I tried using the about:blank page of Firefox as the starting point on which to build content, but could not succeed. After googling on this issue i found that doing this is not a good idea, since many sites use the about:blank page to create an empty iframe and then set its source. I also read that doing this could make your site prone to XSS attacks. Maybe thats why Firefox blocked access to it (?)

Then i thought about using Google as my start page. I decided to add a 10-day weather forecast at the top of the screen. Had to do some screenscraping to get this content. Next came news. I decided to use the RSS feeds for this purpose. I came across a site->www.webrss.com that will parse the xml content of the RSS feed and provide it via a javascript call. It also gives you the option of converting the rss feed to html, php or asp call. I added feeds from Google News, Times of India, CNN and BBC. Here is how it looks now (click on the image to get a bigger view):

Custom homepage using GreaseMonkey scripts

I decided to hold off on adding mail since that requires authentication.

Here’s the code so far:

// ==UserScript==
// @name           Homepage
// @namespace      http://amarphadke.com/userscripts/customhomepage
// @include        http://www.google.co.in
// @include        http://www.google.com
// ==/UserScript==
 
GM_xmlhttpRequest({
    method: 'GET',
    url: 'http://www.weather.com/outlook/travel/businesstraveler/tenday/44130?from=36hr_topnav_business',
    headers: {
        'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey',
        'Accept': 'application/xml,text/xml',
    },
    onload: function(responseDetails) {
        var startIndex = responseDetails.responseText.indexOf('<div id="tenDayWrap">');
        var endIndex = responseDetails.responseText.indexOf('<div id="TFbuttonB">', startIndex);
        var reqdString = responseDetails.responseText.substring(startIndex,endIndex);
        reqdString = reqdString.replace(/&deg;/g, "==deg==");
        reqdString = reqdString.replace(/&nbsp;/g, " ");
        reqdString = reqdString.replace(/<br>/g, "<br/>");
        reqdString = reqdString.replace(/\"\" width/g, "\" width");
        reqdString = closeImgTags(reqdString);
 
	var parser = new DOMParser();
	var dom = parser.parseFromString(reqdString,"application/xml");
	var tdWraps = getArrayOf(dom,"tdWrap");
 
	var weatherTableHTML = "<table border='0' cellspacing='0' cellpadding='0' width='100%' style='font-size:11px;background-image:url(http://i.imwx.com/web/common/backgrounds/tenday_bkgd.jpg)'><tr>";
 
	for(var i=0;i<tdWraps.length;i++){
		var divs = tdWraps[i].getElementsByTagName("div");
		var dayOfWeek, date,imgSrc, imgText, high, low ;
		for(var j=0;j<divs.length;j++){
			if(divs[j].getAttribute("class")=="tdDate"){
				dayOfWeek = getDayOfWeek(divs[j]);
				date = getDate(divs[j]);
			}
			if(divs[j].getAttribute("class")=="tdForecast"){
				imgSrc = getImageSrc(divs[j]);
				imgText = getImageText(divs[j]);
			}
			if(divs[j].getAttribute("class")=="tdTemps"){
				high = getHighTemp(divs[j]);
				low = getLowTemp(divs[j]);
			}
		}
 
		weatherTableHTML+=renderCol(dayOfWeek, date,imgSrc, imgText, high, low);
	}
 
	weatherTableHTML+="</tr></table>";
 
 
	document.body.innerHTML = "";
	document.title="It's my custom homepage";
	document.body.setAttribute("style","margin:0px;");
 
	var weatherDiv = document.createElement('div');
	weatherDiv.innerHTML = "<div style='text-align:center;font-weight:bold;font-weight:14px;font-family:Verdana;background-image:url(http://i.imwx.com/web/common/backgrounds/tenday_bkgd.jpg);'>Weather</div>";
 
	var newElement = document.createElement('div');
	newElement.setAttribute("style","text-align:center;vertical-align:center;font-weight:bold;font-family:Verdana;");
	newElement.innerHTML = weatherTableHTML;
 
	weatherDiv.appendChild(newElement);
	document.body.appendChild(weatherDiv);
 
	var newsDiv = document.createElement('div');
	newsDiv.innerHTML = "<div style='text-align:center;font-weight:bold;font-weight:14px;font-family:Verdana;background-image:url(http://i.imwx.com/web/common/backgrounds/tenday_bkgd.jpg);'>News</div>";
	addWebRSSFeed(newsDiv, 7885);
	addWebRSSFeed(newsDiv, 7887);
	addWebRSSFeed(newsDiv, 7888);
	addWebRSSFeed(newsDiv, 7889);
 
	document.body.appendChild(newsDiv);
    }
});
 
function addWebRSSFeed(parentDiv, feedId){
	var feed = document.createElement('iframe');
    	feed.setAttribute("src", "http://www.webrss.com/get_mysite.php?mysiteId="+feedId);
    	feed.setAttribute("width","300px");
    	feed.setAttribute("height","400px");
    	feed.setAttribute("frameborder","0");
    	parentDiv.appendChild(feed);
}
 
function getArrayOf(parentNode, tdClassName){
	var tdWraps = new Array(0);
	var divArrays = parentNode.getElementsByTagName("div");
	for(var i=0;i<divArrays.length;i++){
		if(divArrays[i].getAttribute("class")==tdClassName){
			tdWraps[tdWraps.length] = divArrays[i];
		}
	}
	return tdWraps;
}
 
function closeImgTags(string){
	var updatedString = "";
	var lastIndexOfImg=0;
	var endBracketIndex = 0;
	while((lastIndexOfImg=string.indexOf("<img ", lastIndexOfImg+1)) != -1){
		updatedString+= string.substring(endBracketIndex, lastIndexOfImg);
		var endBracketIndex = string.indexOf(">", lastIndexOfImg);
		updatedString+= string.substring(lastIndexOfImg, endBracketIndex)+"/";
	}
	updatedString+= string.substring(endBracketIndex);
	return updatedString;
}
 
function getDayOfWeek(tdDateDiv){
	return tdDateDiv.getElementsByTagName("a")[0].firstChild.textContent;
}
 
function getDate(tdDateDiv){
	return tdDateDiv.getElementsByTagName("p")[0].lastChild.textContent;
}
 
function getImageSrc(tdForecast){
	return tdForecast.getElementsByTagName("img")[0].getAttribute("src");
}
 
function getImageText(tdForecast){
	return tdForecast.getElementsByTagName("p")[0].lastChild.textContent;
}
 
function getHighTemp(tdTemps){
        return tdTemps.getElementsByTagName("strong")[0].textContent.replace(/==deg==/g,"&deg; F");
}
 
function getLowTemp(tdTemps){
        return tdTemps.getElementsByTagName("p")[0].lastChild.textContent.replace(/==deg==/g,"&deg; F");
}
 
 
function renderCol(dayOfWeek, date,imgSrc, imgText, high, low){
        return "<td width='10%' align='center'>"+dayOfWeek+","+date+"<div style='background-color:white;width:81px;height:75px; border: 1px solid #F0EBD5;'><br/><img src='"+imgSrc+"' border='0'/><br/></div>"+imgText+"<br/>"+high+", "+low+"</td>";
}

After reading this post, you may ask why build this when there are sites that offer this kind of feature for free (Eg., Google, Yahoo, BBC, etc.,). My answer to that would be ->This customization is being done on the client side. You have more flexibility in terms of what content to display and from what source. Can you imagine iGoogle giving you a widget to read your Yahoo emails? or BBC presenting a news widget on its portal the content of which comes from Times of India? or how easily can you convince Google to build a 10-day weather forecast widget for you on iGoogle?

Playing around with Greasemonkey

December 26th, 2008

Greasemonkey is an extension for firefox which allows users to write custom scripts that can change the UI and behavior of web-pages.

I tried writing a small script that would change the layout of an RSS-feed aggregator site->www.waywework.it. Here is a screen shot of how it looks after applying the custom Greasemonkey script.
Modified layout of www.waywework.it

And here is the script:

 
// ==UserScript==
// @name           WayWeWork.it
// @namespace      http://amarphadke.com/userscripts/waywework.it
// @include        http://waywework.it/
// ==/UserScript==
 
var blogsAndPosts = new Array();
var contentDivCollection = document.evaluate(
    "//div[@class='left left_column']",
    document,
    null,
    XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
    null);
if(contentDivCollection.snapshotLength==1){
	document.write("<html><head>");
	document.write("</head><body bgcolor='#F1F4E2'>");
 
	document.write('<div align="center"><img src="http://waywework.it/images/logo.png?1227062722"/></div>n');
 
	var contentDiv = contentDivCollection.snapshotItem(0);
	var currentPost = '';
	for(var i=0;i<contentDiv.childNodes.length;i++){
		if(contentDiv.childNodes[i].nodeName == "H1" ){
			currentPost = contentDiv.childNodes[i].innerHTML;
		}
 
		if(contentDiv.childNodes[i].nodeName == "H2" ){
			if(contentDiv.childNodes[i].childNodes.length==2){
				var currentBlog = contentDiv.childNodes[i].childNodes[1].innerHTML;
				var currentBlogURL = contentDiv.childNodes[i].childNodes[1].href;
				currentPost+= " ("+stripLastChar(contentDiv.childNodes[i].childNodes[0].textContent)+")";
 
				currentBlog = (currentBlog+"").replace(/>s*</a>/g,'>Blank</a>');
				currentPost = (currentPost+"").replace(/>s*</a>/g,'>Blank</a>');
 
				addToArray(currentBlog, currentBlogURL, currentPost);
				currentPost = '';
			}
 
		}
	}
 
	blogsAndPosts = blogsAndPosts.sort();
	document.write("<div style='padding-left:150px;'><ul>n");
	for(var i=0;i<blogsAndPosts.length;i++){
		var blogsToPost = blogsAndPosts[i];
		if(i>0){
			document.write("<br/>");
		}
		document.write("<li><a style='font-weight:bold;color:black;font-family:Verdana,Tahoma;text-decoration:none;font-size:14px' href='"+blogsToPost[1]+"'>"+blogsToPost[0]+"</a>");
		document.write("<ul>");
		for(var j=2;j<blogsToPost.length;j++){
			document.write("<li><div style='font-family:Tahoma,Verdana;font-size:12px;text-decoration:none;'>"+blogsToPost[j]+"</div></li>");
		}
		document.write("</ul>");
		document.write("</li>");
	}
	document.write("</ul></div>");
 
	document.write("</body></html>");
	document.close();
}
 
function stripLastChar(string){
	return string.substr(0, string.length-3);
}
 
 
function addToArray(blog, blogURL,post){
	if(blogsAndPosts.length==0){
		addBlog(blogsAndPosts, blog, blogURL, post);
	}
	else{
		var found = false;
		for(var i=0;i<blogsAndPosts.length;i++){
			var blogsToPost = blogsAndPosts[i];
			if(blogsToPost[0] == blog){
				blogsToPost[blogsToPost.length] = post;
				found = true;
				break;
			}
		}
		if(!found){
			addBlog(blogsAndPosts, blog, blogURL, post);
		}
	}
}
 
function addBlog(blogsArray, blog, blogURL, post){
	if(blog == '' || post == ''){
		return;
	}
	var blogsToPost = new Array();
	blogsToPost[0] = blog;
	blogsToPost[1] = blogURL;
	blogsToPost[2] = post;
	blogsArray[blogsArray.length] = blogsToPost;
}

Next step, try and see if we can build a custom browser homepage(about:blank) with a Google Searchbar, Yahoo mail and weather.com weather.

Sony Erricson, Toshiba, Vodafone & 11 others join Open Handset Alliance

December 11th, 2008

The Open Handset Alliance announced the joining of 14 additional companies yesterday. This includes the likes of Sony Erricson, Toshiba and Vodafone amongst others.

New members will either deploy compatible Android devices, contribute significant code to the Android Open Source Project, or support the ecosystem through products and services that will accelerate the availability of Android-based devices.

The new members will add to the momentum of Android device availability, providing developers with an even greater opportunity to deploy compelling applications that will reach a global audience. As a result, consumers around the world benefit from a superior mobile experience that features less expensive devices, more compelling services, rich Internet applications, and easier-to-use interfaces.

The complete list reads: AKM Semiconductor Inc., ARM, ASUSTek Computer Inc., Atheros Communications, Borqs, Ericsson, Garmin International Inc., Huawei Technologies, Omron Software Co. Ltd, Softbank Mobile Corporation, Sony Ericsson, Teleca AB, Toshiba Corporation and Vodafone.

For more information visit the press release here.

Learnings from the Past: Rules Engine

December 4th, 2008

I was working with a bank in the mid-west US a few years back. They were building an on-line mortgage application that would allow brokers to submit loan applications and get real time product and pricing information. After some initial discussions it was decided to build a product and pricing engine in-house using an of-the-shelf Rules engine. I will try and summarize my learning’s about choosing the right Rules Engine below:

1) Choose a small vendor with a robust product but few clients. This would ensure a fast and accurate response to your queries and issues.

2) Expect Customizations, so get an early commitment from the vendor in terms of resources. Get somebody from the vendor side who knows the product inside-out and willing to adjust the product api’s as per your need.

3) Find out how easy it is for a layman to define rules. After all its the business who is going to manage the rules in the long term..not IT.

4) Find out how easily the product can scale. How many tps can it support. You don’t want your application to choke up due to the product that you’ve bought.

5) Find out how easily you can plug-in custom code/ui with the engine. Does it allow itself to be wrapped inside another front-end application for providing a consistent look and feel? Does it support external authentication/authorization?

6) Find out how easy it is to debug the rules. In our case, the rule engine used to generate java code which was easy to debug.

7) Find out how easy it is to simulate conditions. Having something to visualize a “what if” scenario always helps the business.