Archive for the ‘Technology’ Category

Learnings from the Past: Hibernate related Best Practices

Thursday, 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

Friday, 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();
	}
}

Update on the GreaseMonkey Custom homepage script

Wednesday, 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?

Confguring Firefox for Single signon

Sunday, September 28th, 2008

If you use Firefox for browsing your company intranet sites that are single signon enabled, you must really hate getting redirected to the login page. Ever wished there was a way you could use Firefox without stopping at the login page? Now, there is a way out.

Follow these simple steps to enable single signon in Firefox:

  1. Open Firefox, type “about:config” in the addressbar (without “)
  2. Filter options with “negotiate” (without “)
  3. Edit “network.negotiate-auth.trusted-uris” and add your company’s domain name (eg., .mycompany.com)
  4. No restart required

If you want to get more details visit this site or just google for “Firefox single signon”.