Monthly Archives: August 2009

UnsatisfiedLinkError: checkExtVer when unit testing gwt-ext

I ran into an issue while trying to test some innocent looking GWT-EXT code for a Google Web Toolkit app:

[java]
@Override
public void onActivate( com.gwtext.client.widgets.Panel panel )
{
if ( !( panel instanceof MyCustomGwtExtPanel ) )
{
throw new IllegalArgumentException( "Panel must be a MyCustomGwtPanel." );
}

… snip …
}
[/java]

The test

I had one test to test that an exception is thrown:

[java]
@Test(expectedExceptions = IllegalArgumentException.class)
public void testOnActivate_unexpectedPanel()
{
Panel panel = new Panel();
new TripReportsPanelListener().onActivate( panel );
}
[/java]

Test failures

It was so simple, I guess it just had to fail:

Caused by: java.lang.UnsupportedOperationException: ERROR: GWT.create() is only usable in client code! It cannot be called, for example, from server code. If you are running a unit test, check that your test case extends GWTTestCase and that GWT.create() is not called from within an initializer or constructor.
at com.google.gwt.core.client.GWT.create(GWT.java:85)
at com.google.gwt.user.client.ui.UIObject.(UIObject.java:140)
… 23 more

By using GWTMockUtilities.disarm in the setup and GWTMockUtilities.restore in the teardown, I made some progress by ended up with this error instead:

java.lang.UnsatisfiedLinkError: checkExtVer
at com.gwtext.client.widgets.Component.checkExtVer(Native Method)
at com.gwtext.client.widgets.Component.(Component.java:108)

The solution

The solution is right there in the stacktrace from Google.

I extended GWTTestCase but continued to get the same errors. The reason is simple enough, but has bitten me a a couple of times before. GWTTestCase is written using the old JUnit 3 style tests. My tests were using TestNG annotations which tells TestNG to run the tests as TestNG tests. Consequently the setup and teardown methods in GWTTestCase never get called. JUnit acts the same way if you mix and match JUnit 3 and 4 style test declarations.

It’s been a frustrating experience with UnsatisfiedLinkErrors and NoClassDefFoundErrors, but I eventually got what should be a simple unit test working. And it sucks that my seemingly innocent unit test have so many dependencies. It also stinks that GWT must be started up to run the tests resulting in a feedback loop increase of 10-15 seconds.

(Solution) PS3: cannot perform background download with uninstalled content

PS3

I ran into an interesting issue while trying to download a video in the background on the PlayStation 3:

There is content that has not been installed. You must first install the content to be able to perform a background download.

Since I just got the PS3 Slim (the price drop was the selling point), I started downloading several demos of games.

It turns out just downloading the demo is not enough, each demo must be manually installed. If the game under the game menu has a clear bubble around the icon, then the game has not been installed. I had five demos downloaded but not installed.

So the solution to this: install your demos and addons. Then grab a drink and wait, the install is slow.

Not exactly a good user friendly experience.

Five code commenting anti-patterns

Bad comments can be a little bit of a problem, even if they appear harmless.

Bad comments can be a little bit of a problem, even if they appear harmless.

In some projects (most?) comments multiply like tribbles creeping up everywhere. Sometimes the comments are especially useful but other times comments useless are destructive to maintainability. Contrary to some beliefs, bad and inaccurate comments are not mostly harmless.

Bad comments take many forms. There are four forms that I think could be considered anti-patterns:

1. Programming 101 comments

Consider the following code snippet:

[java]
// Print "Hello World" to
// standard output without
// a new line.
System.out.print("Hello World");
[/java]

No doubt this sort of commenting is useful to students in a class, but in the real world even non-Java programmers can figure this one out. Programming 101 comments are most commonly used by students and recent graduates after learning a new language feature or library.

2. Stating the obvious comments

Consider the following code snippet:

[java]
// can use express checkout
if (shoppingCart.getItems().size() > 10)
{
System.out.println("Please be courteous; don’t use express checkout.");
}
[/java]

Consider stating the obvious comments as a code smell. In fact we can improve this method by making it self-documenting. Instead of writing a comment, make the code say it instead:

[java]
public static final int MAX_EXPRESS_CHECKOUT_ITEMS = 10;

if (canUseExpressCheckout(shoppingCart))
{
System.out.println("Please be courteous; don’t use express checkout.");
}

public canUseExpressCheckout(ShoppingCart shoppingCart)
{
return shoppingCart.getItems.size() <= MAX_EXPRESS_CHECKOUT_ITEMS;
}
[/java]

3. Comment Me! comments

“Comment Me!” comments are the lazy man’s way of commenting. If you’re getting paid by the number of lines or the number of comments, than this type of commenting is great. Otherwise, it’s a waste of your time to put them and a waste of time for the person who has to read this junk.

Don't let your code look like this.

Don't let your code look like this.

4. Code memorial comments

Code memorial comments proclaim “The code is dead. Long live the dead code!” Not exactly the kind of proclamation any new king would like to hear.

Rather than letting bad or good code die and receive a proper burial in your favorite code repository, these comments are embalmed and enshrined for all to see. When old code is enshrined, it takes away precious screen real estate from the living code.

I’ve heard several defenses of this anti-pattern. Requirements keep changing, we’ll just have to put it back. Make it easy to put it back.

I suspect the reason for holding onto dead code is deeper than any of that. Imagine spending hours or days working on a problem. No doubt it can be emotional to throw away your hard work.

Delete the old code anyhow. The new found cleanliness will be extremely liberating. Besides, you can always lay flowers by its grave in your version control system’s history.

5. I WAS HERE comments

Comments can take on the form of being a replacement for version control features (like CVS’ annotate command). These can take on forms similar to this:

[java]
// ARL 010109 begin
… some code here …
// ARL 010109 end
[/java]

There are several problems with I WAS HERE comments. First, it clutters code unnecessarily. Second, it duplicates functionality that already exists in version control systems. Third, it’s error prone.

the cake is a lie.

the cake is a lie.

The comment is a lie.

Comments always lie. If not now, they will soon. Often code changes but the comments do not. I have seen countless cases of comments that lied literally the second they were committed.

Bad/incorrect documentation is worse than no documentation. I suppose Stack Overflow isn’t exactly the best place to a consensus, but the comments and stories are interesting. In my not-so-humble opinion, misleading documentation – especially that that appears authoritative – really sucks.

Striving to make clean code, simple code (low cyclomatic complexity) and self-documenting code (good class, method, variable names and unit tests) do wonders to lessen the need for comments.

Gmail spam filter fail, is it hitting you too?

As a food, spam is good

As a food, spam is good

I’ve been noticing an alarming increase in spam making it through Gmail‘s spam filters. After talking with a friend about it at lunch last week, I suspect I’m not alone.

Kevin C. Tofel asks Is Gmail’s Spam Filtering Failing For You, Too?.My answer is a solid yes.

Kevin talks about it taking him 15-30 minutes a day to mark items as spam between his two accounts. Over the course of a year (365 days) he will have spent somewhere 90-182 hours dealing with spam. Ouch.

My situation isn’t nearly as bad, especially since I have only one inbox. I’d estimate daily 1-3 pieces of spam make it into my inbox per day. Since I normally keep my inbox completely empty, it doesn’t take much effort to mark the spam.

The spam making it through Google’s spam filter’s appears to be pretty standard and obvious stuff:

spam-sample

Currently my spam folder has 17 pieces of spam caught in the last 24 hours with 2 pieces missed. That’s a total of 19 pieces of spam with Google’s spam filter batting 88.24%. Compared to my way old hotmail account, that rocks.

Perhaps I shouldn’t bother marking spam? After all, I’m currently using 946 MB (12%) of your 7362 MB. Go ahead and spam me, my email can take it! Besides, what if I started taking up an interest in zoophilia and exotic “seex” positions?

Up next: Twitter spam.

Swift bank code is required to receive payments from the Apple iPhone App Store sales

I tried to add my USAA bank account as my bank account to receive payments from Apple’s iPhone App Store. Unfortunately USAA does not have a swift bank code (see their FAQ on the subject).

I emailed Apple on the subject, here’s their reply:

Dear Ash,

The iTunes App Store makes Applications available for sale to customers around the world. Apple uses various global currencies to pay you, depending on the customer’s location. Apple pays via wire or electronic transfer, using a “SWIFT code” to route these payments. If a SWIFT code is not on file for you, Apple cannot pay you for purchases made by global customers.

If you do not have an account with a bank that utilizes a SWIFT code, Apple recommends you establish such an account at your earliest convenience. Large, international banks utilize SWIFT codes. Credit Unions do not have this capability.

Apple does not recommend any particular bank, and recommends you research to determine which bank will best meet your needs.

Examples of US-based banks that utilize SWIFT codes:
Bank of America
JPMorgan Chase Bank
Wachovia Bank
Wells Fargo Bank
Citibank

Examples of Canada-based banks that utilize SWIFT codes:
CIBC
RBC
TD Canada

Apple may send multiple payments each month, depending on the regions and currencies in which you had activity. Your bank will handle any currency conversions when payments are applied to your account.

For reference, the locations and currencies are listed below.
United States – USD
Canada – CAD
Europe – EUR
United Kingdom – GBP
Australia – AUD
Japan – JPY
Rest of World – USD

If you have any further questions, please don’t hesitate to contact us.

Kind regards,

Callie
iTunes Finance
 Apple Inc.

I hope Arvest has a swift code, otherwise I’ll have to open yet another bank account with yet another company.

iPhone App Store sales (free download) statistics, first 10 weeks

As some of you may know, I’ve got an app in the iPhone App Store called Xbox Points. It’s a free app that does one thing: convert Microsoft Points to various currencies including USD, USD, AUD, CAD, EUR, GBP, and JPY.

Last I checked, there were three or four iPhone apps that have this exact functionality. Although they look more polished, all of them charged at least $0.99. So for my competition, mine is the only one that is free. Although some of the non-free versions also convert Nintendo Points.

You can check out the full data hosted on Google Fusion Tables.

Downloads per week (excluding updates)

On average the app is downloaded 529.2 times per week or 75.6 times per day. Not bad for a simple but functional app (I actually use it on occasion).

The below graphs and charts show the total sales (excluding updates) per week. I made a mistake and the second week is missing, but I think these charts get the point across quite well anyhow.

xbox-points-sales-per-week-2

xbox-points-sales-per-week

Total sales by country

Not surprisingly the United States leads the list of downloads per country.

To make this information more digestible, I’ve excluded 28 countries with less than 5 downloads.

xbox-points-sales-per-country

Updates per week

I submitted an update (it took 3 weeks to get approved, ugh). During the week of 6/29/2009 my update was released to the App Store.

Even if Xbox Points was a paid app, updates would be free.

xbox-points-updates-per-week-2

xbox-points-updates-per-week

What the…?

Just for fun, I’d like to point of some of the WTF-worthy review comments left for my app:

Review by =^.^=donkey on July 3, 2009:

I don’t know wat to do in this app do u get free points?

Review by xxRambo15xx on July 8, 2009:

I don’t know do u buy the points at whatever price u want or u get them 4 free

Review by Caddy239 on July 20, 2009:

How do u use this app do have 2 pay 4 it or is it free idk wat 2 do with it.

Review by pimp22juice on June 29, 2009:

How does this app work and do I get fre points

Review by Dsneek on July 28, 2009:

How do you even work this????????write back

Well, that’s the internet for you…

Hudson and the Sonar plugin fail: MavenInstallation NoSuchMethodError

No. Not this Hudson.

No. Not this Hudson.

We ran into an interesting and less than informative error when configuring Maven with our Hudson installation. Maven worked great, as expected, but the Sonar plugin stopped working and were causing builds to fail.

The error message wasn’t terribly helpful:


FATAL: hudson.tasks.Maven$MavenInstallation.forNode(Lhudson/model/Node;Lhudson/model/TaskListener;)Lhudson/tasks/Maven$MavenInstallation;
java.lang.NoSuchMethodError: hudson.tasks.Maven$MavenInstallation.forNode(Lhudson/model/Node;Lhudson/model/TaskListener;)Lhudson/tasks/Maven$MavenInstallation;
at hudson.plugins.sonar.SonarPublisher.getMavenInstallationForSonar(SonarPublisher.java:204)
at hudson.plugins.sonar.SonarPublisher.executeSonar(SonarPublisher.java:213)
at hudson.plugins.sonar.SonarPublisher.perform(SonarPublisher.java:177)
at hudson.model.AbstractBuild$AbstractRunner.performAllBuildStep(AbstractBuild.java:372)
at hudson.model.AbstractBuild$AbstractRunner.performAllBuildStep(AbstractBuild.java:360)
at hudson.model.Build$RunnerImpl.post2(Build.java:183)
at hudson.model.AbstractBuild$AbstractRunner.post(AbstractBuild.java:345)
at hudson.model.Run.run(Run.java:943)
at hudson.model.Build.run(Build.java:112)
at hudson.model.ResourceController.execute(ResourceController.java:93)
at hudson.model.Executor.run(Executor.java:119)

A little Googling, I found just two hits.

One result was helpful in which it said the Sonar plugin is compatible with Hudson 1.306+. Currently we’re running 1.303. We’re not exactly far behind, but apparently far enough behind.

Backing up Hudson

While there is a backup plugin for Hudson. The plugin would be ideal, but in case just installing the plugin screws something up, best do a manual backup.

The easiest way to manually backup Hudson is to just copy your Hudson working directory. However, space is limited for us, so a backup that was more selected was necessary. This script seemed to backup the most important configuration files (it wouldn’t make for a pretty recovery, but it’d work):

[bash]
#/bin/sh

cp $HUDSON_HOME/*.jar $NEWHUDSON_HOME/
cp $HUDSON_HOME/*.xml $NEWHUDSON_HOME/
cp -r $HUDSON_HOME/plugins $NEWHUDSON_HOME
for job in $HUDSON_HOME/jobs/*; do
echo "Processing $job"
mkdir -p "$NEWHUDSON_HOME/jobs/$job"
cp "$job/config.xml" "$NEWHUDSON_HOME/jobs/$job/config.xml"
done
[/bash]

All aboard!

All aboard!

All aboard the fail boat

The upgrade appeared to go well, but after manually starting the Windows service, I get an error. Amusingly, the hudson.err.log showed some slight inconsistencies:

Jul 27, 2009 2:38:03 PM hudson.model.UpdateCenter$DownloadJob run
INFO: Installation successful: hudson.war
Invalid or corrupt jarfile C:hudsonhudson.war

Hudson the Butler can’t make up his mind; it’s claiming success before imploding on itself.

Hudson recuperated

Skimming around and very annoyed my butler had blatantly lied to me, I noticed hudson.war was sitting at only 2 MB. Yah, that can’t be right.

Luckily the fix was easy: Hudson was successfully fixed by manually downloading the newest hudson.war and replacing the messed up version.

It turns out I really did not need to backup Hudson. Though naturally if I hadn’t backed up Hudson I will have needed my backup!

Upgrading to Hudson 1.317 solved the mysterious java.lang.NoSuchMethodError error. I would not have thought configuring a Maven installation rather than using Hudson’s default would cause issues. Go figure.