Skip navigation

I’ve been working recently on a project that heavily relies on Hibernate.  I’ve learned a ton about Hibernate in the past few months.  Hibernate is excellent, it really saves a ton of time when it comes to dealing with data object persistence.  However, it can be difficult sometimes to get it to work exactly the way you want it to.

Some of the more advanced mappings are not quite as simple as the documentation, and even the excellent book Java Persistence with Hibernate will have you believe.  For instance, mapping Map collections properly.  The cleanest way to manage a collection of child objects is like this:

Parent parent = new Parent();

Child child = new Child();

parent.getChildren().put(“someChild”, child);

HibernateUtil.getSession().save(parent);

This works very well (with cascade = all) by default.  In order for the above example to work, the collection has to be mapped with the “inverse = false”.  The inverse flag is a matter of some confusion.  It simply tells Hibernate who will control the relationship between the parent and the child.  If inverse = false, the parent is the master, if it is true the child is the master.  This means, if the child is the master, the parent property MUST be set or the relationship will not be persisted, even if you add the child to the parent’s children collection.  If the parent is the master, the object must be added to its children collection, or the relationship will not be persisted.  Underneath it all, the inverse flag manages how the queries during inserts and updates are written.

The problem occurs when deletion of children needs to be managed as well.  Add this bit of code to the above example:

parent.getChildren().remove(“someChild”);

HibernateUtil.getSession().save(parent);

In order for this to work, the cascade option needs to be set to “all-delete-orphan.”  This instructs Hibernate to delete the children if they no longer belong to the parent.  An important thing to note is: if the collection is mapped with inverse=”true” all-delete-orphan DOES NOT DO ANYTHING.  This is something that I did not see in my reading (although I’m sure it’s there somewhere).  When the collection is mapped with inverse = true, you need to set the child’s parent property to null, or manually delete the object.

If you are using inverse=false, you may run into a weird issue with all-delete-orphan.  If your child’s parent foreign key relationship is set to not allow null values in the database, you will receive errors like “ParentFK cannot be null.”  This is because prior to deleting the child object Hibernate executes a query like this: “update Children set ParentFK = null where ChildID = 99;” .  Why it does this is beyond me.  However I do know how to fix the issue.  What you have to do is set the map’s key column to not null, and set update = true.  Then, in the child mapping, set the parent many to one to not insert or update.  You will need to do the same for the index of the map mapping.  Here’s what the mappings should look like:

<map name=”children” cascade=”all-delete-orphan” inverse=”false”>

<key column=”ParentFK” not-null=”true” update=”true” />

<index column=”Name” type=”string” update=”true” />

<element type=”Child” />

</map>

The child’s parent property should look like this:

<many-to-one name=”parent” column=”ParentFK” class=”Parent” update=”false” insert=”false” />

<property name=”name” column=”Name” updated=”false” insert=”false” />

This will get rid of the nasty issue of Hibernate trying to update the foreign key to null before it deletes children.  Another problem I ran into with map collections is when the index property for the child object did not exist on the child iteself, but on an object that the child references.  To fix this, all you need to do is change the index to a map-key, and use a formula like so:

<map name=”children” cascade=”all-delete-orphan” inverse=”false”>

<key column=”ParentFK” not-null=”true” update=”true” />

<map-key column=”o.Name” type=”string” formula=”(select o.Name from Catalog.OtherObjects o where o.OtherObjectID = OtherObjectFK)” />

<element type=”Child” />

</map>

Notice I included the catalog name before the table name, this is required if you do not select a default database in your hibernate configuration (I do this because we have multiple databases which our objects are stored in).

Well, hopefully these findings are helpful to others.  I’m sure they are not original, but I had a hard time digging this information up online.  As a matter of fact I did not find this information anywhere out there surprisingly.  I had to piece it together by going through the documentation and running unit tests.

Advertisements

If you are looking for a simple, easy to use and configure web framework for Java, Grails is the way to go.  I spent a ton of time looking around for Java web frameworks and I landed on Grails.  I am in the process of rebuilding my company’s public facing website as well as their intranet.  Because the two sites need access to the same data, I decided to build a common class library which handles all data access.  Although Grails is meant to handle all mapping and data acess for you, it was pretty easy to work in my existing data access project.

The first thing I would like to note about Grails is that there is virtually no configuration.  You will have to edit one or two configuration files if you want to customize certain aspects of the application, but unlike other frameworks, there is ZERO xml configuration.  I have run into a number of challenges as my website, like all websites, has a bunch of unique features.  Due to the excellent documentation for Grails I have had little to no trouble overcoming these challenges.

Because my application uses it’s own set of class factories, I was not able to simply drop my hibernate.xml.cfg and mapping files into grails.  I need to have complete control over how the objects are loaded and saved to the database.  This meant I would need something similar to the OpenSessionInViewFilter.  The problem with adding a fitler is that I didn’t want to open up sessions for every request, just requests that needed acess to the data.  After reading through the documentation I found that Grails has support for controller filters, here is a simple way to open up and close sessions for Hibernate in a grails filter.  Drop a file named HibernateFilters.groovy into the grails-app/conf directory:

public class HibernateFilters {
def filters = {
ensureSession(controller:’*’, action:’*’) {
before = {
HibernateUtil.getCurrentSession().beginTransaction();
}

afterView = {
HibernateUtil.getCurrentSession().getTransaction().commit();
}
}
}
}

Between tag libraries, templates and layouts, building UI in a Grails application is a breeze.  The UI code is so clean because of the way the rendering engine works.  There is no need for server side code in the views (although it is possible to put it there).

AJAX support is excellent in Grails.  It solves the common problem of rendering of UI on the server side as well as the client side.  It is simple to render a template or view and return it in an AJAX call.  This is huge, becuase you won’t find yourself writing the same UI code twice.  In my experience with .NET this was one of the most frustrating parts of building AJAX applications.  Also, Grails makes it really simple to render JSON and XML.  You can simply use “as JSON” or “as XML” in your render calls.

All in all, Grails is a quick and easy to use framework that provides all the tools a web developer needs to build a rich and dynamic web application with minimal headaches.  Out of the frameworks I’ve used (ASP.NET, CakePHP, Wicket, Tapestry), Grails is definitely at the top of the list (followed closely by CakePHP).

Check out the official grails website.

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

Recently I was tasked with building a pretty smiple CMS for my companie’s websites.  The major problem I ran into was picking a rich text editor that is easy enough to use with dojo.  There are a number of choices out there, but every single one has major problems.  These major problems include: cost, bugs, eas of use etc.  For instance, FCK is free, but it has a number of bugs.  TinyMCE is free as well, but it doesn’t play nicely with dojo.  Dojo has a built in rich text editor, but from what I understand building plugins for it is an absolute nightmare.  There are a ton of other free ones out there, but a lot of them seemed like they didn’t have very large user bases or very good support.

I decided to go with TinyMCE because it has a wide set of features and the plugins seem easy enough to develop.  The problem with TinyMCE at first glance was it didn’t look like it’d be easy to use with dojo.  The documentation is pretty poor in that it doesn’t have many examples.  I struggled for hours trying to find an example that would allow me to create a TinyMCE editor on the fly.  Finally (no thanks to the documentation), I came up with this:

dojo.provide("my.widgets.RichEditor");

dojo.require("dijit._Widget");
dojo.require("dijit._Templated");
dojo.require("dijit.form.Button");
dojo.require("dijit.form.TextBox");

dojo.declare("my.widgets.RichEditor", [ dijit._Widget, dijit._Templated ], {
	templatePath :dojo.moduleUrl("my", "widgets/layout/RichEditor.htm"),
	templateString :"",
	widgetsInTemplate :true,
	width: 600,
	height: 400,
	editor: null,
	value: "",
	editorNode: null,
	initialized: false,

	postCreate : function() {
		this.inherited(arguments);

		var ed = new tinymce.Editor(this.editorNode.id, {
			theme: "advanced",
			plugins : "safari,spellchecker,pagebreak,style,layer,table,advhr,advimage,advlink,emotions,iespell,inlinepopups,insertdatetime,media,searchreplace,print,contextmenu,paste,directionality,noneditable,visualchars,nonbreaking,xhtmlxtras",
			theme_advanced_buttons1 : "save,newdocument,|,bold,italic,underline,strikethrough,|,justifyleft,justifycenter,justifyright,justifyfull,|,styleselect,formatselect,fontselect,fontsizeselect",
			theme_advanced_buttons2 : "cut,copy,paste,pastetext,pasteword,|,search,replace,|,bullist,numlist,|,outdent,indent,blockquote,|,undo,redo,|,link,unlink,anchor,image,cleanup,help,code,|,insertdate,inserttime,preview,|,forecolor,backcolor",
			theme_advanced_buttons3 : "tablecontrols,|,hr,removeformat,visualaid,|,sub,sup,|,charmap,emotions,iespell,media,advhr,|,print,|,ltr,rtl,|,fullscreen",
			theme_advanced_buttons4 : "insertlayer,moveforward,movebackward,absolute,|,styleprops,spellchecker,|,cite,abbr,acronym,del,ins,attribs,|,visualchars,nonbreaking,template,blockquote,pagebreak,|,insertfile,insertimage",
			theme_advanced_toolbar_location : "top",
			theme_advanced_toolbar_align : "left",
			theme_advanced_statusbar_location : "bottom",
			theme_advanced_resizing : true,
		});

		setTimeout(function() { ed.render(); }, 50);

		this.editor = ed;
		this.editor.onInit.add(dojo.hitch(this, function() {
			this.initialized = true;
			this.setValue(this.value);
		}));
	},

	setValue: function(value) {
		if (this.initialized) {
			this.editor.setContent(value);
		} else {
			this.value = value;
		}
	},

	getValue: function() {
		if (this.initialized) {
			return this.editor.getContent();
		} else {
			return this.value;
		}
	},

	destroy: function() {
		this.inherited(arguments);
		this.editor.destroy();
		tinyMCE.remove(this.editor);
	}
});

That is a dojo widget that allows you to use TinyMCE.  I haven’t been brave enough to try and load the TinyMCE js on the fly, I will have to try that one later on…

Who is this article for?

This article was written for users who have large collections of music, which are sick and tired of the long load times, slow response, and horrible interfaces associated with iTunes, Windows Media Player, and the Zune Software (just to name a few of the widespread players). This article is NOT written with any sort of bias towards Apple, Microsoft, and/or any other parties, but rather from personal frustrations, experience, and findings to solve a problem encountered by myself.

A little background…

I’ve finally centralized all of my music collection onto a new D-Link DNS323 NAS in my home. No longer are the days of switching between four different machines to try and find a certain album or song. With this consolidation came a catalog consisting of over 70GB of MP3s, which amounts to approximately 17,500 songs. I’m not sure if anyone else has tried managing this amount of music in iTunes, Windows Media Player, or the Zune Software, but you end up with a hell of a mess…It’s tough or takes a while to edit multiple files at the same time to organize your collection, it takes FOREVER to load (especially over the network), and you’re locked in to a product that’s made for your mainsteam, “I don’t want to have to think, thinking is hard.” type of user that requires flashy, stupid people, waving their arms all over the place in a TV commercial.

The solution?

Amarok. This music player just kicks ass. It uses SQLLite internally, but can also use PostgreSQL or MySQL as well for its cataloging and statistics. This in-turn makes for a damn fast loading catalog and it even works fast with your catalog attached over the network! It has lots of great features, is easily extensible, and makes editing multiple files and organizing your catalog easier than before!

The problem?

It runs on Linux and is not native to Windows. I need to be running Windows XP Pro on this machine, because it’s mainly built for my gaming addiction…However, I want to use Amarok at the same time, so I decided to find the easiest way to run Amarok on top of XP…and so the trials began…

KDE on Windows…aka, crap, I can’t believe I bothered trying.

Well, I first tried the method described on the Amarok download page for Windows, which resides here. This required installing KDE on Windows…I figured this would be pretty sweet, since it would pretty much be a seemless desktop integration. Well, I got Amarok up and running in it, but it was an older version, and I couldn’t get it to catalog my music on my network share what-so-ever. The only other option would be to figure out how to compile the latest, stable, version of the code for Amarok and get that into KDE on Windows…Yeah, I’m all set with spending all that time.

andLinux…and, if you have x64 you’re screwed

Second, I decided to try a solution that uses andLinux, which is posted on the Amarok forums here. Well I can’t say that this does or doesn’t work, because low-and-behold, there is no 64-bit andLinux build, so I couldn’t test this at all.

MS Virtual PC…better, but still nothing

Third, I decided to just say screw it and install Ubuntu 8.10 into a Virtual Machine using Microsoft Virtual PC 2007. Now MS Virtual PC 2007 only supports 32-bit OS’, so I downloaded the 32-bit image of Ubuntu and gave that a go…Holy crap was the video messed up during the installer, so I pressed F6 and switched into the ‘Safe Graphics Mode’ and was able to install. Once I finally got it up and running, there were still some issues like messed up graphics during boot and no sound. So I tried all the tricks that are hanging around the net for the graphics, and that worked…Then I tried the sound-card tricks, but no go with 8.10…It just won’t see the emulated Sound Blaster 16.

VirtualBox…F**k yeah! (to the tune of DVDA’s America F**k Yeah!)

So, I searched some more, and then realized that VirtualBox is now available for Windows AND has a 64-bit version too…go figure! So at this point I decided to install VirtualBox and try out Ubuntu 8.10 in a VM through that instead. I also downloaded the 64-bit ISO of Ubuntu at this point, since there’s no point in not using my machine’s 64-bit capabilities. I fired up the VM and got an error saying that the 64-bit OS wasn’t supported (can’t remember the exact wording)…The answer to this problem? Go into the Settings of the VM and then to the Advanced tab. You’ll see a property called Enable VT-x/AMD-V, check this box off, then go to the Audio properties and select the ICH AC97 controller and the Windows DirectSound Host Driver, and you’ll be good to go.

You’ll probably still see some whacky video issues, but just disregard them for now. After installing Ubuntu, make sure you run the Update Manager (System -> Administration -> Update Manager) Once that’s done, open up a new Terminal (Applications -> Accessories -> Terminal) and type in sudo aptitude install amarok and you’re on your way to a much faster and enjoyable music-playing and cataloging experience!

One remaining thing you’ll want to do after getting your Ubuntu Guest OS up and running, is to install the Guest Additions for a more seamless experience. To do this click on the Devices menu within the window the OS is running in, then click on Install Guest Additions… This will mount a new image to cdrom0, which you’ll see appear on the desktop of the Ubuntu machine. Ignore the error about not being able to run it, and open a new Terminal (Applications -> Accessories -> Terminal) Then go to the mounted cdrom image by typing cd /media/cdrom0 Next, if you’re running 64-bit, type sudo ./VBoxLinuxAdditions-amd64.run otherwise type sudo ./VBoxLinuxAdditions-x86.run Enter your password, and then reboot when it’s done installing. Done!

One AMAZING difference I found between running Ubuntu on MS VPC vs. VirtualBox was about a 4x improvement in overall performance! It loaded 4x faster, it compiled and installed the same updates approximately that much faster, and there was no ‘visual lag’ when attempting to move the mouse around or click on a menu or button.

Other

Sound issues? Check here.

Need to mount a remote share on your NAS? Check here.

One of my tasks with Online College is to frequently post to discussion threads. They require that you do a minimum of ‘x’ posts per week, depending on the class. The way the threads are structured, it’s really tough to tell how many posts you’ve done for the week if you’ve forgotten.

Each post lists your full name, so it should be pretty easy to simply do a ‘Find (CTRL+F) ‘ in Internet Explorer 7 to scan the page and just count how many times your name comes up before the search wraps…Apparently it’s not.

This morning I found IE7 hitting my name twice when doing the search, even though when I scanned down the remained of the forums I found my name an additional 3 times.

Moral of the story: Be weary of Internet Explorer 7’s real ability to do a simple text search, which has only been around since the dawn of time.

Well, I spent quite a bit of time trying to figure out what was going on with IE this time…I use Dojo 1.1 for the application I’m building at work, and with their theming system for Dijit, they have a tundra.commented.css file, which includes each individual widget’s corresponding CSS file via an @import statement.

Well, I thought, “Hey, this is pretty cool and easy to manage, so I’ll do it this way for my widgets as well for development purposes (we compile our CSS into separate, compressed files, afterward)…Later on that day, QA came to me and said “Hey, this thing here isn’t showing up!” and “I can’t see this dialog anymore, do you know where it went?!” Sure enough, I went over to look at my QA colleagues’ screen, and things looked all mangled and wrong…But only in IE! What the heck could be going on?!

Come to find out, after using the awesome IE Development Toolbar (yes, I’m being sarcastic), the styles weren’t even loaded into the browser!! Well, WTF?! So I cleared my cache, deleted all cookies, etc., just to make damn sure nothing funky there was happening…(refresh)…Same thing, again.

After a while of fussing with style sheets, checking other browsers, getting the generic -218760-whatever error error code, etc. I finally decided to include them all via <link> tags. Now a new error, only, a run-time error this time! “Invalid Argument.” and then..BAM! a second error! “There is not enough free memory to perform this operation.” Again…WTF?!

So, at this point, I was desperate and decided to try including them via JavaScript. What’s that syntax again? document.createStylesheet? Hmmm…Better go look it up…”Whoa, what in the name is this?! You can only create 31 stylesheets with document.createStylesheet, but yet can add as many as you want if you do a document.createElement(“STYLE”) and append it to the DOM?! Hmmmmm….”

So now, I was curious, and imported only 31 stylesheets for widgets that I knew would show right away…Sure enough, everything I specified, show up perfectly! Then, to take it a step further, I decided to split out the master CSS file with all of the @imports, into 2 files, and then import those…Amazingly, it worked!

So there you have it, the answer to fixing the extremely ridiculous, and hardly documented, IE CSS @import issue.

Lessons learned

  • IE seems to support only 31 @import statements per CSS-file, <link></link> tags on a given page, or creations of stylesheets via document.createStylesheet
  • You may noticed styles start getting ‘lost’ or ‘messed-up’, or you may get the errors “Invalid argument.” or “There is not enough free memory to perform this operation.” when this limit is reached.
  • To get around this limitation…
    • Split your @imports into 2 or more files, and then load those files.
    • Use document.createElement(“style”) statements and append those items to the HEAD element
  • IE most likely uses the same internal methods for @imports and/or <link></link> tags, that document.createStylesheet uses, due to this 31-limit.

Resources

createStyleSheet Method – http://msdn.microsoft.com/en-us/library/ms531194(VS.85).aspx

For a long time I have been trying to find the right JavaScript framework for serious AJAX development.  At this point there are a few choices.  However, they all have a long way to go.  It seems that Dojo is the most mature one around and it has a pretty good user base.  However the documentation and examples still are lacking.

Recently I picked up a small side project.  I didn’t want to spend a whole lot of time on it so using a JavaScript framework would really speed things up.  I chose Dojo and development has gone fairly smoothly.  The only issue is there are some real mysteries about the framework that I had a hard time solving.

The first issue is the ComboBox and FilteredSelect.  I had the hardest time finding an easy way to simply add items to them.  Dojo uses a data model that is similar to ASP.NET data binding.  The difference is in ASP.NET you can add individual items to all the controls instead of binding if you want to.  With Dojo you cannot (from what I can tell).  So, the easiest way to add items to a ComboBox or FilteredSelect is this way:

var options = new dojo.data.ItemFileWriteStore({data: {identifier: ‘value’, items:[]}});
for (var i = 0; i < collection.length; i++) {
options.newItem({name: collection[i].Name, value: collection[i].ID});
}
dropDownNode.store = options;

Take note of the identifier portion of the ItemFileWriteStore constructor.  It tells the drop down the property name of the item that corresponds to the value of the drop down item.  I had a really hard time finding this information, and when I did find it, it wasn’t in the official documentation.  This is a very simple concept that any UI developer needs to know when building UI.

The next issue I faced, and actually I had faced in the past, was using the Dojo grid.  I had no luck with the grid in the past and decided I wouldn’t waste my time trying again.  The reason it is so difficult is getting it to bind correctly with JSON is not very easy.  The JSON has to be formatted just right or the grid will not work, and I’ve never been able to get it formatted right apparently because the grid has never worked for me.

So I decided to build my own simple grid.  I didn’t need anything complicated, it just needs to display in a table format and allow the user to select a row (for editing, deleting etc.)  Below is the solution I came up with.

Here’s the template:

<table class=”dojoxGrid-row-table list” dojoAttachPoint=”containerNode”>
<thead dojoAttachPoint=”headerNode” class=”dojoxGrid-header”>

</thead>
<tbody dojoAttachPoint=”bodyNode”>
<tr>
<td><img src=”images/5-1.gif” /></td>
</tr>
</tbody>
</table>

And the widget:

dojo.provide(“hockey.widgets.list”);

dojo.declare(“hockey.widgets.list”,
[dijit._Widget, dijit._Templated],
{
templatePath: dojo.moduleUrl(“hockey”,”widgets/layout/list.htm”),
templateString: “”,
list: null,
columns: null,
selectedItem: null,
emptyListText: “”,

selectedId: function() {
if (this.selectedItem != null) {
var returnValue = this.list[this.selectedItem.itemRowIndex][“ID”];

if (!isNaN(returnValue))
return parseInt(returnValue);
}

return 0;
},

selectedIndex: function() {
if (this.selectedItem != null) {
return this.selectedItem.itemRowIndex;
}

return 0;
},

removeRow: function(index) {
if (this.bodyNode.childNodes.length > index) {
this.bodyNode.removeChild(this.bodyNode.childNodes[index]);
}
},

postCreate: function() {
this.inherited(“postCreate”, arguments);
},

clear: function() {
if (this.headerNode.hasChildNodes()) {
while (this.headerNode.childNodes.length > 0) {
this.headerNode.removeChild(this.headerNode.firstChild);
}
}

if (this.bodyNode.hasChildNodes()) {
while (this.bodyNode.childNodes.length > 0) {
this.bodyNode.removeChild(this.bodyNode.firstChild);
}
}
},

showLoading: function() {
this.clear();

var row = document.createElement(“row”);
var cell = document.createElement(“cell”);
var loading = document.createElement(“img”);

loading.src = “images/5-1.gif”;

cell.appendChild(loading);
row.appendChild(cell);

this.bodyNode.appendChild(row);
},

bind: function() {
var instance = this;

this.clear();

// generate columns
if (this.columns != null && this.columns.length > 0) {
for (var columnIndex = 0; columnIndex < this.columns.length; columnIndex++) {
var header = document.createElement(“th”);
header.innerHTML = this.columns[columnIndex].headerText;
header.className = “dojoxGrid-cell”;

this.headerNode.appendChild(header);
}
}

// generate rows
if (this.list != null && this.list.length > 0) {
for (var itemIndex = 0; itemIndex < this.list.length; itemIndex++) {
var row = document.createElement(“tr”);
row.className = “dojoxGrid-row”;
row.itemRowIndex = itemIndex;

// loop through each column
for (var columnIndex = 0; columnIndex < this.columns.length; columnIndex++) {
var cell = document.createElement(“td”);
var cellValue = “”;

if (this.columns[columnIndex].formatString)
cellValue = this.list[itemIndex][this.columns[columnIndex].dataField].format(this.columns[columnIndex].formatString);
else
cellValue = this.list[itemIndex][this.columns[columnIndex].dataField];

if (!cellValue)
cellValue = ” “;

cell.innerHTML = cellValue;

if (this.columns[columnIndex].bindCallback) {
this.columns[columnIndex].bindCallback(this.list[itemIndex], cell);
}

row.appendChild(cell);
row.onclick = function() {
instance.selectItem(this);
}
}

this.bodyNode.appendChild(row);
}
} else {
var row = document.createElement(“tr”);
var cell = document.createElement(“td”);

cell.innerHTML = this.emptyListText ? this.emptyListText : “Nothing to show”;

if (this.columns)
cell.setAttribute(“colspan”, this.columns.length);

row.appendChild(cell);
this.bodyNode.appendChild(row);
}

this.containerNode.style.display = “none”;
setTimeout(function() { instance.containerNode.style.display = “table”; }, 50);
},

selectItem: function(item) {
// unselect the last item
if (this.selectedItem != null) {
dojo.removeClass(this.selectedItem, “dojoxGrid-row-selected”);
}

// select the next item
if (item != null) {
dojo.addClass(item, “dojoxGrid-row-selected”);
this.selectedItem = item;
}

console.log(this.selectedItem);
}
});

It is a very simple grid, but it does exactly what I need it to.

The last issue I faced was uploading a file.  I wanted to be able to upload an image file using Dojo without having to use something like Adobe AIR (although it is really cool, I didn’t have time to learn it for this project).  After much searching around I found a few examples.  Most of the examples I found were for older versions of Dojo and did not work.  I finally pieced together exactly what I needed to do.  All that needs to be done is the upload input has to be wrapped in a form.  The form should be built just like it would if an actual postback were occurring.  Then, to upload the file use the following code:

// gather all parameters from a form
dojo.io.iframe.send({
form: “imageForm”,
hanldeAs: “text”,
load: function(data) {
// handle callback here
},
error: function(data) {
// handle errors here
}
});

The form property should be the id of the form you want to post.  Make sure the form’s enctype is multipart/form-data and the method is post.  On the page that the form is being posted to make sure to return something.  And for whatever reason, that something has to be wrapped in a textarea tag or Dojo will assume an error has occurred.

There you have it, things that I have learned about Dojo that do not seem to be common knowledge.

In perusing the Ubuntu forums, I have run into a ton of posts asking how to install gnome themes (gtk themes / metacity themes, icon themes, emerald).  I have decided to share what I’ve gathered over the last couple years.  Although it’s rather simple, I couldn’t find this information anywhere.

GTK / Metacity Themes

To install GTK and Metacity themes, all you need to do is extract the archive to the correct place.  Both GTK and Metacity themes can go into the same themes folder.  The thing most people don’t realize, and nobody mentions for the most part, is that to properly install a theme you need to put it in the /usr/share/themes directory.  The reason is so that it’s installed for all users.  The reason you want to install it for all users is so that when you run an application with root privelages it uses your theme.  So, pretty much anything in the System -> Administration will not use the theme if you do not install it into the /usr/share/themes directory.  Before you install, you need to determine the type of archive.  Most archives are in tar.gz format.  There are some that are in tar.bz2 format as well though.  To install a theme that’s packaged in a tar.gz file run the following command:

sudo tar xzvf name-of-archive.tar.gz –directory=/usr/share/themes

To install themes packaged in a tar.bz2 file run this command:

sudo tar xjvf name-of-archive.tar.bz2 –directory=/usr/sahre/themes

Once the extraction is complete, all you need to do is go to System -> Preferences -> Themes, click customize and pick the new GTK / Metacity theme.

Icon Themes

Installing an icon theme is equally simple.  The same deal applies, you just need to extract to the correct location so that programs run as root use the correct icons.  Also, see my post about Nautilus and emblems, it will make your icon themes much prettier.  The correct directory for icon themes is /usr/share/icons.  To install a theme that’s packaged in a tar.gz file run the following command:

sudo tar xzvf name-of-archive.tar.gz –directory=/usr/share/icons

To install themes packaged in a tar.bz2 file run this command:

sudo tar xjvf name-of-archive.tar.bz2 –directory=/usr/share/icons

Emerald Themes

If you don’t know what Emerald is, you should check it out.  It’s a window decoration manager that does a really nice job.  To use it you’ll need a compositing manager such as compiz.  To install emerald simply run this command:

sudo aptitude install emerald

To install a theme for Emerald go to System -> Preferences -> Emerald Theme Manager, click the Import button, and select the .emerald file.  Your new theme will show up in the Emerald Theme Manager theme list, to apply it just click on it.

That’s pretty much it, installing a theme is really easy but again I didn’t see any comprehensive guides so I figured I’d share what I know.  Hopefully this helps people customize Ubuntu to their liking.

I use Ubuntu, and for anyone who hasn’t tried it (especially if you use windows) give it a shot.  You will be very impressed.  This weekend I was looking for a really good icon pack.  After the upgrade to hardy I stopped using Thunar for my file manager.  This is because the new version of Nautilus has fixes for a lot of the things I had problems with.  However, there were a few issues with the icon themes I have been trying.  I’m actually using an icon theme that’s in the ubuntu repositories.  The icon theme is used by Ubuntu Studio, which is a version of Ubuntu aimed at multimedia development.

Obviously when picking an icon pack the most important thing is how they look graphically.  A lot of new icon themes have issues with emblems.  Most noticeably the desktop emblem which you see in your home folder on the desktop folder.  A lot of icon themes are missing this emblem, so what you see is the default gnome desktop emblem.  A lot of times the default emblem just doesn’t fit well.  What I’ve found is that most icon themes actually have a desktop icon, they just aren’t duplicated in the emblems directory.

Lame Desktop Emblem

So, what you can do is copy the desktop icon into the emblems folder and rename it to emblem-desktop.svg (or png depending on the type of theme).

After this change, it looks like this:

Much Better Desktop Emblem

There’s also a bug in Nautilus that will cause the emblems to be cut off if the icons are sized above 100%. You can see this in action here:

Cut Off Emblems

There’s a pretty simple fix for this problem.  All that is needed to fix this issue is to create a single file that instructs Nautilus to render the emblems on the righthand side of the icons.  The bug seems to only occur when the emblems are close to the right edge of the icons.  The file needs to be called folder.icon, and it needs to go in your /usr/share/icons/{ThemeName}/{IconSize}/places/ folder.  The file should look like this:

[Icon Data]
AttachPoints=0,0|0,400|0,800

After the fix the emblems will look something like this:

Nice Full Emblems

You can tweak the attach points to your liking, but keep the x value near the left side.  The braces separate the position of each of the potential emblems.  Three should be enough, most icons won’t have more than two emblems.  Like I said, tweak away.  Hopefully this picky people out there (like me).