Skip navigation

Tag Archives: Dojo

Once again, I’m apparently working in uncharted territory for the Dojo framework. I am working on a rich application that uses a Tree View to navigate certain data. When a user makes changes, the tree needs to update itself. In previous applications I’ve resorted to refreshing the entire tree due to the fact that the dijit.Tree is not very developer friendly. It is difficult to set up in the first place due to lack of documentation. This time however, I decided to dive into the code and figure out how to refresh only certain nodes in the tree. Here is what I found, it is pretty straight forward:

dojo.provide("my.ContentTreeNode");

dojo.require("dijit.Tree");

dojo.declare("my.ContentTreeNode", [dijit._TreeNode], {

	_setIconClassAttr: function(iconClass) {
		dojo.query("> .dijitTreeRow > .dijitTreeContent > .dijitIcon", this.domNode).attr("class", "dijitIcon dijitTreeIcon " + iconClass);
	},

	updateChildItems: function(items) {
		this.clearChildren();
		// set the child items of the node
		this.setChildItems(items);

		this.tree._expandNode(this, true);
	},

	clearChildren: function() {
		var childNodes = this.getChildren();

		dojo.forEach(childNodes, function(childNode) {
			// remove the node from the tree's item node map
			delete this.tree._itemNodesMap[this.tree.model.getIdentity(childNode.item)];
			// remove each node
			this.removeChild(childNode);
		}, this);
	}

});

As you can see, I extended dijit._TreeNode in order to add a few methods.
In order to use this tree node, you’ll also have to extend the dijit.Tree:

dojo.provide("MyContentTree");

dojo.require("dijit.Tree");
dojo.require("my.ContentTreeNode");

dojo.declare("my.ContentTree", [ dijit.Tree ], {
	_createTreeNode: function(args) {
		return new my.ContentTreeNode(args);
	}
});
Advertisements

I just thought I’d share this since Dojo’s documentation is lacking useful information. It seems like no matter how many documentation websites the guys at Dojo create, they never fully document everything. Here is a perfect example. I couldn’t for the life of me find out how to programatically create a dijit.PopupMenuItem. Through trial and error I figured this out:

var menuItem = new dijit.PopupMenuItem({
	label: "My Item",
	iconClass: "myIconClass",
	popup: new dijit.Menu()
});

The key being the popup property. If you don’t initialize the popup property with an instance of a new dijit.Menu, the whole thing doesn’t work. You can then use menuItem.popup.addChild(…) to add new menu items to the popup menu item.

Introduction

I’ve been working dilligently lately on a new internal web application.  I made the decision long ago that the app would be a full blown AJAX application.  After deciding to go with DWR, Java and Hibernate running on Tomcat, I quickly noticed that my situation was pretty rare.  It seems as though everyone out there also uses Spring in combination with DWR and Hibernate.  I briefly went through the spring documentation and could not find a reason why I would need to use it for our application.

Along the way I’ve run into a few issues specifically with DWR and Hibernate and I’d like to share the experience for anyone else out there looking to do the same.

OpenSessionInViewFilter

I kept hearing about this magical filter for Spring that handles all of your Hibernate session management inside of AJAX application woes.  I was having all kinds of issues with sessions being closed, lazy loaded collections throwing exceptions etc.  The solution was this servlet filter:

public class OpenSessionInViewFilter implements Filter {

private static Log log = LogFactory.getLog(OpenSessionInViewFilter.class);

private SessionFactory sf;

public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {

try {
log.debug(“Starting a database transaction”);
Factory.beginTransaction();

// Call the next filter (continue request processing)
chain.doFilter(request, response);

// Commit and cleanup
log.debug(“Committing the database transaction”);
Factory.commitTransaction();

} catch (StaleObjectStateException staleEx) {
log
.error(“This interceptor does not implement optimistic concurrency control!”);
log
.error(“Your application will not work until you add compensation actions!”);
// Rollback, close everything, possibly compensate for any permanent
// changes
// during the conversation, and finally restart business
// conversation. Maybe
// give the user of the application a chance to merge some of his
// work with
// fresh data… what you do here depends on your applications
// design.
throw staleEx;
} catch (Throwable ex) {
// Rollback only
ex.printStackTrace();

// log the error
log.error(“Error in application”, ex);

try {
if (sf.getCurrentSession().getTransaction().isActive()) {
log.debug(“Trying to rollback database transaction after exception”);
Factory.rollbackTransaction();
}
} catch (Throwable rbEx) {
log.error(“Could not rollback transaction after exception!”,
rbEx);
}

// Let others handle it… maybe another interceptor for exceptions?
throw new ServletException(ex);
}
}

public void init(FilterConfig filterConfig) throws ServletException {
log.debug(“Initializing filter…”);
log.debug(“Obtaining SessionFactory from static HibernateUtil singleton”);
sf = Factory.getFactory();
}
}

It is very simple, it just opens up a transaction for each incoming request and either commits or rolls it back when all is finished.  The fact that it is a filter seems to be a key, I wrote a similar servlet that did not have the same effect.

DWR Converters

DWR comes with a number of converters that work really nicely.  The most interesting one happens to be the Hibernate3 converter.  It does a decent job.  There is one major issue with the converter though for my unique situation.  I have a number of data objects with properties that I do not want passed to the client.  For instance, any password property (for obvious reasons), image / blob properties etc.  DWR has a nice feature that you can use to exclude properties so they are not delivered to the client.  To use this feature you simply add the following to your objects convert node in dwr.xml:

<param name=”exclude” value=”properties, to, exclude” />

The only issue with this feature is it also excludes the property when transmitting data UP to the server.  So if you need to update one of these fields it is ignored on the way in.  Also, when you send the object to the server, DWR’s Hibernate3 converter starts with a fresh object.  This means, your properties that are excluded will be wiped out.  These are pretty serious problems that were not very easy to solve. The solution was to come up with my own converter which I named the H3SmartBeanConverter. This was adapted from a combination of the H3BeanConverter and the BasicObjectConverter which are a part of DWR.  You can download it below.

The first thing I had to change was the way the excludes are handled.  If the converter is performing an inbound conversion, all properties are converted regardless of the exclude rules:

// Access rules mean we might not want to do this one
// only check exclude rules when creating an outbound object, if write is required, allow everything to go through
if (!isAllowedByIncludeExcludeRules(name) && !writeRequired) {
continue;
}

I also needed lazy loaded properties to be initialized and passed down to the client as long as the property is not excluded.  To accomplish this I modified the section of code dealing with hibernate lazy properties:

if (readRequired) {
// This might be a lazy-collection so we need to double check
Object retval = method.invoke(example, new Object[] {});
if (!Hibernate.isInitialized(retval)) {
Hibernate.initialize(retval);
}
}

The final piece of the converter was the piece that fixes the problem of excluded properties being wiped out.  In the convertInbound method of the converter, I check to see if the object being converted extends my data object class.  If it does I load the object from the database prior to loading properties:

// if the bean is a data object, first load it from the database.  This will prevent properties that are not passed to the client from being deleted.
if (DataObject.class.isAssignableFrom(beanType)) {
// get the id field
String rawID = (String)tokens.get(“id”);
String[] split = ParseUtil.splitInbound(rawID);
String splitValue = split[LocalUtil.INBOUND_INDEX_VALUE];
String splitType = split[LocalUtil.INBOUND_INDEX_TYPE];

InboundVariable nested = new InboundVariable(iv.getLookup(), null, splitType, splitValue);
TypeHintContext incc = createTypeHintContext(inctx, (Property)properties.get(“id”));
Integer id = (Integer)converterManager.convertInbound(((Property)properties.get(“id”)).getPropertyType(), nested, inctx, incc);

if (id > 0) {
bean = Factory.get(beanType, id);
}
}

The Factory class is a basic wrapper for the Hibernate session object.  Factory.get calls Session.get under the covers.  Now, pre-loading the object from the database created yet another issue.  I was getting the error: “A collection with cascade=”alldeleteorphan” was no longer referenced by the owning entity.”  The problem here was once the object was loaded from the database, the collection properties were being overwritten with regular collection objects.  Hibernate uses specialized collection classes to handle persistence.  So, I had to add a bit of code to iterate through the collections instead of overwriting them:

// handle collections
if (Collection.class.isAssignableFrom(propType)) {
Collection collection = (Collection)property.getValue(bean);
collection.clear();

for (Object obj : (Collection)output) {
collection.add(obj);
}
} else if (Map.class.isAssignableFrom(propType)) {
Map map = (Map)property.getValue(bean);
map.clear();

for (Object obj : ((Map)output).entrySet()) {
Map.Entry mapEntry = (Map.Entry)obj;

map.put(mapEntry.getKey(), mapEntry.getValue());
}
} else {
property.setValue(bean, output);
}

Depending on which collections you are using you may need to add more code to handle them.  Finally, I had a working solution to my two biggest problems with DWR and Hibernate.  So far this solution has worked out really well, I have not run into any other issues.  If anyone has any suggestions as to how to do this in a simpler fashion, I’m all ears.

Download Java Classes:

H3SmartBeanConverter.java

OpenSessionInViewFilter.java