Sunday, February 28, 2010

Java's Swing framework for Javascript and Canvas

It is 0051 on Sunday morning and I just had a wild thought...

I've been developing applications in Javascript using jQuery and taking an interest in Sproutcore. I love jQuery but most definitely subscribe to Sproutcore's architecture where it makes a very strong statement about web applications being distinct from web documents (there is a place for both).

...and then it occurred to me... why not try and implement Java's Swing framework in Javascript and use the Canvas element almost exclusively for web applications? Do away as much as possible with CSS and HTML - I don't think they're great for web applications anyhow...

I don't doubt that it'd be a lot of work implementing Swing in Javascript, but there's an awful lot of design to be leveraged there.

I think I'll sleep on it...

Wednesday, February 17, 2010

Does Grails lower the barriers of entry to programming?

Grails is the Groovy equivalent of Ruby on Rails and I've been working with Grails for the past couple of months.

I'm a big fan of programming by convention and adopted Apache Maven many moons ago for just this reason. Grails also promotes programming by convention and by and large its quite reasonable for building server hosted web applications.

However one thing in particular has been bothering me. It seems that there is a view that Grails saves you the trouble of worrying about what goes on underneath it. This attitude is similar to one where using ORMs such as Hibernate save you the need to understand databases.

These attitudes are fraught with dangers as has been beautifully pointed out by Joel Spolsky in his Leaky Abstractions article.

I would agree that Grails can assist in increasing productivity but not at the expense of knowing what goes on under the hood. As Joel puts it, "... the abstractions save us time working, but they don't save us time learning".

Today I came across a Grails situation where I compared a newly instantiated domain object with one obtained through GORM. From my perspective both objects were equivalent as they had the same identifiers. I had wrongly assumed though that GORM had adopted the Hibernate convention of providing hashCode() and equals() on a natural key. Because Grails does all this magic for me, I had just assumed that this would be done. Well, it doesn't; and that's fair enough; how would GORM know what the natural key is? (declaring the natural key could be a nice feature for GORM). The point is that I could quickly find the problem because I'm familiar with Java/JPA/Hibernate and know that hashCode() and equals() should be provided for natural keys.

My advice is to stay curious. Don't be content with copy and pasting a bit of code you found using Google, seeing it work but not understanding it. This situation will bite you on the bum when your product goes live and you get a problem that you can't diagnose because you don't really understand what the code is doing.

Programming computers is a complex business and that's a fact that should not be avoided. Embrace the complexity.

Saturday, February 13, 2010

Processing Javamail emails for specific mime types

I recently had to write some software that received an email and processed it for iCal events. I wanted my program to be tolerant of receiving the iCal events within the body or as a attachment. Furthermore if the attachment was a mail message then I wanted to look inside that email for the event and so on...

When I thought about the problem initially I thought that processing just the attachments of an email would be sufficient. That's not the case given Microsoft Outlook though. I quickly learnt that Outlook sends its iCal events within the body of an email and not as an attachment.

I ended up with the following bit of Javamail/iCal4j/Java code that should generally be useful for processing mail messages. In my case I'm looking for a text/calendar object but it could be anything of course. The addCalendarDataToCalendars function can be called recursively and adds any calendar objects it finds to the collection passed in. Enjoy.


/**
* Process a data handler's content looking for a
* calendar object.
*
* @param calendars
* the collection of calendars that we
* must add any found calendar to.
* @param dataHandler
* the data handler to use.
*/
private void addCalendarDataToCalendars(
Collection calendars,
DataHandler dataHandler) {
MimeType mimeType;
try {
mimeType =
new MimeType(dataHandler.getContentType());

if (mimeType.getBaseType()
.equals("text/calendar")) {
// Calendar data is what we're ultimately
// searching for.
CalendarBuilder builder =
new CalendarBuilder();
Calendar calendar = builder.build(
dataHandler.getInputStream());
calendars.add(calendar);

} else if (mimeType.getBaseType()
.equals("message/rfc822")) {
// Found an object representing an email
// address in its entirety.
Message mailMessage = (Message) dataHandler
.getContent();
addCalendarDataToCalendars(calendars,
mailMessage.getDataHandler());

} else if (mimeType.getBaseType()
.startsWith("multipart/")) {
// Found a bunch of attachments so
// process them each.
Multipart multipart =
(Multipart) dataHandler.getContent();
for (int i = 0, n = multipart.getCount();
i < n; ++i) {
Part part = multipart.getBodyPart(i);

String disposition =
part.getDisposition();

if ((disposition != null)
&& ((disposition.equals(Part.ATTACHMENT)
|| (disposition.equals(Part.INLINE))))) {
addCalendarDataToCalendars(calendars,
part.getDataHandler());

} else if (disposition == null) {
MimeBodyPart mimeBodyPart =
(MimeBodyPart) part;
addCalendarDataToCalendars(calendars,
mimeBodyPart.getDataHandler());

}
}
}

} catch (MimeTypeParseException e) {
logger
.warn("Ignoring attachment - could not " +
"parse the mime type of: "
+ dataHandler.getContentType()
+ " - " + e);
} catch (IOException e) {
logger.error(e);
} catch (ParserException e) {
logger
.warn("Ignoring attachment - could not " +
"interpret the calendar object: "
+ dataHandler.getName() + " - " + e);
} catch (MessagingException e) {
logger
.warn("Ignoring attachment - could not " +
"decode the calendar object: "
+ MimeUtility.getEncoding(dataHandler)
+ " - " + e);
}
}