stevelibonati

February 5, 2012

spring-mvc-test with a sprinkle of mockito

Filed under: spring, spring-mvc, unit test — Tags: , , , , , , , , — stevelibonati @ 1:44 pm

Sam Brannen and Rossen Stoyanchev presented ‘Spring 3.1 and MVC Testing Support’ at Spring 2GX back in October 2011. The idea is that you can write a unit test that accepts a Spring app context defining your Spring MVC components and be able to test your Controllers and all the Spring MVC ‘wiring’ by passing a mock request. The test itself does not have a reference to a Controller and thus you can simply send a request and receive a response. This type of test is very powerful in that you are testing Spring configuration, @RequestMapping annotations, controller behavior and view resolution.

Up to this point, we’ve tested Controllers only as plain vanilla unit tests which of course Spring recommends. Be sure to take a look at section 10.2 of http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/testing.html. This typically means instantiating the controller and invoking its methods directly, asserting on the response object and injecting mock objects for any dependencies. This is good but for the webapp under consideration we leave out all the view resolution which primarily consists of jaxb marshalling which can be quite particular when you mess up the xml annotations.

To supplement this style of testing and to test the app ‘end to end’, something Sam and Rossen alluded to in their presentation, my team has built a SoapUI test suite that would act as a client to the application (a REST app in this case). Of course the app needs to be built, deployed and running. Any integrated components like databases, web services, etc need to be up as well. The SoapUI tests are definitely a solid approach for testing your web app end to end but wouldn’t there be a lot of value in leveraging the Spring MVC Test Support?

Not only can I test the Controller behavior but whereas before I was asserting only on a response object, now I can assert on the actual response. Again, in this case, we’re talking xml documents (but obviously this can be applied to any response format or content type, json, html, etc …). There was one problem with this that I quickly realized when I started playing with this test framework. The framework attempts to resolve all @Autowired dependencies. What does this mean? Well, if you value a nice decoupled app layer architecture (and I know you do), your Controllers will have Services injected into them and your Services will have DAO’s injected into them. In our case, we have DAO objects that integrate with a database as well as SOAP web services. So I want to test my Controllers but I don’t want an end to end ‘integration test’ that tests the app through all its dependencies. That’s a bit too heavy and would be a nightmare for the continuous integration.

So what I need is to leverage the Spring MVC Test support framework but figure out how to short circuit those @Autowired dependencies and inject a mock in which I can control the behavior. In the end, it was a really simple thing to achieve but I will admit my colleague and I tossed it around a bit before figuring out the solution.

So what was the magic that we needed? We recognized the simple fact that in the tests we don’t have a reference to the Controller, hence, you can’t directly inject a mocked Service object. We also have to come up with a way for Spring not to chase down all those @Autowired dependencies.

Lets consider each separately though they are related. First off, how do I get a Mockito mock object into the app context? A little googling led to this post, injecting-mockito-mocks-into-a-spring-bean. So something like the following works:

<bean id="bookService" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="com.test.service.book.BookService" />
</bean>

Interestingly, the post doesn’t discuss how to define behavior on the mock object which is something I quickly realized needed to be accomplished. Take a look at the following :

package com.test.controller.book;

import static org.hamcrest.Matchers.equalTo;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.server.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.server.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.server.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.server.result.MockMvcResultMatchers.xpath;

import java.net.URI;
import java.util.ArrayList;
import java.util.List;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.server.MockMvc;
import org.springframework.test.web.server.RequestBuilder;
import org.springframework.test.web.server.request.DefaultRequestBuilder;
import org.springframework.test.web.server.samples.context.GenericWebXmlContextLoader;
import org.springframework.test.web.server.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import com.test.bean.Book;
import com.test.bean.Books;
import com.test.service.book.BookService;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = TestGenericWebXmlContextLoader.class, locations = {
		"classpath:applicationContext.xml", "classpath:mockitoTestContext.xml" })
public class BookControllerTestContextTests {

	@Autowired
	private WebApplicationContext wac;

	private MockMvc mockMvc;

	private BookService bookService;

	@Before
	public void setup() {

		this.mockMvc = MockMvcBuilders.webApplicationContextSetup(this.wac)
				.build();

		bookService = wac.getBean(BookService.class);

		Books books = new Books();

		List bookList = new ArrayList();

		Book book2 = new Book();
		book2.setIsbn("0307408841");
		book2.setTitle("In the Garden of Beasts: Love, Terror, and an American Family in Hitler's Berlin");

		bookList.add(book2);

		books.setBooks(bookList);
		when(bookService.getBooks()).thenReturn(books);
	}

	@Test
	public void testGetBooks() throws Exception {
	

		mockMvc.perform(get("/books").header("foo","bar"))
				.andExpect(status().isOk())
				.andExpect(content().type(MediaType.APPLICATION_XML))
				.andExpect(
						xpath("/books/books/title")
								.string(equalTo("In the Garden of Beasts: Love, Terror, and an American Family in Hitler's Berlin")));
	}

}

class TestGenericWebXmlContextLoader extends GenericWebXmlContextLoader {

	public TestGenericWebXmlContextLoader() {
		super("src/main/resources", false);
	}

}

I can pull the mock object out of the application context using getBean(). From there, I can define the behavior on the mock object. This comes to an interesting point. There is at least some coupling going on at this point. The test is somewhat aware of the Service mock that will ultimately be invoked via the Controller. I imagine there may be a way to abstract this out, maybe to some parent test class that defines all the behaviors of all the service mock objects in the app. I’ve not yet gone to that level but its possible.

So now I have something a little bit stronger than my initial Controller unit tests. Now I can simply invoke mockMvc.perform(get(“/books”) and get back the full fledged xml response thats gone through jaxb marshalling and I can assert on the structure and content of the xml. In addition, I have assurance that all the Spring MVC infrastructure is in place and working. I can run it on the continuous build and get an assurance that no one has broken any Spring MVC annotations, JAXB annotations and any Spring MVC configurations in the app context.

The other extremely important thing to point out is that I in fact had to get rid of @Autowired on the controller and use @Resource.

Take a look at the tip in section 3.11.3 of http://static.springsource.org/spring/docs/2.5.x/reference/beans.html#beans-autowired-annotation.

The reason being is the Mockito mocked object is a proxied class of your Service interface. @Autowired resolves by type. @Resource resolves by bean name reference. This was just what was needed to resolve the (mock) Service dependency.

Take a look a the following links for reference. The framework is in github and not currently part of the Spring distribution.

https://github.com/SpringSource/spring-test-mvc/

https://github.com/SpringSource/spring-test-mvc/tree/master/src/test/java/org/springframework/test/web/server/samples/context

http://rstoyanchev.github.com/spring-31-and-mvc-test/#1

I also posted the complete project at http://dl.dropbox.com/u/60625371/spring-mvc-sandbox.zip. The maven pom file has the dependency for spring-mvc-test along with the repository defined.

I look forward to having the Spring MVC testing support included in future versions of Spring and a special thanks to Sam and Rossen over at Springsource!

August 30, 2011

Yoda code?

Filed under: code review — stevelibonati @ 9:18 am

“Take you I will.” “Hard to see, the dark side is.” “The boy you trained, gone he is. Consumed by Darth Vader.”

We all remember that fun loving little green Jedi, right ? One of his characteristic trademarks was in his horrific grammatical butcherings. Loved he is, nonetheless (<– that one was mine 🙂 ). Okay lets get to the point. It's cute, I suppose when Yoda does it. It doesn't take on the same level of charm when I'm doing code reviews.

if (null != pageState) {

if (null != action && null == transactionPageState) {

if (0 != pageSize) {

This style of coding is very unnatural for me. It reminds me of Yoda. For me, it reduces the readability and causes me to pause and flip it around in my head which breaks the natural rhythm of the code I'm reading. I wonder why developers write code like this ? If I were writing it the null would most assuredly be on the right side. Why ? That's how I think. If such and such is null do this. Or I can be Yoda … If null such and such is , do this. You see for me its quite backwards.

Now, I only speak one language and that's English (not counting programming languages). I'm wondering if the tendency to do what I call backward comparisons stem from developers being multilingual? I really don't know if that has anything to do with it or there is some other reasoning behind it. In the end though, I feel it detracts from the readability which to me is a major factor overall in a code review. I certainly accept that I need to be conscious of imposing my own personal style of development on developers and not stifling their own expressiveness. Is this a style thing or is it a real hindrance in readability in which it should be pointed out during review ?

Curious I am to learn the opinions of others.

July 29, 2010

HTTP Headers Survive Redirect

Filed under: HTTP — Tags: — stevelibonati @ 8:39 pm

A software vendor suggested a solution that involved reading HTTP Header variables after a redirect. I was always under the impression that HTTP Headers set on a response would not survive a redirect. I googled a bit but didn’t come up with anything substantial.

I put together a quick little sample app to prove the point.
Using Live HTTP headers …
the initial request:
http://localhost:7001/sandbox/redirect.html

GET /sandbox/redirect.html HTTP/1.1
Host: localhost:7001
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,es-es;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Cookie: locale=en

in the app:


@Controller
public class RedirectController {

	@RequestMapping("/redirect.html")
	public ModelAndView redirectHandler(final HttpServletResponse response)
			throws Exception {

		response.setHeader("foo", "foo");
		response.setHeader("bar", "bar");
		response.setHeader("baz", "baz");

		ModelAndView modelAndView = new ModelAndView();
		modelAndView.setView(new RedirectView("/sandbox/image.html"));

		return modelAndView;
	}
}

the response (noting the custom headers):

HTTP/1.1 302 Moved Temporarily
Date: Thu, 29 Jul 2010 20:02:22 GMT
Transfer-Encoding: chunked
Location: http://localhost:7001/sandbox/image.html
bar: bar
baz: baz
foo: foo
Content-Language: en-US
X-Powered-By: Servlet/2.5 JSP/2.1

the subsequent request (redirect)
http://localhost:7001/sandbox/image.html

GET /sandbox/image.html HTTP/1.1
Host: localhost:7001
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,es-es;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Cookie: locale=en

At this point you can see the original response headers are not included in the request. Also can be seen from the debugger on the server side :

March 20, 2009

A twist on CustomItemRenderers

Filed under: Flex, Uncategorized — Tags: , , , — stevelibonati @ 2:57 pm

Most of the custom ItemRenderer solutions I’ve seen make use of Flex’s Event Dispatching framework to bubble up an Event that passes the item. For example, a ButtonRenderer that would dispatch a custom Event when the button was clicked.

I had the challenge of coming up with a CustomItemRenderer that would be used in a PureMVC application. The handlers or event listeners would pretty much be turning around and dispatching a Notification (to change the View for instance).

The requirement was for a renderer that behaved like a HyperLink. Pretty simple , right ? Well , sure not a big deal to style a Label or Text like a Hyperlink. That was just one piece of it . But what about the behavior ? What happens when the link is clicked ?

I got this idea of passing in a function to the Renderer and having the Renderer execute a callback on the function. This way the Renderer is handling 1. the rendering part and 2. the behavior or Click part, however it need never know what its executing. It simply grabs the data element of the renderer and passes it along to a function . The function would then be responsible for whatever real behavior that would occur , like dispatching a PureMVC notification or whatever. It can also typecast the Object to its real type and use it however it chooses.

So here’s what I came up with …

package 
{
	import flash.events.MouseEvent;
	
	import mx.containers.Canvas;
	import mx.controls.Text;
	import mx.controls.dataGridClasses.DataGridListData;
	import mx.controls.listClasses.BaseListData;
	import mx.controls.listClasses.IDropInListItemRenderer;
	import mx.controls.listClasses.IListItemRenderer;
	import mx.core.IDataRenderer;
	import mx.core.ScrollPolicy;
	import mx.events.FlexEvent;

	/**
	 * Renders the item as a Text Link. 
	 * TextLinkRenderer provides a public callback Function that will be invoked upon the clicking the link.
	 * The data element will be passed through to this function, so the callback function's signature should
	 * be myFunction(object:Object)
	 * 
	 * 
	 */

	public class TextLinkRenderer extends Canvas implements IDataRenderer, IDropInListItemRenderer, IListItemRenderer
	{
	  	private var _data:Object;
	    	     
    	private var _listData:BaseListData;
    	
    	private var text:Text = new Text();
    	
		/**
		 * this function will be called upon the link's MouseEvent.CLICK
		 * 
		 * typical usage will be 
		 * public var renderer:ClassFactory = new ClassFactory(TextLinkRenderer);
		 * renderer.properties = {callback : myCallback};  
		 * 
		 */
		public var callback:Function;
		
		public function TextLinkRenderer()
		{
			super();
			this.horizontalScrollPolicy = ScrollPolicy.OFF;
			this.verticalScrollPolicy = ScrollPolicy.OFF;
						
    		text.mouseChildren = false;
    		text.useHandCursor = true;
    		text.buttonMode = true;
    		text.setStyle("textDecoration", "underline");
    		text.setStyle("fontWeight", "normal");
    		text.setStyle("color", "blue");
    		
    		text.addEventListener(MouseEvent.CLICK, onClick);
		}
		
		private function onClick(event:MouseEvent):void
		{
			callback(data);
		}

	    // Make the data property bindable.
	    [Bindable("dataChange")]
	    
	    // Define the getter method.
	    public override function get data():Object {
	        return _data;
	    }
	    
	    // Define the setter method, and dispatch an event when the property
	    // changes to support data binding.
	    public override function set data(value:Object):void {
	        _data = value;
	        dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
	        
	         invalidateProperties();
		    }
		
		 // Make the listData property bindable.
	    [Bindable("dataChange")]

	    // Define the getter method.
	    public function get listData():BaseListData
	    {
	      return _listData;
	    }
	    
	    // Define the setter method,
	    public function set listData(value:BaseListData):void
	    {
	      _listData = value;
	      invalidateProperties();
	    }
	    
	     override protected function createChildren():void 
		{
			super.createChildren();
			addChild(text);
		} 
		
		override protected function measure():void {
			super.measure();
			this.width = text.width;
			this.height = text.height;
		}
		
		override protected function commitProperties():void 
		{
			super.commitProperties();
			if (listData is DataGridListData)
	      	{
	    		text.text = DataGridListData(listData).label;
	      	}
		}
	}
}

Theres only one other catch . Figuring out how to inject or set the callback. Turns out this wasn’t such a challenge once you figure out ClassFactory.

public var renderer:ClassFactory = new ClassFactory(TextLinkRenderer);
renderer.properties = {callback : myCallback}; 

January 26, 2009

Flex PDF Excel Views

Filed under: Flex — Tags: — stevelibonati @ 7:54 pm

There seems to be a gap when it comes to printing in Flex. The browser print does a pretty horrible job. There’s the FlexPrintJob class and many solutions derived from it but according to blog posts and such it seems its never a fully adequate solution. There probably exists a few solutions for generating Excel spreadsheets as well. They usually have to do with copying to the System clipboard and then using Javascript or ActiveX objects to open up Excel and paste the data in. While these solutions may work fine for you, I’ve found that a pretty simple integration with Spring can also do the trick.

If your already using Spring on your server side, a pretty straightforward solution is to use Spring’s org.springframework.web.servlet.view.document.AbstractPdfView and org.springframework.web.servlet.view.document.AbstractExcelView. The respective iText API and Apache POI-HSSF API are fairly straighforward to work with, especially if your dealing with tabular data like that of a DataGrid.

The essential idea is to wrap up your data on the client side in Flex in XML or JSON and use navigateToURL to open a new browser window with a .pdf or .xls file. In fact … something like the following :

var xml:XML = <export>
	<row>
		<cell>Chapel Hill</cell>
		<cell>NC</cell>
		<cell>34015</cell>
	</row>
	<row>
		<cell>Charlotte</cell>
		<cell>NC</cell>
		<cell>34016</cell>
	</row>
	<row>
		<cell>Clinton</cell>
		<cell>NC</cell>
		<cell>34017</cell>
	</row>
	<row>
		<cell>Clyde</cell>
		<cell>NC</cell>
		<cell>34018</cell>
	</row>
	<row>
		<cell>Concord</cell>
		<cell>NC</cell>
		<cell>34020</cell>
	</row>
	<row>
		<cell>Durham</cell>
		<cell>NC</cell>
		<cell>34024</cell>
	</row>
</export>;
			
	var urlRequest:URLRequest = new URLRequest("export.do");
	urlRequest.method = URLRequestMethod.POST;
			
	var variables:URLVariables = new URLVariables();
        variables.export = xml;
        urlRequest.data = variables;

	navigateToURL(urlRequest,"_blank");

Now I have hardcoded data but fill in the gaps to extract the data from your DataGrid or assemble it in any way you like. Maybe you need to print the data in a datagrid but you also need some contextual information as a header?

Thats pretty much the extent of the client side. The server side uses Spring MVC and as such needs to provide a Spring MVC applicationContext file, usually <servletname>-servlet.xml. Here’s what I got:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">

	<bean id="handlerMapping"
		class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping" />
	<bean id="exportController"
		class="spring.mvc.controller.ExportController">
		<property name="xmlRules" value="classpath:xmlRules.xml" />
	</bean>
	
	<bean id="printController"
		class="spring.mvc.controller.PrintController">
		<property name="xmlRules" value="classpath:xmlRules.xml" />
	</bean>
	<bean id="viewResolver"
		class="org.springframework.web.servlet.view.ResourceBundleViewResolver" />
</beans>

And in the web.xml:

 <servlet>
        <servlet-name>spring</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
        	<param-name>contextConfigLocation</param-name>
			<param-value>classpath:spring-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
 

Now you just need your Controller and your concrete implementation of the AbstractExcelView or AbstractPdfView.

package spring.mvc.controller;

import java.io.StringReader;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.digester.Digester;
import org.apache.commons.digester.xmlrules.DigesterLoader;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.Resource;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;

import spring.mvc.Constants;
import spring.mvc.model.Export;

public class ExportController extends AbstractController {

	public final static Log LOG = LogFactory.getLog(ExportController.class);

	private Resource xmlRules;

	public ModelAndView handleRequestInternal(final HttpServletRequest request,
			final HttpServletResponse response) throws Exception {
		
		Digester digester = DigesterLoader.createDigester(xmlRules.getURL());
		Export export = new Export();
		digester.push(export);
		String xml = request.getParameter(Constants.EXPORT_NAME);
		digester.parse(new StringReader(xml));

		ModelAndView mav = new ModelAndView(Constants.EXCEL_VIEW_NAME);
		mav.addObject(Constants.EXPORT_NAME, export);
		return mav;
	}

	public void setXmlRules(Resource xmlRules) {
		this.xmlRules = xmlRules;
	}

}

And the View :

package spring.mvc.view;

import java.util.Collection;
import java.util.Iterator;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.springframework.web.servlet.view.document.AbstractExcelView;

import spring.mvc.Constants;
import spring.mvc.model.Cell;
import spring.mvc.model.Export;
import spring.mvc.model.Row;

public class ExcelView extends AbstractExcelView {

	@SuppressWarnings("unchecked")
	@Override
	protected void buildExcelDocument(final Map model,
			final HSSFWorkbook wb, final HttpServletRequest request,
			final HttpServletResponse response) throws Exception {

		HSSFSheet sheet = wb.createSheet();

		Export export = (Export) model.get(Constants.EXPORT_NAME);
		Collection rows = export.getRows();
		
		int i = 0;
		for (Iterator rowIterator = rows.iterator(); rowIterator.hasNext();) {
			
			Row row = (Row) rowIterator.next();
			HSSFRow hssfRow = sheet.createRow(i++);
			Collection cells = row.getCells();
			
			int j = 0;
			for (Iterator cellIterator = cells.iterator(); cellIterator.hasNext();) {
				
				Cell cell = (Cell) cellIterator.next();
				HSSFCell hssfCell = hssfRow.createCell(j++);
				setText(hssfCell, cell.getValue());
			}
		}
	}

}

OK. I cheated a little with the XML parsing. I’m using Commons Digester, so it wouldn’t be complete without showing the xmlRules.xml file:

<?xml version="1.0"?>
<!DOCTYPE digester-rules 
  PUBLIC "-//Jakarta Apache //DTD digester-rules XML V1.0//EN" 
    "http://jakarta.apache.org/commons/digester/dtds/digester-rules.dtd">

<digester-rules>
	<pattern value="export">
		<pattern value="row">
			<object-create-rule classname="spring.mvc.model.Row" />
			<set-properties-rule />
			<set-next-rule methodname="addRow" />
			<pattern value="cell">
				<object-create-rule classname="spring.mvc.model.Cell" />
				<call-method-rule methodname="setValue" paramcount="0" />
				<set-next-rule methodname="addCell" />
			</pattern>
		</pattern>
	</pattern>
</digester-rules>

and the glue that binds things : the views.properties file that is used by ResourceBundleViewResolver

excel.(class)=spring.mvc.view.ExcelView
pdf.(class)=spring.mvc.view.PdfView

Now be careful with this, I got hung up cause in the Spring docs it doesnt have the parentheses around class. I opened up a Jira with Spring to let them know the docs need updating.

Thats about it! Hopefully the code snippets are more than enough to put the various pieces together. In the end we have Flex PDF printing and Excel export for very little expense. Thanks Spring!

This was a protoype app but we are planning to use the same concept in a prod app. I’ll update this post to see if we encounter any gotchas or how well it scales. I’m already thinking the xml might be a bit of an overhead. I think a port to JSON would be quite simple. Any other ideas, please let me know.

January 17, 2009

FlexUnit Testing in PureMVC

Filed under: Flex, Uncategorized — Tags: , , , — stevelibonati @ 9:39 pm

No sooner than I wrapped my head around the PureMVC framework (having used Cairngorm in the past), I was tasked with figuring out how to unit test the app. There’s not a whole lot to go on out on the web. There are a few posts on the PureMVC forums which were helpful getting me thinking in the right direction. I will list the links that I found at the end of this post.

Through the postings in the PureMVC forums, I found the “tiny library”, puremvc-flexunit-testing contributed by Larry Marburger. I’ve found this to be pretty useful. There were also some other approaches which involved extending Facade and intercepting Notifications. I didn’t go this route though it seemed like a smart idea. Admittedly, one of the reasons I didnt go this route was because I was confused about how to separate the extended version of Facade from the one contained in the .swc.

Some of the issues with unit testing in PureMVC is basically the dependencies in the framework itself. Mediators call proxies. Notifications are sent. Commands are invoked. Basically the framework needs to be bootstrapped for all this to occur. However, do you really want to unit test all these layers of the framework? That seems more of an integration test and so that was something that I had to toss around a while. For example, if I want to test a Mediator, do I want to test the model state change that occurs after a notification is sent that invokes a Command, that gets a proxy, that makes a remote object call … (you get the picture) or do I want to simply test that if I dispatch an event, which would typically occur from a UI component, that a Notification occurred. I can do this pretty easily using the puremvc-flexunit-testing library.

public function testOnTryLogin():void {
    registerObserver(View.getInstance(), null, ApplicationFacade.DO_LOGIN, handleTestOnTryLoginResponse, 10000);
    loginForm.dispatchEvent(new Event(LoginForm.TRY_LOGIN));
}

public function handleTestOnTryLoginResponse(e:PureMVCNotificationEvent):void {
    assertEquals(e.notification.getName(), ApplicationFacade.DO_LOGIN);
}

However, what if, in my Mediator, I invoked a proxy directly? In this case I don’t really see an option except to let the communication flow through the proxy and the service. I think this is about the point where I started to think about MockServices. Now this is not really a new idea, in fact, I believe it was also mentioned in the forums by the author of PureMVC.

This whole concept of MockServices seems like a great idea. I can be disconnected from the server. I plug a MockService into my Proxy or Delegate or ServiceProxy, however your doing it, and away we go. But you have a lot of services, the data is not trivial, etc. Basically, it becomes an effort to write the MockServices. Also, isn’t it beneficial to actually have a unit test that does invoke the remote service, in an effort to test the communication from client to server? So, I’m not really sure how we will proceed going forward. Do we take the time to write the mock services or have the server up and call the remote services through the unit tests or perhaps some mix of the two. I’d be interested to hear what others are doing.

So I discussed a little bit about testing the mediators and if your mediators are doing nothing more than sending a notification, its trivial. The proxies can basically be tested the same way. Its your choice whether you invoke a mock object or the real service. But the end result is the same, in your unit test your doing assertions on whether the Proxy sent the correct notification. You can also go further, and inspect the body of the notification or the data element of the Proxy.

Another test I introduced was an ApplicationFacade test. I’m not sure if this is an extremely valuable test, but time will tell. This basically tests that the Mediators can be retrieved, the Proxies can be retrieved, the Commands are registered, etc. In order to do this however you essentially have to bootstrap the whole framework. That is,  something like the following:

facade.notifyObservers( new Notification( ApplicationFacade.STARTUP, new MockMyFlexTest() ) );

At which point I learned you can’t inject the Application itself because the visual components don’t get instantiated when your invoking through FlexUnit. I read a little bit about this. It seems to be the expected behavior, though I admit I don’t know all the intricate details of the DisplayList and so forth. What does it even matter if they are instantiated or not? Well, rememeber, the ApplicationMediator, when it registers the other mediators, it injects the views. Somewhere along the line, I was hitting the equivalent of a NullPointerException. So I created a MockView object where I ensure the view components are instantiated. I believe this is a general problem in PureMVC when you are using deferred instantiation.

 

public function ApplicationMediator(viewComponent:Object) {
    super(NAME, viewComponent);

    facade.registerMediator(new LoginFormMediator(app.loginScreen));
    facade.registerMediator(new ViewBooksFormMediator(app.viewBooksScreen));
    facade.registerMediator(new EditUpdateBookFormMediator(app.editUpdateBookScreen));
    facade.registerMediator(new DeleteBookFormMediator(app.deleteBookScreen));
}

The other thing I noted was that if I bootstrapped the framework in my unit tests by issuing the ApplicationFacade.STARTUP notification, then subsequent test cases will be affected by the state of the Singletons, (Facade, Model, View, Controller) so you have to ensure that you cleanup these objects in tearDown. I had to add a destroy method to ApplicationFacade that set instance to null.

I’ve heard that PureMVC multicore makes unit testing less painful, but at this point I have relative confidence that most if not all of the client code is tested. I haven’t really spent too much time testing the Commands as they generally get a Proxy and call a method on the Proxy. If you’ve already tested the proxy, then theres not much to test. However to be thorough, you can include these tests as well.

A great resource is the actual unit tests for PureMVC itself to start you thinking in the right direction. The biggest thing for me was to kind of break the intercommunication of the framework so that classes can be tested in isolation but in reality there are always a few players working together. Even with the puremvc-flexunit-testing library, its the View that registers an observer for the callback:


view.registerObserver(notificationName, new Observer(handler, this));

So whether your testing a Mediator, a Proxy, theres always a few framework components working together, notifications being sent, etc. But you don’t have to bootstrap the entire framework just to unit test.

I have to say when I first started looking at all this it seemed kind of complex. In the end, I suppose the bulk of the hurdle was really having something like the puremvc-flexunit-testing library which intercepts a notification and dispatches an event. Another important lesson is that you need to clean up after yourself with regard to the Singletons.

Look forward to hearing your experiences with PureMVC and FlexUnit.

As promised here are some resources I found that helped me along:
http://marburger.cc/posts/flexunit-testing-puremvc-code
http://forums.puremvc.org/index.php?topic=419.0
http://forums.puremvc.org/index.php?topic=276.0
http://forums.puremvc.org/index.php?topic=213.0
http://elromdesign.com/blog/2008/12/30/flexunit-asynchronous-tests-with-puremvc-framework/#

October 26, 2008

Flex Remote Objects and Hibernate. What’s the problem?

Filed under: Flex — Tags: , — stevelibonati @ 7:55 pm

About a year ago, I was the tech lead/developer for an enterprise app that our team built with Flex, Flex Data Services (now LCDS) using Flex Remote Objects for data communication between client and server. At the time we set out to use Spring/Hibernate in the service/dao tiers. We ran into considerable issues and ditched Hibernate for Spring JDBC (which worked out quite nicely, by the way). Recently, this issue came up again and while I recall that the issue had to do with LCDS doing introspection on Java objects and resolving all of the objects dependencies, I wanted to revisit this and elaborate.

Without going into any detailed explanation just yet, the fundamental issue is that when LCDS is creating the client side Actionscript objects, it is using introspection on the Java objects and attempts to resolve all references. Depending on how your model objects are constructed, there is a potential that your entire database can be returned in a single Remote object method call! Just a slight performance issue, right ?<sarcasm>

OK. Well, I’m going to lay out the steps I took to construct this example. I knew that Oracle had some sample schemas, so I figured I can use this for the example app. Also, I’m going to use BlazeDS instead of LCDS, but they are essentially the same codebase.

  1. download and install Oracle 10g
  2. download Blaze DS
  3. setup plain vanilla Blaze DS dynamic webapp in Eclipse
  4. create hibernate.cfg.xml to be used to create mappings with Hibernate tools (used Hibernate Tools Eclipse Plugin)
  5. download Oracle jdbc driver
  6. generate java classes and hbm.xml files (used Hibernate Tools Eclipse Plugin)
  7. get jars and dependencies ; spring, hibernate, etc …
  8. build out DAO’s, service layer, Unit Tests
  9. build Spring beans files
  10. define transaction semantics
  11. configure datasource in Tomcat
  12. expose service bean to LCDS
  13. create client side Actionscript classes (generated with XDoclet)
  14. set up client Flex app

The schema information for the HR database can be found at http://stanford.edu/dept/itss/docs/oracle/10g/server.101/b10771/scripts003.htm

I basically want to illustrate that if the Flex app calls a server side method, getEmployees(), the Collection that is returned will consist of Employee objects, however all of the object’s references will be resolved as well. Basically, your getting more than you asked for! You want a Collection of employees but you’ll get the Employee’s Department, and the Department’s Location.

I am going to simplify the generated mappings to establish just this level of nesting. The schema is actually much more complex. So remember, the goal is to illustrate that when a remote object call for employees is made, you will also get the Employee’s Department, and the Department’s Location. I’m hoping its obvious that this behavior can be potentially harmful for the client side app resulting in obvious performance issues and latency.

I’d like to point a few things about the server-side piece. I am using the FlexSpringFactory to expose Spring beans as remote objects.

In the remoting-config.xml file:

<destination id="employeesService">
<properties>
<factory>spring</factory>
<source>employeesService</source>
</properties>
</destination>

Also, another interesting thing to point out is when you are using the HibernateTransactionManager http://static.springframework.org/spring/docs/2.0.x/api/org/springframework/orm/hibernate/HibernateTransactionManager.html, The Hibernate Session is open for the scope of the transaction.

2008-10-26 08:21:49,545 DEBUG [org.springframework.beans.factory.support.DefaultListableBeanFactory] - <Returning cached instance of singleton bean 'employeesService'>

2008-10-26 08:21:49,748 DEBUG [org.springframework.orm.hibernate3.HibernateTransactionManager] - <Using transaction object &#91;org.springframework.orm.hibernate3.HibernateTransactionManager$HibernateTransactionObject@17c398e&#93;>

2008-10-26 08:21:49,748 DEBUG [org.springframework.orm.hibernate3.HibernateTransactionManager] - <Creating new transaction with name &#91;example.hr.service.EmployeesService.getEmployees&#93;: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly>

2008-10-26 08:21:49,904 DEBUG [org.hibernate.impl.SessionImpl] - <opened session at timestamp: 12250237097>

2008-10-26 08:21:49,904 DEBUG [org.springframework.orm.hibernate3.HibernateTransactionManager] - <Opened new Session &#91;org.hibernate.impl.SessionImpl@122cc7e&#93; for Hibernate transaction>

2008-10-26 08:21:49,904 DEBUG [org.springframework.orm.hibernate3.HibernateTransactionManager] - <Preparing JDBC Connection of Hibernate Session &#91;org.hibernate.impl.SessionImpl@122cc7e&#93;>

2008-10-26 08:21:49,951 DEBUG [org.hibernate.jdbc.ConnectionManager] - <opening JDBC connection>

2008-10-26 08:21:49,951 DEBUG [org.springframework.jdbc.datasource.DataSourceUtils] - <Setting JDBC Connection &#91;org.apache.commons.dbcp.PoolableConnection@c5f20f&#93; read-only>

2008-10-26 08:21:49,951 DEBUG [org.hibernate.impl.SessionImpl] - <setting flush mode to: NEVER>

2008-10-26 08:21:49,967 DEBUG [org.hibernate.transaction.JDBCTransaction] - <begin>

2008-10-26 08:21:49,967 DEBUG [org.hibernate.transaction.JDBCTransaction] - <current autocommit status: true>

2008-10-26 08:21:49,967 DEBUG [org.hibernate.transaction.JDBCTransaction] - <disabling autocommit>

2008-10-26 08:21:49,967 DEBUG [org.hibernate.jdbc.JDBCContext] - <after transaction begin>

2008-10-26 08:21:49,982 DEBUG [org.springframework.orm.hibernate3.HibernateTransactionManager] - <Exposing Hibernate transaction as JDBC transaction &#91;org.apache.commons.dbcp.PoolableConnection@c5f20f&#93;>

2008-10-26 08:21:49,998 DEBUG [org.springframework.transaction.support.TransactionSynchronizationManager] - <Bound value &#91;org.springframework.jdbc.datasource.ConnectionHolder@1302b69&#93; for key &#91;org.apache.commons.dbcp.BasicDataSource@292cb2&#93; to thread &#91;http-8080-Processor24&#93;>

2008-10-26 08:21:49,998 DEBUG [org.springframework.transaction.support.TransactionSynchronizationManager] - <Bound value &#91;org.springframework.orm.hibernate3.SessionHolder@1a2f5b1&#93; for key &#91;org.hibernate.impl.SessionFactoryImpl@17210a5&#93; to thread &#91;http-8080-Processor24&#93;>

2008-10-26 08:21:49,998 DEBUG [org.springframework.transaction.support.TransactionSynchronizationManager] - <Initializing transaction synchronization>

2008-10-26 08:21:49,998 DEBUG [org.springframework.transaction.interceptor.TransactionInterceptor] - <Getting transaction for &#91;example.hr.service.EmployeesService.getEmployees&#93;>

The Java model objects and Hibernate mappings are not terribly interesting, nor are the dao and service implementations. However, if you’d like the full src for the example, just let me know. I’d like to show the client side code. There are two files, main.mxml and HRExample.as (and the ActionScript model classes).

main.xml:

<?xml version="1.0" encoding="utf-8"?>
<hr:HRExample xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:hr="example.hr.*" creationComplete="init()">

<mx:DataGrid id="_employeesDataGrid" height="100%" width="100%" />

<mx:RemoteObject id="employeesService" showBusyCursor="true"
destination="employeesService" endpoint="{endpoint}" />

</hr:HRExample>

HRExample.as:

package example.hr
{
import mx.collections.ArrayCollection;
import mx.collections.ItemResponder;
import mx.controls.DataGrid;
import mx.core.Application;
import mx.rpc.AsyncToken;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.remoting.mxml.RemoteObject;

public class HRExample extends Application
{
[Bindable]
public var endpoint:String;

[Bindable]
public var _employees:ArrayCollection;
public var _employeesDataGrid:DataGrid;
public var employeesService:RemoteObject;

private var _asyncGetEmployees:AsyncToken;

public function init():void {
endpoint = Application.application.parameters.endpoint;
trace("endpoint: " + endpoint);
getEmployees();
}

public function getEmployees():void {
_asyncGetEmployees = employeesService.getEmployees();
_asyncGetEmployees.addResponder(new ItemResponder(getEmployeesResultHandler, getEmployeesFaultHandler));
} 

private function getEmployeesResultHandler(event:Object, token:Object=null):void {
var re:ResultEvent = event as ResultEvent;
_employees = re.result as ArrayCollection;
_employeesDataGrid.dataProvider = _employees;
}

protected function getEmployeesFaultHandler(event:Object, token:Object=null):void {
trace(FaultEvent(event).message.toString());
}
}
}

As an aside, the Flex code shows the concept of code behind, as well as dynamically setting the endpoint for remote objects (I’m passing it in as a flashvar but you can read it in from a properties file). There is also the usage of using AsyncToken to define the handlers for the remote objects as opposed to defining in mxml. All of these things are really out of the scope of this post, but interesting nonetheless.

The best way to illustrate that the Employees objects have their dependencies populated is to set a breakpoint in the ResultHandler. Wait a sec, there was an exception thrown on the server.

2008-10-26 13:14:22,243 DEBUG [org.springframework.orm.hibernate3.HibernateTransactionManager] - 

2008-10-26 13:14:22,243 DEBUG [org.hibernate.transaction.JDBCTransaction] - 

2008-10-26 13:14:22,243 DEBUG [org.hibernate.jdbc.JDBCContext] - 

2008-10-26 13:14:22,243 DEBUG [org.hibernate.impl.SessionImpl] - 

2008-10-26 13:14:22,243 DEBUG [org.hibernate.transaction.JDBCTransaction] - 

2008-10-26 13:14:22,243 DEBUG [org.hibernate.transaction.JDBCTransaction] - 

2008-10-26 13:14:22,243 DEBUG [org.hibernate.jdbc.JDBCContext] - 

2008-10-26 13:14:22,243 DEBUG [org.hibernate.jdbc.ConnectionManager] - 

2008-10-26 13:14:22,243 DEBUG [org.hibernate.impl.SessionImpl] - 

2008-10-26 13:14:22,243 DEBUG [org.springframework.orm.hibernate3.HibernateTransactionManager] - 

2008-10-26 13:14:22,243 DEBUG [org.springframework.orm.hibernate3.HibernateTransactionManager] - 

2008-10-26 13:14:22,243 DEBUG [org.springframework.transaction.support.TransactionSynchronizationManager] - 

2008-10-26 13:14:22,243 DEBUG [org.springframework.transaction.support.TransactionSynchronizationManager] - 

2008-10-26 13:14:22,243 DEBUG [org.springframework.transaction.support.TransactionSynchronizationManager] - 

2008-10-26 13:14:22,243 DEBUG [org.springframework.jdbc.datasource.DataSourceUtils] - 

2008-10-26 13:14:22,243 DEBUG [org.springframework.orm.hibernate3.HibernateTransactionManager] - 

2008-10-26 13:14:22,243 DEBUG [org.springframework.orm.hibernate3.SessionFactoryUtils] - 

2008-10-26 13:14:22,243 DEBUG [org.hibernate.impl.SessionImpl] - 

2008-10-26 13:14:22,243 DEBUG [org.hibernate.jdbc.ConnectionManager] - 

2008-10-26 13:14:22,243 DEBUG [org.hibernate.jdbc.ConnectionManager] - 

2008-10-26 13:14:22,243 DEBUG [org.hibernate.jdbc.JDBCContext] - 

2008-10-26 13:14:22,243 DEBUG [org.hibernate.jdbc.ConnectionManager] - 

2008-10-26 13:14:22,243 DEBUG [org.hibernate.impl.SessionImpl] - 

2008-10-26 13:14:22,243 ERROR [org.hibernate.LazyInitializationException] - 

org.hibernate.LazyInitializationException: could not initialize proxy - the owning Session was closed

at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:60)

at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:111)

at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.invoke(CGLIBLazyInitializer.java:172)

at example.hr.model.Departments$$EnhancerByCGLIB$$cdafdf51.getDepartmentName()

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)

at java.lang.reflect.Method.invoke(Method.java:585)

at flex.messaging.io.BeanProxy$BeanProperty.get(BeanProxy.java:738)

at flex.messaging.io.BeanProxy.getBeanValue(BeanProxy.java:199)

at flex.messaging.io.BeanProxy.getValue(BeanProxy.java:176)

at flex.messaging.io.amf.Amf3Output.writePropertyProxy(Amf3Output.java:565)

at flex.messaging.io.amf.Amf3Output.writeCustomObject(Amf3Output.java:518)

at flex.messaging.io.amf.Amf3Output.writeObject(Amf3Output.java:193)

at flex.messaging.io.amf.Java15Amf3Output.writeObject(Java15Amf3Output.java:26)

at flex.messaging.io.amf.Amf3Output.writeObjectProperty(Amf3Output.java:243)

The infamous org.hibernate.LazyInitializationException: could not initialize proxy – the owning Session was closed.

So whats going on here ? Well as far as I can see, getEmployees() was called, demarcated in a transaction, but Hibernate inserted its own CGLIB enhanced class for the Departments reference as is evident by the following:

example.hr.model.Departments$$EnhancerByCGLIB$$cdafdf51.getDepartmentName()

Again, BlazeDs is trying to resolve the references on Employees but the method has been called and the Session has been closed. So now what ? Well the classic solution to this problem, though is it really a soultion in this case? is to use something like the OpenSessionInViewFilter to keep the Session open beyond the scope of the transaction.

So I add the following to the web.xml:

<filter>  
<filter-name>openSessionInViewFilter</filter-name>  
<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>  
</filter>  

<filter-mapping>  
<filter-name>openSessionInViewFilter</filter-name>  
<url-pattern>/*</url-pattern>  
</filter-mapping> 

And now, stopping in the Flex debugger, you can see the departments and locations references are indeed populated.

A couple of other things to point out. These are supposed to be Actionscript typed objects, not Object. What defines that is this entry in the Actionscript objects:

  [Bindable]
  [RemoteClass(alias=”example.hr.model.Employees”)]

 But take a look at this :

[BlazeDS] Serializing AMF/HTTP response

Version: 3

  (Message #0 targetURI=/2/onResult, responseURI=)

    (Typed Object #0 'flex.messaging.messages.AcknowledgeMessage')

      destination = null

      headers = (Object #1)

      correlationId = "71E4C052-EAA8-2C29-8355-3A72D6E01254"

      messageId = "5BB2635D-9409-C8B4-C014-B3FDEB7818BA"

      timestamp = 1.2250462845E12

      clientId = "5BB25FEE-AC08-0C36-298C-227236A13FD5"

      timeToLive = 0.0

      body = (Externalizable Object #2 'flex.messaging.io.ArrayCollection')

        (Array #3)

          [0] = (Typed Object #4 'example.hr.model.Employees')

            employees = (Typed Object #5 'example.hr.model.Employees')

              employees = (Typed Object #6 'example.hr.model.Employees')

                employees = null

                hireDate = 1987-06-17

                employeeId = 100

                email = "SKING"

                phoneNumber = "515.123.4567"

                departments = (Typed Object #8 'example.hr.model.Departments$$EnhancerByCGLIB$$ed4227b6')

                  departmentName = "Executive"

                  departmentId = 90

                  employees = (Ref #6)

                  locations = (Typed Object #9 'example.hr.model.Locations$$EnhancerByCGLIB$$3396d6b3')

                    stateProvince = " Washington "

                    postalCode = "98199"

                    streetAddress = " 2004 Charade Rd "

Notice again, ‘example.hr.model.Locations$$EnhancerByCGLIB$$3396d6b3’ . These classes will never type properly to their corresponding Actionscript classes.

It’s enough to make you want to scrap Hibernate for Spring JDBC! 🙂

So, to sum up, there are various issues with using Hibernate and Remote Objects. BlazeDS or LCDS attempts to resolve object references upon introspecting objects while converting to Actionscript. The behavior may differ depending on the way in which you define your transactions or whether or not you implement OpenSessionInViewFilter. From experience, it’s more difficult to work with than its worth.

So, what else can be done ? Well, I mentioned more than once Spring JDBC. But let’s say your deadset on Hibernate … The first thing that comes to my mind, and not necessarily the best thing, is to have a layer on top of the service layer that essentially constructs DTO’s and it would be these beans that are exposed as remote objects. In this way, this layer can have explicit control about how DTO objects are constructed. This layer can populate references or set them to null, create subsets of the domain model, etc… I see this as a time consuming effort, but maybe for the best? I’ve also seen some suggestions on the web of using AOP to set the references to null. That’s another approach. Beyond that, I’d be interested to know your thoughts and suggestions. This clearly is an issue. I’ve seen many a blog entry dedicated to the topic. If anyone wants the full src, please let me know, and if anyone would like me to elaborate on any of the setup steps, I’d be happy to.

Good Luck if your using Hibernate and Remote Objects in Flex!

August 26, 2008

Display Tag Date Column Sorting

Filed under: Java and all things closely related — Tags: — stevelibonati @ 1:35 am

Ran into a little trouble with sorting date columns using Display Tag . I googled around a little and saw this post by our good friend Matt Raible. Sure enough the property on the JavaBean was a String. So, luckily I was easily able to change the property to be java.util.Date. Recompile/Redeploy 🙂 . And the columns are sorting just fine as promised.

But wait. I dont want them to look like that (.toString() on Date) . I need them in MM/DD/YYYY format. OK, the table has a Decorator already. I’ll just add getProp to the table decorator, and I’m done.

The Dates are nicely formatted now. But what the … ? The sorting is broken. I’m back where I started … sorting Strings.

So here’s how I was able to solve it … A little examination of the docs, and I found <display-column> attributes. The sortProperty attribute talks about , ‘This can be used when the column body is filled or a decorator is used and column should sort on undeorated values. ‘  Yes, and that is a direct quote of a typo !

So , to boil it all down, the Date column sorting doesn’t work by specifying a sortProperty and using a tableDecorator. It only works with a columnDecorator.

So I wind up with :
 

             <display:column style="white-space: nowrap;" property="<%=prop%>" sortable="true" sortProperty="<%=prop%>" decorator="<%=colDecorator%>" headerClass="sortable" titleKey="<%=label%>" />

in the .jsp. (I know it’s not JSTL ! Don’t get me started … ) And a simple column Decorator:
 

package com.wach.muni.webapp.util;

 

import java.text.SimpleDateFormat;

import java.util.Date;

 

import javax.servlet.jsp.PageContext;

 

import org.displaytag.decorator.DisplaytagColumnDecorator;

import org.displaytag.exception.DecoratorException;

import org.displaytag.properties.MediaTypeEnum;

 

public class ColDateDecorator implements DisplaytagColumnDecorator {

 

      private static SimpleDateFormat mmddyyyy = new SimpleDateFormat(

                  "MM/dd/yyyy");

 

      private String formatDate(Date date) {

            return mmddyyyy.format(date);

      }

 

      public Object decorate(Object columnValue, PageContext pageContext, MediaTypeEnum media) throws DecoratorException {

            Date date = (Date)columnValue;

            return formatDate(date);

      }

}

August 15, 2008

XDoclet, Hibernate, @hibernate.composite-id

Filed under: Java and all things closely related — Tags: — stevelibonati @ 12:27 am

After trying many variations of @hibernate.composite-id, XDoclet just wouldn’t generate the <composite-id/> element in the .hbm.xml. I’m using xdoclet-1.3-SNAPSHOT.jar, xdoclet-hibernanate-module-1.3-SNAPSHOT.jar distributed in appfuse-1.9.3.

Finally I found Gavin King’s entry on Jira. This eventually led me to the solution.

In the composite key class, add the @hibernate.property annotations for the properties of the composite key:

package com.wach.muni.model;

 

public class AppPropertyId implements java.io.Serializable {

 

      private static final long serialVersionUID = -6404027678245456081L;

 

      private String appName;

 

      private String keyName;

 

      public AppPropertyId() {

      }

 

      /**
       * @hibernate.property column="appName"
       */

      public String getAppName() {

            return this.appName;

      }

 

      public void setAppName(String appName) {

            this.appName = appName;

      }

 

      /**
       * @hibernate.property column="keyName"
       */

      public String getKeyName() {

            return this.keyName;

      }

 

      public void setKeyName(String keyName) {

            this.keyName = keyName;

      }

 

      public boolean equals(Object other) {

            if ((this == other))

                  return true;

            if ((other == null))

                  return false;

            if (!(other instanceof AppPropertyId))

                  return false;

            AppPropertyId castOther = (AppPropertyId) other;

 

            return (this.getAppName() == castOther.getAppName())

                        || (this.getAppName() == null ? false

                                    : (castOther.getAppName() == null ? false : this

                                                .getAppName().equals(castOther.getAppName())))

                        && (this.getKeyName() == castOther.getKeyName())

                        || (this.getKeyName() == null ? false

                                    : (castOther.getKeyName() == null ? false : this

                                                .getKeyName().equals(castOther.getKeyName())));

      }

 

      public int hashCode() {

            int result = 17;

 

            result = 37 * result + this.getAppName().hashCode();

            result = 37 * result + this.getKeyName().hashCode();

            return result;

      }

}


 In the persistent class, use the @hibernate.id to annotate the composite-id class.

package com.wach.muni.model;

 

import java.util.Date;

 

/**
 * @hibernate.class table="AppProperty"
 */

public class AppProperty implements java.io.Serializable {

 

      private static final long serialVersionUID = 4481252152417733310L;

 

      private AppPropertyId id;

 

      private String value;

 

      private String updateUser;

 

      private Date updateTime;


      public AppProperty() {

      }


      public AppProperty(AppPropertyId id) {

            this.id = id;

      }


      /**
       * @hibernate.id
       */

      public AppPropertyId getId() {

            return this.id;

      }

 

      public void setId(AppPropertyId id) {

            this.id = id;

      }

 

      /**
       * @hibernate.property column="value"
       */

      public String getValue() {

            return this.value;

      }

 

      public void setValue(String value) {

            this.value = value;

      }

 

      /**
       * @hibernate.property column="updateUser"
       */

      public String getUpdateUser() {

            return this.updateUser;

      }

 

      public void setUpdateUser(String updateUser) {

            this.updateUser = updateUser;

      }

 

      /**
       * @hibernate.property column="updateTime" type="timestamp"
       */

      public Date getUpdateTime() {

            return this.updateTime;

      }

 

      public void setUpdateTime(Date updateTime) {

            this.updateTime = updateTime;

      }

 

}


Finally, the generated Hibernate mapping.

<?xml version="1.0" encoding="UTF-8"?>

 

<!DOCTYPE hibernate-mapping PUBLIC

    "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 

    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

 

<hibernate-mapping>

 

      <class name="com.wach.muni.model.AppProperty" table="AppProperty">

 

            <!-- Use of @hibernate.id for composite IDs is deprecated, use @hibernate.composite-id instead -->

            <composite-id name="id"

                  class="com.wach.muni.model.AppPropertyId">

 

                  <!-- Defining the key-property element from @hibernate.property tags is deprecated, use @hibernate.key-property instead -->

                  <key-property name="appName" type="java.lang.String"

                        column="appName">

 

                  </key-property>

 

                  <!-- Defining the key-property element from @hibernate.property tags is deprecated, use @hibernate.key-property instead -->

                  <key-property name="keyName" type="java.lang.String"

                        column="keyName">

 

                  </key-property>

 

            </composite-id>

 

            <property name="value" column="value">

 

            </property>

 

            <property name="updateUser" column="updateUser">

 

            </property>

 

            <property name="updateTime" type="timestamp"

                  column="updateTime">

 

            </property>

 

      </class>

 

</hibernate-mapping>


Blog at WordPress.com.