Monthly Archives: May 2009

MediaWiki bot using JWBF (Java)

wiki! wiki!

wiki! wiki!

One of the nice things about Java is there seems to be a friendly (or at least relatively friendly) API for everything.

Java Wiki Bot Framework (JWBF) is a Java framework for working with wikis running MediaWiki (the wiki software used by Wikipedia and all the other Wikimedia Foundation projects).

A little off subject: who thought it was a good idea to name two distinct but related things WikiMedia and MediaWiki? Good luck keeping those terms straight.

Back to JWBF: It is pleasantly easy to use. Admittedly, my needs are far from complex: replace the text of a couple of templates on a regular basis. It’s hardly glamorous programming, but it should save us some time at work. Now we’ll have more time to sing along with
Dr. Horrible’s Sing-Along Blog.
Invent a freeze ray.

Happily the library exists in the central maven repository, so getting it included in my project is easy enough:

[xml]
<dependency>
<groupId>net.sourceforge</groupId>
<artifactId>jwbf</artifactId>
<version>1.2-186</version>
<scope>compile</scope>
</dependency>
[/xml]

Actually using the framework is as easy as this:

[java]
MediaWikiBot mediaWikiBot =
new MediaWikiBot( "http://hostname/wiki/" );
mediaWikiBot.login( "CurrentlyDeployedBot", "password" );
Article article = new Article(mediaWikiBot.readContent(
"Template:Currently deployed/Arinc" ), mediaWikiBot );
article.setText( "Test" );
article.save();
[/java]

It’s always nice when things are this easy. *cheers*

One more thing: if you’re looking to create a bot or script for automatic or semi-automatic usage of Wikipedia, please review Wikipedia’s bot policy first.

Saving app data with the iPhone SDK and Cocoa is easy with NSUserDefaults

A quick look at NSUserDefaults suggests saving app data with Cocoa and the iPhone SDK is easy. I haven’t gotten much of a chance to use it so there might be some gotchas.

To store nonsensitive data using NSUserDefaults is as easy as a couple of lines of code:

[objc]
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:usernameTextField.text forKey:@"username"];
[defaults synchronize];
[/objc]

The last line is particularly important as it actually saves the data to disk.

Retrieving data is equally as easy:

[objc]
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSLog([defaults objectForKey:@"username"]);
[/objc]

NOTE: Using NSUserDefaults is a really bad idea for sensitive data like passwords. A significantly safer solution would be to use the iPhone’s keychain. Apple has an example found in GenericKeychain.

The API can be difficult to understand and cumbersome to use. Luckily, there is a solution for that from Buzz Anderson and it can be found on github.

SSH warns “it is possible that someone is doing something nasty!”

Let this be a warning to all: SSH knows what you’re doing and SSH is not pleased.

Innuendos aside, I got this nice little error trying to SSH into my brand new server at home. And by “brand new”, I mean it’s my wife’s old desktop that is now running Ubuntu Server 9.04.

[text]
$ ssh myserver
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that the RSA host key has just been changed.
The fingerprint for the RSA key sent by the remote host is
95:00:83:54:de:45:f1:g3:45:34:be:88:98:42:e2:c8.
Please contact your system administrator.
Add correct host key in /home/ashlux/.ssh/known_hosts to get rid of this message.
Offending key in /home/ashlux/.ssh/known_hosts:2
RSA host key for myserver has changed and you have requested strict checking.
Host key verification failed
[/text]

So how did this happen? Basically, I used to have my router setup to forward the SSH port to my computer. Basically my computer at work had the RSA key for my computer, not the new server.

Contrary to the tidbit about requesting “strict checking”, using the -o stricthostkeychecking=no gave the same result.

Luckily the error message gave me one big hint as to how to fix this: “Offending key in /home/ashlux/.ssh/known_hosts:2″. The solution was simple enough, just delete the second line of the known_hosts.

SSH is working again. *cheers*

potbs4j 0.4: Java library for Pirates of the Burning Sea web services

Pirates in your Java, sacking the classloader.

Pirates in your Java, sacking the classloader.

A few weeks ago I put together a Twitter publisher for Pirates of the Burning Sea (PotBS) for my wife.  She plays the game perhaps a bit too religiously. :-)  Anyhow, two things get published to twitter: (1) server status changes, and (2) in-game port status changes (who is battling who).

To make life a little easier, I wrote potbs4j, a Java library  to work with PotBS services. (My name for the library is genius, really!)  There really wasn’t any need to couple the twitter publisher with XML parsing and HttpURLConnection.

I doubt ayone will make use of the library, but if anyone is really interested in using it, I’ve worked out some usage instructions here.

If you’re using Maven (highly recommended), just include the potbs4j in your list of dependencies:

[xml]
<dependency>
<groupId>com.ashlux.potbs</groupId>
<artifactId>potbs4j</artifactId>
<version>0.4</version>
</dependency>
[/xml]

Since potbs4j does not exist in the central repository, you will also need to add my maven repository (it’s slow but works) to your pom.xml:

[xml]
<repositories>
<repository>
<id>ashlux-repository</id>
<url>http://www.ashlux.com/maven2/repo</url>
<releases>
<enabled>true</enabled>
</releases>
</repository>
<repository><id>ashlux-snapshot-repository</id>
<url>http://www.ashlux.com/maven2/snapshotRepo</url>
<snapshots>
<enabled>true</enabled>
</snapshots></repository>
</repositories>
[/xml]

You can get the server status with the following code:

[java]
ServerStatusService serverStatusService =
new ServerStatusServiceImpl("apikey", "userid");

// get list of all server statuses
ServerListDocument serverListDocument =
serverStatusService.getAllServerStatuses();

// get server status for a single server
ServerDocument serverDocument =
serverStatusService.getServerStatus(ServerName.Antigua);
[/java]

You can get the server status with the following code:

[java]
LandmarkStatusService landmarkStatusService =
new LandmarkStatusServiceImpl("apikey", "userid");
// get list of all port statuses for a server

PortListDocument portListDocument =
landmarkStatusService.getAllLandmarkStatuses(ServerName.Antigua);

// get list of all port statuses for a server
PortListDocument portListDocument =
landmarkStatusService.getLandmarkStatus(ServerName.Antigua, PortName.IRSH_PT);
[/java]

There are also two utility classes NationUtils (for converting a nation to a nationality) and PortUtils (for converting a PortName.Enum to a string).

An important thing to keep in mind: the library currently does NOT cache results, so you will need to handle this on your own.  Both of the services do implement a method called getMinimumUpdateFrequency that returns the minimum number of minutes you should wait until calling the service.

The source code is hosted on github in all its glory.

How to: HTTP POST and Google’s ClientLogin using Objective-C and the iPhone SDK

PLEASE NOTE (2009-08-12): You probably do not want to use this method (though it’s great for learning). A much better solution is to use the gdata-objectivec-client. Example code on using that library can be found in one of my projects hosted on github.

I’ve been working on an iPhone app that uses one of Google’s APIs.  The following appears to work just fine in a non-jailbroke phone.

Creating the request

[objc]

NSURL *url = [NSURL URLWithString:@"https://www.google.com/accounts/ClientLogin"];

NSMutableURLRequest *loginRequest = [NSMutableURLRequest requestWithURL:url];

[loginRequest setHTTPMethod:@"POST"];</code>

//set headers

[loginRequest addValue:@"Content-Type" forHTTPHeaderField:@"application/x-www-form-urlencoded"];

[loginRequest addValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];

NSString *requestBody = [[NSString alloc]

initWithFormat:@"Email=%@&amp;Passwd=%@&amp;service=finance&amp;source=%@",

username, password, [NSString stringWithFormat:@"%@%d", @"ashlux-igFinance-1.0"]];

[loginRequest setHTTPBody:[requestBody dataUsingEncoding:NSASCIIStringEncoding]];

[/objc]

This part is fairly straight-forward. The service=finance should be changed to the Google service you are using.  Also ashlux-igFinance-1.0 should be changed to the format company-appname-version.

The default timeout interval for the request connection is 60 seconds.  To change this, use setTimeoutInterval:

[objc]

[loginRequest setTimeoutInterval:30]; // set timeout for 30 seconds

[loginRequest setTimeoutInterval:90]; // set timeout for 90 seconds

[/objc]

Sending the request

To send the request, I am sending a synchronous request (which will block) to Google:

[objc]

NSHTTPURLResponse *response = NULL;

NSData *responseData = [NSURLConnection sendSynchronousRequest:loginRequest returningResponse:&amp;response error:nil];

NSString *responseDataString = [[NSString alloc] initWithData:responseData encoding:NSASCIIStringEncoding];

NSLog(@"Response from Google: %@", responseDataString);

[/objc]

Objective-C does have asynchronous IO (non-blocking), but that’s for another time.  For most purposes, asynchronous is the way to go to avoid blocking the application.

Reading the response

For my purposes, I am storing the data in a struct called GoogleClientLogin to represent Google’s ClientLogin data (that’s simple enough, so I will not list it here).  For simplicity, I am not interpreting the response status beyond success (code=200) and failure (code=anything else).

[objc]

if ([response statusCode] == 200) {

NSLog(@"Login successful.");

GoogleClientLogin *aGoogleClientLogin = [GoogleClientLogin alloc];

aGoogleClientLogin.username = username;

aGoogleClientLogin.password = password;

NSString *authToken = [[responseDataString componentsSeparatedByString:@"Auth="] objectAtIndex:1];

NSLog(@"Google authToken=%@", authToken);

aGoogleClientLogin.authToken = authToken;

return [aGoogleClientLogin autorelease];

} else {

NSLog(@"Login failed.");

return nil;

}

[/objc]

That is more or less all there is to it. There is room for better error handling, but the effort would be better spent elsewhere on the applcation.

Next up, I’ve really need to figure out how to work in unit tests using Xcode.  My previous attempts at getting OCUnit working were full of fail special thanks to Apple’s out-of-date documentation.

Unexpected error 0xE800003A application verification failed

I’ve been spending time lately learning to use Objective-C and the iPhone API.  While trying to deploy a sample iPhone app through Xcode, I ran into a pesky error message.

Your mobile device has encountered an unexpected error (0xE800003A) during the install.

Application verification failed.

Configuring Xcode is a quick and easy two step process (assuming you’ve setup the appropriate certificates, provisioning, etc. with Apple):

  1. Set your bundle identifier in Info.plist (This is your APP ID excluding the Bundle Seed ID).
  2. Set the project code signing identity to your provisioning profile.

That’s it!

Of course, I ran into three different errors when trying to deploy it to my iPhone:

  1. Bundle identifier is incorrect (typo or bundle seed ID was included).
  2. Incorrect provisioning profile was selected.
  3. Clean and rebuild and deploy (Build > Clean All Targets).

The first two problems where easy to resolve, but the third item was definitely tricky.  Who would have thought you’d need to clean the project after making that sort of change?