Thursday, November 12, 2009

Growl-based Hudson notifier

Since the Java-based notifier wasn't working properly for me, I quickly played around with a Cocoa-based notifier for Hudson. The result is a small piece of code that is neither properly refactored nor very clean.

It does its job, though, and that's why I felt like open source'ing it. Maybe somebody would like to help me flesh out the tool? The source code is hosted at Google Code:

http://code.google.com/p/hudson-on-cocoa/

Enhanced Fonts in cocos2d

One thing I came across in cocos2d was an issue with the font rendering. Since the fonts produced by Hiero bitmap font editor are very tightly packed on their containing images, rendering artefacts can appear when cocos2d displays the characters on screen.

Several people have reported these issues but I did not find a solution that fixes the problem in all cases. For that reason I have slightly amended the Hiero bitmap font editor: the modified editor outputs the fonts with more space between individual characters and amending the characters rectangles in the font definition files. This works very well and fixes the issue for me. I'll try to get in touch with Hiero's maintainer (who seems to be this guy) and will check whether there is any chance of having this feature integrated into the official version of the editor.

Another thing relating to fonts is the calculation of a string's width and, based on these calculations, a proper algorithm for line and page breaking. As I did not find such a facility within cocos2d source code, I enhanced the BitmapFontAtlas class to provide all of these. Basically, before calling [label setString:@""] to set the text for a label, you can assign a size to the label using

label.size = CGSizeMake(width, height);

If such a size has been assigned, a call to

[label setString:@"A very long string with reeeeeaaaaalllllllyyyyyyyyyyyyyyyyyyy long words and\nforced line breaks."];

will split the string according to the given size. The string will first be divided into lines (obeying the maximum width assigned earlier), then into pages (additionally obeying the maximum height). The first page will then be used as the string displayed by the label.

To choose a different page and figure out the amount of pages available within the label, you can then use the properties label.currentPage and label.pages respectively. If you assume that you want to switch to page 1 and this is within the amount of pages available, you can just assign label.page = 1 and be done.

If you don't assign a size before your call to [label setString:@""], the text is going to be rendered as with the unmodified version of BitmapFontAtlas. The only added benefit is that forced line-breaks using @"\n" are supported.

For this change, too, I'll try to contribute back to the cocos2d project. I will keep you updated!

Friday, November 6, 2009

Databases: Reserved Keywords

With technologies like Hibernate, deployment of database-driven application should've become a no-brainer: change the driver, change the DataSource's configuration in some XML file and off you go!

Reality is slightly different: a Grails-based web-application we're working on right now is configured for three different databases - depending on the system we're deploying to. During development, it's configured to use an in-memory HSQLDB. When working in development mode, things just work - there has not been a single reserver-keyword hickup so far.

For production, we have two different databases that we can test on: MS SQL Server 2005 and MySQL. For MS SQL Server 2005, first-time deployment has been a pain. During schema generation only the first error is that occurs is reported in the logs and thus we had to perform a couple of deploy -> bugfix -> deploy -> ... cycles as a couple of issues with reserved keywords popped up.

In MySQL, things have been going pretty well with no need to change mapping because of keywords. Just yesterday, however, I added a new field to one of our domain classes and ran into the first keyword issue on MySQL, too.

Now here's the list of keywords you need to avoid while working on the different databases. I'll try to keep this up-to-date as I encounter more such issues.

MS SQL Server 2005
  • "user" - Won't be accepted as table name in schema generation
  • "rule" - Won't be accepted as table name in schema generation
MySQL 5.1
  • "key" - Even though this keyword can be used as a column name in schema creation, adding to the table with an "insert into" will lead to an unhelpful 'you have a syntax error near ...' SQLGrammarException to be thrown

Which brings me to another question? Why does the Hibernate database dialect not automatically prevent these keywords to be used in mappings and provide replacement values? The dialect could, for example, prefix every known reserved keyword with a "_" character - deployment problem solved.

Sunday, November 1, 2009

My first barcamp

Yesterday I participated in my first barcamp. To be honest, I broke one of the most important rules about barcamps but more on that later.

The mobile devcamp Munich 2009 - or #mdc09 for short - took place at the Intel offices in Feldkirchen on Saturday. As I didn't know anything about barcamps until that day, here's a brief run down of what happened for anybody interested in visiting a barcamp one of these days.

Basically a barcamp is this event where loads of people show up, quickly introduce themselves (by the ritual, everyone's allowed to only give their name and three keywords or tags) and self-organize a day full of sessions on a broad range of topics. Whoever is ready to give a session enters the stage, briefly describes what they are going to talk about and asks for a show of hands indicating people's interest in the topic. In #mdc09's case, about 15 - 20 people gave a session and rooms were allocated based on the interest of the general public in these sessions. The sessions themselves are very casual and are not at all like the classic speeches that you'll know from conferences. Participation in the session is hugely encouraged - which leads to very fruitful discussions!

Besides all the sessions, the people who show up at a barcamp are really great! Everybody has a story and a lot of insights to share. People are very approachable and open to meet and greet.

Maybe I was a very lucky first-timer at a barcamp and things just went especially well. Still, if the next barcamp is only half as interesting it's going to be a big win! So in case you're interested, go visit a barcamp whenever you get the chance. You won't regret it!

Regarding the broken rule: there are a couple of rules encouraging participation in a barcamp. Amongst others, first-timers are supposed to give a session themselves during the first barcamp they visit. So ... I did not make it this far but am very much prepared to do so at the next barcamp I'll attend. To learn more about the other rules of barcamps, go visit @flobby's blog (German). For upcoming barcamps in your area, have a look at the official barcamp wiki.

Now, go, enjoy your first barcamp!

Friday, October 30, 2009

Running Actions in cocos2d during onEnter

I just discovered a requirement in the onEnter method when you try to run a duration-based Action in cocos2d. Whenever you're trying to do something like this within the onEnter method of a subclass of Layer:

[node runAction:[FadeIn actionWithDuration:1]];

make sure you call the super class' onEnter method. Otherwise the action will not perform what you try to achieve. Working code looks like this:



Hope this helps someone and saves some time!

Thursday, October 29, 2009

Note to Self on Void Pointers and CallFuncND

This is no news to anyone except for people who have never been exposed to void pointers (void*) before. Coming from a Java-background, I never had the opportunity (or dare I say hassle) to deal with pointers, obtaining memory addresses and de-referencing those. Whenever I was confronted with C / C++, I tried my best not to dive too deep into that matter.

Now in Objective C, I finally wanted to get rid of a compiler warning I was getting because I was dealing with void* as a data type. Basically, void* is a pointer to some data in memory of which the type is unknown to the compiler. Therefore, any address to some data in memory can be stuffed into a variable of type void*. Both of the following is valid:

void* data;
int i = 5;
float f = 6.5;

data = &i; // first assignment: works
data = &f; // second assignment: works, too

Now to get back to the data you stuffed inside, you cannot just de-reference it just like that. You need to cast it to the proper pointer type of the data that is stored at the particular address you're pointing to. In other words, you'd have to do the following (continuing the code above):

int* iPtr = (int*) data; // if data contains &i
float* fPtr = (float*) data; // if data contains &f

Since cocos2d uses void* as the data type to piggy-back any kind of user data within a CallFuncND action, I at least had to get that straight!

Sunday, October 18, 2009

Mac OS X auto-starting Tomcat

To complete my dive into continuous integration with Hudson I wanted to have Tomcat (the Servlet Container of choice) auto-run in the background on my Mac box.

First of all, download the latest Tomcat distribution from the project's homepage. Now change the script files in its "bin" directory so that they can be executed using "chmod +x *.sh". This is all you have to do to get a working copy of Tomcat on your system. Give it a spin by executing the startup shell script and navigating to "http://localhost:8080/". You will already notice that the startup will cause an icon to appear in your dock that you might rather not want to see (first of all it's ugly and clutters the dock and secondly, it doesn't provide much functionality anyways). We'll get rid of that icon a little later.

Now to have Tomcat startup automatically on system start, you have to create an entry in "/Library/StartupItems". By default, the directory is protected and you're not allowed to copy anything here. Use the Finder to change the permissions of the directory and grant yourself rights to read and write the directory. Now create a new directory "Tomcat" and place the following two files inside.

File "Tomcat":
#!/bin/sh

. /etc/rc.common

export CATALINA_HOME=/Applications/apache-tomcat-6.0.20
export JAVA_OPTS=-Djava.awt.headless=true

StartService ()
{
ConsoleMessage "Starte Tomcat Server"
su -c $CATALINA_HOME/bin/startup.sh
}
StopService ()
{
ConsoleMessage "Stoppe Tomcat Server"
su -c $CATALINA_HOME/bin/shutdown.sh
}
RestartService ()
{
ConsoleMessage "Starte Tomcat Server erneut"
su -c $CATALINA_HOME/bin/shutdown.sh
su -c $CATALINA_HOME/bin/startup.sh
}
RunService "$1"
where you substitute "" for the user account that should be used for building your iPhone app (the name of your home directory should work). This setting is crucial as Xcode will not build your project unless your running build as the user whose key chain contains the iPhone developer certificate. If you're trying to build using a different account you will get error messages saying that a suitable provisioning profile could not be found.

File "StartupParameters.plist":


These files will allow Tomcat to automatically launch when the system starts. To make this work, give execution rights to the file "Tomcat" using "chmod +x Tomcat" in directory "/Library/StartupItems/Tomcat". Now change the ownership of the files and their folder using the command "chmod -R -v 0:0 Tomcat" in directory"/Library/StartupItems". This will give ownership of the files and the directory to root - a requirement by the service launcher.

You can now test whether things went smoothly by executing the command "sudo SystemStarter start Tomcat". If things go well you will notice that no icon appears in your dock on launch. This is due to the Java parameter "-Djava.awt.headless=true" that we specified in the script "Tomcat" above. If you would like to keep the icon, just comment the line using a "#".

If things do not go well it's most likely due to some missing permissions. Check the logs in the utility application "Console" that you can find in "/Applications/Utiltities".

This post followed the German description by Kai Surendorf and adjusted things where appropriate. The "java.awt.headless" tweak was found on Hardy Ferentschik's blog.

Now I'm off to write a small Hudson/Growl integration app since the Java-based notifier available on Hudson's site didn't seem to properly invoke Growl on my machine. Maybe the recent Growl update or Snow Leopard broke the tool.

Stay tuned!