<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-17885082</id><updated>2012-01-16T16:24:57.340+01:00</updated><category term='mobile'/><category term='technorati'/><category term='yahoo'/><category term='marathon'/><category term='web'/><category term='apple'/><category term='development'/><category term='stuff'/><category term='zattoo'/><category term='christmas'/><category term='web development'/><category term='skype'/><category term='advertising'/><category term='sutini'/><category term='scriggity'/><category term='firefox'/><category term='objective-c'/><category term='osgi'/><category term='guice'/><category term='git'/><category term='browser'/><category term='spring'/><category term='starbucks'/><category term='sports'/><category term='jug-ka'/><category term='video'/><category term='singapore'/><category term='out&apos;n about'/><category term='boxing'/><category term='pics'/><category term='jaiku'/><category term='hibernate'/><category term='scala'/><category term='plazes'/><category term='java'/><category term='security'/><category term='lucene'/><category term='pownce'/><category term='jboss rules'/><category term='blog'/><category term='gae'/><category term='wordbuzz'/><category term='traveling'/><category term='android'/><category term='iPhone'/><category term='running'/><category term='blogger'/><category term='half marathon'/><category term='groovy'/><category term='flickr'/><category term='ipod'/><category term='twitter'/><category term='drools'/><category term='mac'/><category term='jboss'/><category term='microsoft'/><category term='karlsruhe'/><category term='quality'/><category term='fun'/><category term='iPad'/><category term='testing'/><category term='bangkok'/><category term='joost'/><category term='google'/><title type='text'>my take on things</title><subtitle type='html'>{ by david linsin }</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default?start-index=101&amp;max-results=100'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>515</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-17885082.post-4302537067289123301</id><published>2011-03-01T06:07:00.000+01:00</published><updated>2011-03-01T06:10:35.588+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='karlsruhe'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='blog'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='iPad'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='spring'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><category scheme='http://www.blogger.com/atom/ns#' term='quality'/><category scheme='http://www.blogger.com/atom/ns#' term='mobile'/><category scheme='http://www.blogger.com/atom/ns#' term='git'/><category scheme='http://www.blogger.com/atom/ns#' term='jug-ka'/><category scheme='http://www.blogger.com/atom/ns#' term='mac'/><category scheme='http://www.blogger.com/atom/ns#' term='google'/><category scheme='http://www.blogger.com/atom/ns#' term='android'/><category scheme='http://www.blogger.com/atom/ns#' term='ipod'/><category scheme='http://www.blogger.com/atom/ns#' term='gae'/><category scheme='http://www.blogger.com/atom/ns#' term='objective-c'/><title type='text'>303</title><content type='html'>This is my last blog post here on blogger.com! You'll find my new thoughts, projects and development ramblings at &lt;a href="http://dlinsin.github.com"&gt;http://dlinsin.github.com&lt;/a&gt;. I migrated to blogger.com in &lt;a href="http://dlinsin.blogspot.com/2005/10/lets-go.html"&gt;late 2005&lt;/a&gt; and was always quite happy with it. More than 5 years and exactly 502 posts later it's time to move on to something new. Subscribe to &lt;a href="http://dlinsin.github.com"&gt;my new blog feed&lt;/a&gt;, check &lt;a href="http://github.com/dlinsin"&gt;my stuff on github&lt;/a&gt; or &lt;a href="http://twitter.com/furryfishapps"&gt;follow me&lt;/a&gt; on Twitter.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-4302537067289123301?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/4302537067289123301/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=4302537067289123301' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/4302537067289123301'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/4302537067289123301'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2011/03/303.html' title='303'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-4445746095152849706</id><published>2011-02-15T19:30:00.000+01:00</published><updated>2011-02-15T19:32:15.800+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='iPad'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='ipod'/><category scheme='http://www.blogger.com/atom/ns#' term='objective-c'/><title type='text'>ShareKit Pimping</title><content type='html'>&lt;p&gt;A couple of months ago, I &lt;a href="http://dlinsin.blogspot.com/2010/11/word-buzz-getting-better-twitter.html"&gt;improved Word Buzz' Twitter sharing feature&lt;/a&gt; significantly, by leveraging a &lt;a href="https://github.com/dlinsin/iphone-twitter"&gt;tweaked version&lt;/a&gt; of an existing framework. Unfortunately, I had only heard of &lt;a href="http://getsharekit.com"&gt;ShareKit&lt;/a&gt; at that time, that's the reason why I decided to implement my own solution!&lt;/p&gt;&lt;p&gt;In case you don't know about ShareKit or like me only heard about it:&lt;/p&gt;&lt;blockquote&gt;SharKit adds full sharing capabilities to your App. It's a drop-in library, which supports services like Twitter, Facebook, Instapaper and many more. You can share links, text and pictures with a customizable user interface. It evens queues shared items until an internet connection is available.&lt;/blockquote&gt;&lt;p&gt;SharKit is awesome and I really regret not using it to add Twitter sharing to &lt;a href="http://itunes.apple.com/app/word-buzz/id388372038?mt=8"&gt;Word Buzz&lt;/a&gt;. It has a well documented API and is really easy to extend, there's even a step-by-step guide on ShakreKit's website, which describes the process. I followed that guide to add &lt;a href="https://github.com/dlinsin/ShareKit"&gt;TwitPic and yfrog capability&lt;/a&gt; to ShareKit for one of our next &lt;a href="http://twitter.com/furryfishApps"&gt;furryfishApps&lt;/a&gt; projects and that's what I'd like to write about in this post.&lt;/p&gt;&lt;p&gt;ShareKit comes with built-in Twitter support, which you can find in &lt;em&gt;SHKTwitter&lt;/em&gt; and its authentication form &lt;em&gt;SHKTwitterForm&lt;/em&gt;. It uses &lt;a href="http://bit.ly"&gt;bit.ly&lt;/a&gt; to share images, which has a similar API as TwitPic and yfrog. I piggybacked on that implementation, however dropped oAuth support in favor of xAuth.&lt;/p&gt;&lt;p&gt;I know there are &lt;a href="http://hueniverse.com/2010/06/xauth-a-terrible-horrible-no-good-very-bad-idea/"&gt;a lot of discussions&lt;/a&gt; on xAuth, however I found it the &lt;a href="http://www.reynoldsftw.com/2010/03/using-xauth-an-alternate-oauth-from-twitter/"&gt;easiest way&lt;/a&gt; to provide the most benefit for iPhone App users, without compromising security in a hurtful way. In order to get your iPhone App ready to use xAuth with Twitter, you need to sign up at &lt;a href="http://developer.twitter.com/"&gt;http://developer.twitter.com/&lt;/a&gt;. I described the process in a &lt;a href="http://dlinsin.blogspot.com/2010/11/word-buzz-getting-better-twitter.html"&gt;previous post&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;The previously mentioned class &lt;em&gt;SHKTwitter&lt;/em&gt; inherits from &lt;em&gt;SHKOAuthSharer&lt;/em&gt;, which does all the heavy lifting in terms of authentication for you! All you need to do is hook into the API calls and customize your authentication screen:&lt;/p&gt;&lt;script src="https://gist.github.com/827260.js?file=gistfile1.m"&gt;&lt;/script&gt; &lt;p&gt;&lt;noscript&gt;&lt;pre&gt;return [NSArray arrayWithObjects:&lt;br /&gt;[SHKFormFieldSettings label:SHKLocalizedString(@"Username") key:@"username" type:SHKFormFieldTypeText start:nil],&lt;br /&gt;[SHKFormFieldSettings label:SHKLocalizedString(@"Password") key:@"password" type:SHKFormFieldTypePassword start:nil],&lt;br /&gt;[SHKFormFieldSettings label:SHKLocalizedString(@"Send to Twitter") key:@"sendToTwitter" type:SHKFormFieldTypeSwitch start:SHKFormFieldSwitchOn],&lt;br /&gt;nil];&lt;/pre&gt;&lt;/noscript&gt;&lt;/p&gt;&lt;p&gt;So let's say the user wants to share a picture, taps on the share icon and select TwitPic. He authenticates after being prompt for his Twitter credentials. When the authentication was successful, you want to show some sort of form to your users, where they can enter a comment or in case of Twitter, their status. You can totally customize the UI, without any dependency to ShareKit. In fact &lt;em&gt;SHKTwitterForm&lt;/em&gt;, ShareKits built-in Twitter form, is a simple &lt;em&gt;UIViewController&lt;/em&gt; with its delegate set to &lt;em&gt;SHKTwitter&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;The actual sharing part of the code is as straight forward as the rest of ShareKit. It's a little verbose, but once your understood the concept, it's easy to extend:&lt;/p&gt;&lt;script src="https://gist.github.com/827294.js?file=gistfile1.m"&gt;&lt;/script&gt; &lt;p&gt;&lt;noscript&gt;&lt;pre&gt;- (void)sendImage {&lt;br /&gt;&lt;br /&gt;NSString * oauthHeader = [self oauthHeader];&lt;br /&gt;&lt;br /&gt;NSURL *serviceURL = [NSURL URLWithString:@"http://yfrog.com/api/xauth_upload"];&lt;br /&gt;OAMutableURLRequest *oRequest = [[OAMutableURLRequest alloc] initWithURL:serviceURL&lt;br /&gt;consumer:super.consumer&lt;br /&gt;token:super.accessToken&lt;br /&gt;realm:@"http://api.twitter.com/"&lt;br /&gt;signatureProvider:super.signatureProvider];&lt;br /&gt;&lt;br /&gt;[oRequest prepare];&lt;br /&gt;[oRequest setValue:nil forHTTPHeaderField:@"Authorization"];&lt;br /&gt;&lt;br /&gt;[oRequest setHTTPMethod:@"POST"];&lt;br /&gt;[oRequest setValue:@"https://api.twitter.com/1/account/verify_credentials.json" forHTTPHeaderField:@"X-Auth-Service-Provider"];&lt;br /&gt;[oRequest setValue:oauthHeader forHTTPHeaderField:@"X-Verify-Credentials-Authorization"];&lt;br /&gt;&lt;br /&gt;NSString *boundary = @"a21ff70823f9";&lt;br /&gt;NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",boundary];&lt;br /&gt;[oRequest setValue:contentType forHTTPHeaderField:@"Content-Type"];&lt;br /&gt;&lt;br /&gt;NSMutableData *body = [NSMutableData data];&lt;br /&gt;&lt;br /&gt;[body appendData:[[NSString stringWithFormat:@"--%@\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];&lt;br /&gt;[body appendData:[@"Content-Disposition: form-data; name=\"key\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];&lt;br /&gt;[body appendData:[self.yfrogAPIKey dataUsingEncoding:NSUTF8StringEncoding]];&lt;br /&gt;[body appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];&lt;br /&gt;&lt;br /&gt;[body appendData:[[NSString stringWithFormat:@"--%@\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];&lt;br /&gt;[body appendData:[@"Content-Disposition: form-data; name=\"media\"; filename=\"upload.jpg\"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];&lt;br /&gt;[body appendData:[@"Content-Type: image/jpg\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];&lt;br /&gt;[body appendData:[self imageData]];&lt;br /&gt;[body appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];&lt;br /&gt;&lt;br /&gt;[body appendData:[[NSString stringWithFormat:@"--%@--\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];&lt;br /&gt;&lt;br /&gt;[oRequest setHTTPBody:body];&lt;br /&gt;&lt;br /&gt;[self sendDidStart];&lt;br /&gt;&lt;br /&gt;// Start the request&lt;br /&gt;OAAsynchronousDataFetcher *fetcher = [OAAsynchronousDataFetcher asynchronousFetcherWithRequest:oRequest&lt;br /&gt;delegate:self&lt;br /&gt;didFinishSelector:@selector(sendImage:didFinishWithData:)&lt;br /&gt;didFailSelector:@selector(sendImage:didFailWithError:)];&lt;br /&gt;&lt;br /&gt;[fetcher start];&lt;br /&gt;&lt;br /&gt;[oRequest release];&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/p&gt;&lt;p&gt;As you can see most of the work goes into setting up the authentication headers for the request, as well as filling in the elements of the request body. There are APIs like &lt;a href="http://allseeing-i.com/ASIHTTPRequest"&gt;ASIHTTPRequest&lt;/a&gt;, which handle this for you, however ShareKit doesn't use them and I didn't want to introduce a 3rd party library. If you take a look at &lt;a href="https://github.com/Gurpartap/GSTwitPicEngine"&gt;Gurpartap's TwitPic engine&lt;/a&gt;, you can see how easy and simple the code would be.&lt;/p&gt;&lt;p&gt;Overall it's a breeze to develop with &lt;a href="http://getsharekit.com"&gt;SharKit&lt;/a&gt; and I'm glad I digged in deeper. For our next project we'll definitely use it and you should at least take a look at it before rolling your own implementation.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-4445746095152849706?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/4445746095152849706/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=4445746095152849706' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/4445746095152849706'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/4445746095152849706'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2011/02/sharekit-pimping.html' title='ShareKit Pimping'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-7053166481722177462</id><published>2011-01-31T17:03:00.000+01:00</published><updated>2011-01-31T17:03:00.693+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='quality'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='iPad'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='ipod'/><category scheme='http://www.blogger.com/atom/ns#' term='objective-c'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><title type='text'>UI Test Automation with Instruments</title><content type='html'>One of the most impressive talks for me at &lt;a href="http://dlinsin.blogspot.com/2010/06/wwdc10.html"&gt;WWDC 2010&lt;/a&gt; was session 306 - "Automating Use Interface Testing with Instruments". I've been wanting to check it out ever since iOS 4 was released. A couple of weeks ago, I finally had a chance to give it a test ride with &lt;a href="http://mobile.synyx.de/2010/09/i-think-i-spider-1-0-released/"&gt;"I think I spider"&lt;/a&gt;, one of the Apps I developed.&lt;br/&gt;&lt;br/&gt;All you need to get started is an App, Instruments and some basic JavaScript skills. Apple provides a set of &lt;a href="http://developer.apple.com/library/ios/#documentation/ToolsLanguages/Reference/UIATargetClassReference/UIATargetClass/UIATargetClass.html"&gt;JavaScript libraries&lt;/a&gt;, that you can use to drive your tests and simulate user interaction. Your custom test scripts are run using the Automation Instrument in Apple's Instruments App, targeting your App either in the Simulator or on an actual device.&lt;br/&gt;&lt;br/&gt;You can test almost every aspect of user interaction, using Apple's JavaScript library. No matter if you want to test shaking, device orientation or the basics like tapping and swiping, you can do all that using basic JavaScript function calls. Apple's &lt;a href="https://developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/InstrumentsUserGuide/Built-InInstruments/Built-InInstruments.html%23//apple_ref/doc/uid/TP40004652-CH6-SW75"&gt;documentation&lt;/a&gt; is quite solid, as most of them are, and explains the process in detail.&lt;br/&gt;&lt;br/&gt; For "I think I spider" I covered the basic use cases in terms of UI, to make sure it still works after adding new features. Here is a basic example of the JavaScript involved to test "opening" the book, after starting the App:&lt;br /&gt;&lt;br /&gt;&lt;script src="https://gist.github.com/803902.js"&gt; &lt;/script&gt;&lt;noscript&gt;&lt;pre&gt;// setup&lt;br/&gt;var target = UIATarget.localTarget();&lt;br /&gt;var appWindow = target.frontMostApp().mainWindow();&lt;br /&gt;var element = target;&lt;br/&gt;&lt;br /&gt;// first test&lt;br /&gt;var testName = "Start Screen Test";&lt;br /&gt;UIALogger.logStart(testName);&lt;br/&gt;&lt;br /&gt;UIALogger.logMessage("Tapping start screen");&lt;br /&gt;appWindow.elements()["start_screen"].tap(); // open the book&lt;br/&gt;&lt;br /&gt;if (appWindow.elements()["main_screen"].isValid()) {&lt;br /&gt;  UIALogger.logFail(testName);&lt;br /&gt;} else {&lt;br/&gt;&lt;br /&gt;  UIALogger.logPass(testName);&lt;br /&gt;}&lt;/pre&gt;&lt;/noscript&gt;&lt;br/&gt;&lt;br/&gt;You can see that the JavaScript API is quite easy to use and yet very powerful! After setting this up in the Automation Instrument and running it, you can see the Simulator firing up and tapping the &lt;em&gt;UIImageView&lt;/em&gt; with the accessibility label "startscreen" after it became available. It then tests, if the main screen of the App was loaded and either passes or fails the test.&lt;br/&gt;&lt;br/&gt; In order to make our App testable, I had to set the accessibility labels on the elements we wanted to reference from the script. That was the only change I had to make in our code. Since you should take accessibility into consideration anyways, it was a reasonable effort.&lt;br/&gt;&lt;br/&gt;  Apple did an awesome job giving us developers the ability to catch regressions and make our life easier. However, there is room for improvement, which has been nicely &lt;a href="http://blog.airsource.co.uk/index.php/2010/08/13/ui-automation-on-the-iphone/"&gt;summarized&lt;/a&gt; by &lt;a href="http://www.airsource.co.uk"&gt;Air Source&lt;/a&gt;. For us, a missing &lt;em&gt;UIALogger.warn&lt;/em&gt; function in the JavaScript library was the biggest downside. Sometimes it's okay for a test to fail under certain conditions, but you still want to get a warning about it. I use &lt;em&gt;UIALogger.logMessage&lt;/em&gt; for those cases as a workaround, but it's quite easy to miss those lines, since they don't stand out.&lt;br/&gt;&lt;br/&gt;Overall, I think it's a huge improvement to have a UI testing tool for iOS Apps at hand. There is room for improvement, but the current state of UI Test Automation is already priceless!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-7053166481722177462?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/7053166481722177462/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=7053166481722177462' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/7053166481722177462'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/7053166481722177462'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2011/01/ui-test-automation-with-instruments.html' title='UI Test Automation with Instruments'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-1693728623983962283</id><published>2011-01-10T19:54:00.001+01:00</published><updated>2011-01-10T19:57:00.699+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mobile'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='iPad'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='git'/><category scheme='http://www.blogger.com/atom/ns#' term='ipod'/><category scheme='http://www.blogger.com/atom/ns#' term='objective-c'/><title type='text'>5 Star Rating</title><content type='html'>A &lt;a href="http://dlinsin.blogspot.com/2010/09/i-think-i-spider.html"&gt;couple of months ago&lt;/a&gt; we release our first own App "&lt;a href="http://mobile.synyx.de/2010/09/i-think-i-spider-1-0-released/"&gt;I think I spider&lt;/a&gt;" at Synyx. It features German quotes and sayings directly translated to English. It doesn't make much sense to a none-native German speaker, but believe me it's hilarious for us.&lt;br /&gt;&lt;br /&gt;The App let's you rate those quotes with a 5 star rating, which is directly incorporated into a ranking. Up until today the component in charge of the ranking is a simple bunch of &lt;em&gt;UIImageViews&lt;/em&gt; wired up with Interface Builder and set appropriately when tapped. That means if you want to give a 3 star rating, tapping on the 3rd &lt;em&gt;UIImageView&lt;/em&gt; would fill up the 2 previous one as well. Same goes for changing your mind and going from a 5 to 3 star rating. The logic would unhighlight the 4th and 5th star.&lt;br /&gt;&lt;br /&gt;Now, this works great and it certainly looks beautiful, but have you ever rated an App in the App Store on your iOS device? This is how it looks like:&lt;br /&gt;&lt;br /&gt;&lt;object width="480" height="385"&gt;&lt;param name="movie" value="http://www.youtube.com/v/cbOll4SWwmU?fs=1&amp;amp;hl=en_US"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/cbOll4SWwmU?fs=1&amp;amp;hl=en_US" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="480" height="385"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;Apple did a great job of just getting everything right with this small little control. The way you can slide your finger over it and the stars light up is just great. Also notice that if you want to reset the rating and go down to 0 stars, you need to swipe your finger all the way to the left. Note, that you can also touch slightly below the star, in order to see the stars above your fingers.&lt;br /&gt;&lt;br /&gt;All those little details were the requirements for the next version of the rating feature of &lt;a href="http://itunes.apple.com/de/app/i-think-i-spider/id390639989?mt=8"&gt;I think I spider&lt;/a&gt;. However, having some spare time over the holidays, I though I'd write a little component, which does all of that and more: &lt;strong&gt;DLStarRating&lt;/strong&gt;. You can find the code on &lt;a href="https://github.com/dlinsin/DLStarRating"&gt;github&lt;/a&gt;, along with a sample project of how to use the stuff in your next App.&lt;br /&gt;&lt;br /&gt;&lt;object width="480" height="385"&gt;&lt;param name="movie" value="http://www.youtube.com/v/0fpeBo2H7Tc?fs=1&amp;amp;hl=en_US"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/0fpeBo2H7Tc?fs=1&amp;amp;hl=en_US" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="480" height="385"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;&lt;em&gt;DLStarRating&lt;/em&gt; consists of a configurable number of custom &lt;em&gt;UIButtons&lt;/em&gt; called &lt;em&gt;DLStarView&lt;/em&gt;. They have a different background image for their normal and highlighted state. Those buttons are wrapped in a &lt;em&gt;UIControl&lt;/em&gt; called &lt;em&gt;DLStarRatingControl&lt;/em&gt;, which does all the touch handling. The buttons are centered in the &lt;em&gt;DLStarRatingControl&lt;/em&gt; so keep that in mind when configuring its size.&lt;br /&gt;&lt;br /&gt;It's quite easy to use &lt;em&gt;DLStarRating&lt;/em&gt; in your code. You can wire it up in Interface Builder (although you won't be able to configure it from there) or in your &lt;em&gt;UIViewController&lt;/em&gt; subclass:&lt;br /&gt;&lt;br /&gt;&lt;script src="https://gist.github.com/772510.js?file=gistfile1.m"&gt;&lt;/script&gt;&lt;noscript&gt;&lt;pre&gt;DLStarRatingControl *customNumberOfStars = [[DLStarRatingControl alloc] initWithFrame:CGRectMake(0, 230, 320, 230) andStars:3];&lt;br /&gt;customNumberOfStars.backgroundColor = [UIColor groupTableViewBackgroundColor];&lt;br /&gt;customNumberOfStars.autoresizingMask =  UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;&lt;br /&gt;customNumberOfStars.rating = 2;&lt;br /&gt;[self.view addSubview:customNumberOfStars];&lt;/pre&gt;&lt;/noscript&gt;&lt;br /&gt;&lt;br /&gt;To customize the stars, you can replace &lt;em&gt;star.png&lt;/em&gt; and &lt;em&gt;star_highlighted.png&lt;/em&gt; in the images folder under &lt;em&gt;DLStarRating&lt;/em&gt; with your own. The only requirement is that the two images must have the same size.&lt;br /&gt;&lt;br /&gt;If you discover any bugs or have a great idea you want implemented, open an issue on &lt;a href="https://github.com/dlinsin/DLStarRating/issues"&gt;github&lt;/a&gt; or fork the project and help me out!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-1693728623983962283?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/1693728623983962283/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=1693728623983962283' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/1693728623983962283'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/1693728623983962283'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2011/01/5-star-rating.html' title='5 Star Rating'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-589707145751741695</id><published>2010-12-15T07:07:00.001+01:00</published><updated>2010-12-15T07:08:31.815+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='wordbuzz'/><category scheme='http://www.blogger.com/atom/ns#' term='mobile'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='iPad'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='ipod'/><category scheme='http://www.blogger.com/atom/ns#' term='objective-c'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><title type='text'>Word Buzz Lite</title><content type='html'>&lt;a href="http://itunes.apple.com/app/word-buzz/id388372038?mt=8"&gt;&lt;img src="http://lh5.ggpht.com/_myUhtr_A51Y/TMnUXMVXCuI/AAAAAAAATkQ/DvVOs4Kbfx0/114x114.png?imgmax=800" alt="114x114.png" border="0" width="114" height="114" style="float:left;padding-right:10px;" /&gt;&lt;/a&gt;When we first planned Word Buzz, we didn't want to release a free version. It has a more exclusive feeling to it, if you can't test before you try. However, to boost your user base, there's nothing better than a free version. So here it is: &lt;a href="http://itunes.apple.com/app/word-buzz-hd-lite/id404852163?mt=8"&gt;Word Buzz Lite&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;It's a universal App with no limitation except for one: no Game Center! It's not our decision, but more of &lt;a href="http://dlinsin.blogspot.com/2010/11/word-buzz-to-support-game-center.html"&gt;Apple's technical limitation&lt;/a&gt; to only allow one App (bundle id) per Game Center instance. Another difference is the number of free built-in themes. The free version comes with only one free theme and more available through in-app purchases.&lt;br/&gt;&lt;br/&gt;When developing &lt;a href="http://dlinsin.blogspot.com/2010/07/apple-push-notifications-on-google.html"&gt;Doublemill&lt;/a&gt;, which is a separate App for &lt;a href="http://itunes.apple.com/app/doublemill/id368886888?mt=8"&gt;iPhone&lt;/a&gt; and &lt;a href="http://itunes.apple.com/app/doublemill-for-ipad/id370791547?mt=8"&gt;iPad&lt;/a&gt;, I decided to use different XCode projects for the lite, premium and iPad version, hence different source bases for the three Apps. For Word Buzz, we decided to use a different strategy. A multi-target project for full and lite version, which are both universal Apps.&lt;br/&gt;&lt;br/&gt;Having different source bases has a couple of drawbacks, which I experienced the hard way. One of it is fixing bugs! It's is a copy-paste task, with all its pitfalls and pain in testing. In addition to that, XCode's support for handling multiple projects isn't exactly supporting you. However, evolving the 3 different Apps, was easy and didn't affect the others in any way.&lt;br/&gt;&lt;br/&gt;Developing a multi-target project comes with its own set of challenges. You are basically programming based on a &lt;a href="http://en.wikipedia.org/wiki/C_preprocessor#Macro_definition_and_expansion"&gt;preprocessor conditional inclusion or exclusion&lt;/a&gt;, which I've explained in an &lt;a href="http://dlinsin.blogspot.com/2010/08/testing-apps-with-in-app-purchases-in.html"&gt;earlier blog post&lt;/a&gt;. For Word Buzz is was quite easy to cut out the additional themes and the Game Center support. However, I can imagine it can become quite complicated if you want to exclude sophisticated features. XCode comes with awesome support for multiple targets and its Organizer can still handle releases for two different versions, although it's one source base. In order to get me started, I used &lt;a href="http://chris-fletcher.com/2010/10/14/how-to-create-a-lite-version-of-your-iphone-app/"&gt;Chris Fletcher's blog post&lt;/a&gt; on building a lite version for your iPhone App. It covers the basics and brings you up to speed. &lt;br/&gt;&lt;br/&gt;When it comes to evolving Word Buzz, we needed to shift our minds from the source-based to a SCM-based evolution model. We needed to maintain different branches, which would get quite complicated from time to time. Thanks to Git and &lt;a href="http://github.com"&gt;github&lt;/a&gt;, we never experienced any problems bringing the various versions (branches) back together. Once you've changed your mental model to a DSCM system, it's hard to switch back, at least for me.&lt;br/&gt;&lt;br/&gt;We are quite happy to have used multiple targets. Everything is covered under the hood of one XCode project and fixes go right into both versions. Check out &lt;a href="http://itunes.apple.com/app/word-buzz-hd-lite/id404852163?mt=8"&gt;Word Buzz Lite&lt;/a&gt; and let us know what you think! &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-589707145751741695?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/589707145751741695/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=589707145751741695' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/589707145751741695'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/589707145751741695'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/12/word-buzz-lite.html' title='Word Buzz Lite'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_myUhtr_A51Y/TMnUXMVXCuI/AAAAAAAATkQ/DvVOs4Kbfx0/s72-c/114x114.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-4335464276789098072</id><published>2010-12-06T06:30:00.000+01:00</published><updated>2010-12-06T08:32:06.266+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='wordbuzz'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='iPad'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='ipod'/><category scheme='http://www.blogger.com/atom/ns#' term='objective-c'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><title type='text'>Why Word Buzz doesn't support iOS 3.1</title><content type='html'>&lt;a href="http://itunes.apple.com/app/word-buzz/id388372038?mt=8"&gt;&lt;img src="http://lh5.ggpht.com/_myUhtr_A51Y/TMnUXMVXCuI/AAAAAAAATkQ/DvVOs4Kbfx0/114x114.png?imgmax=800" alt="114x114.png" border="0" width="114" height="114" style="float:left;padding-right:10px;" /&gt;&lt;/a&gt; Word Buzz supports 3.2+, which means it doesn't support iOS 3 on iPhone. Although, my software developer ego doesn't like the fact, that we don't support the older versions of iOS, it was the right decision to do so.&lt;br/&gt;&lt;br/&gt;It was a business decision to not support iOS 3.1, that's the short story. Rumors have it, that there are &lt;a href="https://twitter.com/stroughtonsmith/status/1300025644687361"&gt;less than 10%&lt;/a&gt; of iOS 3.x devices out there - including iPads. That's a small enough number, to justify locking out the folks, which &lt;a href="http://twitter.com/furryfishApps/status/4247028213227520"&gt;haven't upgraded&lt;/a&gt; yet. &lt;br/&gt;&lt;br/&gt;The long story is, that we simply didn't develop with iOS 3.1 in mind. The are so many new features in iOS 3.2, which we wanted to use, that we couldn't just port it back. Let me give you a few examples:&lt;br/&gt;&lt;br/&gt;Since iOS 3.2 you can use custom fonts in an easy and simple fashion. You add an array to your &lt;em&gt;Info.plist&lt;/em&gt; called &lt;em&gt;UIAppFonts&lt;/em&gt; and each item contains the filename of the font you put into your App bundle. There has been &lt;a href="http://stackoverflow.com/questions/360751/can-i-embed-a-custom-font-in-an-iphone-application"&gt;no easy way&lt;/a&gt; of accomplishing this before iOS 3.2, so we didn't want to sacrifice design and roll-out the 3.1 version with a system font.&lt;br/&gt;&lt;br/&gt;Another great new feature in iOS 3.2 are &lt;a href="http://developer.apple.com/library/ios/#documentation/general/conceptual/iPadProgrammingGuide/GestureSupport/GestureSupport.html"&gt;Gesture Recognizers&lt;/a&gt;. If you don't know what I'm talking about stop right here and go read up on it - you are missing out! Although, Word Buzz doesn't use Gesture Recognizers extensively, we didn't wanna g back to the 19th century and use those old &lt;a href="http://developer.apple.com/library/ios/documentation/general/conceptual/Devpedia-CocoaApp/EventHandlingiPhone.html#//apple_ref/doc/uid/TP40009071-CH13"&gt;UITouch event handling&lt;/a&gt; stuff.&lt;br/&gt;&lt;br/&gt;Those were just the two most obvious examples out of many! We sat down and tried to make Word Buzz run on our iPhone 3G, which still runs 3.1.3, but after a day worth of coding, we gave up. We'd love to make it run, but we'll rather invest in future proofing Word Buzz, using &lt;a href="http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/Blocks/Articles/00_Introduction.html"&gt;Blocks&lt;/a&gt; and &lt;a href="http://developer.apple.com/library/ios/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html"&gt;GCD&lt;/a&gt;! We can't wait to drop iOS 3.2 support! &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-4335464276789098072?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/4335464276789098072/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=4335464276789098072' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/4335464276789098072'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/4335464276789098072'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/12/why-word-buzz-doesn-support-ios-31.html' title='Why Word Buzz doesn&amp;#39;t support iOS 3.1'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_myUhtr_A51Y/TMnUXMVXCuI/AAAAAAAATkQ/DvVOs4Kbfx0/s72-c/114x114.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-5490318674545634728</id><published>2010-11-29T18:17:00.000+01:00</published><updated>2010-11-29T18:19:21.513+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='wordbuzz'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='iPad'/><category scheme='http://www.blogger.com/atom/ns#' term='ipod'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><title type='text'>Word Buzz to support Game Center</title><content type='html'>&lt;a href="http://itunes.apple.com/app/word-buzz/id388372038?mt=8"&gt;&lt;img src="http://lh5.ggpht.com/_myUhtr_A51Y/TMnUXMVXCuI/AAAAAAAATkQ/DvVOs4Kbfx0/114x114.png?imgmax=800" alt="114x114.png" border="0" width="114" height="114" style="float:left;padding-right:10px;" /&gt;&lt;/a&gt; Today &lt;a href="http://twitter.com/furryfishApps"&gt;furryfishApps&lt;/a&gt; is proud to announce, that &lt;a href="http://itunes.apple.com/app/word-buzz/id388372038?mt=8"&gt;Word Buzz&lt;/a&gt; now supports Game Center. A lot of work went into making this happen and we learned quite a bit on the way. I won't go into technical detail here, but rather cover what we've implemented.&lt;br/&gt;&lt;br/&gt;Word Buzz lets you share your highscore and &lt;a href="https://word-buzz.appspot.com/index.jsp?badge=rookie"&gt;achievements&lt;/a&gt; on Game Center. It comes with over 16 different themes (set of words) and a lot more available for download. Each of those themes gets its own score, which will be added to your overall highscore. The sum of all highscores is shared on Game Center. &lt;br/&gt;&lt;br/&gt;&lt;strong&gt;Why didn't we just share the highscore of each theme?&lt;/strong&gt; &lt;em&gt;We couldn't! Game Center lets you only share up to 20 different highscores, which is probably reasonable. It might be a bit confusing to have 40 different highscore lists. &lt;/em&gt;&lt;br/&gt;&lt;br/&gt;Word Buzz is available on all iOS devices. You can play on your iPhone as well as on your iPad or iPod touch. Unfortunately, there is no way with Game Center to sync more than 20 highscores between devices. That means, you cannot just pick up with your iPad, where you left off with your iPhone. We are working on a homegrown solution, but until that happens, it's best to stick on one device. &lt;br/&gt;&lt;br/&gt;When playing Word Buzz, you can unlock over 10 different achievements. All of them are shared on Game Center automatically, as soon as you unlock them. &lt;br/&gt;&lt;br/&gt;&lt;strong&gt;What happens if you are offline?&lt;/strong&gt; &lt;em&gt;Don't worry! Your achievements and highscores are safely stored on the device and uploaded to Game Center as soon as you sign in!&lt;/em&gt;&lt;br/&gt;&lt;br/&gt;Go challenge your friends and enjoy &lt;a href="http://twitter.com/WordBuzzApp"&gt;Word Buzz&lt;/a&gt; with Game Center.  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-5490318674545634728?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/5490318674545634728/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=5490318674545634728' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/5490318674545634728'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/5490318674545634728'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/11/word-buzz-to-support-game-center.html' title='Word Buzz to support Game Center'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_myUhtr_A51Y/TMnUXMVXCuI/AAAAAAAATkQ/DvVOs4Kbfx0/s72-c/114x114.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-6152988939632488410</id><published>2010-11-22T17:10:00.001+01:00</published><updated>2010-11-22T17:12:07.387+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='karlsruhe'/><category scheme='http://www.blogger.com/atom/ns#' term='wordbuzz'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='iPad'/><category scheme='http://www.blogger.com/atom/ns#' term='mac'/><category scheme='http://www.blogger.com/atom/ns#' term='ipod'/><category scheme='http://www.blogger.com/atom/ns#' term='objective-c'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><title type='text'>Game Center Presentation at Cocoa Heads Karlsruhe</title><content type='html'>&lt;img src="http://lh5.ggpht.com/_myUhtr_A51Y/TOYupJ8KRNI/AAAAAAAATp4/hA94G3894Ao/hero-gamecenter.jpg?imgmax=800" alt="hero-gamecenter.png" border="0" width="114" height="114" style="float:left;padding-right:10px;" /&gt;On Wednesday, I'll be giving a presentation on Apple's &lt;a href="http://www.apple.com/game-center/"&gt;Game Center&lt;/a&gt; at the local &lt;a href="http://twitter.com/cocoaheads_ka/status/6628243050336256"&gt;CocoaHeads&lt;/a&gt; Group, here in Karlsruhe. &lt;br/&gt;&lt;br/&gt;Game Center is Apple's social gaming network, that lets you invite friends to play, share your highscore and achievements through leaderboards and it even auto-matches you with other players for online-multiplayer games.&lt;br/&gt;&lt;br/&gt;If you are interested in Game Center, drop by &lt;a href="http://retrogames.info/"&gt;RetroGames&lt;/a&gt; on &lt;a href="http://groups.google.com/group/cocoaheads-karlsruhe/t/2d0a42fee498f4bc"&gt;Wednesday 24th of November at 19:00&lt;/a&gt;. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-6152988939632488410?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/6152988939632488410/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=6152988939632488410' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/6152988939632488410'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/6152988939632488410'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/11/game-center-presentation-at-cocoa-heads.html' title='Game Center Presentation at Cocoa Heads Karlsruhe'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_myUhtr_A51Y/TOYupJ8KRNI/AAAAAAAATp4/hA94G3894Ao/s72-c/hero-gamecenter.jpg?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-6801485193299688608</id><published>2010-11-15T17:10:00.000+01:00</published><updated>2010-11-15T17:10:00.880+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='wordbuzz'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='iPad'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='ipod'/><category scheme='http://www.blogger.com/atom/ns#' term='objective-c'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><title type='text'>Universal App - Word Buzz goes iPhone/iPod touch</title><content type='html'>&lt;a href="http://itunes.apple.com/app/word-buzz/id388372038?mt=8"&gt;&lt;img src="http://lh5.ggpht.com/_myUhtr_A51Y/TMnUXMVXCuI/AAAAAAAATkQ/DvVOs4Kbfx0/114x114.png?imgmax=800" alt="114x114.png" border="0" width="114" height="114" style="float:left;padding-right:10px;" /&gt;&lt;/a&gt; We have been working on bringing &lt;a href="http://furryfishApps.com/wordbuzz"&gt;Word Buzz&lt;/a&gt; to iPhone and iPod touch for more than a month now. We started out with an iPad only version, because in the beginning we thought the concept of Word Buzz wouldn't work on a smaller device. After a none-stop coding weekend and a heavy testing session, we knew it would work and started hacking on iPhone and iPod touch support. &lt;br /&gt;&lt;br /&gt;We decided to build a universal App, because of pervasive Game Center support and a unique selling point! The latter is a no brainer: you are willing to spend a couple more bugs, if you get an iPad version for free. The former is a technical limitation of Game Center. You can only hook up your leaderboards and achievements with one App Id. &lt;br /&gt;&lt;br /&gt;Although it was easy enough to port Word Buzz to iPhone and iPod touch, we ran into a couple of pitfalls and one of them is worth sharing with you: the default device of your universal App! What does that mean? Well, if you want to have different &lt;em&gt;xib&lt;/em&gt; files in your App for iPhone and iPad, you need to qualify them with a suffix &lt;em&gt;-iPhone / -iPad&lt;/em&gt;. In your &lt;em&gt;Info.plist&lt;/em&gt;, you configure your alternate main &lt;em&gt;xib&lt;/em&gt; with &lt;em&gt;NSMainNibFile~iphone / NSMainNibFile~ipad&lt;/em&gt;. Since we started out with an iPad App, we decided to make iPad the default, meaning my alternate main &lt;em&gt;xib&lt;/em&gt; configuration in Info.plist was &lt;em&gt;NSMainNibFile~iphone&lt;/em&gt; and point to &lt;em&gt;MainWindow-iPhone.xib&lt;/em&gt;.&lt;br /&gt;&lt;br /&gt;This works great for iPhone and iPads, but it doesn't work for iPod touches and it also doesn't work with iOS 3.x. Somehow iPod touches (even with iOS 4) and iPhones with iOS 3.x don't respect the &lt;em&gt;NSMainNibFile~iphone&lt;/em&gt; configuration in &lt;em&gt;Info.plist&lt;/em&gt;. They always use the &lt;em&gt;NSMainNibFile&lt;/em&gt; config without any suffix. &lt;br /&gt;&lt;br /&gt;We only found out about this after testing on an actual device, which shows how important testing on each and every device is. We decided to make iPhone / iPod touch devices the default and qualify every iPad resource with &lt;em&gt;-iPad&lt;/em&gt;, just like Apple actually intended it to be done. &lt;br /&gt;&lt;br /&gt;Word Buzz for iPad is available on the &lt;a href="http://itunes.apple.com/app/word-buzz/id388372038?mt=8"&gt;App Store&lt;/a&gt;, the iPhone / iPod touch version is coming in a couple of days as a free update. Let us know what you think about it in the comments or on &lt;a href="http://twitter.com/WordBuzzApp"&gt;Twitter&lt;/a&gt;. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-6801485193299688608?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/6801485193299688608/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=6801485193299688608' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/6801485193299688608'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/6801485193299688608'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/11/universal-app-word-buzz-goes-iphoneipod.html' title='Universal App - Word Buzz goes iPhone/iPod touch'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_myUhtr_A51Y/TMnUXMVXCuI/AAAAAAAATkQ/DvVOs4Kbfx0/s72-c/114x114.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-1416515902734565989</id><published>2010-11-06T08:48:00.001+01:00</published><updated>2010-11-06T08:48:34.136+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='wordbuzz'/><category scheme='http://www.blogger.com/atom/ns#' term='iPad'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='twitter'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><title type='text'>Word Buzz getting a better Twitter integration!</title><content type='html'>&lt;a href="http://itunes.apple.com/app/word-buzz/id388372038?mt=8"&gt;&lt;img src="http://lh5.ggpht.com/_myUhtr_A51Y/TMnUXMVXCuI/AAAAAAAATkQ/DvVOs4Kbfx0/114x114.png?imgmax=800" alt="114x114.png" border="0" width="114" height="114" style="float:left;padding-right:10px;" /&gt;&lt;/a&gt;If you follow our &lt;a href="http://twitter.com/furryfishApps"&gt;tweets&lt;/a&gt;, which you should, you might have &lt;a href="http://twitter.com/furryfishApps/status/29370826939"&gt;seen&lt;/a&gt; that we have been working hard on a better Twitter integration for &lt;a href="http://furryfishApps.com/wordbuzz"&gt;Word Buzz&lt;/a&gt;. When looking around on github, I found a &lt;a href="https://github.com/mattgemmell/MGTwitterEngine"&gt;lot&lt;/a&gt; of &lt;a href="https://github.com/bengottlieb/Twitter-OAuth-iPhone"&gt;frameworks&lt;/a&gt; for Twitter and iOS, but only &lt;a href="http://github.com/st3fan/iphone-twitter"&gt;Stefan's&lt;/a&gt; was lightweight and easy enough to integrate it into Word Buzz.&lt;br /&gt;&lt;br /&gt;Most of the frameworks are full blown Twitter engines, which is not what we need for Word Buzz. Our requirements are simple: share your achievements (maybe your highscore in the future) with your followers. In addition to that, Twitter is our support channel. We wanted to allow posting public messages to &lt;a href="https://twitter.com/WordBuzzApp"&gt;@WordBuzzApp&lt;/a&gt; from within the App. Sounds easy, right? No need to pull in a full fledged Twitter engine for that!&lt;img src="http://lh6.ggpht.com/_myUhtr_A51Y/TNUIT5V9QpI/AAAAAAAATlw/vOZOl7R2CoY/iphone-twitter.png?imgmax=800" alt="iphone-twitter.png" border="0" width="184" height="358" style="float:right;padding:5px;" /&gt;&lt;br /&gt;&lt;br /&gt;Stefan's provides an API, which is really easy to use and integrate. There are no 3rd party dependencies, no need to fiddle with your &lt;a href="http://dlinsin.blogspot.com/2010/05/don-forget-your-linker-flags.html"&gt;linker flags&lt;/a&gt; - just an API to use! It even comes with a reasonable UI in Twitter style colors. Unfortunately, there was no iPad UI, which we had to implement myself. However, it was easy enough to come up with a Xib for iPad. Stefan's API provides delegate protocols, which you can implement to handle events such as "authentication succeeded". This way it's easy to separate the Twitter logic from your own code. The UIViewControllers provided, handle the most basic tasks like checking if you entered enough information to process with authentication.&lt;br /&gt;&lt;br /&gt;We are using xAuth for authentication, which Twitter has to enable for your App. It took a couple of days until they came around to let us in, but it was the best solutions. Stefan's API provides xAuth out of the box and it works seamlessly. We added a Keychain integration to save your credentials and a flag in Settings.app to reset them. &lt;br /&gt;&lt;br /&gt;Overall, it took us about 1 days to natively integrate Twitter into &lt;a href="http://furryfishApps.com/wordbuzz"&gt;Word Buzz&lt;/a&gt; - thanks to Stefan's API! If anybody is interested, we are happy to provide the iPad UI and the Keychain integration.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-1416515902734565989?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/1416515902734565989/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=1416515902734565989' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/1416515902734565989'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/1416515902734565989'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/11/word-buzz-getting-better-twitter.html' title='Word Buzz getting a better Twitter integration!'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_myUhtr_A51Y/TMnUXMVXCuI/AAAAAAAATkQ/DvVOs4Kbfx0/s72-c/114x114.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-9013621125989168712</id><published>2010-10-28T21:47:00.000+02:00</published><updated>2010-10-28T21:52:45.798+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='wordbuzz'/><category scheme='http://www.blogger.com/atom/ns#' term='mobile'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='iPad'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><title type='text'>What's new?</title><content type='html'>For the past couple of weeks it has been rather quiet here on my blog and there's a reason for that, as you can imagine. &lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh6.ggpht.com/_myUhtr_A51Y/TMnUWDuojvI/AAAAAAAATkI/Tc-mdZUoveI/furryfishApps.png?imgmax=800" alt="furryfishApps.png" border="0" width="249" height="63" style="float:left;padding:10px;" /&gt;First of all, I'd like to announce our new company &lt;a href="http://furryfishApps.com"&gt;furryfishApps&lt;/a&gt;, which I've co-founded with my &lt;a href="http://ipublicity.com"&gt;wife&lt;/a&gt; Sutini. My future private endeavors in terms of iOS development will all take place under the hood of furryfishApps. Sutini is in charge of all the marketing, social media and publicity tasks, which are so important, but we developers never have time for. I'm glad to have her on board! &lt;br /&gt;&lt;br /&gt;We are still in the midst of figuring out what to put on our website, but we already have an awesome logo and you can follow us on &lt;a href="http://twitter.com/furryfishApps"&gt;Twitter&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh5.ggpht.com/_myUhtr_A51Y/TMnUXMVXCuI/AAAAAAAATkQ/DvVOs4Kbfx0/114x114.png?imgmax=800" alt="114x114.png" border="0" width="114" height="114" style="float:right;" /&gt;&lt;br /&gt;Our first App is a fast and engaging spelling game called &lt;a href="http://furryfishapps.com/wordbuzz/"&gt;Word Buzz&lt;/a&gt;! It's your job to spell a bunch of words as fast as possible, without making any mistake. You'll be scored on how fast you can spell and there are a whole lot of achievements for you to unlock. Go grab it here on the &lt;a href="http://itunes.apple.com/app/word-buzz/id388372038?mt=8"&gt;App Store&lt;/a&gt;. We welcome any feedback on &lt;a href="http://twitter.com/WordBuzzApp"&gt;Twitter&lt;/a&gt; or &lt;a href="mailto:wordbuzz[AT]furryfishapps.com"&gt;Email&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Word Buzz is iPad only at the moment, but an iPod/iPhone version with Game Center support is &lt;a href="http://twitter.com/WordBuzzApp/status/27250806573"&gt;underway&lt;/a&gt;. I'll have some posts coming up soon on how the development of Word Buzz came along. It was a pretty exciting ride, as you can imagine. &lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-9013621125989168712?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/9013621125989168712/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=9013621125989168712' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/9013621125989168712'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/9013621125989168712'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/10/what-new.html' title='What&amp;#39;s new?'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_myUhtr_A51Y/TMnUWDuojvI/AAAAAAAATkI/Tc-mdZUoveI/s72-c/furryfishApps.png?imgmax=800' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-5234527914903556848</id><published>2010-09-22T07:00:00.000+02:00</published><updated>2010-09-22T07:05:11.189+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='iPad'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='ipod'/><category scheme='http://www.blogger.com/atom/ns#' term='objective-c'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><title type='text'>APN Device Tokens</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://mobile.synyx.de"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh5.ggpht.com/_myUhtr_A51Y/S_S5FKdslFI/AAAAAAAATD8/BMoHT9I9OTc/s144/logo_mobile_RGB_300px.png" border="0" alt=""id="" /&gt;&lt;/a&gt;&lt;em&gt;I'm writing for the &lt;a href="http://www.synyx.de"&gt;Synyx GmbH &amp; Co. KG&lt;/a&gt; mobile solutions blog. From time to time, I'll cross post articles here, if I think they are of interest for you. If you'd like to read all of my other posts, subscribe to the &lt;a href="http://mobile.synyx.de"&gt;Synyx Mobile Solutions Blog&lt;/a&gt;.&lt;/em&gt;&lt;br/&gt;&lt;br /&gt;&lt;br /&gt;When you enable Apple Push Notifications (APN) for your App, your device generates a unique device token and pass it to the &lt;em&gt;didRegisterForRemoteNotificationsWithDeviceToken&lt;/em&gt; method in your App delegate. Usually, you'll hand the token to &lt;a href="http://mobile.synyx.de/2010/07/sending-apple-push-notifications-with-notnoops-java-apns-library/"&gt;your server&lt;/a&gt; in order for it to push notifications to your device. &lt;br /&gt;&lt;br /&gt;There are &lt;a href="http://stackoverflow.com/questions/1943722/iphone-apns-device-tokens-in-sandbox-vs-production/2261758#2261758"&gt;different tokens&lt;/a&gt; for production and sandbox, depending on which provisioning profile you build/sign your App with. One &lt;a href="http://dlinsin.blogspot.com/2010/07/push-notifications-in-production.html"&gt;more gotcha&lt;/a&gt;, you need to be aware of when it comes to those device tokens: &lt;strong&gt;don't mix production and sandbox tokens&lt;/strong&gt;!  &lt;br /&gt;&lt;br /&gt;If you try to send a push notification to the production servers, using a device token meant for the sandbox, Apple's servers totally block all other notification, which you are trying to send with the same connection. If you have a scheduled  push notification like we do with "I think I spider", one wrong device token ruins the fun for everyone!&lt;br /&gt;&lt;br /&gt;We decided to separate our production and test environment and use &lt;a href="http://dlinsin.blogspot.com/2010/08/testing-apps-with-in-app-purchases-in.html"&gt;preprocessor conditional inclusion&lt;/a&gt; to point to different urls in our App. Unfortunately, we had to learn the hard way how tedious this gotcha can be to track down!&lt;br /&gt;&lt;br /&gt;&lt;small&gt;Supported by:&lt;br/&gt;Sherweb - &lt;a href="http://www.sherweb.com/hosted-exchange"&gt;Hosted Exchange Hosting&lt;/a&gt;&lt;br/&gt;Vircom - &lt;a href="http://www.vircom.com/"&gt;Email Security Software&lt;/a&gt;&lt;/small&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-5234527914903556848?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/5234527914903556848/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=5234527914903556848' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/5234527914903556848'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/5234527914903556848'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/09/apn-device-tokens.html' title='APN Device Tokens'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_myUhtr_A51Y/S_S5FKdslFI/AAAAAAAATD8/BMoHT9I9OTc/s72-c/logo_mobile_RGB_300px.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-8104474252871907974</id><published>2010-09-14T07:32:00.001+02:00</published><updated>2010-09-14T07:34:43.360+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='fun'/><category scheme='http://www.blogger.com/atom/ns#' term='ipod'/><category scheme='http://www.blogger.com/atom/ns#' term='android'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><title type='text'>I think I Spider</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://mobile.synyx.de"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh5.ggpht.com/_myUhtr_A51Y/S_S5FKdslFI/AAAAAAAATD8/BMoHT9I9OTc/s144/logo_mobile_RGB_300px.png" border="0" alt=""id="" /&gt;&lt;/a&gt;&lt;em&gt;I'm writing for the &lt;a href="http://www.synyx.de"&gt;Synyx GmbH &amp; Co. KG&lt;/a&gt; mobile solutions blog. From time to time, I'll cross post articles here, if I think they are of interest for you. If you'd like to read all of my other posts, subscribe to the &lt;a href="http://mobile.synyx.de"&gt;Synyx Mobile Solutions Blog&lt;/a&gt;.&lt;/em&gt;&lt;br/&gt;&lt;br/&gt;&lt;a href="http://itunes.apple.com/de/app/i-think-i-spider/id390639989?mt=8"&gt;&lt;img src="http://mobile.synyx.de/wp-content/uploads/2010/09/app-icon-512px.png" alt="I Think I Spider" border="0" width="128" height="128" style="float:left;padding-right:5px" /&gt;&lt;/a&gt;I'm proud to present, that our first own App has made it to the &lt;a href="http://itunes.apple.com/de/app/i-think-i-spider/id390639989?mt=8"&gt;App Store&lt;/a&gt; last week. It's a beautiful mobile interface to the web site &lt;a href="http://ithinkispider.com"&gt;http://ithinkispider.com&lt;/a&gt;. You can find out more over at the &lt;a href="http://mobile.synyx.de/2010/09/i-think-i-spider-1-0-released/"&gt;Synyx Mobile Solutions Blog&lt;/a&gt;, or just hit the App icon and download it for free from the App Store! &lt;br /&gt;&lt;br /&gt;My lovely mister singing club!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-8104474252871907974?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/8104474252871907974/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=8104474252871907974' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/8104474252871907974'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/8104474252871907974'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/09/i-think-i-spider.html' title='I think I Spider'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_myUhtr_A51Y/S_S5FKdslFI/AAAAAAAATD8/BMoHT9I9OTc/s72-c/logo_mobile_RGB_300px.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-5671217135163635765</id><published>2010-08-31T07:29:00.001+02:00</published><updated>2010-09-03T09:57:36.796+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mobile'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='iPad'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='ipod'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><title type='text'>Testing Apps with In App Purchases in Simulator</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://mobile.synyx.de"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh5.ggpht.com/_myUhtr_A51Y/S_S5FKdslFI/AAAAAAAATD8/BMoHT9I9OTc/s144/logo_mobile_RGB_300px.png" border="0" alt=""id="" /&gt;&lt;/a&gt;&lt;em&gt;I'm writing for the &lt;a href="http://www.synyx.de"&gt;Synyx GmbH &amp; Co. KG&lt;/a&gt; mobile solutions blog. From time to time, I'll cross post articles here, if I think they are of interest for you. If you'd like to read all of my other posts, subscribe to the &lt;a href="http://mobile.synyx.de"&gt;Synyx Mobile Solutions Blog&lt;/a&gt;.&lt;/em&gt;&lt;br/&gt;&lt;br /&gt;&lt;br /&gt;If you add a store to your app and use &lt;a href="http://developer.apple.com/iphone/library/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40008267-CH1-SW1"&gt;In App Purchases&lt;/a&gt; to collect your payments, there are a couple of limitations your have to live with. One of those limitations is &lt;a href="http://developer.apple.com/iphone/library/documentation/NetworkingInternet/Conceptual/StoreKitGuide/DevelopingwithStoreKit/DevelopingwithStoreKit.html#//apple_ref/doc/uid/TP40008267-CH103-SW1"&gt;not being able to fully test your App in the iPhone Simulator&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Store Kit does not operate in iPhone Simulator. When running your application in iPhone Simulator, Store Kit logs a warning if your application attempts to retrieve the payment queue. Testing the store must be done on actual devices.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Although, there is not way to test Store Kit itself, you can still test the parts of your App that use and build on the information retrieved from Store Kit. You can use a &lt;a href="http://en.wikipedia.org/wiki/C_preprocessor#Macro_definition_and_expansion"&gt;preprocessor conditional inclusion&lt;/a&gt; to determine, whether you are running on the simulator and then "mock" the Store Kit calls or don't execute them at all.&lt;br /&gt;&lt;br /&gt;&lt;script src="http://gist.github.com/553246.js?file=gistfile1.m"&gt;&lt;/script&gt;&lt;noscript&gt;&lt;pre&gt;#if TARGET_IPHONE_SIMULATOR&lt;br /&gt;  // mock product description		&lt;br /&gt;#else&lt;br /&gt;  SKProductsRequest *productRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIds];&lt;br /&gt;  productRequest.delegate = self;&lt;br /&gt;  [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;&lt;br /&gt;  [productRequest start];		&lt;br /&gt;#endif&lt;/pre&gt;&lt;/noscript&gt;&lt;br /&gt;&lt;br /&gt;Keep in mind: this might not work for your App, however, it did work for my Apps and it's better than not testing your code at all. The optimal solution would definitely be to connect to the In App Purchase Sandbox environment from the Simulator. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-5671217135163635765?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/5671217135163635765/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=5671217135163635765' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/5671217135163635765'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/5671217135163635765'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/08/testing-apps-with-in-app-purchases-in.html' title='Testing Apps with In App Purchases in Simulator'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_myUhtr_A51Y/S_S5FKdslFI/AAAAAAAATD8/BMoHT9I9OTc/s72-c/logo_mobile_RGB_300px.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-17320980584213312</id><published>2010-08-12T06:56:00.001+02:00</published><updated>2010-08-12T06:56:24.090+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='objective-c'/><title type='text'>Private Properties in Objective-C</title><content type='html'>When a co-worker asked me if there's a way to declare a Objective-C property to be private, I could come up with an answer. Even after googling for a while, I couldn't find an example, easy enough to understand. Every time I have to search for something more than a couple of minutes and I can't find a &lt;a href="http://stackoverflow.com/questions/1408350/is-it-bad-to-use-properties-for-private-variables-just-for-the-memory-manageme"&gt;definite source&lt;/a&gt; for an answer, I think it's worth a blog post. &lt;br /&gt;&lt;br /&gt;Declaring a private property is easy, once you understood the &lt;a href="https://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocCategories.html#//apple_ref/doc/uid/TP30001163-CH20-SW2"&gt;concept&lt;/a&gt; of &lt;em&gt;Categories&lt;/em&gt; and &lt;em&gt;Extensions&lt;/em&gt;:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;A category can add methods to any class, including the root class. Methods added to NSObject become available to all classes that are linked to your code.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Maybe you've used this before to enhance the functionality of a framework class. I've used it e.g. to add a shuffle method to &lt;em&gt;NSArray&lt;/em&gt;. &lt;em&gt;Extensions&lt;/em&gt; take the concept of a &lt;em&gt;category&lt;/em&gt; one step further:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Class extensions are like “anonymous” categories, except that the methods they declare must be implemented in the main @implementation block for the corresponding class.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;With an extension, you can easily implement private property declarations:&lt;br /&gt;&lt;br /&gt;&lt;script src="http://gist.github.com/518655.js"&gt;&lt;/script&gt;&lt;noscript&gt;&lt;pre&gt;// Person.h&lt;br /&gt;&lt;br /&gt;@interface Person {&lt;br /&gt; NSString *name;&lt;br /&gt; @private&lt;br /&gt; NSNumber *age;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@property (retain) NSString *name;&lt;br /&gt;&lt;br /&gt;@end&lt;br /&gt;&lt;br /&gt;// Person.m&lt;br /&gt;&lt;br /&gt;#import "Person.h"&lt;br /&gt;&lt;br /&gt;// class extension&lt;br /&gt;@interface Person () {&lt;br /&gt;}&lt;br /&gt; &lt;br /&gt; @property (retain) NSNumber *age;&lt;br /&gt;&lt;br /&gt;@end&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;@implementation&lt;br /&gt;&lt;br /&gt;@synthesize name,age;&lt;br /&gt;&lt;br /&gt;// other stuff&lt;br /&gt;&lt;br /&gt;@end&lt;/pre&gt;&lt;/noscript&gt;&lt;br /&gt;&lt;br /&gt;The &lt;em&gt;@private&lt;/em&gt; declaration takes care of protecting the instance variable age, only the Person implementation is allowed to access it. The class &lt;em&gt;extension&lt;/em&gt; in Person.m declares the &lt;em&gt;property&lt;/em&gt; and it's being &lt;em&gt;synthesized&lt;/em&gt; just the way you would with a public &lt;em&gt;property&lt;/em&gt;. &lt;br /&gt;&lt;br /&gt;Due to the anonymous nature of the class &lt;em&gt;extension&lt;/em&gt;, nobody including the Person.h file can see the &lt;em&gt;synthesized&lt;/em&gt; getters and setters, hence a private &lt;em&gt;property&lt;/em&gt;.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-17320980584213312?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/17320980584213312/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=17320980584213312' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/17320980584213312'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/17320980584213312'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/08/private-properties-in-objective-c.html' title='Private Properties in Objective-C'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-6065680816213900468</id><published>2010-08-06T09:52:00.001+02:00</published><updated>2010-08-06T09:56:24.721+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='objective-c'/><title type='text'>Improve your dealloc method</title><content type='html'>If you are programming for any iOS device, chances are high, you've came across &lt;em&gt;properties&lt;/em&gt;. They give you these tedious accessor methods for free and in addition to that some nice memory management features. &lt;br /&gt;&lt;br /&gt;While explaining a colleague how to implement &lt;em&gt;dealloc&lt;/em&gt;, he pointed me to the excellent &lt;a href="http://cocoadevcentral.com/d/learn_objectivec/"&gt;Objective-C guide &lt;/a&gt;of &lt;a href="http://cocoadevcentral.com"&gt;Cocoa Dev Central.&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;&lt;script src="http://gist.github.com/511002.js"&gt;&lt;/script&gt;&lt;noscript&gt;&lt;pre&gt;// Person.h&lt;br /&gt;&lt;br /&gt;@interface Person {&lt;br /&gt; NSString *name;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@property (retain) NSString *name;&lt;br /&gt;&lt;br /&gt;@end&lt;br /&gt;&lt;br /&gt;// Person.m&lt;br /&gt;&lt;br /&gt;#import "Person.h"&lt;br /&gt;&lt;br /&gt;@implementation&lt;br /&gt;&lt;br /&gt;@synthesize name;&lt;br /&gt;&lt;br /&gt;// other stuff&lt;br /&gt;&lt;br /&gt;- (void) dealloc {&lt;br /&gt;    [name release];&lt;br /&gt;    name = nil;&lt;br /&gt;    [super dealloc];&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@end&lt;/pre&gt;&lt;/noscript&gt;You can see a pretty straightforward implementation of an Objective-C class there, with an implementation of &lt;em&gt;dealloc&lt;/em&gt;, the way you are used to. However, since we have synthesized getters and setters, we can simply write the following:&lt;br /&gt;&lt;br /&gt;&lt;script src="http://gist.github.com/511003.js"&gt;&lt;/script&gt;&lt;noscript&gt;&lt;pre&gt;- (void) dealloc {&lt;br /&gt;    self.name = nil;&lt;br /&gt;    [super dealloc];&lt;br /&gt;}&lt;/pre&gt;&lt;/noscript&gt;Basically, the implementation of the accessor at runtime handle memory management for us here, which I think is pretty neat. &lt;br /&gt; &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-6065680816213900468?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/6065680816213900468/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=6065680816213900468' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/6065680816213900468'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/6065680816213900468'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/08/improve-your-dealloc-method.html' title='Improve your dealloc method'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-4871584553496424221</id><published>2010-07-19T06:10:00.000+02:00</published><updated>2010-07-19T06:16:53.962+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='ipod'/><category scheme='http://www.blogger.com/atom/ns#' term='gae'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><title type='text'>Apple Push Notifications on Google Appengine</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://doublemill.blogspot.com"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh6.ggpht.com/_myUhtr_A51Y/S900IqmyZjI/AAAAAAAATCQ/YpGbR64H5kE/doublemill-512.png?imgmax=144" border="0" alt=""id="BLOGGER_PHOTO_ID_5191388009271083842" /&gt;&lt;/a&gt;&lt;span style="font-style:italic;"&gt;Last year, I worked on a Nine Men's Morris (Mühle) implementation for Android, called &lt;a href="http://doublemill.blogspot.com/"&gt;Doublemill&lt;/a&gt;. Since I'm an iPhone user, I decided to port the game to iPhone/iPod touch and the iPad. There are 3 versions: &lt;a href="http://itunes.apple.com/app/doublemill/id368886888?mt=8"&gt;Doublemill Premium&lt;/a&gt;, &lt;a href="http://itunes.apple.com/de/app/doublemill-lite/id362714027?mt=8"&gt;Doublemill Lite&lt;/a&gt; and &lt;a href="http://itunes.apple.com/app/doublemill-for-ipad/id370791547?mt=8"&gt;Doublemill for iPad&lt;/a&gt;.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I've written about the &lt;a href="http://dlinsin.blogspot.com/search/label/gae"&gt;Google Appengine (GAE)&lt;/a&gt; a couple of times before and the same goes for &lt;a href="http://dlinsin.blogspot.com/2010/05/apple-push-notifications-gotchas.html"&gt;Apple Push Notifications (APN)&lt;/a&gt;. For Doublemill I had to bring those two technologies together and it was a pain and pleasure at the same time.&lt;br /&gt;&lt;br /&gt;Let me start with &lt;a href="http://dlinsin.blogspot.com/2010/07/push-notifications-in-production.html"&gt;the pain&lt;/a&gt;! Apples Push Notifications in a restricted environment like the Google Appengine is no fun to implement! My first approach was to use &lt;a href="http://github.com/notnoop/java-apns"&gt;notnoop's java-apn&lt;/a&gt;, which I've used before and works quite well. Unfortunately, there is &lt;a href="http://code.google.com/p/googleappengine/issues/detail?id=792&amp;colspec=ID%20Type%20Status%20Priority%20Stars%20Owner%20Summary%20Log%20Component"&gt;currently no way&lt;/a&gt; of using a client certificate, which you'll need in order to talk to Apple's server on the Google Appengine. There are some &lt;em&gt;javax.security&lt;/em&gt; classes missing on GAE.&lt;br /&gt;&lt;br /&gt;That's where the pleasure part begins! Thanks to &lt;a href="http://urbanairship.com/"&gt;Urban Airship&lt;/a&gt;, it's easy to hook up your application running on Google Appengine to Apple's Push Notifications! And the best part - it's free (although not unlimited)! Urban Airshipe provides an &lt;a href="http://urbanairship.com/docs/push_index.html"&gt;easy to use REST interface&lt;/a&gt;, which you can call from your Java code on GAE with a good old &lt;em&gt;HttpURLConnection&lt;/em&gt;. It uses Basic Auth over Https to ensure that only your application can send Push Notifications to your iPhone App.&lt;br /&gt;&lt;br /&gt;I won't provide any code for you here, because it's the basic concept that's the most interesting part when bringing APN to GAE. The first thing you need to do, is store your user's deviceToken. You should do that each time the App launches, to ensure it's the correct token. That token, along with the information you want to present to your user, needs to be passed to Urban Airship's REST interface, everytime you want to send a notification. A Cron Job or Task on Google Appengine could handle that for you. &lt;br /&gt;&lt;br /&gt;If your application is designed with notifications in mind, then it's quite easy to bring Apple Push Notifications to the Google Appengine, thanks to Urban Airship!&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-4871584553496424221?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/4871584553496424221/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=4871584553496424221' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/4871584553496424221'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/4871584553496424221'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/07/apple-push-notifications-on-google.html' title='Apple Push Notifications on Google Appengine'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_myUhtr_A51Y/S900IqmyZjI/AAAAAAAATCQ/YpGbR64H5kE/s72-c/doublemill-512.png?imgmax=144' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-2481283744772349161</id><published>2010-07-06T06:08:00.000+02:00</published><updated>2010-07-06T06:13:35.808+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mobile'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='iPad'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='ipod'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><title type='text'>Push Notifications in Production</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://doublemill.blogspot.com"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh6.ggpht.com/_myUhtr_A51Y/S900IqmyZjI/AAAAAAAATCQ/YpGbR64H5kE/doublemill-512.png?imgmax=144" border="0" alt=""id="BLOGGER_PHOTO_ID_5191388009271083842" /&gt;&lt;/a&gt;&lt;span style="font-style:italic;"&gt;Last year, I worked on a Nine Men's Morris (Mühle) implementation for Android, called &lt;a href="http://doublemill.blogspot.com/"&gt;Doublemill&lt;/a&gt;. Since I'm an iPhone user, I decided to port the game to iPhone/iPod touch and the iPad. There are 3 versions: &lt;a href="http://itunes.apple.com/app/doublemill/id368886888?mt=8"&gt;Doublemill Premium&lt;/a&gt;, &lt;a href="http://itunes.apple.com/de/app/doublemill-lite/id362714027?mt=8"&gt;Doublemill Lite&lt;/a&gt; and &lt;a href="http://itunes.apple.com/app/doublemill-for-ipad/id370791547?mt=8"&gt;Doublemill for iPad&lt;/a&gt;.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In a &lt;a href="http://dlinsin.blogspot.com/2010/05/apple-push-notifications-gotchas.html"&gt;previous blog post&lt;/a&gt; I highlighted a couple of gotchas you need to be aware of when implementing Apple Push Notifications. Unfortunately, I didn't really pay attention to one of them myself:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;The first problem I ran into, had to do with my provisioning profile for development. I enabled APN in my Provisioning Portal, installed the certificate and implemented all the delegate methods according to the documentation, but somehow the method&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;was called with error code 3000 - "no valid 'aps-environment' entitlement string found for application". I was pretty sure, I setup everything correctly. However, &lt;a href="http://unlikelyteacher.com/2010/04/05/apple-push-notification-failed-to-register-with-error-error-domainnscocoaerrordomain/"&gt;I missed &lt;/a&gt;one little thing: &lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;After you have generated your Client SSL certificate, create a new provisioning profile containing the App ID you wish to use for notifications.&lt;/blockquote&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;This bit me after submitting &lt;a href="http://itunes.apple.com/app/doublemill/id368886888?mt=8"&gt;Doublemill Premium&lt;/a&gt; to the App Store and being rejected for promoting Push Notifications in my App description, but according to Apple, not implementing it. During beta testing, everything worked fine. I had a correct implementation and the App was asking me for permission to use Push Notifications. &lt;br /&gt;&lt;br /&gt;However, I did forget to create a new distribution provisioning profile for the App Store, after enabling Push Notifications for production. So, it didn't work for the reviewer at Apple. Forgetting about something like this is very painful, especially since I'm waiting for the 3rd round of review for the next release of Doublemill. That makes it 3 weeks since the initial submission. &lt;br /&gt;&lt;br /&gt;When asking about this on the Apple Developer Forums, Mike was so kind to point out a neat trick on the command line, which helps you identify, whether APN for production is enabled: &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;codesign -dvvvv --entitlements - /path/to/App&lt;br/&gt;&lt;br /&gt;&amp;lt;key&amp;gt;aps-environment&amp;lt;/key&amp;gt;&lt;br /&gt;&amp;lt;string&amp;gt;production&amp;lt;/string&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;If you see &lt;em&gt;aps-environment=production&lt;/em&gt;, you should be save. Push notifications should work. If the key is missing, then you should create a new provisioning profile and rebuild you App with that. &lt;br /&gt;&lt;br /&gt;It would be nice if Apple built this kind of checks into XCode or at least invalidate the provisioning profiles as soon as you enable Push Notifications in your App ID.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-2481283744772349161?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/2481283744772349161/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=2481283744772349161' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/2481283744772349161'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/2481283744772349161'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/07/push-notifications-in-production.html' title='Push Notifications in Production'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_myUhtr_A51Y/S900IqmyZjI/AAAAAAAATCQ/YpGbR64H5kE/s72-c/doublemill-512.png?imgmax=144' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-6134079167777938199</id><published>2010-06-30T06:20:00.000+02:00</published><updated>2010-06-30T06:46:01.331+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='quality'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='iPad'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='ipod'/><category scheme='http://www.blogger.com/atom/ns#' term='objective-c'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><title type='text'>UI Prototyping iPhone Apps</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://mobile.synyx.de"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh5.ggpht.com/_myUhtr_A51Y/S_S5FKdslFI/AAAAAAAATD8/BMoHT9I9OTc/s144/logo_mobile_RGB_300px.png" border="0" alt=""id="" /&gt;&lt;/a&gt;&lt;em&gt;I'm writing for the &lt;a href="http://www.synyx.de"&gt;Synyx GmbH &amp; Co. KG&lt;/a&gt; mobile solutions blog. From time to time, I'll cross post articles here, if I think they are of interest for you. If you'd like to read all of my other posts, subscribe to the &lt;a href="http://mobile.synyx.de"&gt;Synyx Mobile Solutions Blog&lt;/a&gt;.&lt;/em&gt;&lt;br/&gt;&lt;br /&gt;&lt;br /&gt;Before &lt;a href="http://dlinsin.blogspot.com/2010/06/wwdc10.html"&gt;flying off to WWDC&lt;/a&gt; last month, I watched a whole bunch of sessions from 2009. Among others a session on "Prototyping iPhone User Interfaces" by Bret Victor. If you haven't watched it and you've got access to the WWDC videos - stop right here and watch the video!&lt;br /&gt;&lt;br /&gt;In his session, Bret shows how to prototype an interface only by interacting with screenshots! It's amazing that a simple screenshot on the device can show you so much more than by just looking at it in a document or print out. It inspired me to use his framework and the whole process for our own development. &lt;br /&gt;&lt;br /&gt;Unfortunately, the code for the session isn't available and neither Bret nor the frameworks evangelist, mentioned in the presentation, got back to me about the code. After some digging, I found &lt;a href="http://www.fruitstandsoftware.com/blog/2009/07/uiview-manipulation-made-easier-with-a-category/"&gt;Michael Fey's blog&lt;/a&gt;, who was able to successfully reverse engineer the missing parts of source code, which were not shown in the presentation.&lt;br /&gt;&lt;br /&gt;Michael's &lt;em&gt;UIViewAdditions&lt;/em&gt; basically allow easy access to frame properties and give you a neat init method, which adds the passed &lt;em&gt;UIView&lt;/em&gt; as a parent:&lt;br /&gt;&lt;br /&gt;&lt;script src="http://gist.github.com/455669.js"&gt;&lt;/script&gt;&lt;noscript&gt;&lt;pre&gt;- (id)initWithParent:(UIView *)parent {&lt;br /&gt;  self = [self initWithFrame:CGRectZero];&lt;br /&gt;  if (!self)&lt;br /&gt;    return nil;&lt;br /&gt;  [parent addSubview:self];&lt;br /&gt;  return self;&lt;br /&gt;}&lt;br /&gt;+ (id) viewWithParent:(UIView *)parent {&lt;br /&gt;  return [[[self alloc] initWithParent:parent] autorelease];&lt;br /&gt;}&lt;/pre&gt;&lt;/noscript&gt;&lt;br /&gt;&lt;br /&gt;There wasn't much left to do for me. I only coded the class &lt;em&gt;Root&lt;/em&gt;, which is the parent of all &lt;em&gt;UIImageView&lt;/em&gt; instances. It provides a couple of methods to slide images back and forth:&lt;br /&gt;&lt;br /&gt;&lt;script src="http://gist.github.com/455671.js"&gt;&lt;/script&gt;&lt;noscript&gt;&lt;pre&gt;@synthesize pageIndex = _pageIndex;&lt;br/&gt;&lt;br /&gt;- (id) initWithParent:(UIView *)parent {&lt;br /&gt;  self = [super initWithParent:parent];&lt;br /&gt;  if (self == nil) {&lt;br /&gt;    return nil;&lt;br /&gt;  }&lt;br /&gt;  self.userInteractionEnabled = YES;&lt;br /&gt;  self.size = self.window.size;&lt;br /&gt;  [[UIImageView viewWithParent:self] setImageWithName:@"dailies"];&lt;br /&gt;  self.pageIndex = 0;&lt;br /&gt;  return self;&lt;br /&gt;}&lt;br/&gt;&lt;br /&gt;- (void)setPageIndex:(int)index {&lt;br /&gt;  if (index &amp;lt; 0 || index &amp;gt;= [self.subviews count]) {&lt;br /&gt;    return;&lt;br /&gt;  }&lt;br /&gt;  _pageIndex = index;&lt;br /&gt;  for (int i = 0; i &amp;lt; [self.subviews count]; i++) {&lt;br /&gt;    UIImageView *page = [self.subviews objectAtIndex:i];&lt;br /&gt;    page.x = (i &amp;lt; _pageIndex) ? -self.width : (i &amp;gt; _pageIndex) ? self.width : 0;&lt;br /&gt;  }&lt;br /&gt;}&lt;br/&gt;&lt;br /&gt;- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {&lt;br /&gt;  self.pageIndex++;&lt;br /&gt;}&lt;/pre&gt;&lt;/noscript&gt;&lt;br /&gt;&lt;br /&gt;With those two classes and a couple of screenshots, it is fairly easy to create an App that looks and feels almost real. I created a short demo video, which shows how easy it is to get a good feeling if your App is going to work or not:&lt;br /&gt;&lt;br /&gt;&lt;object width="480" height="385"&gt;&lt;param name="movie" value="http://www.youtube.com/v/UMs3i51mghM&amp;hl=en_US&amp;fs=1&amp;"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/UMs3i51mghM&amp;hl=en_US&amp;fs=1&amp;" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="480" height="385"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;Now don't forget those are only screenshots and the App might need to load stuff over the network or do some animation, hence it might not feel the same. However, this process of prototyping an UI is powerful enough to give you an idea, whether the workflow or the UI in general is going to "work" or needs some tweaking. &lt;br /&gt;&lt;br /&gt;You can download the source code for the two classes, along with a sample project from &lt;a href="http://github.com/dlinsin/district9/tree/master/UIPrototyping/"&gt;github&lt;/a&gt;.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-6134079167777938199?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/6134079167777938199/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=6134079167777938199' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/6134079167777938199'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/6134079167777938199'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/06/ui-prototyping-iphone-apps.html' title='UI Prototyping iPhone Apps'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_myUhtr_A51Y/S_S5FKdslFI/AAAAAAAATD8/BMoHT9I9OTc/s72-c/logo_mobile_RGB_300px.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-1037446854129980755</id><published>2010-06-14T06:01:00.000+02:00</published><updated>2010-06-14T06:45:03.913+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='traveling'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='iPad'/><category scheme='http://www.blogger.com/atom/ns#' term='ipod'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><title type='text'>WWDC10</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://developer.apple.com/wwdc"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh5.ggpht.com/_myUhtr_A51Y/TBKaJsQ41XI/AAAAAAAATfw/V_Cj02QcpAQ/s288/IMG_1338.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5191388009271083842" /&gt;&lt;/a&gt;It was my &lt;a href="http://developer.apple.com/wwdc/"&gt;first WWDC&lt;/a&gt; this year and I'm blown away in any aspect! In order to not sound like an Apple fan boy, I'll highlight 3 aspects, which every conference has and compare them to my &lt;a href="http://dlinsin.blogspot.com/2010/03/mobile-times-2010.html"&gt;previous developer conference experiences&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;The speakers were all Apple engineers. They knew how to present and almost all of them were very entertaining and fun to listen to. Most important of all - they knew what they were talking about! This is something no conference I've ever been to can compete with - not even the &lt;a href="http://dlinsin.blogspot.com/2009/05/springone-2009-recap.html"&gt;Spring Source conferences&lt;/a&gt;! They were close, but mostly featured some pretty bad talks from none Spring Source consultants, which kind of ruined the experience!&lt;br /&gt;&lt;br /&gt;This is one aspect that makes the conference worth attending. I could simply go up to the Core Animations guy and ask him why the stuff is not working as I expect it to. The best thing was, I got and answer that actually &lt;a href="http://itunes.apple.com/app/doublemill-fur-ipad/id370791547?mt=8"&gt;solved my problem&lt;/a&gt;!&lt;br /&gt;&lt;br /&gt;Another aspect is the choice of topics and the quality of the presentations! Everything kind of fits in nicely to the whole App theme. You could listen to talks on Interface Design, followed by an in depth session on Core Animation and finish off with listening to the latest innovations on the new iPhone 4. The quality of each talk was amazing! This is the first conference ever, where I wasn't close to dozing off once! Every session was interesting and inspiring, although they sometimes overlapped in terms of content!&lt;br /&gt;&lt;br /&gt;To be fair, I'm kind of new to the iPhone platform, so if everything was interesting for me. I don't know how that holds true for fellow developers working on iPhone Apps for a couple of years. Other conferences I attended had quality talks, too - no doubt! &lt;a href="http://dlinsin.blogspot.com/2008/12/devoxx-2008-coming.html"&gt;Devoxx&lt;/a&gt;, e.g. always had great talks, but there was mostly one or two a day and the rest was mostly boring. &lt;br /&gt;&lt;br /&gt;Third and last is the organizational aspect of the conference. It's amazing how Apple manages to keep 5000 developers under control. The food, although not extensive, was very good. Soft drinks all through the day, as well as breakfast and lunch. Another thing that I was blown away is the Wifi. Apple managed to provide a fairly stable internet connection for 5000 developers with probably more than 10000 devices. It didn't always work, as you could see in Steve's keynote, but most of the time it was stable and fast. &lt;br /&gt;&lt;br /&gt;Even though I had to travel almost a day, going back and forth to San Francisco and the conference tickets are super expensive, it's definitely worth it. So WWDC11, here I come!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-1037446854129980755?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/1037446854129980755/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=1037446854129980755' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/1037446854129980755'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/1037446854129980755'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/06/wwdc10.html' title='WWDC10'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_myUhtr_A51Y/TBKaJsQ41XI/AAAAAAAATfw/V_Cj02QcpAQ/s72-c/IMG_1338.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-490756251327029388</id><published>2010-06-02T06:43:00.000+02:00</published><updated>2010-06-02T07:47:31.527+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><title type='text'>Book Review: The Business of iPhone App Development</title><content type='html'>&lt;span style="font-style:italic;"&gt;&lt;a href="http://www.apress.com"&gt;Apress&lt;/a&gt; was kind enough to pass me a copy of this book, which I agreed to review in return.&lt;/span&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.amazon.de/gp/product/1430224592?ie=UTF8&amp;tag=mytakeonthing-21&amp;linkCode=as2&amp;camp=1638&amp;creative=6742&amp;creativeASIN=1430224592"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh5.ggpht.com/_myUhtr_A51Y/S_9Y2kv72fI/AAAAAAAATdo/a6tLOWgQEi0/s144/9781430227335.gif" border="0" alt=""id="" /&gt;&lt;/a&gt;After the &lt;a href="http://dlinsin.blogspot.com/2009/12/book-review-iphone.html"&gt;last book I reviewed&lt;/a&gt;, which was rather technical, I decided to go for a book with a tad more business angle. Personally, I was disappointed by this book, but that might not be the case for you.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.amazon.de/gp/product/1430224592?ie=UTF8&amp;tag=mytakeonthing-21&amp;linkCode=as2&amp;camp=1638&amp;creative=6742&amp;creativeASIN=1430224592"&gt;Business of iPhone App Development&lt;/a&gt; covers exactly what the title promises: the whole process, beginning with the idea of an App over development to submitting it to the store. It's chopped up in &lt;a href="http://books.google.com/books?id=gebdTqLjj5EC&amp;lpg=PP1&amp;pg=PR4#v=onepage&amp;q&amp;f=false"&gt;10 chapter&lt;/a&gt;, covering various steps of the process. &lt;br /&gt;&lt;br /&gt;I'm not going into great detail explaining the content of the book. Instead, I'll write about what I didn't like. I think that should help you make a decision whether to get the book or not.&lt;br /&gt;&lt;br /&gt;The reason why I didn't like this book is very simple: most of the information in this book can be obtained from the net or using common sense!&lt;br /&gt;&lt;br /&gt;Let me elaborate: I think being able to obtain any information on the net these days is quite obvious. That doesn't mean you shouldn't buy this book. If you need a compact resource on topics like "Setting Up Xcode" or "Submission to the App Store", this book is definitely worth getting. If you have done this before, like me, you will be disappointed, because there's nothing new! If you are looking into more business-related topics, there's definitely topics worth reading. However, most of the technical content can be found in Apple's documentation or on stackoverflow.com. Since the book has a lot of technical chapters, I was a little bored revisiting information I already knew.&lt;br /&gt;&lt;br /&gt;The more business-like chapters are definitely worth reading, especially if you are new to the whole App Store game. However, in my opinion a huge portion of the presented information are simple common sense! Using marketing channels like Twitter or Social Media like Facebook is standard these days and I believe there are no developers out there not knowing about those. However, some of the information seem like common sense, but it's great to have a resource to revisit from time to time, like e.g. &lt;a href="http://dlinsin.blogspot.com/2010/05/lessons-learned-iphone-review.html"&gt;App Store Rejection reasons&lt;/a&gt;. Another great chapter is "Protecting Your Intellectual Property", which you probably can't find anywhere else. It covers EULAs, licensing and trademarks.&lt;br /&gt;&lt;br /&gt;Overall, &lt;a href="http://www.amazon.de/gp/product/1430224592?ie=UTF8&amp;tag=mytakeonthing-21&amp;linkCode=as2&amp;camp=1638&amp;creative=6742&amp;creativeASIN=1430224592"&gt;Business of iPhone App Development&lt;/a&gt; encompasses a lot of information and the authors did a great job, trying to cover a lot of ground. However, I can only fully recommend this book to you, if you are new to the world of &lt;a href="http://www.amazon.de/gp/product/1430224592?ie=UTF8&amp;tag=mytakeonthing-21&amp;linkCode=as2&amp;camp=1638&amp;creative=6742&amp;creativeASIN=1430224592"&gt;iPhone development&lt;/a&gt;. Otherwise, you might want to think about it twice.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-490756251327029388?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/490756251327029388/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=490756251327029388' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/490756251327029388'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/490756251327029388'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/06/book-review-business-of-iphone-app.html' title='Book Review: The Business of iPhone App Development'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_myUhtr_A51Y/S_9Y2kv72fI/AAAAAAAATdo/a6tLOWgQEi0/s72-c/9781430227335.gif' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-6783699392065198089</id><published>2010-05-28T06:05:00.000+02:00</published><updated>2010-05-28T06:05:00.671+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='iPad'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='ipod'/><category scheme='http://www.blogger.com/atom/ns#' term='objective-c'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><title type='text'>Apple Push Notifications Gotchas</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://doublemill.blogspot.com"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh6.ggpht.com/_myUhtr_A51Y/S900IqmyZjI/AAAAAAAATCQ/YpGbR64H5kE/doublemill-512.png?imgmax=144" border="0" alt=""id="BLOGGER_PHOTO_ID_5191388009271083842" /&gt;&lt;/a&gt;&lt;span style="font-style:italic;"&gt;Last year, I worked on a Nine Men's Morris (Mühle) implementation for Android, called &lt;a href="http://doublemill.blogspot.com/"&gt;Doublemill&lt;/a&gt;. Since I'm an iPhone user, I decided to port the game to iPhone/iPod touch and the iPad. There are 3 versions: &lt;a href="http://itunes.apple.com/app/doublemill/id368886888?mt=8"&gt;Doublemill Premium&lt;/a&gt;, &lt;a href="http://itunes.apple.com/de/app/doublemill-lite/id362714027?mt=8"&gt;Doublemill Lite&lt;/a&gt; and &lt;a href="http://itunes.apple.com/app/doublemill-for-ipad/id370791547?mt=8"&gt;Doublemill for iPad&lt;/a&gt;.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I'm implementing Apple Push Notifications (APN) for Doublemill Premium right now and ran into some gotchas, which I'm pretty sure are implemented somewhere else, but I'm gonna share them here with you. If you are new to APN, checkout Apple's documentation. It's pretty good and will get you quite far.&lt;br /&gt;&lt;br /&gt;The first problem I ran into, had to do with my provisioning profile for development. I enabled APN in my Provisioning Portal, installed the certificate and implemented all the delegate methods according to the documentation, but somehow the method&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;was called with error code 3000 - "no valid 'aps-environment' entitlement string found for application". I was pretty sure, I setup everything correctly. However, &lt;a href="http://unlikelyteacher.com/2010/04/05/apple-push-notification-failed-to-register-with-error-error-domainnscocoaerrordomain/"&gt;I missed &lt;/a&gt;one little thing: &lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;After you have generated your Client SSL certificate, create a new provisioning profile containing the App ID you wish to use for notifications.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;That's what the Provisioning Portal tells you to do after you enabled APN. Your existing provisioning profiles are not being hooked up to APN, you really need to create new ones.&lt;br /&gt;&lt;br /&gt;Another problem I ran into is a blocked Wifi port. Apple's documentation says:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;If a cellular or WiFi connection is not available, neither the application:didRegisterForRemoteNotificationsWithDeviceToken: method or the application:didFailToRegisterForRemoteNotificationsWithError: method is called. For WiFi connections, this sometimes occurs when the device cannot connect with APNs over port 5223.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;That also means neither of the methods is called when 5223 is blocked. Fortunately I came across a very helpful post on the Apple Developer Forums (sorry can't link to that here), which pointed me to a &lt;a href="http://www.atnan.com/2009/7/27/enabling-verbose-push-notification-service-apns-logs"&gt;mobileconfig&lt;/a&gt;, enabling APN logging. If you have your phone connected and can see the log statements, you can find out if your port is blocked or something else is wrong. Don't forget to restart your phone after installing the mobileconfig, in order to enable the logging. For Doublemill the statements look something like this:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Connecting courier stream to sandbox.push.apple.com on port 5223&lt;br/&gt;&lt;br /&gt;Connecting courier stream to push.apple.com on port 5223&lt;br/&gt;&lt;br /&gt;Connecting to courier 2-courier.sandbox.push.apple.com&lt;br/&gt;&lt;br /&gt;Connecting to courier 19-courier.push.apple.com&lt;br/&gt;&lt;br /&gt;Connected to courier 2-courier.sandbox.push.apple.com (17.000.34.73)&lt;br/&gt;&lt;br /&gt;Sending connect message with token 'xyz'&lt;br/&gt;&lt;br /&gt;Connected to courier 19-courier.push.apple.com (17.000.36.88)&lt;br /&gt;Sending connect message with token 'abc'&lt;br/&gt;&lt;br /&gt;Recieved connected response OK&lt;br/&gt;&lt;br /&gt;Sending filter message for enabled hashes { 34345 = "de.linsin.games.doublemill"; } and ignored hashes {}&lt;br/&gt;&lt;br /&gt;...&lt;br/&gt;&lt;br /&gt;Recieved connected response OK&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;These log statements are priceless, when hunting down the reason why you won't receive push notification in your application!&lt;br /&gt;&lt;br /&gt;The last gotcha for today is one that I couldn't really find in &lt;a href="http://developer.apple.com/iphone/library/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/ProvisioningDevelopment/ProvisioningDevelopment.html#//apple_ref/doc/uid/TP40008194-CH104-SW2"&gt;Apple's documentation&lt;/a&gt; or at least I didn't really understand it that way. Anyways, if you distribute your App with an Ad-Hoc provisioning profile, which you would do for &lt;a href="http://dlinsin.blogspot.com/2010/04/iphone-app-beta-testing.html"&gt;beta testing&lt;/a&gt;, the destination of your push notifications are not the sandbox, but the production. Come to think of it - it does make sense. However, you do need to create the production certificate and a &lt;strong&gt;new&lt;/strong&gt; provisioning profile for it. Of course, your backend needs to be able to handle both destinations, as well! &lt;br /&gt;&lt;br /&gt;Overall, APN is a cool feature and if you are running a server for your App anyways, it actually is for free! If your backend is running on &lt;a href="http://dlinsin.blogspot.com/search/label/gae"&gt;Google App Engine&lt;/a&gt;, it's a different story, which I'll talk about in a future blog post.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-6783699392065198089?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/6783699392065198089/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=6783699392065198089' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/6783699392065198089'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/6783699392065198089'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/05/apple-push-notifications-gotchas.html' title='Apple Push Notifications Gotchas'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_myUhtr_A51Y/S900IqmyZjI/AAAAAAAATCQ/YpGbR64H5kE/s72-c/doublemill-512.png?imgmax=144' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-6719742897849457779</id><published>2010-05-20T06:22:00.004+02:00</published><updated>2010-06-28T12:19:55.234+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mobile'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='ipod'/><category scheme='http://www.blogger.com/atom/ns#' term='objective-c'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><title type='text'>Lessons learned - iPhone Review</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://mobile.synyx.de"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh5.ggpht.com/_myUhtr_A51Y/S_S5FKdslFI/AAAAAAAATD8/BMoHT9I9OTc/s144/logo_mobile_RGB_300px.png" border="0" alt=""id="" /&gt;&lt;/a&gt;&lt;em&gt;I'm writing for the &lt;a href="http://www.synyx.de"&gt;Synyx GmbH &amp; Co. KG&lt;/a&gt; mobile solutions blog. From time to time, I'll cross post articles here, if I think they are of interest for you. If you'd like to read all of my other posts, subscribe to the &lt;a href="http://mobile.synyx.de"&gt;Synyx Mobile Solutions Blog&lt;/a&gt;.&lt;/em&gt;&lt;br/&gt;&lt;br /&gt;&lt;br /&gt;When you submit an App to the Apple App Store it has to go through a "rigorous quality check", conducted by Apple. Although &lt;a href="http://apprejections.com/index.php/post/171"&gt;there are &lt;/a&gt;&lt;a href="http://www.mobileorchard.com/avoiding-iphone-app-rejection-from-apple/"&gt;plenty of resources &lt;/a&gt;&lt;a href="http://iphone.derheckser.com/2009/07/10/suffering-from-modus-operandi-of-reviewer-team/"&gt;out there&lt;/a&gt;, here's a short rundown of what we've learned ourselves so far:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Marketing Apps are not allowed&lt;/strong&gt;&lt;/li&gt;&lt;br /&gt;If the sole purpose of your App is marketing, you'll have a hard time getting your App through. You need to add what Apple calls "user functionality". That could be something simple like a photo gallery or a feature to reserve a room or table.&lt;br /&gt;&lt;br /&gt;	&lt;li&gt;&lt;strong&gt;You cannot tease your users with features that they have to pay for&lt;/strong&gt;&lt;/li&gt;&lt;br /&gt;If you are offering a lite version of your App, you cannot add disabled functionality, which would be enabled in the paid version. A lite version usually is offered separately from a paid version, which means the user will constantly see disabled menu items or buttons, since the App will never be updated. Instead add a info section about the paid version in your App, which describes what the paid version offers.&lt;br /&gt;&lt;br /&gt;	&lt;li&gt;&lt;strong&gt;Don't ask your users to upgrade&lt;/strong&gt;&lt;/li&gt;&lt;br /&gt;You cannot add an alert in a free/lite version of your App, which asks users to upgrade or buy the paid version. Instead you should add a "buy me" button or a section in your App further describing what your paid version offers.&lt;br /&gt;&lt;br /&gt;	&lt;li&gt;&lt;strong&gt;Build a working App&lt;/strong&gt;&lt;/li&gt;&lt;br /&gt;You are definitely rejected if your App is buggy! If the reviewer thinks he found a bug, he'll reject your App. A crash is the worst case scenario, but &lt;a href="http://dlinsin.blogspot.com/2010/05/don-forget-your-linker-flags.html"&gt;it happens&lt;/a&gt;. However, don't depend on Apple as your QA, because the review times are too long to go back and forth this way. &lt;br /&gt;&lt;br /&gt;	&lt;li&gt;&lt;strong&gt;Don't infringe Trademarks or Copyrights&lt;/strong&gt;&lt;/li&gt;&lt;br /&gt;Don't mention Apple, &lt;a href="http://www.geek.com/articles/mobile/apple-demands-a-developer-removes-android-references-from-iphone-app-2010024/"&gt;Android&lt;/a&gt; or any other Trademark therefore - as long as you don't own it. You should also resist to use iPhone like icons or images.  &lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;If you respect all of these restrictions and gotchas, you should be save to get your App through the review process. I say "should", because it all appears to depend on the person who reviews your App. Let us know what you experienced, submitting your Apps, I bet there are a lot more of these gotchas.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-6719742897849457779?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/6719742897849457779/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=6719742897849457779' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/6719742897849457779'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/6719742897849457779'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/05/lessons-learned-iphone-review.html' title='Lessons learned - iPhone Review'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_myUhtr_A51Y/S_S5FKdslFI/AAAAAAAATD8/BMoHT9I9OTc/s72-c/logo_mobile_RGB_300px.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-650416457592681242</id><published>2010-05-10T07:16:00.000+02:00</published><updated>2010-05-10T07:17:08.519+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='objective-c'/><title type='text'>Don't Forget your Linker Flags</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://doublemill.blogspot.com"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh6.ggpht.com/_myUhtr_A51Y/S900IqmyZjI/AAAAAAAATCQ/YpGbR64H5kE/doublemill-512.png?imgmax=144" border="0" alt=""id="BLOGGER_PHOTO_ID_5191388009271083842" /&gt;&lt;/a&gt;&lt;span style="font-style:italic;"&gt;Last year, I worked on a Nine Men's Morris (Mühle) implementation for Android, called &lt;a href="http://doublemill.blogspot.com/"&gt;Doublemill&lt;/a&gt;. Since I'm an iPhone user, I decided to port the game to iPhone/iPod touch and the iPad. There are 3 versions: &lt;a href="http://itunes.apple.com/app/doublemill/id368886888?mt=8"&gt;Doublemill Premium&lt;/a&gt;, &lt;a href="http://itunes.apple.com/de/app/doublemill-lite/id362714027?mt=8"&gt;Doublemill Lite&lt;/a&gt; and Doublemill for iPad.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Doublemill is out the door and &lt;a href="http://itunes.apple.com/app/doublemill/id368886888?mt=8"&gt;available for download&lt;/a&gt; in the App Store - finally! It has been more than two weeks since my submission. The reason why it took so long, besides Apple, which takes up to a week at the moment to review Apps, is my stupidity!&lt;br /&gt;&lt;br /&gt;I'm using &lt;a href="http://github.com/gabriel/yajl-objc"&gt;Objective-C bindings for YAJL&lt;/a&gt; to create and parse JSON in &lt;a href="http://doublemill.blogspot.com/2010/05/video-how-to-iii.html"&gt;Doublemill's multiplayer feature&lt;/a&gt;. It comes with a &lt;a href="http://en.wikipedia.org/wiki/Objective-C#Categories"&gt;Category&lt;/a&gt; for &lt;em&gt;NSString&lt;/em&gt;, which basically makes it a two liner to create a JSON representation of a string:&lt;br /&gt;&lt;br /&gt;&lt;script src="http://gist.github.com/395688.js"&gt;&lt;/script&gt;&lt;noscript&gt;&lt;pre&gt;#import "NSObject+YAJL.h"&lt;br /&gt;&lt;br /&gt;NSDictionary *vals = [jsonResponse yajl_JSON];&lt;br /&gt;[player fill:vals];&lt;br /&gt;&lt;br /&gt;- (void)fill:(NSDictionary *)vals {&lt;br /&gt;	NSString *key;&lt;br /&gt;	for (key in vals) {&lt;br /&gt;		[self setValue:[vals objectForKey:key] forKey:key];&lt;br /&gt;	}&lt;br /&gt;}&lt;/pre&gt;&lt;/noscript&gt;&lt;br /&gt;&lt;br /&gt;Okay it's not a two liner, but it's close! Anyways, in order to add this library to your project, one way is to download the build and add the static library to your Xcode project. It'll work seamlessly in the Simulator. If you want to deploy it to your iPhone or any iPhone, you need to add linker flags:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Under 'Other Linker Flags' in the Test target, add -ObjC and -all_load (So NSObject+YAJL category is loaded).&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;This is very common and no big deal actually. If you want to know more about why you need this, &lt;a href="http://developer.apple.com/mac/library/qa/qa2006/qa1490.html"&gt;check out the Apple Developer Connection&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;I added those flags for the my development environment and also for my &lt;a href="http://dlinsin.blogspot.com/2010/04/iphone-app-beta-testing.html"&gt;beta testers&lt;/a&gt;. However, I totally missed this in my Xcode AppStore build configuration settings and since you cannot &lt;a href="http://furbo.org/2008/11/12/the-final-test/"&gt;really test&lt;/a&gt; your final build, it went in review without those linker flags. &lt;br /&gt;&lt;br /&gt;The reviewer at Apple found the bug, which even resulted in a crash of Doublemill. I'm grateful they found it, however I'm not really happy, that I had to wait another week to get &lt;a href="http://itunes.apple.com/app/doublemill/id368886888?mt=8"&gt;Doublemill&lt;/a&gt; out the door. In the end it was my mistake and I'm sure in the future, I'll make sure to have those build profiles setup more carefully. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-650416457592681242?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/650416457592681242/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=650416457592681242' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/650416457592681242'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/650416457592681242'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/05/don-forget-your-linker-flags.html' title='Don&amp;#39;t Forget your Linker Flags'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_myUhtr_A51Y/S900IqmyZjI/AAAAAAAATCQ/YpGbR64H5kE/s72-c/doublemill-512.png?imgmax=144' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-6484844251629083345</id><published>2010-05-03T06:16:00.001+02:00</published><updated>2010-05-03T06:16:06.823+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='iPad'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><title type='text'>iPad Apps have priority</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://doublemill.blogspot.com"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh6.ggpht.com/_myUhtr_A51Y/S900IqmyZjI/AAAAAAAATCQ/YpGbR64H5kE/doublemill-512.png?imgmax=144" border="0" alt=""id="BLOGGER_PHOTO_ID_5191388009271083842" /&gt;&lt;/a&gt;&lt;span style="font-style:italic;"&gt;Last year, I worked on a Nine Men's Morris (Mühle) implementation for Android, called &lt;a href="http://doublemill.blogspot.com/"&gt;Doublemill&lt;/a&gt;. Since I'm an iPhone user, I decided to port the game to iPhone/iPod touch and the iPad. There are 3 versions: Doublemill Premium, &lt;a href="http://itunes.apple.com/de/app/doublemill-lite/id362714027?mt=8"&gt;Doublemill Lite&lt;/a&gt; and Doublemill for iPad.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I had to &lt;strong&gt;wait for a week&lt;/strong&gt;, after &lt;a href="http://doublemill.blogspot.com/2010/04/doublemill-premium-under-way.html"&gt;submitting Doublemill Premium&lt;/a&gt; for review to the App Store, until Apple decided to have a look at it. Unfortunately, we were rejected - for a good reason! As it turned out, I didn't link my libraries correctly, which turned out to cause a crash every time you wanted to play online, but I digress... &lt;br /&gt;&lt;br /&gt;It took me roughly 2-3 hours to find the bug, fix it and resubmit the App. Now I'm waiting for five days again. &lt;br /&gt;&lt;br /&gt;Yesterday, I finished polishing Doublemill for iPad, after reworking the UI and implementing a nicer Player vs. Player experience. Since it was a rainy day and there was only Star Trek on TV, I decided to submit it for review, since it would take at least a week until they had a look at it. I want to be ready for the European start of the iPad and if there's anything wrong with Doublemill for iPad, it would take at least 2 weeks to get it into the App Store - that's what I thought!&lt;br /&gt;&lt;br /&gt;After the usual email, you get after submitting, I &lt;strong&gt;instantly&lt;/strong&gt; received another, saying my App is &lt;strong&gt;in review&lt;/strong&gt;. Just like that - without waiting for a week. That got me really upset! I invested almost 4 weeks of my spare time, to gett Doublemill Premium ready and polished and I had to wait for a week until it was in review.&lt;br /&gt;&lt;br /&gt;To me this is a clear signal, that iPad Apps are more important to Apple than iPhone Apps - at the moment. I hope this will balance again soon. It is understandable, that they want the iPad App market filled as soon as possible, but that's no reason to make the iPhone App developers wait more than a week. That takes us back to where we were a year ago: waiting and waiting and waiting - just to be rejected!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-6484844251629083345?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/6484844251629083345/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=6484844251629083345' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/6484844251629083345'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/6484844251629083345'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/05/ipad-apps-have-priority.html' title='iPad Apps have priority'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_myUhtr_A51Y/S900IqmyZjI/AAAAAAAATCQ/YpGbR64H5kE/s72-c/doublemill-512.png?imgmax=144' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-7889047164103225294</id><published>2010-04-26T06:05:00.000+02:00</published><updated>2010-04-26T06:13:17.716+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='quality'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><title type='text'>Is your App Accessible?</title><content type='html'>I recently bought an iPhone 3GS and while nothing is really new, just a lot faster, there's a menu in the settings which is called "Accessibility". I haven't noticed it before, but after reading through &lt;a href="http://www.amazon.de/gp/product/1430227338?ie=UTF8&amp;tag=mytakeonthing-21&amp;linkCode=as2&amp;camp=1638&amp;creative=6742&amp;creativeASIN=1430227338"&gt;Apress' "The Business of iPhone App Development"&lt;/a&gt;, I got curious. There is one setting, which is very interesting, called "White on Black". &lt;br /&gt;&lt;br /&gt;&lt;a href="http://picasaweb.google.de/lh/photo/K_dQEzwUbvxx_wdTNyI5clbtqYQ7FPu9N9JoMnd71b0?feat=embedwebsite"&gt;&lt;img src="http://lh3.ggpht.com/_myUhtr_A51Y/S9QdIEQ61sI/AAAAAAAAS_g/zhJuXVY14MA/s288/acc2.png" /&gt;&lt;/a&gt; &lt;a href="http://picasaweb.google.de/lh/photo/oXeiiUEAYdB7fLJx5gJo5lbtqYQ7FPu9N9JoMnd71b0?feat=embedwebsite"&gt;&lt;img src="http://lh3.ggpht.com/_myUhtr_A51Y/S9Qc5exJjxI/AAAAAAAAS_U/-iru95F7mH4/s288/acc1.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This setting is for folks with any form of color-deficient vision. It helps them to still recognize the difference between certain colors. According to "The Business of iPhone App Development" roughly 1 out of 12 males of European decent are affected by this problem. &lt;br /&gt;&lt;br /&gt;After reading this, I had to check how &lt;a href="http://itunes.apple.com/us/app/doublemill-lite/id362714027?mt=8"&gt;Doublemill&lt;/a&gt; looks like with this setting switched on. I'm proud to say, that it looks kinda cool and everything works as expected! See for yourself:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://picasaweb.google.de/lh/photo/YlB2G6HIj7TpDnXJuBQHQ1btqYQ7FPu9N9JoMnd71b0?feat=embedwebsite"&gt;&lt;img src="http://lh4.ggpht.com/_myUhtr_A51Y/S9Qhw-uwGAI/AAAAAAAATAE/yWdpIIZcglA/s288/acc3.png" /&gt;&lt;/a&gt; &lt;a href="http://picasaweb.google.de/lh/photo/lnJJicp-9i9YbRyjvVRHIlbtqYQ7FPu9N9JoMnd71b0?feat=embedwebsite"&gt;&lt;img src="http://lh4.ggpht.com/_myUhtr_A51Y/S9Qh_9aYNRI/AAAAAAAATAM/8BxjJ9J69bU/s288/acc4.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;If you want to make your App accessible to the broadest audience possible, check if it works in Apple's "White on Black" mode.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-7889047164103225294?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/7889047164103225294/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=7889047164103225294' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/7889047164103225294'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/7889047164103225294'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/04/is-your-app-accessible.html' title='Is your App Accessible?'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_myUhtr_A51Y/S9QdIEQ61sI/AAAAAAAAS_g/zhJuXVY14MA/s72-c/acc2.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-2008537269316007485</id><published>2010-04-16T07:01:00.001+02:00</published><updated>2010-04-16T07:02:17.111+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='mac'/><category scheme='http://www.blogger.com/atom/ns#' term='ipod'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><title type='text'>iPhone App Beta Testing</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://doublemill.blogspot.com"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh4.ggpht.com/_myUhtr_A51Y/S4IOKqU_REI/AAAAAAAAS6g/022ZkcnXb64/s144/doublemill-lite-512.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5191388009271083842" /&gt;&lt;/a&gt;&lt;span style="font-style:italic;"&gt;Last year I was working on a Nine Men's Morris mobile game called &lt;a href="http://doublemill.blogspot.com/"&gt;Doublemill&lt;/a&gt;. My job was to implement a REST-based server back-end on the Google App Engine. You might &lt;a href="http://dlinsin.blogspot.com/2009/08/cron-jobs-on-google-app-engine.html"&gt;remember&lt;/a&gt; a couple of &lt;a href="http://dlinsin.blogspot.com/2009/04/more-playing-with-gae.html"&gt;posts&lt;/a&gt; on that. My colleagues were responsible for coding the Android client. Since I'm an iPhone user and I'm not going to switch to Android anytime soon, I decided to port the game to the iPhone/iPod touch platform.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I'm closing in on the first release of the paid version of &lt;a href="http://itunes.com/apps/davidlinsin"&gt;Doublemill&lt;/a&gt;. The only thing missing is a beta test. I'm past the &lt;a href="http://stackoverflow.com/questions/40154/how-do-you-beta-test-an-iphone-app"&gt;technical problems&lt;/a&gt;, which I encountered when I &lt;a href="http://dlinsin.blogspot.com/2010/03/doublemill-private-beta.html"&gt;released a beta version&lt;/a&gt; of Doublemill Lite a couple of weeks ago. This is more about how to find people, willing to thoroughly and extensively test your App.&lt;br /&gt;&lt;br /&gt;Unfortunately, I can't use &lt;a href="https://www.mturk.com/mturk/welcome"&gt;Amazon's Mechanical Turk&lt;/a&gt; - although I'm willing to pay a small amount for people to find bugs in my App. Amazon doesn't let me use it without a US credit card, so I had to look for an alternative. I came accross &lt;a href="http://ibetatest.com"&gt;iBetaTest.com&lt;/a&gt;. I used it for &lt;a href="http://itunes.com/apps/doublemilllite"&gt;Doublemill Lite&lt;/a&gt; and I'm also using it for the paid version. Although the site has little flaws (it's still in beta itself), the idea is great and it works pretty well. However, there is still a problem: the people who are using the site and who are supposed to test your App. &lt;br /&gt;&lt;br /&gt;After I added the &lt;a href="http://ibetatest.com/iphone/controllers/apps/?appId=460"&gt;beta for Doublemill&lt;/a&gt;, I received a lot of requests, mostly from testers which are not testing any other Apps or look like they have just signed up. I gave a couple of them the benefit of the doubt and let them in, only to experience, that I wasted my &lt;a href="http://furbo.org/2010/03/22/udid-not/"&gt;valuable device IDs&lt;/a&gt;.  &lt;br /&gt;&lt;br /&gt;At the time this blog was published, I have only received feedback from 2 of my 7 testers. The other just haven't replied. I know, that at least one has downloaded and signed up for the multiplayer feature, but hasn't provided feedback on any aspect of the App. That's just plain disappointing. After Doublemill Lite, this is the second time I wasted device IDs, it won't happen to me anymore.&lt;br /&gt;&lt;br /&gt;After all, there's always something to learn from your mistakes: be picky on beta testers and choose people you know they'll give you valuable, honest and thorough feedback! It takes quite some time to setup a beta test and organize all device IDs, so don't neglect Q&amp;A in your project planning. &lt;br /&gt;&lt;br /&gt;I'm fortunate enough to have a couple of people around me, which help out with testing and give me exactly the feedback I need - although it's not always what I want to hear. &lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-2008537269316007485?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/2008537269316007485/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=2008537269316007485' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/2008537269316007485'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/2008537269316007485'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/04/iphone-app-beta-testing.html' title='iPhone App Beta Testing'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_myUhtr_A51Y/S4IOKqU_REI/AAAAAAAAS6g/022ZkcnXb64/s72-c/doublemill-lite-512.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-1273899567038585349</id><published>2010-04-01T07:47:00.000+02:00</published><updated>2010-04-01T07:47:02.132+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='mac'/><category scheme='http://www.blogger.com/atom/ns#' term='android'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><title type='text'>One week in the App Store</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://doublemill.blogspot.com"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh4.ggpht.com/_myUhtr_A51Y/S4IOKqU_REI/AAAAAAAAS6g/022ZkcnXb64/s144/doublemill-lite-512.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5191388009271083842" /&gt;&lt;/a&gt;&lt;span style="font-style:italic;"&gt;Last year I was working on a Nine Men's Morris mobile game called &lt;a href="http://doublemill.blogspot.com/"&gt;Doublemill&lt;/a&gt;. My job was to implement a REST-based server back-end on the Google App Engine. You might &lt;a href="http://dlinsin.blogspot.com/2009/08/cron-jobs-on-google-app-engine.html"&gt;remember&lt;/a&gt; a couple of &lt;a href="http://dlinsin.blogspot.com/2009/04/more-playing-with-gae.html"&gt;posts&lt;/a&gt; on that. My colleagues were responsible for coding the Android client. Since I'm an iPhone user and I'm not going to switch to Android anytime soon, I decided to port the game to the iPhone/iPod touch platform.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;One week ago &lt;a href="http://doublemill.blogspot.com/2010/03/doublemill-lite-hits-app-store.html"&gt;Doublemill Lite hit the App Store&lt;/a&gt; and it has been received very well, according to the download numbers. I'm pretty stunned by the &lt;b&gt;magnitude of more downloads&lt;/b&gt; we have compared to the Google Marketplace. It could be that we only have full fledged paid version for Android out there. However, you can refund your money with Google within 24 hours, if you don't like the game, so basically every App is also a Lite version. &lt;br /&gt;&lt;br /&gt;I believe in the App Store as a channel of distribution, although only after one week we already see the negative sides of it: &lt;b&gt;user ratings&lt;/b&gt;. &lt;a href="http://itunes.apple.com/de/app/doublemill-lite/id362714027?mt=8"&gt;Doubemill Lite&lt;/a&gt; started off pretty good with 3-4 stars the first couple of days, but then rapidly dropped down to 1-2 stars. First, I was pretty bumped about it, but then realized that you are only asked to rate the App when deleting it from your phone. Considering, that less than 3% of people who downloaded the App also rated it, I guess most of the others might still happily play with Doublemill Lite.&lt;br /&gt;&lt;br /&gt;We received some feedback, mostly about the controls, which we've already addressed and improved. There should be &lt;b&gt;an update in the next couple of weeks&lt;/b&gt;, but at the moment I'm kinda handicapped, because my MacBook Pro died on me and I have to wait for my new one to arrive next week. With the soon to be released update, we've also added iPad compatibility, so you can have a virtual Nine Men's Morris Board sitting on your table. &lt;br /&gt;&lt;br /&gt;There's also been progress on the &lt;b&gt;premium version&lt;/b&gt; of Doublemill, which will go Beta any-time soon. So if you are interested in testing it, drop me a line and I'll hook you up!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-1273899567038585349?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/1273899567038585349/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=1273899567038585349' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/1273899567038585349'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/1273899567038585349'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/04/one-week-in-app-store.html' title='One week in the App Store'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_myUhtr_A51Y/S4IOKqU_REI/AAAAAAAAS6g/022ZkcnXb64/s72-c/doublemill-lite-512.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-5976711590197396857</id><published>2010-03-20T10:26:00.001+01:00</published><updated>2010-03-20T10:28:16.365+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='ipod'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><title type='text'>Being Rejected</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://doublemill.blogspot.com"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh4.ggpht.com/_myUhtr_A51Y/S4IOKqU_REI/AAAAAAAAS6g/022ZkcnXb64/s144/doublemill-lite-512.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5191388009271083842" /&gt;&lt;/a&gt;&lt;span style="font-style:italic;"&gt;Last year I was working on a Nine Men's Morris mobile game called &lt;a href="http://doublemill.blogspot.com/"&gt;Doublemill&lt;/a&gt;. My job was to implement a REST-based server back-end on the Google App Engine. You might &lt;a href="http://dlinsin.blogspot.com/2009/08/cron-jobs-on-google-app-engine.html"&gt;remember&lt;/a&gt; a couple of &lt;a href="http://dlinsin.blogspot.com/2009/04/more-playing-with-gae.html"&gt;posts&lt;/a&gt; on that. My colleagues were responsible for coding the Android client. Since I'm an iPhone user and I'm not going to switch to Android anytime soon, I decided to port the game to the iPhone/iPod touch platform.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Tonight I got an email from Apple telling me that they have rejected &lt;a href="http://dlinsin.blogspot.com/2010/03/doublemill-lite-submitted-to-app-store.html"&gt;Doublemill Lite&lt;/a&gt;! Although it is kind of annoying, the email was somewhat clear on what I need to to do to fix the problem. It even had a screenshot attached, pointing out what my mistake was - if it really was one.&lt;br /&gt;&lt;br /&gt;This is the screenshot apple sent back to me:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://picasaweb.google.de/lh/photo/YwvdFDshSVhmGVSTlPSiiA?authkey=Gv1sRgCKXY3MiLxprXDQ&amp;feat=embedwebsite"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh6.ggpht.com/_myUhtr_A51Y/S6SMGBMFRGI/AAAAAAAAS8U/Uj5BYdpMHqw/s288/IMG_3023.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5191388009271083842" /&gt;&lt;/a&gt;What's wrong here, you might think? At least that's what I thought! It turns out that &lt;strong&gt;you cannot tease your users with features they can never get with that particular version&lt;/strong&gt; of your application. &lt;br /&gt;&lt;br /&gt;Doublemill Lite features a Player vs. Player and Player vs. iPhone mode. You can challenge your friend by using the same device or play against your iPhone. The upcoming Doublemill Premium version will feature a Player vs. World mode, where you can challenge the world over any kind of internet connection. &lt;br /&gt;&lt;br /&gt;My idea was to point out that this is a Lite version by putting the Premium features in the menu. People could immediately see, what's to come or what they are missing out on. However, Apple doesn't think that it's a good idea. And they might even be right. Doublemill Lite is not going to be updated with those features, because we will release a dedicated Premium version soon. People only using the Lite version will always see the disabled menu items and that's kind of messy.&lt;br /&gt;&lt;br /&gt;I took the liberty to fix two other things in addition to addressing Apple's issues to remove the two menu items. Now we are back on track and hopefully in the App Store early next week.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-5976711590197396857?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/5976711590197396857/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=5976711590197396857' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/5976711590197396857'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/5976711590197396857'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/03/being-rejected.html' title='Being Rejected'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_myUhtr_A51Y/S4IOKqU_REI/AAAAAAAAS6g/022ZkcnXb64/s72-c/doublemill-lite-512.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-8935573860037815688</id><published>2010-03-18T20:17:00.001+01:00</published><updated>2010-03-18T20:17:58.773+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='mac'/><category scheme='http://www.blogger.com/atom/ns#' term='ipod'/><category scheme='http://www.blogger.com/atom/ns#' term='android'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><title type='text'>Doublemill Lite submitted to the App Store</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://doublemill.blogspot.com"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh4.ggpht.com/_myUhtr_A51Y/S4IOKqU_REI/AAAAAAAAS6g/022ZkcnXb64/s144/doublemill-lite-512.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5191388009271083842" /&gt;&lt;/a&gt;&lt;span style="font-style:italic;"&gt;Last year I was working on a Nine Men's Morris mobile game called &lt;a href="http://doublemill.blogspot.com/"&gt;Doublemill&lt;/a&gt;. My job was to implement a REST-based server back-end on the Google App Engine. You might &lt;a href="http://dlinsin.blogspot.com/2009/08/cron-jobs-on-google-app-engine.html"&gt;remember&lt;/a&gt; a couple of &lt;a href="http://dlinsin.blogspot.com/2009/04/more-playing-with-gae.html"&gt;posts&lt;/a&gt; on that. My colleagues were responsible for coding the Android client. Since I'm an iPhone user and I'm not going to switch to Android anytime soon, I decided to port the game to the iPhone/iPod touch platform.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;It's done! &lt;a href="http://doublemill.blogspot.com"&gt;Doublemill Lite&lt;/a&gt; is on Apple's table for review!&lt;br /&gt;&lt;br /&gt;I started working on the iPhone port of Doublemill almost 6 weeks ago. I decided early on to release a free version of the game. Actually, I wanted to release it &lt;a href="http://dlinsin.blogspot.com/2010/02/iphone-development.html"&gt;after only 2 weeks&lt;/a&gt; of intensive hacking. By that time I had implemented a full Player vs. Player feature. &lt;br /&gt;&lt;br /&gt;However, my colleagues convinced me to first implement a Player vs. iPhone feature before releasing the free version and I'm glad they did. Although it meant a lot of additional hours of work, it cleared the way to the full version, because I completely finished implementing the foundation of the game. After just two weeks I got the Player vs. iPhone feature fully working.&lt;br /&gt;&lt;br /&gt;The last two weeks I spent in &lt;a href="http://dlinsin.blogspot.com/2010/03/doublemill-private-beta.html"&gt;beta testing&lt;/a&gt;. The funny things is, I almost caught all the major bugs myself. The feedback I got was positive without exception and I was confident enough to &lt;a href="http://doublemill.blogspot.com/2010/03/now-we-have-to-wait.html"&gt;release Doublemill Lite today&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;It's going to be the third free Nine Men's Morris game on the App Store, but with all due respect, I think our game can easily beat the competition in terms of UI design and iPhone compliance. If you don't believe me, I'll post a full featured marketing post on Doublemill as soon as it's available on the App Store. Stay tuned and &lt;a href="http://twitter.com/doublemill"&gt;follow us on Twitter @doublemill&lt;/a&gt;!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-8935573860037815688?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/8935573860037815688/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=8935573860037815688' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/8935573860037815688'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/8935573860037815688'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/03/doublemill-lite-submitted-to-app-store.html' title='Doublemill Lite submitted to the App Store'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_myUhtr_A51Y/S4IOKqU_REI/AAAAAAAAS6g/022ZkcnXb64/s72-c/doublemill-lite-512.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-3451620427578017378</id><published>2010-03-08T06:25:00.000+01:00</published><updated>2010-03-08T06:26:02.825+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Is the Future of Mobile Apps the Web?</title><content type='html'>The &lt;a href="http://dlinsin.blogspot.com/2010/03/mobile-times-2010.html"&gt;Mobile Times 2010&lt;/a&gt; conference in Dusseldorf, which took place last Thursday, featured a lot of talks on how to do approach cross platform development. A lot of them suggested web development technics as a way to escape native vendor lock-in, such as Apple's iPhone OS.&lt;br /&gt;&lt;br /&gt;There are mainly two approaches:&lt;br /&gt;&lt;br /&gt;1. You develop a web site using HTML, JavaScript and CSS zip it and deploy it together with a Player&lt;br /&gt;&lt;br /&gt;2. Or you develop a custom website which is served from your servers &lt;br /&gt;&lt;br /&gt;The first approach is usually tackled using a framework such as &lt;a href="http://phonegap.com/"&gt;PhoneGap&lt;/a&gt;. It event provides an API to include platform specific functionality such as GPS in your application. &lt;br /&gt;&lt;br /&gt;The second approach either uses traditional web development technics or targets a specific browser, such as WebKit. One example is jQTouch, a jQuery plugin with cross platform support - unfortunately only WebKit. &lt;br /&gt;&lt;br /&gt;The presentations, advocating those two approaches, mentioned mainly two advantages:&lt;br /&gt;&lt;br /&gt;1. No need to comply with platform policies (think of Apple's App Store)&lt;br /&gt;&lt;br /&gt;2. And you can leverage cheap web coding skills&lt;br /&gt;&lt;br /&gt;Those are the facts, I collected during Mobile Times 2010. Unfortunately, the benefits using the web development approaches were &lt;a href="http://www.mobile-times.de/pdf/slides/Mobile_Times_2010_Mobile_Apps_Crossplatfrom.pdf"&gt;not really balanced&lt;/a&gt; by any downsides.&lt;br /&gt;&lt;br /&gt;I really doubt that the web development approach is the way to go in terms of cross platform mobile app development. I think frameworks like PhoneGap are doing an awesome job, putting an abstraction around the native platform. &lt;br /&gt;&lt;br /&gt;Unfortunately you get the least denominator of your platform is capable of and that's a real downside to me. If I put on "average Joe head", I want an app that is using the full potential of my phone. I don't care that it was cheaper to develop a web-based solution and I definitely don't care that the framework I'm using is only offering partial support for my phone features. &lt;br /&gt;&lt;br /&gt;Another problem is platform defragmentation! I won't go into it - don't worry - I simply want to point out one downside: &lt;strong&gt;In my humble opinion, it is not possible with web development technics to "write once, run anywhere"!&lt;/strong&gt; You will have to tweak the CSS and work around the abstractions of the framework your are using! It might be cheaper, because we all know that the average web development frickler (amateur craftsman), only charges what he deserves. However, I don't think it gets you anywhere in the long run.&lt;br /&gt;&lt;br /&gt;I think we shouldn't approach mobile application development with only the engineering side in mind. Instead we should rather focus on the customer and what he asks from a mobile app. I think the success of the App Store has proven that native apps are the way to go, even if they mean &lt;a href="http://dlinsin.blogspot.com/2010/02/iphone-development.html"&gt;more effort&lt;/a&gt; on the development side.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-3451620427578017378?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/3451620427578017378/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=3451620427578017378' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/3451620427578017378'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/3451620427578017378'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/03/is-future-of-mobile-apps-web.html' title='Is the Future of Mobile Apps the Web?'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-8462502876966323027</id><published>2010-03-06T16:13:00.001+01:00</published><updated>2010-03-06T16:13:23.408+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='ipod'/><category scheme='http://www.blogger.com/atom/ns#' term='objective-c'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><title type='text'>Doublemill Private Beta</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://doublemill.blogspot.com"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh4.ggpht.com/_myUhtr_A51Y/S4IOKqU_REI/AAAAAAAAS6g/022ZkcnXb64/s144/doublemill-lite-512.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5191388009271083842" /&gt;&lt;/a&gt;&lt;span style="font-style:italic;"&gt;Last year I was working on a Nine Men's Morris mobile game called &lt;a href="http://doublemill.blogspot.com/"&gt;Doublemill&lt;/a&gt;. My job was to implement a REST-based server back-end on the Google App Engine. You might &lt;a href="http://dlinsin.blogspot.com/2009/08/cron-jobs-on-google-app-engine.html"&gt;remember&lt;/a&gt; a couple of &lt;a href="http://dlinsin.blogspot.com/2009/04/more-playing-with-gae.html"&gt;posts&lt;/a&gt; on that. My colleagues were responsible for coding the Android client. Since I'm an iPhone user and I'm not going to switch to Android anytime soon, I decided to port the game to the iPhone/iPod touch platform.&lt;br /&gt;&lt;br /&gt;If you own a iPod touch or an iPhone and have some time at hand, &lt;a href="http://ibetatest.com/iphone/controllers/apps/?appId=433"&gt;sign up at iBetaTest.com&lt;/a&gt; for the private beta of Doublemill Lite. Don't want to sign up? No problem, just drop me a line and I'll hook you up. &lt;br /&gt;&lt;br /&gt;I appreciate your feedback and opinion on my first Objective-C endeavor.  &lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-8462502876966323027?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/8462502876966323027/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=8462502876966323027' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/8462502876966323027'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/8462502876966323027'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/03/doublemill-private-beta.html' title='Doublemill Private Beta'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_myUhtr_A51Y/S4IOKqU_REI/AAAAAAAAS6g/022ZkcnXb64/s72-c/doublemill-lite-512.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-2268364864814702999</id><published>2010-03-01T05:31:00.001+01:00</published><updated>2010-03-01T05:31:53.334+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='out&apos;n about'/><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Mobile Times 2010</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://mobile-times.de"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh6.ggpht.com/_myUhtr_A51Y/S4TvaHHFMvI/AAAAAAAAS7A/PyteSx3h9NQ/s288/mobile-times.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5191388009271083842" /&gt;&lt;/a&gt;On Thursday, I'll be attending the mobile times 2010 conference in Düsseldorf, Germany. It's a rather small one day event and as the name suggests, all about mobile development. &lt;br /&gt;&lt;br /&gt;There's only a two or three sessions, I'm really interested in, since my focus is iPhone and Android. However, there's one session which I'm really looking forward to: &lt;a href="http://www.mobile-times.de/programmplan"&gt;"Cross-compiling Android Apps for iPhone with XMLVM"&lt;/a&gt;. The reason why I'm interested in this is that I just went through a rather unpleasant experience of porting our Android game &lt;a href="http://doublemill.blogspot.com"&gt;Doublemill&lt;/a&gt; to the iPhone OS. If there was a faster and more efficient solution, I'd be happy - frankly, I don't think it works!&lt;br /&gt;&lt;br /&gt;I'll blog more about my roller-coaster ride from Android to iPhone in a future post, but for now I'm looking forward to Mobile Times 2010.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-2268364864814702999?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/2268364864814702999/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=2268364864814702999' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/2268364864814702999'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/2268364864814702999'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/03/mobile-times-2010.html' title='Mobile Times 2010'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_myUhtr_A51Y/S4TvaHHFMvI/AAAAAAAAS7A/PyteSx3h9NQ/s72-c/mobile-times.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-971267177941245905</id><published>2010-02-22T05:59:00.001+01:00</published><updated>2010-02-22T06:00:30.044+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='mac'/><category scheme='http://www.blogger.com/atom/ns#' term='ipod'/><title type='text'>iPhone Development</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://doublemill.blogspot.com"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh4.ggpht.com/_myUhtr_A51Y/S4IOKqU_REI/AAAAAAAAS6g/022ZkcnXb64/s144/doublemill-lite-512.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5191388009271083842" /&gt;&lt;/a&gt;&lt;span style="font-style:italic;"&gt;Last year I was working on a Nine Men's Morris mobile game called &lt;a href="http://doublemill.blogspot.com/"&gt;Doublemill&lt;/a&gt;. My job was to implement a REST-based server back-end on the Google App Engine. You might &lt;a href="http://dlinsin.blogspot.com/2009/08/cron-jobs-on-google-app-engine.html"&gt;remember&lt;/a&gt; a couple of &lt;a href="http://dlinsin.blogspot.com/2009/04/more-playing-with-gae.html"&gt;posts&lt;/a&gt; on that. My colleagues were responsible for coding the Android client. Since I'm an iPhone user and I'm not going to switch to Android anytime soon, I decided to port the game to the iPhone/iPod touch platform.&lt;br /&gt;&lt;br /&gt;This will be a series of blog posts on how a rookie at Objective-C and the whole iPhone OS came about to release an iPhone app.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I started out with a complete UI, stitched together in Interface Builder in only 15 minutes and ended up on a roller-coaster ride with 3 weekends straight spent on coding and a lot of understanding from my wife. It was really amazing bringing the game to life. &lt;br /&gt;&lt;br /&gt;That brings me to the first difference between being a Java back-end guy and an iPhone developer. It's not so much the different programing languages or operating systems, but the fact that you bring something to life. You can hold it in your hands an show it to people. I can even show it to my parents and they can give it a try. &lt;br /&gt;&lt;br /&gt;When I was working on the back-end of &lt;strong&gt;Doublemill&lt;/strong&gt;, the only thing I was able to show someone was an URL and the JSON response when you make a HTTP GET. Although my wife is very supportive, when it comes to my pet projects, she's much more pleased with an iPhone App than with a JSON response.&lt;br /&gt;&lt;br /&gt;I learned a lot about the iPhone platform, the tool chain and Objective-C during the past couple of weeks and I must say, I get more and more intrigued with diving in completely. I'll blog a lot more once &lt;strong&gt;Doublemill Lite&lt;/strong&gt;, the first version is out the door, until then there's still a lot to do. &lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-971267177941245905?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/971267177941245905/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=971267177941245905' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/971267177941245905'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/971267177941245905'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/02/iphone-development.html' title='iPhone Development'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_myUhtr_A51Y/S4IOKqU_REI/AAAAAAAAS6g/022ZkcnXb64/s72-c/doublemill-lite-512.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-4390002731655764667</id><published>2010-02-09T08:06:00.001+01:00</published><updated>2010-02-09T08:06:23.822+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='karlsruhe'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='jug-ka'/><category scheme='http://www.blogger.com/atom/ns#' term='google'/><title type='text'>GWT @ JUG-Ka</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://picasaweb.google.com/lh/photo/zDGbEVVB5ihDeoGRJltMvA?authkey=Gv1sRgCKXY3MiLxprXDQ&amp;feat=embedwebsite"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh4.ggpht.com/_myUhtr_A51Y/SnWVogdEjOI/AAAAAAAAPiQ/o9MjIPCDE_s/s144/jug_logo_original_transparent.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5191388009271083842" /&gt;&lt;/a&gt;Tomorrow night there'll be the another event of the &lt;a href="http://jug-ka.de"&gt;Java User Group Karlsruhe&lt;/a&gt;. It'll feature a talk on the &lt;a href="http://code.google.com/webtoolkit/"&gt;Google Web Toolkit&lt;/a&gt; by &lt;a href="http://pgt.de/"&gt;Papick Taboada&lt;/a&gt;, who is quite an expert on the topic. The second part of the event will be a get-together after the talk, where you can discuss GWT over a beer. My company &lt;a href="http://www.synyx.de"&gt;Synyx GmbH &amp; Co. KG&lt;/a&gt; is kindly sponsoring the drinks. &lt;br /&gt;&lt;br /&gt;The talk will take place at &lt;a href="http://groups.google.com/group/jug-karlsruhe/web/how-to-find-us?_done=%2Fgroup%2Fjug-karlsruhe%3F"&gt;University Karlsruhe&lt;/a&gt; in room 101 and start at 7:15pm. The room is located in the basement, which you will find easily, if you follow our duke featured logo.&lt;br /&gt;&lt;br /&gt;If you are interested in our lottery, where you can win a &lt;a href="http://www.jetbrains.net"&gt;JetBrains&lt;/a&gt; or &lt;a href="http://www.zeroturnaround.com"&gt;ZeroTurnaround&lt;/a&gt; license, submit your details through our &lt;a href="https://spreadsheets.google.com/viewform?formkey=dEdZY1dTZi1xMGpMQW5mbW1iTmFFcmc6MA"&gt;online form&lt;/a&gt;. The winners are going to be announced at the end of the talk and must be present.&lt;br /&gt;&lt;br /&gt;Last but not least, check us out on &lt;a href="http://twitter.com/jugka"&gt;Twitter&lt;/a&gt; and subscribe to our &lt;a href="http://www.google.com/calendar/embed?src=ehlt6rbl1cd1s2b7t9bkj6beek%40group.calendar.google.com&amp;ctz=Europe/Berlin"&gt;Google Calendar&lt;/a&gt; or join our &lt;a href="https://www.xing.com/net/jug-karlsruhe/"&gt;Xing Group&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-4390002731655764667?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/4390002731655764667/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=4390002731655764667' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/4390002731655764667'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/4390002731655764667'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/02/gwt-jug-ka.html' title='GWT @ JUG-Ka'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_myUhtr_A51Y/SnWVogdEjOI/AAAAAAAAPiQ/o9MjIPCDE_s/s72-c/jug_logo_original_transparent.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-5337261770768166800</id><published>2010-01-25T05:40:00.000+01:00</published><updated>2010-01-25T05:49:00.340+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='karlsruhe'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='jug-ka'/><title type='text'>Community Gathering @ JUG-Ka</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://picasaweb.google.com/lh/photo/zDGbEVVB5ihDeoGRJltMvA?authkey=Gv1sRgCKXY3MiLxprXDQ&amp;feat=embedwebsite"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh4.ggpht.com/_myUhtr_A51Y/SnWVogdEjOI/AAAAAAAAPiQ/o9MjIPCDE_s/s144/jug_logo_original_transparent.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5191388009271083842" /&gt;&lt;/a&gt;After the &lt;a href="http://dlinsin.blogspot.com/2010/01/watch-java-user-group-karlsruhe-online.html"&gt;first successful&lt;/a&gt; &lt;a href="http://jug-ka.de"&gt;JUG Karlsruhe&lt;/a&gt; talk on &lt;a href="http://dlinsin.blogspot.com/2010/01/groovy-jug-ka.html"&gt;Groovy&lt;/a&gt;, we will kick off the community involvement with a gathering &lt;a href="https://www.xing.com/events/community-gathering-451220"&gt;this coming Wednesday&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Everybody who wants to get involved in our little &lt;a href="http://dlinsin.blogspot.com/search/label/jug-ka"&gt;Java User Group&lt;/a&gt; here in &lt;a href="http://dlinsin.blogspot.com/search/label/karlsruhe"&gt;Karlsruhe&lt;/a&gt; is invited. We are looking for people with lots of ideas, sense of community and passion. I prepared a couple of slides, which will help us structure the gathering, but mainly we are hoping to get your feedback and ideas heard. &lt;br /&gt;&lt;br /&gt;You can help steer the JUG in the direction which you want it to go. Just join us on Wednesday at 7:15pm, the usual time and the usual place. The &lt;a href="http://groups.google.com/group/jug-karlsruhe/web/how-to-find-us?_done=%2Fgroup%2Fjug-karlsruhe%3F"&gt;University of Karlsruhe&lt;/a&gt; kindly sponsored a room (SR-107), which is located in the basement, next to the usual location.&lt;br /&gt;&lt;br /&gt;I'm looking forward to see you guys on Wednesday to share some ideas on how to improve your Java User Group Karlsruhe.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-5337261770768166800?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/5337261770768166800/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=5337261770768166800' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/5337261770768166800'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/5337261770768166800'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/01/community-gathering-jug-ka.html' title='Community Gathering @ JUG-Ka'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_myUhtr_A51Y/SnWVogdEjOI/AAAAAAAAPiQ/o9MjIPCDE_s/s72-c/jug_logo_original_transparent.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-349653563189457254</id><published>2010-01-18T05:25:00.000+01:00</published><updated>2010-01-18T05:29:24.721+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='mac'/><category scheme='http://www.blogger.com/atom/ns#' term='pics'/><title type='text'>Adding a UINavigationBar to a UITableView</title><content type='html'>A couple of days ago, I implemented a RSS-Reader feature for an app, I'm coding &lt;a href="http://www.synyx.de"&gt;at work&lt;/a&gt;. There's a nice &lt;a href="http://theappleblog.com/2008/08/04/tutorial-build-a-simple-rss-reader-for-iphone/"&gt;tutorial&lt;/a&gt; on this topic, which I can highly recommend to you, although it's a little outdated. I'm using a &lt;em&gt;UITableView&lt;/em&gt; to implement the RSS-Reader feature and I wanted the header section to be a &lt;em&gt;UINavigationBar&lt;/em&gt;. Somehow I couldn't find a proper tutorial on how to achieve this. At least there wasn't anything that explained it in a simple manner. &lt;br /&gt;&lt;br /&gt;You will need a &lt;em&gt;UINavigationController&lt;/em&gt;, in case you want to navigate through the hierarchy of &lt;em&gt;UITableViews&lt;/em&gt;, but we put that thought on hold and I'm gonna show you how to add a custom &lt;em&gt;UINavigationBar&lt;/em&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://picasaweb.google.com/lh/photo/Zge4vjnmf6ygq6BwUE5amQ?authkey=Gv1sRgCKXY3MiLxprXDQ&amp;feat=embedwebsite"&gt;&lt;img src="http://lh6.ggpht.com/_myUhtr_A51Y/S1AtkfaI6KI/AAAAAAAAPsg/fm-GPzvFDHo/s400/screen_final.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This is roughly what the final result should look like. You can see my company's logo at the top, inside of a &lt;em&gt;UINavigationBar&lt;/em&gt;, which in return is inside of a &lt;em&gt;UITableView&lt;/em&gt;. All the components are wrapped in a &lt;em&gt;UITabBarController&lt;/em&gt;, which you can see at the bottom in the form of a tab bar.&lt;br /&gt;&lt;br /&gt;My first approach was to tackle this kind of interface solely in &lt;em&gt;&lt;a href="http://developer.apple.com/tools/interfacebuilder.html"&gt;Interface Builder&lt;/a&gt;&lt;/em&gt;. It's very straight forward, there's nothing fancy here and my first impression was, that it should work out of the box - the box being &lt;em&gt;InterfaceBuilder&lt;/em&gt;. Well, I was able to design almost everything through &lt;em&gt;Interface Builder&lt;/em&gt;, just the part with the &lt;em&gt;UINavigationBar&lt;/em&gt; made me throw in the towel.&lt;br /&gt;&lt;br /&gt;&lt;script src="http://gist.github.com/277957.js?file=gistfile1.m"&gt;&lt;/script&gt;&lt;noscript&gt;&lt;pre&gt;- (void)viewWillAppear:(BOOL)animated {&lt;br /&gt;	[super viewWillAppear:animated];&lt;br /&gt; &lt;br /&gt;	tableViewNavigationBar = [[UINavigationBar alloc] initWithFrame: CGRectMake(0.0f, 0.0f, 320.0f, 44.0f)];&lt;br /&gt; &lt;br /&gt;	UIImage *image = [UIImage imageNamed:@"synyx_150_no_os.png"];&lt;br /&gt;	imageView = [ [ UIImageView alloc ] initWithFrame:CGRectMake(84.0, 1.0, image.size.width, image.size.height) ];&lt;br /&gt;	[imageView setImage:image];&lt;br /&gt; &lt;br /&gt;	[tableViewNavigationBar addSubview:imageView];&lt;br /&gt; &lt;br /&gt;	[super.tableView addSubview:tableViewNavigationBar];&lt;br /&gt;}&lt;/pre&gt;&lt;/noscript&gt;&lt;br /&gt;&lt;br /&gt;This is what the code looks like. It is implemented in the custom &lt;em&gt;UITableViewController&lt;/em&gt;, which is assigned responsibility from the &lt;em&gt;UITabBarController&lt;/em&gt;, as soon as its' tab is active. &lt;br /&gt;&lt;br /&gt;First step is to initialize the &lt;em&gt;UINavigationBar&lt;/em&gt;. In my case, I assign it to an instance variable, which is released in the dealloc method. You can see, that you also have to initialize and add the image yourself, which is usually a simple drag &amp; drop in &lt;em&gt;Interface Builder&lt;/em&gt;. That image is then added to the initialized &lt;em&gt;UINavigationBar&lt;/em&gt;.&lt;br /&gt;&lt;br /&gt;You can see that the solution is only a couple of lines of code, but note that it only works, if you don't want to navigate to a sub view. For that you'd need a &lt;em&gt;UINavigationController&lt;/em&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-349653563189457254?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/349653563189457254/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=349653563189457254' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/349653563189457254'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/349653563189457254'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/01/adding-uinavigationbar-to-uitableview.html' title='Adding a UINavigationBar to a UITableView'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_myUhtr_A51Y/S1AtkfaI6KI/AAAAAAAAPsg/fm-GPzvFDHo/s72-c/screen_final.png' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-311031892119680492</id><published>2010-01-15T13:42:00.001+01:00</published><updated>2010-01-15T13:42:15.902+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='video'/><category scheme='http://www.blogger.com/atom/ns#' term='karlsruhe'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='jug-ka'/><title type='text'>Watch the Java User Group Karlsruhe Online</title><content type='html'>&lt;object width="474" height="443"&gt;&lt;param name="movie" value="http://beta.parleys.com/share/parleysshare2.swf?pageId=1831"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="pageId" value="1831"&gt;&lt;/param&gt;&lt;embed src="http://beta.parleys.com/share/parleysshare2.swf?pageId=1831" type="application/x-shockwave-flash" allowfullscreen="true" width="474" height="443"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;This is our first attempt to record a &lt;a href="http://dlinsin.blogspot.com/2010/01/groovy-jug-ka.html"&gt;talk&lt;/a&gt; of the &lt;a href="http://jug-ka.de"&gt;Java User Group Karlsruhe&lt;/a&gt;. Please forgive us the weird camera angle and the volume, we promise to improve that. Other than that I think it's quite reasonable. &lt;br /&gt;&lt;br /&gt;I'm really impressed by the tools and platform &lt;a href="http://beta.parleys.com"&gt;Parleys.com&lt;/a&gt; provides. The best part is - it's free for JUGs. A big thank you from Karlsruhe to Belgium for that!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-311031892119680492?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/311031892119680492/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=311031892119680492' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/311031892119680492'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/311031892119680492'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/01/watch-java-user-group-karlsruhe-online.html' title='Watch the Java User Group Karlsruhe Online'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-1764903988984531905</id><published>2010-01-11T05:55:00.000+01:00</published><updated>2010-01-11T05:58:19.660+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='karlsruhe'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='jug-ka'/><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>Groovy @ JUG-Ka</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://picasaweb.google.com/lh/photo/zDGbEVVB5ihDeoGRJltMvA?authkey=Gv1sRgCKXY3MiLxprXDQ&amp;feat=embedwebsite"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh4.ggpht.com/_myUhtr_A51Y/SnWVogdEjOI/AAAAAAAAPiQ/o9MjIPCDE_s/s144/jug_logo_original_transparent.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5191388009271083842" /&gt;&lt;/a&gt;The first talk of 2010 at the &lt;a href="http://jug-ka.de"&gt;Java User Group Karlsruhe&lt;/a&gt; is this coming Wednesday. After a successful finale in December 2009 with a &lt;a href="http://dlinsin.blogspot.com/2009/12/scrum-jug-ka_4680.html"&gt;talk on Scrum&lt;/a&gt;, we are turning our attention to the technical side of things again - &lt;a href="http://dlinsin.blogspot.com/search/label/groovy"&gt;Groovy&lt;/a&gt;.  &lt;br /&gt;&lt;br /&gt;We had a &lt;a href="http://dlinsin.blogspot.com/2008/04/get-groovy-with-jug-ka.html"&gt;talk on Groovy&lt;/a&gt; back in early 2008, in case you remember. It was an introduction, whereas this coming talk will target a more advanced audience. &lt;br /&gt;&lt;br /&gt;The speaker of the night will be Dierk König from Canoo Engineering AG, Switzerland. He is a Groovy committer  and author of the book &lt;a href="http://www.amazon.de/gp/product/1932394842?ie=UTF8&amp;tag=mytakeonthing-21&amp;linkCode=as2&amp;camp=1638&amp;creative=6742&amp;creativeASIN=1932394842"&gt;Groovy in Action&lt;/a&gt;. He'll give us an overview of 7 scenarios in which to use Groovy in your project.&lt;br /&gt;&lt;br /&gt;I'm excited, that we are going to have a Groovy expert in the house Wednesday evening. If you have any questions on Groovy, it's your chance to get an answer, so make sure to drop by.&lt;br /&gt;&lt;br /&gt;The talk will take place at &lt;a href="http://groups.google.com/group/jug-karlsruhe/web/how-to-find-us?_done=%2Fgroup%2Fjug-karlsruhe%3F"&gt;University Karlsruhe&lt;/a&gt; in room 102, located in the basement - just follow our duke featured logo. The talk will start at 7:15pm.&lt;br /&gt;&lt;br /&gt;Our monthly lottery of &lt;a href="http://www.jetbrains.net"&gt;JetBrains&lt;/a&gt; and &lt;a href="http://www.zeroturnaround.com"&gt;ZeroTurnaround&lt;/a&gt;, which are each giving away a free license of one of their products is already on. If you are interested in participating in the draw, submit your details through our &lt;a href="https://spreadsheets.google.com/viewform?formkey=dEdZY1dTZi1xMGpMQW5mbW1iTmFFcmc6MA"&gt;online form&lt;/a&gt;. The winners are going to be announced at the end of the talk and must be present.&lt;br /&gt;&lt;br /&gt;To keep up with all the JUG goodness in 2010 subscribe to our &lt;a href="http://www.google.com/calendar/embed?src=ehlt6rbl1cd1s2b7t9bkj6beek%40group.calendar.google.com&amp;ctz=Europe/Berlin"&gt;Google Calendar&lt;/a&gt; or join our &lt;a href="https://www.xing.com/net/jug-karlsruhe/"&gt;Xing Group&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-1764903988984531905?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/1764903988984531905/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=1764903988984531905' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/1764903988984531905'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/1764903988984531905'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/01/groovy-jug-ka.html' title='Groovy @ JUG-Ka'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_myUhtr_A51Y/SnWVogdEjOI/AAAAAAAAPiQ/o9MjIPCDE_s/s72-c/jug_logo_original_transparent.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-5196682248024696850</id><published>2010-01-08T08:56:00.000+01:00</published><updated>2010-01-08T08:57:26.928+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='spring'/><title type='text'>Spring's ResourceBundleMessageSource</title><content type='html'>Last week my colleague Marc spent hours trying to figure out why a &lt;a href="http://java.sun.com/javase/6/docs/api/java/util/ResourceBundle.html"&gt;ResourceBundle&lt;/a&gt; wasn't loaded in a &lt;a href="http://www.springframework.org"&gt;Spring&lt;/a&gt; based web app we are developing here at &lt;a href="http://www.synyx.de"&gt;Synyx&lt;/a&gt;. It turned out to be a broken &lt;a href="http://en.wikipedia.org/wiki/Unicode"&gt;Unicode&lt;/a&gt; representation, which wasn't properly reported by Spring's &lt;em&gt;&lt;a href="http://static.springsource.org/spring/docs/3.0.0.RELEASE/javadoc-api/org/springframework/context/support/ResourceBundleMessageSource.html"&gt;ResourceBundleMessageSource&lt;/a&gt;&lt;/em&gt;.&lt;br /&gt;&lt;br /&gt;In case you don't know &lt;em&gt;ResourceBundleMessageSource&lt;/em&gt;, here's an excerpt of its javadoc:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;MessageSource implementation that accesses resource bundles using specified basenames. This class relies on the underlying JDK's ResourceBundle implementation, in combination with the JDK's standard message parsing provided by MessageFormat.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;We are leveraging the basename functionality of &lt;em&gt;ResourceBundleMessageSource&lt;/em&gt; in our application, the way it's suggested in the javadoc. It's pretty neat and you should definitely check it out, if you've never used it.&lt;br /&gt;&lt;br /&gt;The problem we encountered indicated that the bundle could not be found. This is what we saw in our log statements:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;em&gt;WARNING: ResourceBundle [broken_messages] not found for MessageSource: Can't find bundle for base name broken_messages, locale en_US&lt;/em&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Of course, we tried to fiddle with the classpath, changed the names of the properties files and moved them to different directories in our WAR file - it would only print the above log statement. After hours of trying and failing, Marc discovered an &lt;strong&gt;invalid Unicode character representation&lt;/strong&gt; (something like &lt;em&gt;d\u00Fvid&lt;/em&gt; instead of &lt;em&gt;d\u00FCvid&lt;/em&gt;) in the ResourceBundle, which wouldn't let &lt;em&gt;ResourceBundle&lt;/em&gt; load the damn thing.&lt;br /&gt;&lt;br /&gt;So where's the problem with &lt;em&gt;ResourceBundleMessageSource&lt;/em&gt;? The problem is that it swallowed the &lt;em&gt;cause&lt;/em&gt; of its own &lt;em&gt;MissingResourceException&lt;/em&gt;. Here's what Java's good old &lt;em&gt;ResourceBundle&lt;/em&gt; class tells you in case of a broken &lt;em&gt;\uxxxx&lt;/em&gt; representation:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;em&gt;java.util.MissingResourceException: Can't find bundle for base name broken_messages, locale en_US&lt;br /&gt;...&lt;br /&gt;Caused by: java.lang.IllegalArgumentException: Malformed \uxxxx encoding.&lt;/em&gt;&lt;/blockquote&gt;  &lt;br /&gt;&lt;br /&gt;You can clearly figure out what the problem is and how to solve it. Whereas, with the message of  &lt;em&gt;MissingResourceException&lt;/em&gt; the least you would expect, is to look for the problem in the &lt;em&gt;properties&lt;/em&gt; file itself.&lt;br /&gt;&lt;br /&gt;I created a &lt;a href="http://github.com/dlinsin/area51/tree/master/ResourceBundleMessageSourceBug/"&gt;small test case&lt;/a&gt; to reproduce the problem and filed a &lt;a href="https://jira.springsource.org/browse/SPR-6639"&gt;request for improvement&lt;/a&gt;. I hope there'll be a fix, so that other people don't have to suffer the same way we did.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-5196682248024696850?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/5196682248024696850/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=5196682248024696850' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/5196682248024696850'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/5196682248024696850'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2010/01/spring-resourcebundlemessagesource.html' title='Spring&amp;#39;s ResourceBundleMessageSource'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-4369339766662236644</id><published>2009-12-31T11:34:00.000+01:00</published><updated>2009-12-31T11:35:55.901+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='blog'/><category scheme='http://www.blogger.com/atom/ns#' term='stuff'/><category scheme='http://www.blogger.com/atom/ns#' term='pics'/><title type='text'>Top Content 2009</title><content type='html'>Like &lt;a href="http://dlinsin.blogspot.com/2009/01/top-2008-content.html"&gt;last year&lt;/a&gt;, I'd like to talk a little bit about my blog's top content of the year 2009, as well as a couple of interesting stats. &lt;br /&gt;&lt;br /&gt;Let's start off with some statistics, in particular my blog's page views. They increased by almost 30% compared to last year. I think it's because of posting my blog entries on &lt;a href="http://dzone.com"&gt;dzone&lt;/a&gt;. It brings a nice stream of visitors for each entry, especially if it's a controversial topic. &lt;br /&gt;&lt;br /&gt;&lt;a href="http://picasaweb.google.com/lh/photo/BYr0xpek7SLwF0ExWtC8LA?authkey=Gv1sRgCKXY3MiLxprXDQ&amp;feat=embedwebsite"&gt;&lt;img src="http://lh5.ggpht.com/_myUhtr_A51Y/Szx3p74BavI/AAAAAAAAPqc/QWaQYrtsXkU/s400/top_content_09.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Unfortunately, the time which visitors spent reading through my blog entries did not increase significantly. That indicates readers are accessing a particular blog post, skim through it and then leave again. I would have loved, if people find more interesting content on their way reading through a particular entry. Since that isn't the case, I decided to revamp the style of my blog a couple of weeks ago and remove all unnecessary clutter to improve navigation and load time. &lt;br /&gt;&lt;br /&gt;Let's get on with my blog's top content of the year 2009. This year's winner is &lt;a href="http://dlinsin.blogspot.com/2009/05/more-jdk-7-language-changes.html"&gt;my overview of the proposed JDK 7 language changes&lt;/a&gt;. It's not alone though, there is  &lt;a href="http://dlinsin.blogspot.com/2009/03/jdk-7-language-changes-are-coined.html"&gt;another blog entry&lt;/a&gt; in the top 5, which is about the future of Java the language. For me, there's one big surprise in those top 5 blog posts: &lt;a href="http://dlinsin.blogspot.com/2006/11/jboss-rules-example.html"&gt;my sample on JBoss Rules&lt;/a&gt;, which was the 3rd most accessed post in 2008 and moved up to the 2nd spot in 2009. The most amazing thing about this blog post is the fact that it was written in 2006. So without further ado, here's the top 5 bog posts: &lt;br /&gt;&lt;br /&gt;1. &lt;a href="http://dlinsin.blogspot.com/2009/05/more-jdk-7-language-changes.html"&gt;More JDK 7 Language Changes&lt;/a&gt;&lt;br /&gt;2. &lt;a href="http://dlinsin.blogspot.com/2006/11/jboss-rules-example.html"&gt;JBoss Rules Example&lt;/a&gt;&lt;br /&gt;3. &lt;a href="http://dlinsin.blogspot.com/2009/03/jdk-7-language-changes-are-coined.html"&gt;JDK 7 Language Changes Coined&lt;/a&gt;&lt;br /&gt;4. &lt;a href="http://dlinsin.blogspot.com/2009/11/having-fun-with-jdk-toolsjar.html"&gt;Having Fun with JDK's tools.jar&lt;/a&gt;&lt;br /&gt;5. &lt;a href="http://dlinsin.blogspot.com/2007/02/javas-runtimeexec-openes-too-many-files.html"&gt;Java Runtime.exec Opens Too Many Files&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;What's coming in 2010? To be honest, I don't really know. The past year I published on a weekly basis, which wasn't really a problem, because I had a lot to say. In 2010 I'm planning to change my personal focus from the Java platform to &lt;a href="http://dlinsin.blogspot.com/search/label/iPhone"&gt;the iPhone platform&lt;/a&gt;, &lt;a href="http://dlinsin.blogspot.com/search/label/objective-c"&gt;Objective-C&lt;/a&gt; and &lt;a href="http://dlinsin.blogspot.com/search/label/mobile"&gt;mobile&lt;/a&gt; &lt;a href="http://dlinsin.blogspot.com/search/label/development"&gt;development&lt;/a&gt;. There's still gonna be posts on &lt;a href="http://dlinsin.blogspot.com/search/label/java"&gt;Java&lt;/a&gt;, I just don't know how many and how regular. &lt;br /&gt;&lt;br /&gt;I hope you enjoyed my regular take on development and Java this year! See you in 2010.&lt;br /&gt; &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-4369339766662236644?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/4369339766662236644/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=4369339766662236644' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/4369339766662236644'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/4369339766662236644'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/12/top-content-2009.html' title='Top Content 2009'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_myUhtr_A51Y/Szx3p74BavI/AAAAAAAAPqc/QWaQYrtsXkU/s72-c/top_content_09.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-5631524443681887158</id><published>2009-12-21T05:48:00.000+01:00</published><updated>2009-12-27T19:50:23.229+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mobile'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='mac'/><category scheme='http://www.blogger.com/atom/ns#' term='objective-c'/><title type='text'>Book Review: Beginning iPhone 3 Development</title><content type='html'>&lt;span style="font-style:italic;"&gt;&lt;a href="http://www.apress.com"&gt;Apress&lt;/a&gt; was kind enough to pass me a copy of this book, which I agreed to review in return.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.amazon.de/gp/product/1430224592?ie=UTF8&amp;tag=mytakeonthing-21&amp;linkCode=as2&amp;camp=1638&amp;creative=6742&amp;creativeASIN=1430224592"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh3.ggpht.com/_myUhtr_A51Y/SydfZEdz7mI/AAAAAAAAPpw/A6HipVhq57g/9781430224594.gif?imgmax=800" border="0" alt=""id="" /&gt;&lt;/a&gt;I reviewed a couple of &lt;a href="http://dlinsin.blogspot.com/2009/03/book-review-practical-api-design.html"&gt;fine books&lt;/a&gt; for Apress over the past two years, but I must say that "&lt;a href="http://www.amazon.de/gp/product/1430224592?ie=UTF8&amp;tag=mytakeonthing-21&amp;linkCode=as2&amp;camp=1638&amp;creative=6742&amp;creativeASIN=1430224592"&gt;Beginning iPhone 3 Development&lt;/a&gt;" is one of the finest! The authors did an excellent job, addressing the code and complexity of the underlying platform.&lt;br /&gt;&lt;br /&gt;When I first got my hands dirty with iPhone development everything was new: the IDE, the programming language and the environment to which my code is deployed. I didn't know my way around Xcode and I had no idea what &lt;em&gt;Protocols&lt;/em&gt; are in &lt;a href="http://en.wikipedia.org/wiki/Objective-C"&gt;Objective-C&lt;/a&gt;. This kind of knowledge is what the authors of "Beginning iPhone 3 Development" assume and work with very nicely. They are not trying to explain Xcode in every detail or tell you all about Objective-C, instead they focus on what you need to know right there and then to solve the problem the chapter is focused on.&lt;br /&gt;&lt;br /&gt;I like this concept a lot! This way you get to know Xcode and Objective-C step-by-step in bits and pieces, with really good examples. Although the authors are not explaining all the tools and language features in detail, I think you really get a good sip of background information. &lt;br /&gt;&lt;br /&gt;Another thing which really makes "Beginning iPhone 3 Development" stand out from other books I read lately, is the style of writing. It's not only the words the authors chose to describe a complex junk of Objective-C code, but also the way they approach it. They keep repeating the basic concepts, but always in a different way, so it doesn't get boring to read about stuff like &lt;em&gt;Delegates&lt;/em&gt; and &lt;em&gt;ViewControllers&lt;/em&gt; over and over again. It's just exactly what you need to wrap you head around this sort of topics. &lt;br /&gt;&lt;br /&gt;If you follow the &lt;a href="http://iphonedevelopment.blogspot.com"&gt;blog&lt;/a&gt; of Jeff Lamarche, one of the authors, you can really see that this guy is all about writing. You can find tons of tutorials there for iPhone development, which immediately remind you of "Beginning iPhone 3 Development". The style and tone of writing are quite similar.&lt;br /&gt;&lt;br /&gt;After all the praise there is one downside, it's a minor one, but then again, to you it might be important: the website. It's really just a pointer to a phpbb forum, which is meant to be a place where people can discuss or ask questions about the book. That's all well and good, but the thing wants you to register, even if you merely want to download the source code, which sucks. I just don't need another forum account.&lt;br /&gt;&lt;br /&gt;A colleague raised the question why you need a book, in order to develop for the iPhone? After all, the documentation Apple provides is pretty extensive and of high quality. That is true! &lt;a href="http://developer.apple.com/"&gt;Apple's Developer Connection&lt;/a&gt; provides a plethora of documents on all sorts of topics, all very well written. The problem with those documents is that they don't give you a coherent picture. It's more of a reference documentation. You go there, if you need an answer to some specific problem. &lt;br /&gt;&lt;br /&gt;"Beginning iPhone 3 Development" on the other hand, provides a nice example for each chapter, which leads you through all the features of a certain angle of iPhone development. You can tag along and at the end of the chapter, you have a pretty good understanding of what just happened the past 50 pages.&lt;br /&gt;&lt;br /&gt;For me, "&lt;a href="http://www.amazon.de/gp/product/1430224592?ie=UTF8&amp;tag=mytakeonthing-21&amp;linkCode=as2&amp;camp=1638&amp;creative=6742&amp;creativeASIN=1430224592"&gt;Beginning iPhone 3 Development&lt;/a&gt;" gave me a good insight of what it takes to develop an iPhone application. It even encouraged me to dive deeper and change my &lt;a href="http://dlinsin.blogspot.com/search/label/development"&gt;development perspective&lt;/a&gt; from solely Java towards the iPhone platform. If you are planning to get started with iPhone development, go and buy this book! You won't regret it!&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-5631524443681887158?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/5631524443681887158/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=5631524443681887158' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/5631524443681887158'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/5631524443681887158'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/12/book-review-iphone.html' title='Book Review: Beginning iPhone 3 Development'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_myUhtr_A51Y/SydfZEdz7mI/AAAAAAAAPpw/A6HipVhq57g/s72-c/9781430224594.gif?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-5900569946785839304</id><published>2009-12-07T05:47:00.001+01:00</published><updated>2009-12-07T05:47:27.824+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='quality'/><category scheme='http://www.blogger.com/atom/ns#' term='karlsruhe'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='jug-ka'/><title type='text'>Scrum @ JUG-Ka</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://picasaweb.google.com/lh/photo/zDGbEVVB5ihDeoGRJltMvA?authkey=Gv1sRgCKXY3MiLxprXDQ&amp;feat=embedwebsite"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh4.ggpht.com/_myUhtr_A51Y/SnWVogdEjOI/AAAAAAAAPiQ/o9MjIPCDE_s/s144/jug_logo_original_transparent.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5191388009271083842" /&gt;&lt;/a&gt;The upcoming talk at the &lt;a href="http://jug-ka.de"&gt;Java User Group Karlsruhe&lt;/a&gt; this Wednesday is kind of an experiment. It features Scrum, which according to &lt;a href="http://en.wikipedia.org/wiki/Scrum_(development)"&gt;Wikipedia&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;cite&gt;... is an iterative incremental framework for managing complex work (such as new product development) commonly used with agile software development.&lt;br /&gt;&lt;/cite&gt;&lt;br /&gt;&lt;br /&gt;It's an experiment, because we usually feature sessions on development topics like &lt;a href="http://dlinsin.blogspot.com/2009/11/restful-applications-in-practise-jug-ka.html"&gt;REST&lt;/a&gt;, &lt;a href="http://dlinsin.blogspot.com/2009/09/tuning-hibernate-and-jpa-jug-ka.html"&gt;Hibernate&lt;/a&gt; and &lt;a href="http://dlinsin.blogspot.com/2009/08/java-flex-jug-ka.html"&gt;Flex&lt;/a&gt;. A talk on a management framework, although it's closely related to development, is rather unusual for us. If you are not interested in this kind of topic, let me know, we appreciate your feedback.&lt;br /&gt;&lt;br /&gt;The talk will take place at &lt;a href="http://groups.google.com/group/jug-karlsruhe/web/how-to-find-us?_done=%2Fgroup%2Fjug-karlsruhe%3F"&gt;University Karlsruhe&lt;/a&gt; in room 102, located in the basement. Unusual topics call for unusual timings, so we'll start at 8:00pm this time. Our speaker is Dr. Jürgen Hoffmann of &lt;a href="http://www.scrumcoach.de"&gt;scrumcoach.de&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;This month we are giving away a free book called "&lt;a href="http://www.amazon.de/gp/product/0321503627?ie=UTF8&amp;tag=mytakeonthing-21&amp;linkCode=as2&amp;camp=1638&amp;creative=6742&amp;creativeASIN=0321503627"&gt;Growing Object-Oriented Software, Guided by Tests by Freeman&amp;Pryce&lt;/a&gt;". If you are interested in participating in the lottery, drop me an email. The winners will be announced at the end of the talk. &lt;br /&gt;&lt;br /&gt;This is the last gathering of the year 2009. There's even more to come in 2010, so subscribe to our &lt;a href="http://www.google.com/calendar/embed?src=ehlt6rbl1cd1s2b7t9bkj6beek%40group.calendar.google.com&amp;ctz=Europe/Berlin"&gt;Google Calendar&lt;/a&gt; and stay up-to-date.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-5900569946785839304?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/5900569946785839304/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=5900569946785839304' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/5900569946785839304'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/5900569946785839304'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/12/scrum-jug-ka_4680.html' title='Scrum @ JUG-Ka'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_myUhtr_A51Y/SnWVogdEjOI/AAAAAAAAPiQ/o9MjIPCDE_s/s72-c/jug_logo_original_transparent.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-7932177108590882053</id><published>2009-11-30T05:40:00.000+01:00</published><updated>2009-11-30T05:42:17.345+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='spring'/><title type='text'>Playing with Spring's RestTemplate</title><content type='html'>A couple of week ago, I saw a &lt;a href="http://www.infoq.com/presentations/rest-web-application-architectures"&gt;talk&lt;/a&gt; on &lt;a href="http://www.springsource.org/download"&gt;Spring&lt;/a&gt; 3.0's MVC &lt;a href="http://dlinsin.blogspot.com/2009/11/restful-applications-in-practise-jug-ka.html"&gt;REST&lt;/a&gt; support. It's quite impressive how simple SpringSource made REST development by applying the proven concepts of the core Spring Framework. &lt;br /&gt;&lt;br /&gt;One of those great core concepts is &lt;a href="http://en.wikipedia.org/wiki/Spring_Framework#Data_access_framework"&gt;Templates&lt;/a&gt;. Spring's &lt;a href="http://static.springsource.org/spring/docs/2.5.x/reference/spring-middle-tier.html"&gt;Data Access Framework&lt;/a&gt; is an example, where templates are used extensively. One of the templates is the &lt;em&gt;JDBCTemplate&lt;/em&gt;. It provides a set of predefined methods to access a database, without worrying about &lt;i&gt;Connection&lt;/i&gt; management or &lt;i&gt;Exception&lt;/i&gt; handling. Templates are a nice way to help you the the job done, without constraining flexibility. The book &lt;a href="http://www.amazon.de/gp/product/1590599187?ie=UTF8&amp;tag=mytakeonthing-21&amp;linkCode=as2&amp;camp=1638&amp;creative=6742&amp;creativeASIN=1590599187"&gt;"Building Spring 2 Enterprise Applications"&lt;/a&gt;, which I &lt;a href="http://dlinsin.blogspot.com/2008/05/book-review-building-spring-2.html"&gt;reviewed&lt;/a&gt; last year, there's a whole chapter on &lt;em&gt;Data Access&lt;/em&gt; and the corresponding &lt;em&gt;Templates&lt;/em&gt;. &lt;br /&gt;&lt;br /&gt;The &lt;a href="http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/client/RestTemplate.html"&gt;RestTemplate&lt;/a&gt; is the latest child in the family. Arjen Poutsma from SpringSource wrote a &lt;a href="http://blog.springsource.com/2009/03/27/rest-in-spring-3-resttemplate/"&gt;blog entry&lt;/a&gt; about it and describes it as follows:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;The RestTemplate is the central Spring class for client-side HTTP access. Conceptually, it is very similar to the JdbcTemplate, JmsTemplate, and the various other templates found in the Spring Framework and other portfolio projects. This means, for instance, that the RestTemplate is thread-safe once constructed, and that you can use callbacks to customize its operations.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;In the before mentioned blog post, Arjen shows how to pull pictures from Flickr, using their REST API, and display them in a &lt;em&gt;JFrame&lt;/em&gt;. He uses an XML based approach, whereas I'll show you &lt;a href="http://github.com/dlinsin/github-java-api"&gt;some code&lt;/a&gt;, which handles JSON:&lt;br /&gt;&lt;br /&gt;&lt;script src="http://gist.github.com/236496.js"&gt;&lt;/script&gt;&lt;noscript&gt;&lt;pre&gt;public Issue browse(Repository argRepository, int argIssueNo) {&lt;br /&gt;    RestTemplate template = new RestTemplate();&lt;br /&gt;    template.setMessageConverters(new HttpMessageConverter[] {&lt;br /&gt;      new MappingJacksonHttpMessageConverter()});&lt;br /&gt;    IssueResponse resp = template.getForObject(BASE_URL + "issues/show/{username}/{repo}/{no}", IssueResponse.class, &lt;br /&gt;      argRepository.getOwner(), argRepository.getName(), String.valueOf(argIssueNo));&lt;br /&gt;    return resp.getIssue();&lt;br /&gt;}&lt;/pre&gt;&lt;/noscript&gt;&lt;br /&gt;&lt;br /&gt;You can see it's quite easy to work with a &lt;em&gt;RestTemplate&lt;/em&gt;. You simply create an instance, tell that instance what your expected &lt;em&gt;content&lt;/em&gt; is going to be and you are good to go. In this case we expect the request and response to be &lt;em&gt;&lt;a href="http://en.wikipedia.org/wiki/JSON"&gt;JSON&lt;/a&gt;&lt;/em&gt;. Spring is using the &lt;a href="http://jackson.codehaus.org/"&gt;Jackson JSON Processor&lt;/a&gt;, to automagically map POJOs to JSON and all the way back. &lt;br /&gt;&lt;br /&gt;The concept of a  &lt;em&gt;MessageConverter&lt;/em&gt; is really neat. You can set multiple converters and specify to use a certain converter for certain POJOs. You can also handle multiple content types by setting multiple instances of  &lt;em&gt;MessageConverter&lt;/em&gt;.&lt;br /&gt;&lt;br /&gt;In order to make a REST call with the predefined &lt;em&gt;MessageConverter&lt;/em&gt;, you simply use the &lt;em&gt;getForObject&lt;/em&gt; method. You pass the url to call, the type the response is supposed to be mapped to and the values to be replaced in the url. The type of the response is actually a wrapper class:&lt;br /&gt;&lt;br /&gt;&lt;script src="http://gist.github.com/236519.js"&gt;&lt;/script&gt;&lt;noscript&gt;&lt;pre&gt;public class IssueResponse {&lt;br /&gt;    private Issue issue;&lt;br /&gt;    public Issue getIssue() {&lt;br /&gt;        return issue;&lt;br /&gt;    }&lt;br /&gt;    public void setIssue(Issue argIssue) {&lt;br /&gt;        issue = argIssue;&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/noscript&gt; &lt;br /&gt;&lt;br /&gt;It is necessary since the following JSON response, which is represented in the POJO &lt;em&gt;Issue&lt;/em&gt;, needs some kind of holder object:&lt;br /&gt;&lt;br /&gt;&lt;script src="http://gist.github.com/236521.js"&gt;&lt;/script&gt;&lt;noscript&gt;&lt;pre&gt;{"issue":&lt;br /&gt;  {"number":1,"votes":0,"created_at":"2009/11/01 07:27:56 -0800",&lt;br /&gt;   "body":"This is a sample issue! Used in my RestTemplateSample!",&lt;br /&gt;   "title":"Sample Issue",&lt;br /&gt;   "updated_at":"2009/11/01 09:16:58 -0800","closed_at":null,&lt;br /&gt;   "user":"dlinsin","labels":["minor"],"state":"open"&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;/noscript&gt;&lt;br /&gt;&lt;br /&gt;As you can see, it's really easy to work with Spring's new &lt;em&gt;RestTemplate&lt;/em&gt;. These couple of lines are enough to retrieve a JSON or XML response from a REST call. With the &lt;em&gt;Template&lt;/em&gt; approach, you have the full flexibility to hook almost every method and tweak it to your needs.  &lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-7932177108590882053?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/7932177108590882053/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=7932177108590882053' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/7932177108590882053'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/7932177108590882053'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/11/playing-with-spring-resttemplate.html' title='Playing with Spring&amp;#39;s RestTemplate'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-8648257461689003239</id><published>2009-11-23T01:01:00.000+01:00</published><updated>2009-11-23T01:03:10.035+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mac'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><title type='text'>Finally Running with the Snow Leopard</title><content type='html'>A little note upfront - this is not about software development, so if you are solely here to read about my take on coding, this blog entry is not for you!&lt;br /&gt;&lt;br /&gt;A little more than 2 months ago I pre-ordered Apple's new &lt;a href="http://www.apple.com/macosx/"&gt;Mac OS X 10.6&lt;/a&gt; aka &lt;a href="http://en.wikipedia.org/wiki/Mac_OS_X_Snow_Leopard"&gt;&lt;em&gt;Snow Leopard&lt;/em&gt;&lt;/a&gt; one week before the official release at &lt;a href="http://www.gravis.de/"&gt;Gravis&lt;/a&gt;. As it turns out, it was a bad idea, because it took Gravis more than 6 weeks to deliver, but I digress...&lt;br /&gt;&lt;br /&gt;After finally receiving my &lt;a href="http://www.amazon.de/gp/product/B001AZ3Y66?ie=UTF8&amp;tag=mytakeonthing-21&amp;linkCode=as2&amp;camp=1638&amp;creative=6742&amp;creativeASIN=B001AZ3Y66"&gt;Family Pack&lt;/a&gt;, I installed the &lt;em&gt;Snow Leopard&lt;/em&gt; on my wife's &lt;a href="http://www.flickr.com/photos/sutini/sets/72157615186008213/"&gt;brand new MacBook&lt;/a&gt;. The upgrade was smooth and finished after only about 40 minutes. However, when it came to &lt;a href="http://dlinsin.blogspot.com/2008/05/paying-for-software.html"&gt;my old MacBook Pro&lt;/a&gt;, the tragedy began...&lt;br /&gt;&lt;br /&gt;The first attempt to install &lt;em&gt;Snow Leopard&lt;/em&gt; resulted in the installer quitting and rebooting the old Mac OS X as if nothing happened. Booting from the 10.6 DVD and then running the installer didn't help as well. I tried a couple of times, but the &lt;em&gt;Snow Leopard&lt;/em&gt; wouldn't come out of the cage. Instead, I got an error message like "An error occurred while installing Mac OS X". &lt;br /&gt;&lt;br /&gt;My second attempt was borrowing a colleagues copy of &lt;em&gt;Snow Leopard&lt;/em&gt; and trying to install it using my built-in DVD. You might have guessed it - it failed as well, with the same error message. The next attempt was getting an external drive, which I hooked up using &lt;a href="http://en.wikipedia.org/wiki/IEEE_1394_interface"&gt;FireWire&lt;/a&gt;. Unfortunately, neither my nor my colleagues &lt;em&gt;Snow Leopard&lt;/em&gt; DVD would let me install. &lt;br /&gt;&lt;br /&gt;After I spent hours of trying to tame this animal, I decided to leverage my wife's &lt;a href="http://www.amazon.de/gp/product/B002CJNCNY?ie=UTF8&amp;tag=mytakeonthing-21&amp;linkCode=as2&amp;camp=1638&amp;creative=6742&amp;creativeASIN=B002CJNCNY"&gt;MacBook&lt;/a&gt; as a remote dvd drive. It's pretty easy to setup and worked like a charm, however it didn't get my anywhere installing the new Mac OS X. The same problem here: the installer hangs after a while no matter which DVD I tried.&lt;br /&gt;&lt;br /&gt;My last resort was using a USB stick/drive for installation. I found a nice &lt;a href="http://www.hardwareluxx.de/community/showthread.php?t=643122"&gt;description (german)&lt;/a&gt; on the web, which included the following steps:&lt;br /&gt;&lt;br /&gt;1. create an image of &lt;em&gt;Snow Leopard&lt;/em&gt; using &lt;em&gt;Disk Utility&lt;/em&gt; (I used my wife's MacBook for that)&lt;br /&gt;2. restore that image to your USB stick (I got an &lt;a href="http://www.amazon.de/gp/product/B000J35DR8?ie=UTF8&amp;tag=mytakeonthing-21&amp;linkCode=as2&amp;camp=1638&amp;creative=6742&amp;creativeASIN=B000J35DR8"&gt;8GB stick&lt;/a&gt;)&lt;br /&gt;3. boot from your USB stick (hold option/alt while booting and select the USB drive)&lt;br /&gt;4. run the installer&lt;br /&gt;&lt;br /&gt;My stick didn't work out of the box, I had to reformat it - since it came &lt;em&gt;FAT32&lt;/em&gt; formatted and I had to run &lt;em&gt;"Scan Image for Restore"&lt;/em&gt;, which you can find under the &lt;em&gt;Image&lt;/em&gt; menu in &lt;em&gt;Disk Utility&lt;/em&gt;. After getting my USB drive ready and following the steps above, it worked like a charm. The installation took about an hour and went smoothly, without any problems.&lt;br /&gt;&lt;br /&gt;Although I had so much trouble getting &lt;em&gt;Snow Leopard&lt;/em&gt; installed, it was worth the pain! My system feels much more snappy and responsive now. &lt;em&gt;Snow Leopard&lt;/em&gt; needs less space on my quite limited hard drive and Time Machine backups are reasonably fast now. &lt;br /&gt;&lt;br /&gt;Overall, I'm quite happy jumping though all those hoops to the new &lt;a href="http://www.amazon.de/gp/product/B001AZ3Y66?ie=UTF8&amp;tag=mytakeonthing-21&amp;linkCode=as2&amp;camp=1638&amp;creative=6742&amp;creativeASIN=B001AZ3Y66"&gt;Mac OS X &lt;em&gt;Snow Leopard&lt;/em&gt;&lt;/a&gt;, although the experience wasn't very Apple-like. &lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-8648257461689003239?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/8648257461689003239/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=8648257461689003239' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/8648257461689003239'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/8648257461689003239'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/11/finally-running-with-snow-leopard.html' title='Finally Running with the Snow Leopard'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-9195950651685202607</id><published>2009-11-16T00:44:00.000+01:00</published><updated>2009-11-18T14:44:20.235+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='quality'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><title type='text'>Dependency Injection Obscures Your Code</title><content type='html'>During listening to Software Engineering Radio's &lt;a href="http://www.se-radio.net/podcast/2009-11/episode-148-software-archaeology-dave-thomas"&gt;Episode 148&lt;/a&gt; - "Software Archaeology with Dave Thomas", I got quite annoyed by a comment made by &lt;a href="http://pragdave.pragprog.com/"&gt;one of the respected authors&lt;/a&gt; of "&lt;a href=" http://www.amazon.de/gp/product/020161622X?ie=UTF8&amp;tag=mytakeonthing-21&amp;linkCode=as2&amp;camp=1638&amp;creative=6742&amp;creativeASIN=020161622X"&gt;The Pragmatic Programmer&lt;/a&gt;". &lt;br /&gt;&lt;br /&gt;To quote Dave Thomas correctly, here's what he said at around 23 minutes into the podcast:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;... that is one of the reasons why I, in general, are not very happy with the DI style of development, because I actually think it obscures the code quite a lot, when it comes to reading it ..... what it actually does is, it give you another dimension, that you have to read in order to understand the code.&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;My first reaction was "What the deuce!?!". I mean how can someone claim, that &lt;a href="http://en.wikipedia.org/wiki/Dependency_injection"&gt;Dependency Injection&lt;/a&gt; obscures code and makes reading code harder? Are all those hours I spent &lt;a href="http://dlinsin.blogspot.com/search/label/spring"&gt;blogging&lt;/a&gt; and &lt;a href="http://dlinsin.blogspot.com/2008/11/eclipse-rcp-and-inversion-of-control.html"&gt;promoting&lt;/a&gt; DI in vain? Are the &lt;a href="http://www.springsource.org/"&gt;SpringSource&lt;/a&gt; guys wrong or is Dave Thomas just crazy? &lt;br /&gt;&lt;br /&gt;However, his statement and even his argument are completely reasonable, if you listen to the first couple of minutes of the podcast. Dave said at around 11 minutes into the podcast:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;.... actually, I don't typically use an IDE, I use an editor like Textmate on the Mac.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;And there you go! No wonder, you'll find it harder to read and understand code, if you compare the following two screenshots from a bunch of code, using Spring:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh4.ggpht.com/_myUhtr_A51Y/Sv0eQxuLB_I/AAAAAAAAPn0/Qxc4m-7s738/di_1.png?imgmax=800" alt="di_1.png" border="1px" width="652" height="176" align="center"/&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh3.ggpht.com/_myUhtr_A51Y/Sv0wfPnJdbI/AAAAAAAAPoM/fX0-2AcxfJA/di_2.png?imgmax=800" alt="di_2.png" border="1px" width="652" height="176" align="center" /&gt;&lt;br /&gt;&lt;br /&gt;If you are reading the code above and don't understand where those two dependencies in the constructor come from, your &lt;a href="http://www.jetbrains.com/idea/index.html"&gt;IDE&lt;/a&gt; gives you a hint, without leaving the context of that particular file. You know that, they'll be injected by &lt;a href="http://www.springframework.org"&gt;Spring&lt;/a&gt;. In addition to that, you can even directly navigate to the configuration file with a single click.&lt;br /&gt;&lt;br /&gt;Now if you are using a text editor, you really have no idea where to look for. I guess you would have to &lt;em&gt;grep&lt;/em&gt; for &lt;em&gt;new TxTestService()&lt;/em&gt;. In case of using Spring, not even that would give you any more clues.&lt;br /&gt;&lt;br /&gt;Don't get me wrong, if Dave likes &lt;em&gt;Textmate&lt;/em&gt; better than an IDE - fair enough! I just think everyone should use the right tool for the right job! Frankly, &lt;a href="http://dlinsin.blogspot.com/2007/12/reading-code-or-writing-code.html"&gt;an IDE makes a much better job&lt;/a&gt; here than a plain text editor. &lt;br /&gt;&lt;br /&gt;I think the Java world has amazing tooling and in my opinion there's almost the philosophy, that if there is &lt;a href="http://dlinsin.blogspot.com/search/label/git"&gt;no tool support for a technology&lt;/a&gt; - it sucks. I don't fully agree with that. However, I do think that, when it comes to Java, tool support can definitely &lt;br /&gt;assist you reading and thus understanding code.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-9195950651685202607?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/9195950651685202607/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=9195950651685202607' title='15 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/9195950651685202607'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/9195950651685202607'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/11/dependency-injection-obscures-your-code.html' title='Dependency Injection Obscures Your Code'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_myUhtr_A51Y/Sv0eQxuLB_I/AAAAAAAAPn0/Qxc4m-7s738/s72-c/di_1.png?imgmax=800' height='72' width='72'/><thr:total>15</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-4219458025070582877</id><published>2009-11-06T05:44:00.003+01:00</published><updated>2009-11-17T02:28:35.072+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='karlsruhe'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='jug-ka'/><title type='text'>RESTful applications in practise @ JUG-Ka</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://picasaweb.google.com/lh/photo/zDGbEVVB5ihDeoGRJltMvA?authkey=Gv1sRgCKXY3MiLxprXDQ&amp;feat=embedwebsite"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh4.ggpht.com/_myUhtr_A51Y/SnWVogdEjOI/AAAAAAAAPiQ/o9MjIPCDE_s/s144/jug_logo_original_transparent.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5191388009271083842" /&gt;&lt;/a&gt;This coming &lt;a href="https://www.xing.com/events/rest-praxis-entwerfen-restful-applikationen-392973"&gt;Wednesday&lt;/a&gt; Gregor Roth from 1&amp;1, you might know him from his &lt;a href="http://www.infoq.com/articles/designing-restful-http-apps-roth"&gt;InfoQ article&lt;/a&gt;, is going to give a talk on &lt;a href="http://en.wikipedia.org/wiki/Representational_State_Transfer"&gt;RESTful&lt;/a&gt; applications at the &lt;a href="https://jug-ka.dev.java.net/"&gt;Java User Group Karlsruhe&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;He will show how to implement applications leveraging a RESTful architecture and point out the mismatch between a RESTful and &lt;a href="http://en.wikipedia.org/wiki/Remote_procedure_call"&gt;RPC&lt;/a&gt;-style approach. Furthermore Gregor will highlight the difference between HTTP PUT and POST and finally demonstrate how to implement none CRUD operations using REST. &lt;br /&gt;&lt;br /&gt;I'm really looking forward to this talk. In &lt;a href="http://dlinsin.blogspot.com/2009/10/google-sitebricks.html"&gt;my spare time&lt;/a&gt; I developed the &lt;a href="http://dlinsin.blogspot.com/2009/05/make-it-easy-to-contribute.html"&gt;REST interface&lt;/a&gt; for an &lt;a href="http://doublemill.blogspot.com/"&gt;Android game&lt;/a&gt; and it's going to be interesting to see what we could have done better and hopefully what we've got right.&lt;br /&gt;&lt;br /&gt;As usual, the talk will be held at &lt;a href="http://groups.google.com/group/jug-karlsruhe/web/how-to-find-us?_done=%2Fgroup%2Fjug-karlsruhe%3F"&gt;University Karlsruhe&lt;/a&gt; in room 102, which is located in the basement. We'll start at 7:15pm, if the door opens as planned this time.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.jetbrains.net"&gt;JetBrains&lt;/a&gt; and &lt;a href="http://www.zeroturnaround.com"&gt;ZeroTurnaround&lt;/a&gt; are each giving away a free license of one of their products. If you are interested in participating in our monthly lottery, send me an email. The winners will be announced at the end of the talk. In addition to that, &lt;a href="http://www.atlassian.com/"&gt;Atlassian&lt;/a&gt; sponsored a couple of great T-Shirts, which you can pick up at the end of the talk.&lt;br /&gt;&lt;br /&gt;If you want to know more about upcoming talks, subscribe to our &lt;a href="http://www.google.com/calendar/embed?src=ehlt6rbl1cd1s2b7t9bkj6beek%40group.calendar.google.com&amp;ctz=Europe/Berlin"&gt;Google Calendar&lt;/a&gt;. You'll also be able to see the planned talks for 2010.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-4219458025070582877?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/4219458025070582877/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=4219458025070582877' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/4219458025070582877'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/4219458025070582877'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/11/restful-applications-in-practise-jug-ka.html' title='RESTful applications in practise @ JUG-Ka'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_myUhtr_A51Y/SnWVogdEjOI/AAAAAAAAPiQ/o9MjIPCDE_s/s72-c/jug_logo_original_transparent.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-5765095529026510336</id><published>2009-11-02T05:44:00.000+01:00</published><updated>2009-11-02T05:49:24.483+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>Having Fun with JDK's tools.jar</title><content type='html'>This week I had to trace a bug in a legacy application, I'm maintaining at the moment, which drove me up the wall. The application uses a class from JDK's &lt;em&gt;tools.jar&lt;/em&gt;, to determine the JVM instances currently running. Unfortunately, there are incompatibilities between JDK versions.&lt;br /&gt;&lt;br /&gt;Tracking down the bug took a humongous amount of time, because the fun part of working on a &lt;a href="http://dlinsin.blogspot.com/2007/12/reading-code-or-writing-code.html"&gt;legacy application&lt;/a&gt; is that all the developers, which are responsible for &lt;a href="http://dlinsin.blogspot.com/2007/07/hell-is-other-peoples-code.html"&gt;the code you are maintaining&lt;/a&gt;, are usually no longer available. Don't even get me started on documentation! However, as a &lt;a href="http://dlinsin.blogspot.com/2009/02/do-you-write-unit-tests-for-yourself.html"&gt;good developer&lt;/a&gt;, I created the following JUnit test, to isolate the problem from the rest of the code.&lt;br /&gt;&lt;br /&gt;&lt;script src="http://gist.github.com/221475.js"&gt;&lt;/script&gt;&lt;noscript&gt;&lt;pre&gt;package de.linsin.sample.tools.bug;&lt;br /&gt;&lt;br /&gt;import static org.junit.Assert.*;&lt;br /&gt;&lt;br /&gt;import com.sun.tools.attach.VirtualMachine;&lt;br /&gt;import com.sun.tools.attach.VirtualMachineDescriptor;&lt;br /&gt;&lt;br /&gt;public class ToolsJarTest {&lt;br /&gt;    @Test&lt;br /&gt;    public void testJVMInstances() {&lt;br /&gt;        assertTrue(VirtualMachine.list() &gt; 0);&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/noscript&gt;&lt;br /&gt;If you run this test with Sun's JRE 1.6 and &lt;em&gt;tools.jar&lt;/em&gt; of Sun's JDK 1.6 on the classpath, you won't encounter an problems. However, with my current project, we need backward compatibility with other Java versions. That means, there are multiple &lt;em&gt;tools.jar&lt;/em&gt; on the &lt;em&gt;classpath&lt;/em&gt;, in different versions. Depending on their order, the test above fails or succeeds. &lt;br /&gt;&lt;br /&gt;More precisely, there is a JDK 1.4 and 1.5 version of &lt;em&gt;tools.jar&lt;/em&gt; on the &lt;em&gt;classpath&lt;/em&gt;, in addition to the 1.6 version. 1.6 is needed, because in previous JDKs the class VirtualMachine doesn't reside in the package &lt;em&gt;com.sun.tools.attach&lt;/em&gt;. So running the test without &lt;em&gt;tools.jar&lt;/em&gt; in version 1.6, you'll get a &lt;em&gt;NoClassDefFoundError&lt;/em&gt;.&lt;br /&gt;&lt;br /&gt;So what's the problem you might ask? As long as &lt;em&gt;tools.jar&lt;/em&gt; of JDK 1.6 is on the &lt;em&gt;classpath&lt;/em&gt;, when running the aforementioned test, everything should be okay, right? It should be, but it isn't. As soon as &lt;em&gt;tools.jar&lt;/em&gt; of JDK 1.5 is before the 1.6 version on the runtime &lt;em&gt;classpath&lt;/em&gt;, the test fails. Every other combination works just fine. &lt;br /&gt;&lt;br /&gt;Why &lt;em&gt;tools.jar&lt;/em&gt; of JDK 1.5 causes this problem is a mystery to me. Sun's bug parade, doesn't contain any &lt;a href=" http://blogs.sun.com/jmxetc/entry/how_to_retrieve_remote_jvm"&gt;relevant information&lt;/a&gt; - at least I couldn't find any. Frankly, I think this problem is so absurd, that I cannot motivate myself getting to the bottom of it. &lt;br /&gt;&lt;br /&gt;Nevertheless, the problem exists in our application and we'll have to find a solution. I guess that's the reason why it's a legacy application - I have to deal with problems that are totally outdated and nobody knows the answer to.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-5765095529026510336?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/5765095529026510336/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=5765095529026510336' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/5765095529026510336'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/5765095529026510336'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/11/having-fun-with-jdk-toolsjar.html' title='Having Fun with JDK&amp;#39;s tools.jar'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-9187732692649265861</id><published>2009-10-26T05:35:00.000+01:00</published><updated>2009-10-26T05:35:53.880+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><title type='text'>Push IRC messages to your iPhone</title><content type='html'>In my &lt;a href="http://www.synyx.de"&gt;new company&lt;/a&gt; one of the main communication channels is &lt;a href="http://en.wikipedia.org/wiki/Internet_Relay_Chat"&gt;IRC&lt;/a&gt;. Unfortunately, I'm &lt;a href="http://dlinsin.blogspot.com/2008/06/how-corporation-constrain-productivity.html"&gt;not able to hang out on IRC&lt;/a&gt; all day. That means, I'm not only missing interesting discussions, but I'm also always the last to get to know about interesting developments. To put an end to my offline-misery, I came up with a solution to push IRC messages to my iPhone.&lt;br /&gt;&lt;br /&gt;First a couple of things you need to know. You'll need an &lt;a href="http://bit.ly/19T8V7"&gt;iPhone app to receive push notifications&lt;/a&gt;, more about that later on. Furthermore, you'll need a server, which is able to host a Java-based IRC bot.&lt;br /&gt;&lt;br /&gt;The bot is leveraging the famouse &lt;a href="http://www.jibble.org/pircbot.php"&gt;PircBot&lt;/a&gt; library, which &lt;a href="http://dlinsin-archives.blogspot.com/2005/08/conversation-with-erich-gamma.html"&gt;unfortunately is based on inheritance&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;script src="http://gist.github.com/208294.js"&gt;&lt;/script&gt;&lt;noscript&gt;&lt;pre&gt;public class Bot extends PircBot {&lt;br /&gt;    private boolean silentMode = false;&lt;br /&gt;    private Collection&lt;NotificationService&gt; notificationServices;&lt;br /&gt; &lt;br /&gt;    private Collection&lt;String&gt; messages;&lt;br /&gt;    private Timer messagesTimer;&lt;br /&gt;    static final int MESSAGE_BATCH_SIZE = 20;&lt;br /&gt;    static final int MESSAGE_BATCH_DELAY_MS = 60000;&lt;br /&gt; &lt;br /&gt;    @Override&lt;br /&gt;    protected void onMessage(String argChannel, String argSender, String argLogin, String argHostname, String argMessage) {&lt;br /&gt;        batchMessages(format(argSender, argMessage));&lt;br /&gt;    }&lt;br /&gt; &lt;br /&gt;    @Override&lt;br /&gt;    protected void onJoin(String argChannel, String argSender, String argLogin, String argHostname) {&lt;br /&gt;        User[] users = super.getUsers(argChannel);&lt;br /&gt;        switchSilentMode(users);&lt;br /&gt;    }&lt;br /&gt; &lt;br /&gt;    @Override&lt;br /&gt;    protected void onQuit(String argChannel, String argSender, String argLogin, String argHostname) {&lt;br /&gt;        User[] users = super.getUsers(argChannel);&lt;br /&gt;        switchSilentMode(users);&lt;br /&gt;    }&lt;br /&gt; &lt;br /&gt;    private void switchSilentMode(User[] argUsers) {&lt;br /&gt;        for (int i = 0; i &lt; argUsers.length; i++) {&lt;br /&gt;            User user = argUsers[i];&lt;br /&gt;            if (user.getNick().equals(alterEgoName)) {&lt;br /&gt;                silentMode = true;&lt;br /&gt;                return;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        silentMode = false;&lt;br /&gt;    }&lt;br /&gt; &lt;br /&gt;    private void batchMessages(String argMessage) {&lt;br /&gt;        if (messages.isEmpty()) {&lt;br /&gt;            messages.add(argMessage);&lt;br /&gt;            messagesTimer.schedule(new TimerTask() {&lt;br /&gt;                public void run() {&lt;br /&gt;                    processMessages();&lt;br /&gt;                    messagesTimer.purge();&lt;br /&gt;                }&lt;br /&gt;            }, MESSAGE_BATCH_DELAY_MS);&lt;br /&gt;        } else if (messages.size() &lt; MESSAGE_BATCH_SIZE) {&lt;br /&gt;            messages.add(argMessage);&lt;br /&gt;        } else {&lt;br /&gt;            processMessages();&lt;br /&gt;            messagesTimer.purge();&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt; &lt;br /&gt;    private void processMessages() {&lt;br /&gt;        StringBuilder builder = new StringBuilder();&lt;br /&gt;        for (String message : messages) {&lt;br /&gt;            builder.append(message).append("\n");&lt;br /&gt;        }&lt;br /&gt;        sendToService("message", builder.toString());&lt;br /&gt;        messages.clear();&lt;br /&gt;    }&lt;br /&gt; &lt;br /&gt;    private void sendToService(String argTitle, String argMessage) {&lt;br /&gt;        for (NotificationService service : notificationServices) {&lt;br /&gt;            if (!silentMode) {&lt;br /&gt;                service.notify(argTitle, argMessage);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/noscript&gt;I stripped out some of the code, you can find the full version on &lt;a href="http://github.com/dlinsin/area51/blob/master/alterego/src/main/java/de/linsin/alterego/Bot.java"&gt;GitHub&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The usage of PircBot, as you can see above (line 11, 16 and 22), is quite easy. You simply override the methods you want to hook into, and execute your code. That's pretty much all the magic that is to PircBot. In order to limit the notifications pushed to the iPhone, I batch a certain number of messages for a pre-configured time and eventually send them to a &lt;em&gt;NotificationService&lt;/em&gt;. I'll come back to the &lt;em&gt;NotificationService&lt;/em&gt; in a second, but there's another feature I'd like to point out - silent mode. I don't want to be notified on my iPhone, when I'm actually on IRC myself. So whenver a pre-configured user enters the channel, the Bot will switch to silent mode. As soon as the user leaves the channel, it'll switch back. &lt;br /&gt;&lt;br /&gt;&lt;script src="http://gist.github.com/208326.js"&gt;&lt;/script&gt;&lt;noscript&gt;&lt;pre&gt;public class AppNotificationService implements NotificationService {&lt;br /&gt;    private final String credentials;&lt;br /&gt;    public static final String USER_CREDENTIALS = "user_credentials";&lt;br /&gt;    public static final String NOTIFICATION_LONG_MESSAGE = "notification[long_message]";&lt;br /&gt;    public static final String NOTIFICATION_TITLE = "notification[title]";&lt;br /&gt;    public static final String MESSAGE_LEVEL = "message_level";&lt;br /&gt;    public static final String URL = "https://www.appnotifications.com/account/notifications.xml";&lt;br /&gt;    public static final String HTTP_USERAGENT = "http.useragent";&lt;br /&gt; &lt;br /&gt;    public AppNotificationService(String argCredentials) {&lt;br /&gt;        credentials = argCredentials;&lt;br /&gt;    }&lt;br /&gt; &lt;br /&gt;    public boolean notify(String argTitle, String argMessage) {&lt;br /&gt;        HttpClient client;&lt;br /&gt;        PostMethod method = null;&lt;br /&gt;        try {&lt;br /&gt;            client = setUp();&lt;br /&gt;            method = setUp(argTitle, argMessage);&lt;br /&gt;            int returnCode = client.executeMethod(method);&lt;br /&gt; &lt;br /&gt;            if (returnCode &gt; HttpStatus.SC_OK) {&lt;br /&gt;                return false;&lt;br /&gt;            } else {&lt;br /&gt;                logger.info("Sent notification!");&lt;br /&gt;                return true;&lt;br /&gt;            }&lt;br /&gt;        } catch (Exception e) {&lt;br /&gt;            throw new AppNotificationException();&lt;br /&gt;        } finally {&lt;br /&gt;            if (method != null) {&lt;br /&gt;                method.releaseConnection();&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt; &lt;br /&gt;    HttpClient setUp() {&lt;br /&gt;        HttpClient client = new HttpClient();&lt;br /&gt;        client.getParams().setParameter(HTTP_USERAGENT, "IRC Bot");&lt;br /&gt;        return client;&lt;br /&gt;    }&lt;br /&gt; &lt;br /&gt;    PostMethod setUp(String argTitle, String argMessage) {&lt;br /&gt;        PostMethod method = new PostMethod(URL);&lt;br /&gt;        method.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");&lt;br /&gt; &lt;br /&gt;        method.addParameter(USER_CREDENTIALS, credentials);&lt;br /&gt;        method.addParameter(NOTIFICATION_LONG_MESSAGE, argMessage);&lt;br /&gt;        method.addParameter(NOTIFICATION_TITLE, argTitle);&lt;br /&gt;        method.addParameter(MESSAGE_LEVEL, "2");&lt;br /&gt;        return method;&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/noscript&gt;As you can see, the &lt;em&gt;NotificationService&lt;/em&gt; implementation, simply makes a Http Post request to a service called &lt;a href="http://www.appnotifications.com"&gt;App Notifications&lt;/a&gt;. The request contains a couple of &lt;em&gt;key/value pairs&lt;/em&gt;, which are used to configure your iPhone's push notifications. &lt;br /&gt;&lt;br /&gt;I'm using &lt;a href="http://hc.apache.org/httpcomponents-client/httpclient/"&gt;Commons HttpClient&lt;/a&gt; to makes the Post request, since I have &lt;a href="http://dlinsin.blogspot.com/2009/08/http-basic-authentication-with-android.html"&gt;some experience&lt;/a&gt; with it. In order to use App Notifications, you'll need to setup an account. After the setup, you'll receive a credential String, which must be passed along with the Post request. &lt;br /&gt;&lt;br /&gt;The only part missing is the iPhone app, that you need, in order to receive push notifications. The app is not free, but it's quite reasonable. Actually it's a good deal to pay for the app once and not on a request or monhtly bases. However, keep in mind, that the app can only receive push notifications - nothing else. If you that's what you need, this app is for you.&lt;br /&gt;&lt;br /&gt;Putting together all parts of the puzzle, you'll get an IRC bot, which connects to the channel you want to be notified about. The bot batches messages and sends them to a service, which in turn pushes them to your iPhone. On your iPhone you have the notification app, which displays the messages from IRC.&lt;br /&gt;&lt;br /&gt;The App Notification service is very reliable and super fast. However, there is one downside to the iPhoen app. You cannot switch off the notification sound without also switching off the vibration feedback. The App Notification developer told me, he'll address this issue in a future update. &lt;br /&gt;&lt;br /&gt;I also had to play with the batch size and delay, since there are a lot of messages on the channel, I'm interested in and it got quite irratating getting pounded with notifications constantly. &lt;br /&gt;&lt;br /&gt;Overall, I do admit, that this combination is quite adventurous, but it works very well. If you'd like to check it out, grab the &lt;a href="http://github.com/dlinsin/area51/blob/master/alterego/"&gt;code from GitHub&lt;/a&gt; and let me know what you think.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-9187732692649265861?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/9187732692649265861/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=9187732692649265861' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/9187732692649265861'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/9187732692649265861'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/10/push-irc-messages-to-your-iphone.html' title='Push IRC messages to your iPhone'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-7354783281697002994</id><published>2009-10-21T06:09:00.000+02:00</published><updated>2009-11-17T02:30:50.688+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='karlsruhe'/><category scheme='http://www.blogger.com/atom/ns#' term='jboss rules'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='jug-ka'/><title type='text'>jBPM 4 @ JUG-Ka</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://picasaweb.google.com/lh/photo/zDGbEVVB5ihDeoGRJltMvA?authkey=Gv1sRgCKXY3MiLxprXDQ&amp;feat=embedwebsite"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh4.ggpht.com/_myUhtr_A51Y/SnWVogdEjOI/AAAAAAAAPiQ/o9MjIPCDE_s/s144/jug_logo_original_transparent.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5191388009271083842" /&gt;&lt;/a&gt;Today I'm pleased to welcome &lt;a href="http://www.camunda.com/content/view/83/"&gt;Bernd Rücker&lt;/a&gt; from &lt;a href="http://www.camunda.com/"&gt;camunda services GmbH&lt;/a&gt; to the &lt;a href="https://jug-ka.dev.java.net/"&gt;Java User Group Karlsruhe&lt;/a&gt;. Bernd is going to talk about the Open Source Business Process Engine &lt;a href="http://www.jboss.com/products/jbpm/"&gt;JBoss jBPM&lt;/a&gt;. I'm really excited about this talk, because it's a &lt;a href="http://dlinsin.blogspot.com/search/label/jboss%20rules"&gt;topic&lt;/a&gt; I was quite dedicated to a couple of years ago.&lt;br /&gt;&lt;br /&gt;The session takes place today at &lt;a href="http://groups.google.com/group/jug-karlsruhe/web/how-to-find-us?_done=%2Fgroup%2Fjug-karlsruhe%3F"&gt;University Karlsruhe&lt;/a&gt;, building 50.34 in the multimedia room 102, located in the basement. The talk starts at 7:15pm, our usual timing.&lt;br /&gt;&lt;br /&gt;There will also be our monthly lottery, where &lt;a href="http://www.jetbrains.net"&gt;JetBrains&lt;/a&gt; and &lt;a href="http://www.zeroturnaround.com"&gt;ZeroTurnaround&lt;/a&gt; are each giving away a free license of one of their products. If you are interested in participating in the draw send me an email. The winners are going to be announced at the end of the talk. &lt;br /&gt;&lt;br /&gt;There are still two more great talks coming up this year. You can subscribe to our &lt;a href="http://www.google.com/calendar/embed?src=ehlt6rbl1cd1s2b7t9bkj6beek%40group.calendar.google.com&amp;ctz=Europe/Berlin"&gt;Google Calendar&lt;/a&gt; to get the latest updates. There's also a &lt;a href="http://groups.google.com/group/jug-karlsruhe"&gt;Google&lt;/a&gt; or &lt;a href="https://www.xing.com/net/jug-karlsruhe/"&gt;Xing Group&lt;/a&gt; to stay on top of things.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-7354783281697002994?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/7354783281697002994/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=7354783281697002994' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/7354783281697002994'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/7354783281697002994'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/10/jbpm-4-jug-ka.html' title='jBPM 4 @ JUG-Ka'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_myUhtr_A51Y/SnWVogdEjOI/AAAAAAAAPiQ/o9MjIPCDE_s/s72-c/jug_logo_original_transparent.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-4467498567363753578</id><published>2009-10-19T05:45:00.000+02:00</published><updated>2009-10-19T06:02:57.353+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='spring'/><category scheme='http://www.blogger.com/atom/ns#' term='osgi'/><title type='text'>Spring DM with Annotations</title><content type='html'>A couple of days ago, I implemented a sample application, based on &lt;a href="http://www.springsource.org/osgi"&gt;Spring Dynamic Modules (DM) 1.2.0&lt;/a&gt;, with its &lt;a href="http://static.springsource.org/osgi/docs/1.2.0/reference/html/appendix-extensions.html#appendix-extensions:annotations"&gt;annotation extension&lt;/a&gt;. Unfortunately the documentation doesn't contain any sample code, which might cause some unnecessary work, if you are not too familiar with Spring DM.&lt;br /&gt;&lt;br /&gt;Spring DM's annotation extension allows you to pull in an OSGi service reference by annotating a setter of a property:&lt;br /&gt;&lt;br /&gt;&lt;script src="http://gist.github.com/205746.js"&gt;&lt;/script&gt;&lt;noscript&gt;&lt;pre&gt;import org.springframework.osgi.extensions.annotation.ServiceReference;&lt;br/&gt;&lt;br /&gt;public class MyService {&lt;br /&gt;  private LogService logService;&lt;br/&gt;&lt;br /&gt;  @ServiceReference(cardinality=ServiceReferenceCardinality.C0__1)&lt;br /&gt;  public void setLogService(LogService argLogService) {&lt;br /&gt;    logService= argLogService;&lt;br /&gt;  }&lt;br /&gt;  // other stuf&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;br /&gt;Unfortunately, you don't get rid of the XML configuration completely, but that's not so bad after all, because you still want to let Spring to all your wiring:&lt;br /&gt;&lt;br /&gt;&lt;script src="http://gist.github.com/205762.js"&gt;&lt;/script&gt;&lt;noscript&gt;&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;br /&gt;&amp;lt;beans xmlns="http://www.springframework.org/schema/beans" &lt;br/&gt;xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" &lt;br/&gt;xsi:schemaLocation="http://www.springframework.org/schema/beans  &lt;br/&gt;http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"&amp;gt;&lt;br /&gt;&lt;br/&gt;&amp;lt;bean class="org.springframework.osgi.extensions.annotation.ServiceReferenceInjectionBeanPostProcessor"/&amp;gt;&lt;br /&gt;&amp;lt;bean id="testService" class="de.linsin.sample.springdm.MyService"/&amp;gt;&lt;br /&gt;&lt;br/&gt;&amp;lt;/beans&amp;gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;br /&gt;In addition to the instantion part, you need to configure a &lt;em&gt;BeanPostProcessor&lt;/em&gt;. It tells Spring to handle your methods annotated with &lt;em&gt;@ServiceReference&lt;/em&gt;. If you want all your bundles to use annotations, this might get a little tedious. That's why you can configure annotation processing for all bundles, by defining a &lt;em&gt;fragment bundle&lt;/em&gt;, which overrides the default configuration of &lt;em&gt;Spring DM's extender&lt;/em&gt;:&lt;br /&gt;&lt;br /&gt;&lt;script src="http://gist.github.com/205769.js"&gt;&lt;/script&gt;&lt;noscript&gt;&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;br /&gt;&amp;lt;beans xmlns="http://www.springframework.org/schema/beans" &lt;br /&gt;xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   &lt;br /&gt;xmlns:util="http://www.springframework.org/schema/util" &lt;br /&gt;xsi:schemaLocation="http://www.springframework.org/schema/beans  http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/util  http://www.springframework.org/schema/util/spring-util-2.0.xsd"&amp;gt;&lt;br /&gt;&lt;br/&gt;&amp;lt;util:properties id="extenderProperties"&amp;gt;&lt;br /&gt;&amp;lt;prop key="process.annotations"&amp;gt;true&amp;lt;/prop&amp;gt;&lt;br /&gt;&amp;lt;/util:properties&amp;gt;&lt;br /&gt;&lt;br/&gt;&amp;lt;/beans&amp;gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;br /&gt;Unfortunately, the configuration of the &lt;em&gt;fragement bundle&lt;/em&gt; is &lt;a href="http://jira.springframework.org/browse/OSGI-765"&gt;not&lt;/a&gt; in the &lt;a href="http://static.springsource.org/osgi/docs/1.2.0/reference/html/app-deploy.html#app-deploy:extender-configuration"&gt;offical documentation&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;Now, that you've got a glimpse of how to use annotations with Spring DM and the ways of configuring it, you might ask whether you want to use it or not? For me this boils down to the &lt;a href="http://dlinsin.blogspot.com/2007/04/guice-di-framework.html"&gt;question&lt;/a&gt;, whether you want to have your dependencies in your &lt;a href="http://dlinsin.blogspot.com/2008/07/configuration-notations.html"&gt;xml file&lt;/a&gt; or in your code?&lt;br /&gt;&lt;br /&gt;It's a tricky question and I think annotations only bring real value, if they make your life easier. Let's take a look at &lt;a href="http://static.springsource.org/spring/docs/2.5.x/reference/mvc.html"&gt;Spring's Web MVC&lt;/a&gt; for example: before annotations were introduced, you had to implement an interface in order to code a controller and thus you had dependencies on javax.servlet in your controller code. Spring Web MVC's annotation approach still leaves you with the dependency on Spring, but eliminates the javax.servlet dependency. &lt;br /&gt;&lt;br /&gt;In addition to that, it improves &lt;a href="http://www.olivergierke.de/wordpress/2009/05/unit-testing-annotation-based-spring-mvc-controllers/"&gt;testability of controller classes&lt;/a&gt; significantly, which I think is worth living with the dependency on Spring. &lt;br /&gt;&lt;br /&gt;I think, that the features of Spring DM annotations, at least at the moment, are not compelling enough to have dependencies on it in your code. With the right tooling, Spring's XML configuration should be as easy to handle as annotations.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-4467498567363753578?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/4467498567363753578/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=4467498567363753578' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/4467498567363753578'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/4467498567363753578'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/10/spring-dm-with-annotations.html' title='Spring DM with Annotations'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-4652803423157516326</id><published>2009-10-12T05:48:00.000+02:00</published><updated>2009-10-12T05:48:00.223+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='spring'/><category scheme='http://www.blogger.com/atom/ns#' term='osgi'/><title type='text'>Book Review Dynamic Modules for OSGi</title><content type='html'>&lt;span style="font-style:italic;"&gt;&lt;a href="http://www.apress.com"&gt;Apress&lt;/a&gt; was kind enough to pass me a copy of this book, which I agreed to review in return.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.amazon.de/gp/product/1430216123?ie=UTF8&amp;tag=mytakeonthing-21&amp;linkCode=as2&amp;camp=1638&amp;creative=6742&amp;creativeASIN=1430216123"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh5.ggpht.com/_myUhtr_A51Y/SbjfrBLw6rI/AAAAAAAANTI/gCOR_SSMrP0/9781430216124.gif" border="0" alt=""id="" /&gt;&lt;/a&gt;I have been sitting on &lt;a href="http://www.amazon.de/gp/product/1430216123?ie=UTF8&amp;tag=mytakeonthing-21&amp;linkCode=as2&amp;camp=1638&amp;creative=6742&amp;creativeASIN=1430216123"&gt;Pro Spring Dynamic Modules for Osgi(tm) Service Platforms&lt;/a&gt; for a while, although I got a fresh copy right after if was released earlier this year. &lt;br /&gt;&lt;br /&gt;One reason for this might be, that after reading the first chapter, it felt like I was reading a manual rather than a book. Personally, I like a little bit more subjectiveness, because it improves the reading experience significantly. The author should spice up the dry material, so you won't get bored that easily. Unfortunately, that's what happened to me - I got bored. However, let's turn the spotlight to the content of the book.&lt;br /&gt;&lt;br /&gt;The introduction chapter on OSGi is sufficient to get you up to speed. There are about 60 pages of Spring introduction. However, I think you should at least have some practical experiences with Spring, before digging into Spring Dynamic Modules or even Spring DM Server. It's simply not enough to explain the technicalities, to get someone an understanding of what Spring an its concepts is all about. &lt;br /&gt;&lt;br /&gt;There's lot's of code in the book, which you can &lt;a href="http://apress.com/book/view/1430216123"&gt;download&lt;/a&gt; and play with. If you like to read code, printed in a book, you are probably gonna like "Pro Spring Dynamic Modules for Osgi". For me, a book is not the preffered media to consume code. I have nothing against small code samples, but having pages over pages full of code, is really confusing and hurts readability.&lt;br /&gt;&lt;br /&gt;The manual kind of feeling of the book continues the further you keep reading. Let me give you a concrete example: In chapter 4, called "Spring Dynamic Modules for OSGi", the author explains how the &lt;em&gt;scope&lt;/em&gt; attribute of a bean declaration works:&lt;br /&gt;&lt;br /&gt;&lt;script src="http://gist.github.com/201243.js"&gt;&lt;/script&gt;&lt;noscript&gt;&lt;pre&gt;&amp;lt;bean id="beanToBeExported" scope="bundle" class="com.xyz.MessageServiceImpl"/&amp;gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;br /&gt;Unfortunately, the &lt;i&gt;how&lt;/i&gt; is all there is to the explanation. I expected a real life example of when to use the &lt;em&gt;scope&lt;/em&gt; attribute and where it might not be suitable. I do understand, that the book can't go into details all the time, but especially those powerful Spring  DM features like &lt;em&gt;scoping&lt;/em&gt;, deserve more spotlight. Most of the time, the book stops when it gets interesting and you are left with your own imagination of how to apply that particular feature. &lt;br /&gt;&lt;br /&gt;Despite the criticism, I got some neat tips from the book. The author suggests to split the OSGi dependent and traditional Spring configuration to make life easier for testing and mocking. I also gained a lot of knowledge from chapter 6, called "Versioning with OSGi and Spring". The author explains the concepts and implementation of versioning most of the time in a very understandable manner. &lt;br /&gt;&lt;br /&gt;Overall, I think &lt;a href="http://www.amazon.de/gp/product/1430216123?ie=UTF8&amp;tag=mytakeonthing-21&amp;linkCode=as2&amp;camp=1638&amp;creative=6742&amp;creativeASIN=1430216123"&gt;Pro Spring Dynamic Modules for Osgi(tm) Service Platforms&lt;/a&gt; is a reasonable reference book, with a nice sample application. If you are new to Spring and OSGi, you might have a hard time understanding the use case for those technologies, so I'd suggest to get this book as an addition to some &lt;a href="http://dlinsin.blogspot.com/2008/05/book-review-building-spring-2.html"&gt;basic reading material&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-4652803423157516326?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/4652803423157516326/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=4652803423157516326' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/4652803423157516326'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/4652803423157516326'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/10/book-review-dynamic-modules-for-osgi.html' title='Book Review Dynamic Modules for OSGi'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_myUhtr_A51Y/SbjfrBLw6rI/AAAAAAAANTI/gCOR_SSMrP0/s72-c/9781430216124.gif' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-1726305450030689571</id><published>2009-10-05T05:45:00.002+02:00</published><updated>2009-10-05T09:02:18.646+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='guice'/><category scheme='http://www.blogger.com/atom/ns#' term='google'/><title type='text'>Google Sitebricks</title><content type='html'>Last week I stumbled upon an &lt;a href="http://www.infoq.com/news/2009/09/google-sitebricks"&gt;interview on InfoQ&lt;/a&gt; with a Google developer working on a new web framework called &lt;a href="http://code.google.com/p/google-sitebricks/"&gt;Google Sitebricks&lt;/a&gt;. On my research day at &lt;a href="http://www.synyx.de"&gt;Synyx&lt;/a&gt;, I had time to produce some &lt;a href="http://github.com/dlinsin/area51/tree/master/google-sitebricks-sample/"&gt;sample code&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Google's mission statement goes as follows:&lt;br /&gt;&lt;blockquote&gt;Sitebricks is a simple development layer for web applications built on top of Google Guice. Sitebricks focuses on early error detection, low-footprint code, and fast development. Like Guice, it also balances idiomatic Java with an emphasis on concise code.&lt;/blockquote&gt;&lt;br /&gt;A &lt;em&gt;Sitebrick&lt;/em&gt; based web-application is deployed as a good old WAR file in a servlet container. That's the first information not explicitly stated on the website. If you have worked with another web framework before, you probably know the concept of a controller or a REST-based Resource. You can find the same concept in &lt;em&gt;Sitebricks&lt;/em&gt;, too:&lt;br /&gt;&lt;br /&gt;&lt;script src="http://gist.github.com/200631.js"&gt;&lt;/script&gt;&lt;noscript&gt;&lt;pre&gt;@At("/guestbook")&lt;br /&gt;public class Guestbook {&lt;br /&gt;private List&lt;entry&gt; entries;&lt;br /&gt;private Entry newEntry = new Entry();&lt;br /&gt;&lt;br /&gt;@Inject&lt;br /&gt;private EntryDao entryDao;&lt;br /&gt;&lt;br /&gt;public List&lt;entry&gt; getEntries() {&lt;br /&gt;return entries;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@Get&lt;br /&gt;public void load() {&lt;br /&gt;entries = entryDao.readAll();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@Post&lt;br /&gt;public String save() {&lt;br /&gt;newEntry.setDate(new Date());&lt;br /&gt;Integer id = entryDao.save(newEntry);&lt;br /&gt;return String.format("/sample/guestbook/%1$s", id);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public Entry getNewEntry() {&lt;br /&gt;return newEntry;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public void setNewEntry(Entry argNewEntry) {&lt;br /&gt;newEntry = argNewEntry;&lt;br /&gt;}&lt;br /&gt;}&lt;/pre&gt;&lt;/noscript&gt;&lt;br /&gt;You can see that the class is pretty simple and it reminds me a lot of &lt;a href="http://dlinsin.blogspot.com/2009/05/make-it-easy-to-contribute.html"&gt;Restlet&lt;/a&gt;, which I used extensively the past few months. With the &lt;em&gt;At&lt;/em&gt; annotation you declare the &lt;em&gt;Uri&lt;/em&gt; under which your resource should be available. You can also define variable parts in your &lt;em&gt;Uri&lt;/em&gt;, e.g. &lt;em&gt;/guestbook/:id&lt;/em&gt;, which is &lt;a href="http://github.com/dlinsin/area51/blob/master/google-sitebricks-sample/src/de/linsin/sample/sitebricks/resource/GuestbookEntry.java"&gt;used in my sample&lt;/a&gt; to access an entry of the guestbook.&lt;br /&gt;&lt;br /&gt;The methods annotated with &lt;em&gt;Get&lt;/em&gt; and &lt;em&gt;Post&lt;/em&gt; are being called upon a request with the corresponding method. If you take a closer look at the method &lt;em&gt;load()&lt;/em&gt;, you can see, that it doesn't return any value. That's where the convention or should I call it magic, of the framework begins:&lt;br /&gt;&lt;br /&gt;&lt;script src="http://gist.github.com/200638.js"&gt;&lt;/script&gt;&lt;noscript&gt;&lt;pre&gt;&amp;lt;body&amp;gt;&lt;br /&gt;@Repeat(items=entries, var="entry")&lt;br /&gt;&amp;lt;p&amp;gt;&lt;br /&gt;&amp;lt;a href="/guestbook/${entry.id}"&amp;gt;${entry.id} on ${entry.date}&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;form action="/guestbook" method="post"&amp;gt;&lt;br /&gt;&lt;br /&gt;Name: &amp;lt;input name="newEntry.name" type="text"/&amp;gt;&lt;br /&gt;&lt;br /&gt;Text: &amp;lt;input name="newEntry.text" type="text"/&amp;gt;&lt;br /&gt;&amp;lt;input type="submit" value="save" name="save"/&amp;gt;&lt;br /&gt;&amp;lt;/form&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/body&amp;gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;br /&gt;In your &lt;em&gt;Html&lt;/em&gt; file, that you define along with your resource, there are expressions, that access members of your resource class. In the &lt;a href="http://github.com/dlinsin/area51/blob/master/google-sitebricks-sample/web/Guestbook.html"&gt;&lt;em&gt;Html&lt;/em&gt; snippet above&lt;/a&gt; we iterate over a &lt;em&gt;Collection&lt;/em&gt; named "entries". It's a member of the &lt;a href="http://github.com/dlinsin/area51/blob/master/google-sitebricks-sample/src/de/linsin/sample/sitebricks/resource/Guestbook.java"&gt;Guestbook class&lt;/a&gt; and is assigned whenever there's a &lt;em&gt;Get&lt;/em&gt; request, which triggers the method &lt;em&gt;load()&lt;/em&gt; to be called. The expression &lt;em&gt;items=entries&lt;/em&gt; calls the &lt;em&gt;getEntries()&lt;/em&gt; method on &lt;em&gt;Guestbook&lt;/em&gt;, to return the &lt;em&gt;Collection&lt;/em&gt;.&lt;br /&gt;&lt;br /&gt;Frankly, this strikes me kinda odd. You call a method to populate a member and then its getter to retrieve the value? Shouldn't the annotated &lt;em&gt;Get&lt;/em&gt; method return the value directly? I'm not quite sure what the design goal was here, but the first thing that comes into my mind is concurrency. If your &lt;em&gt;resource&lt;/em&gt; class is a &lt;em&gt;Singleton&lt;/em&gt;, you could run into quite some trouble.&lt;br /&gt;&lt;br /&gt;Take a look at the form in the &lt;em&gt;Html&lt;/em&gt; snipped above. It has two input fields, which are properties, mapped by name to the member &lt;em&gt;newEntry&lt;/em&gt;. The member must be accessible under the resource declared in the &lt;em&gt;action&lt;/em&gt; attribute of the form. This was &lt;a href="http://code.google.com/p/google-sitebricks/issues/detail?id=22"&gt;not mentioned&lt;/a&gt; in the Sitebrick docs either. When you submit the form, the &lt;em&gt;getNewEntry()&lt;/em&gt; method is called and the input field values are set at the returned instance. After that, the annotated &lt;em&gt;save()&lt;/em&gt; method is called with the member &lt;em&gt;newEntry&lt;/em&gt; already populated. &lt;br /&gt;&lt;br /&gt;Again, the design here is rather unusual. You are working on a member, which in case of a &lt;em&gt;Singleton&lt;/em&gt; resource, means there is &lt;a href="http://dlinsin.blogspot.com/2009/05/handling-concurrency-domain-models.html"&gt;shared state&lt;/a&gt;. Of course, if you define your resources not as &lt;em&gt;Singleton&lt;/em&gt;, you don't have any problems. However, it's definitely a pitfall, in my opinion.&lt;br /&gt;&lt;br /&gt;Overall, I think Google Sitebricks contains a couple of cool ideas. I like the &lt;em&gt;expression language&lt;/em&gt; kind of approach, which makes it easy to work with &lt;em&gt;Html&lt;/em&gt; snippets. The possibility to &lt;a href="http://code.google.com/p/google-sitebricks/wiki/EmbeddingBricks"&gt;define little components (Bricks)&lt;/a&gt; and add them in other sites, is another really cool feature. &lt;br /&gt;&lt;br /&gt;The project is still in its early stages and I hope they'll address those odd design decisions, mentioned above. Although it's not standards-based, with Google backing the framework, I could imagine Sitebricks getting some traction.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-1726305450030689571?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/1726305450030689571/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=1726305450030689571' title='16 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/1726305450030689571'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/1726305450030689571'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/10/google-sitebricks.html' title='Google Sitebricks'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><thr:total>16</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-6553932053262635881</id><published>2009-09-28T06:17:00.000+02:00</published><updated>2009-09-28T06:17:00.668+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='karlsruhe'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='hibernate'/><category scheme='http://www.blogger.com/atom/ns#' term='jug-ka'/><title type='text'>Tuning Hibernate and JPA @ JUG-Ka</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://picasaweb.google.com/lh/photo/zDGbEVVB5ihDeoGRJltMvA?authkey=Gv1sRgCKXY3MiLxprXDQ&amp;feat=embedwebsite"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh4.ggpht.com/_myUhtr_A51Y/SnWVogdEjOI/AAAAAAAAPiQ/o9MjIPCDE_s/s144/jug_logo_original_transparent.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5191388009271083842" /&gt;&lt;/a&gt;After &lt;a href="http://dlinsin.blogspot.com/search/label/jug-ka"&gt;two sessions&lt;/a&gt; on client development, we are now turning our attention to the server side again. I'm very happy to welcome &lt;a href="http://rockingcode.blogspot.com/"&gt;Michael Ploed&lt;/a&gt; from &lt;a href="http://www.senacor.com/"&gt;Senacor Technologies AG&lt;/a&gt; to talk about "&lt;a href="https://www.xing.com/events/tuning-hibernate-jpa-anwendungen-371794"&gt;Tuning Hibernate and JPA Applications&lt;/a&gt;" at the&lt;a href="https://jug-ka.dev.java.net/"&gt; Java User Group Karlsruhe&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Michael will show us how to approach tuning of your &lt;a href="http://www.hibernate.org"&gt;Hibernate&lt;/a&gt; powered applications and which options exist, to increase your application's performance. Furthermore he will talk about Hibernate's second-level cache, which I'm looking forward to in particular. Finally, he'll demonstrate his tuning advices with a demo application.&lt;br /&gt;&lt;br /&gt;This month we have a special prize in our monthly lottery: a free pass to the &lt;a href="http://it-republik.de/jaxenter/wjax09/"&gt;W-Jax conference&lt;/a&gt;, held this coming November in Munich. All you've got to do is send me an email and be present at the end of the session, when Michael will draw the winner.&lt;br /&gt;&lt;br /&gt;The session takes place this Wednesday at &lt;a href="http://groups.google.com/group/jug-karlsruhe/web/how-to-find-us?_done=%2Fgroup%2Fjug-karlsruhe%3F"&gt;University Karlsruhe&lt;/a&gt;, building 50.34 in the multimedia room 101, located in the basement. The talk starts at 7pm, a little earlier as usual. &lt;br /&gt;&lt;br /&gt;We have more great talks coming up, the &lt;a href="http://www.google.com/calendar/embed?src=ehlt6rbl1cd1s2b7t9bkj6beek%40group.calendar.google.com&amp;ctz=Europe/Berlin"&gt;next couple of months&lt;/a&gt;. In order to get the latest updates, sign up for our &lt;a href="http://groups.google.com/group/jug-karlsruhe"&gt;Google Group&lt;/a&gt; or join us on &lt;a href="https://www.xing.com/net/jug-karlsruhe/"&gt;Xing&lt;/a&gt;.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-6553932053262635881?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/6553932053262635881/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=6553932053262635881' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/6553932053262635881'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/6553932053262635881'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/09/tuning-hibernate-and-jpa-jug-ka.html' title='Tuning Hibernate and JPA @ JUG-Ka'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_myUhtr_A51Y/SnWVogdEjOI/AAAAAAAAPiQ/o9MjIPCDE_s/s72-c/jug_logo_original_transparent.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-2438408786489430085</id><published>2009-09-22T07:19:00.006+02:00</published><updated>2009-09-22T07:23:42.609+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='spring'/><category scheme='http://www.blogger.com/atom/ns#' term='osgi'/><title type='text'>Spring DM Web Extender Problems</title><content type='html'>In my current project, I'm evaluating moving a large and very old code base to &lt;a href="http://dlinsin.blogspot.com/search/label/osgi"&gt;OSGi&lt;/a&gt;. Part of that is getting the the development team on board. I'm trying to point out, how much you can benefit from designing your application with OSGi in mind and the advantages you can have, running your application in an OSGi container. &lt;br /&gt;&lt;br /&gt;I created some sample code with the help of the book &lt;a href="http://www.amazon.de/gp/product/1430216123?ie=UTF8&amp;tag=mytakeonthing-21&amp;linkCode=as2&amp;camp=1638&amp;creative=6742&amp;creativeASIN=1430216123"&gt;"Pro Spring Dynamic Modules for Osgi(tm) Service Platforms"&lt;/a&gt; to highlight some of my OSGi presentation bulletpoints. One of the samples is running Tomcat inside of Equinox together with &lt;a href="http://dlinsin.blogspot.com/2008/11/eclipse-rcp-and-inversion-of-control.html"&gt;Spring Dynamic Modules&lt;/a&gt;. Although the sample is really simple and I sticked to the steps in the book precisely, Equinox spit out the following Exception:&lt;br /&gt;&lt;br /&gt;&lt;script src="http://gist.github.com/190831.js"&gt;&lt;/script&gt;&lt;noscript&gt;&lt;pre&gt;17.09.2009 09:08:23 org.springframework.osgi.web.deployer.tomcat.TomcatWarDeployer afterPropertiesSet&lt;br /&gt;SEVERE: No Catalina Service found, bailing out&lt;br /&gt;org.springframework.osgi.service.ServiceUnavailableException: service matching filter=[(objectClass=org.apache.catalina.Service)] unavailable&lt;br /&gt;at org.springframework.osgi.service.importer.support.internal.aop.ServiceDynamicInterceptor.getTarget(ServiceDynamicInterceptor.java:395)&lt;br /&gt;at org.springframework.osgi.service.importer.support.internal.aop.ServiceDynamicInterceptor.afterPropertiesSet(ServiceDynamicInterceptor.java:455)&lt;br /&gt;at org.springframework.osgi.service.importer.support.OsgiServiceProxyFactoryBean.createProxy(OsgiServiceProxyFactoryBean.java:185)&lt;br /&gt;at org.springframework.osgi.service.importer.support.AbstractServiceImporterProxyFactoryBean.getObject(AbstractServiceImporterProxyFactoryBean.java:86)&lt;br /&gt;at org.springframework.osgi.service.importer.support.OsgiServiceProxyFactoryBean.getObject(OsgiServiceProxyFactoryBean.java:137)&lt;br /&gt;at org.springframework.osgi.web.deployer.internal.util.Utils.createServerServiceProxy(Utils.java:117)&lt;br /&gt;at org.springframework.osgi.web.deployer.tomcat.TomcatWarDeployer.afterPropertiesSet(TomcatWarDeployer.java:90)&lt;br /&gt;at org.springframework.osgi.web.extender.internal.activator.WarListenerConfiguration.createDefaultWarDeployer(WarListenerConfiguration.java:197)&lt;br /&gt;at org.springframework.osgi.web.extender.internal.activator.WarListenerConfiguration.&lt;init&gt;(WarListenerConfiguration.java:105)&lt;br /&gt;at org.springframework.osgi.web.extender.internal.activator.WarLoaderListener$1.run(WarLoaderListener.java:353)&lt;br /&gt;at java.lang.Thread.run(Thread.java:619)&lt;/pre&gt;&lt;/noscript&gt;&lt;br /&gt;After various failed attempts to google for a solution, I decided to consult the book again. Although, I thought I sticked to the steps closely, I missed an important point: &lt;a href="http://eclipsesource.com/blogs/2009/06/10/osgi-and-start-levels/"&gt;&lt;em&gt;start levels&lt;/em&gt;&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;A start level is simply a non-negative integer value. The Framework has an ‘active start level’ that is used to decide which bundles can be started. Bundles themselves have an associated ‘bundle start level’ which is used to determine when a bundle is started. The bundles at a given start level will all have their start method completely executed before any bundles at a higher level are started. When booted, the Framework monotonically goes through each start level and starts relevant bundles (all the way until the active start level is met).&lt;br /&gt;...&lt;br /&gt;In the end, start levels are there to simply determine the start order of bundles.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;I missed the fact that there is a start order in my Equinox run configuration. The bundles, which are in charge of bootstrapping Tomcat, need to be activated in special order, otherwise the previously mentioned Exception is raised. In particular the following bundles need to start in order:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;...&lt;br /&gt;org.springframework.osgi.catalina.osgi@3:start, \&lt;br /&gt;org.springframework.osgi.catalina.start.osgi@3:start, \&lt;br /&gt;org.springframework.bundle.osgi.web.extender@4:start, \ &lt;br /&gt;...&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In "Pro Spring Dynamic Modules for Osgi" the config only orders the bundles and doesn't add any explicit &lt;em&gt;start levels&lt;/em&gt;. However, I still ran into the same Exception from time to time, depending on other bundles I loaded. I put explicit &lt;em&gt;start levels&lt;/em&gt; in my run configuration to solve those problems.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-2438408786489430085?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/2438408786489430085/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=2438408786489430085' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/2438408786489430085'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/2438408786489430085'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/09/spring-dm-web-extender-problems.html' title='Spring DM Web Extender Problems'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-3315962791761054802</id><published>2009-09-14T06:22:00.000+02:00</published><updated>2009-09-14T06:22:57.932+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='git'/><title type='text'>Git Svn</title><content type='html'>Last week I started working for my &lt;a href="http://www.synyx.de"&gt;new company&lt;/a&gt; and like most of the IT businesses today, we are running subversion for Source Control Management (SCM). While we are still discussing, moving to a Distributed SCM (DSCM), I didn't want to lose the &lt;a href="http://dlinsin.blogspot.com/2009/07/git-stash.html"&gt;advantages&lt;/a&gt; of working with &lt;a href="http://dlinsin.blogspot.com/search/label/git"&gt;&lt;em&gt;git&lt;/em&gt;&lt;/a&gt;. Remembering &lt;a href="http://dlinsin.blogspot.com/2009/06/git-talk-jug-ka.html"&gt;Samuel's talk at Java User Group Karlsruhe&lt;/a&gt; and with a hint from &lt;a href="http://www.olivergierke.de"&gt;Ollie&lt;/a&gt;, I started using &lt;a href="http://www.kernel.org/pub/software/scm/git/docs/git-svn.html"&gt;git-svn&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The &lt;em&gt;git&lt;/em&gt; manual describes the svn capabilities as follows:&lt;br /&gt;&lt;blockquote&gt;git svn is a simple conduit for changesets between Subversion and git. It provides a bidirectional flow of changes between a Subversion and a git repository.&lt;br /&gt;&lt;br /&gt;git svn can track a standard Subversion repository....Once tracking a Subversion repository (with any of the above methods), the git repository can be updated from Subversion by the fetch command and Subversion updated from git by the dcommit command.&lt;/blockquote&gt;&lt;br /&gt;You can create a fully featured &lt;em&gt;git&lt;/em&gt; repository from the &lt;em&gt;trunk&lt;/em&gt; of a subversion repo with the following command:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;git svn init -T https://svn.hostname.de/svn/projects/project/trunk&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The &lt;em&gt;git&lt;/em&gt; repository, that you just created is hooked to the &lt;em&gt;trunk&lt;/em&gt;, passed with URL in the command. To get the &lt;em&gt;HEAD&lt;/em&gt;, just run:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;git svn fetch&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now you are ready to work with &lt;em&gt;git&lt;/em&gt;, as if you had cloned a repo from &lt;a href="http://github.com/dlinsin/"&gt;GitHub&lt;/a&gt;. You can work with branches locally and commit your changes offline. When you want to share your work and &lt;em&gt;push&lt;/em&gt; it back to subversion, you can use the following command:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;git svn dcommit&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You might ask yourself how to get the latest changes from your fellow developers? First, you should have no un-commited changes when you run:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;git svn rebase&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The reason for that, is this command is similar to &lt;em&gt;svn update&lt;/em&gt;. It uses &lt;a href="http://www.kernel.org/pub/software/scm/git/docs/git-rebase.html"&gt;git-rebase&lt;/a&gt; instead of &lt;a href="http://www.kernel.org/pub/software/scm/git/docs/git-merge.html"&gt;git-merge&lt;/a&gt;, in order to preserves linear history.&lt;br /&gt;&lt;br /&gt;Those couple of commands got me started in no time and I didn't have to wait for a company-wide repository in order to have all the advantages that come with &lt;em&gt;git&lt;/em&gt;.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-3315962791761054802?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/3315962791761054802/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=3315962791761054802' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/3315962791761054802'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/3315962791761054802'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/09/git-svn.html' title='Git Svn'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-7223524030784113392</id><published>2009-09-07T06:03:00.000+02:00</published><updated>2009-09-07T06:05:22.305+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><title type='text'>No Improved Exception Handling</title><content type='html'>Last week Joe Darcy &lt;a href="http://blogs.sun.com/darcy/entry/project_coin_final_five"&gt;announced&lt;/a&gt; the &lt;a href="http://openjdk.java.net/projects/coin/"&gt;Project Coin&lt;/a&gt; finalists. There will be 7 "improvements" to the &lt;em&gt;Java Language&lt;/em&gt; for the upcoming JDK 7 release. Unfortunately, one of &lt;a href="http://dlinsin.blogspot.com/2009/03/jdk-7-language-changes-are-coined.html"&gt;my favorite proposals&lt;/a&gt; &lt;a href="http://mail.openjdk.java.net/pipermail/coin-dev/2009-February/000003.html"&gt;"&lt;em&gt;Improved Exception Handling&lt;/em&gt;"&lt;/a&gt; didn't make it. Don't worry, I promise this is not gonna be a rant post, rather my personal view on what I have to work with for the next couple of years. &lt;br /&gt;&lt;br /&gt;I have to admit I was a little pissed at first, when I read,  that "&lt;em&gt;Improved Exception Handling&lt;/em&gt;" didn't get in. Especially, because it was one of the &lt;a href="http://dlinsin.blogspot.com/2009/03/jdk-7-language-changes-are-coined.html"&gt;most requested proposals&lt;/a&gt;. However, after reading Joe's explanation on his blog, I do see, that it is not easy to prefer one proposal over the other:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Improved exception handling would be a fine change for the language, but it ventures near the type system and we do not estimate we have resources to fully develop the feature within JDK 7. I would like to see improved exception handling reconsidered in subsequent efforts to evolve the language.&lt;/blockquote&gt;&lt;br /&gt;At this point I would start the ranting on how &lt;a href="http://www.sun.com"&gt;Sun&lt;/a&gt; couldn't have enough resources, but I made a promise I wouldn't. There's &lt;a href="http://blogs.sun.com/darcy/entry/javaposse_277_ivory_tower"&gt;another reason&lt;/a&gt; I shouldn't start ranting:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;If there are frustrations waiting for Java language changes, I assure you there are also frustrations working on Java language changes. For example, I find it frustrating (and self-inconsistent) that people state "I don't have technical expertise in this area" while simultaneously expecting their preferences to be selected without any contribution on their part. [&lt;a href="http://blogs.sun.com/darcy/entry/project_coin_final_five#comments"&gt;LINK&lt;/a&gt;]&lt;/blockquote&gt;&lt;br /&gt;I guess I am one of those frustrated people. I have no idea, if I lack the technical expertise, but what I do know is that I have enough technical expertise to judge that &lt;a href="http://dlinsin.blogspot.com/2008/01/wonderful-checked-exceptions.html"&gt;this&lt;/a&gt; is a &lt;a href="http://en.wiktionary.org/wiki/PITA"&gt;PITA&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Statement stmt = null;&lt;br /&gt;try {&lt;br /&gt;   // do stuff that uses Statement instance&lt;br /&gt;} catch (SQLException e) {&lt;br /&gt;  // handle state&lt;br /&gt;  log.error("Bad database", e);&lt;br /&gt;} catch (NullPointerException e) {&lt;br /&gt;  // handle state&lt;br /&gt;  log.error("Bad parameter", e);&lt;br /&gt;} catch (Exception e) {&lt;br /&gt;  // handle state&lt;br /&gt;  log.error("Bad luck", e);&lt;br /&gt;} finally {&lt;br /&gt;  try {&lt;br /&gt;    stmt.close();&lt;br /&gt;  } catch (Exception ex) {}&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;It is quite irritating, that this is what Java developers have to work with for the next couple of years. It is annoying, that there is no standard in the &lt;em&gt;Java Language&lt;/em&gt; addressing this problem. There might be workarounds or some OpenJDK whatever branch with a prototype hacked into it, which exactly solves this problem. In my opinion, that's not a solution whatsoever. &lt;br /&gt;&lt;br /&gt;In order to reduce &lt;a href="http://dlinsin.blogspot.com/2009/07/when-unchecked-exceptions-go-wrong.html"&gt;programming errors&lt;/a&gt;, I think the &lt;em&gt;Java Language&lt;/em&gt; must address this problem with a standard solution like "&lt;em&gt;Improved Exception Handling&lt;/em&gt;".&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-7223524030784113392?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/7223524030784113392/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=7223524030784113392' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/7223524030784113392'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/7223524030784113392'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/09/no-improved-exception-handling.html' title='No Improved Exception Handling'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-98197124055245114</id><published>2009-08-31T06:17:00.000+02:00</published><updated>2009-09-25T11:56:19.487+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='karlsruhe'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='jug-ka'/><title type='text'>Java &amp; Flex @ JUG-Ka</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://picasaweb.google.com/lh/photo/zDGbEVVB5ihDeoGRJltMvA?authkey=Gv1sRgCKXY3MiLxprXDQ&amp;feat=embedwebsite"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh4.ggpht.com/_myUhtr_A51Y/SnWVogdEjOI/AAAAAAAAPiQ/o9MjIPCDE_s/s144/jug_logo_original_transparent.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5191388009271083842" /&gt;&lt;/a&gt;I was amazed how many people showed up at our &lt;a href="http://dlinsin.blogspot.com/2009/08/javafx-jug-ka.html"&gt;last session&lt;/a&gt; on &lt;a href="http://java.sun.com/javafx/"&gt;JavaFX&lt;/a&gt; and I would love to see such a crowd at every session. &lt;br /&gt;&lt;br /&gt;Was it the new technology, that made so many people show up? Was it the fact that it was about client development? Or was it simply because we had a speaker from Sun? I'm really curious to know and I would love to get some feedback, in order to improve the topics of our sessions.&lt;br /&gt;&lt;br /&gt;This Wednesday, our new theme on client development continues with a session on Flex &amp; Java. &lt;a href="http://www.adobe.com/products/flex/"&gt;Adobe Flex&lt;/a&gt; is a framework for building web and desktop clients. Corneliu Vasile Creanga from Adobe, will give us an overview of the framework and how it can interact with Java. The session starts at 7:15pm, this Wednesday and takes place at &lt;a href="http://groups.google.com/group/jug-karlsruhe/web/how-to-find-us"&gt;University Karlsruhe&lt;/a&gt; room 101 in the basement. &lt;br /&gt;&lt;br /&gt;There will also be our monthly lottery, where &lt;a href="http://www.jetbrains.net"&gt;JetBrains&lt;/a&gt; and &lt;a href="http://www.zeroturnaround.com"&gt;ZeroTurnaround&lt;/a&gt; are each giving away a free license of one of their products. If you are interested in getting a free copy of &lt;a href="http://www.jetbrains.net/confluence/display/IDEADEV/Home"&gt;IntelliJ&lt;/a&gt; or &lt;a href="http://www.zeroturnaround.com/javarebel/"&gt;JavaRebel&lt;/a&gt;, send me an email to participate in the draw. The winners are gonna be announced at the end of the talk.  &lt;br /&gt;&lt;br /&gt;We have a lot &lt;a href="http://www.xing.com/net/jug-karlsruhe/"&gt;more exciting talks coming up&lt;/a&gt; this year. To stay on top of things, sign up for our &lt;a href="http://groups.google.com/group/jug-karlsruhe"&gt;Google Group&lt;/a&gt; or join us on &lt;a href="https://www.xing.com/net/jug-karlsruhe/"&gt;XING&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-98197124055245114?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/98197124055245114/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=98197124055245114' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/98197124055245114'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/98197124055245114'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/08/java-flex-jug-ka.html' title='Java &amp;amp; Flex @ JUG-Ka'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_myUhtr_A51Y/SnWVogdEjOI/AAAAAAAAPiQ/o9MjIPCDE_s/s72-c/jug_logo_original_transparent.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-3289699145764093216</id><published>2009-08-24T06:00:00.000+02:00</published><updated>2009-08-24T06:04:21.184+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='android'/><category scheme='http://www.blogger.com/atom/ns#' term='gae'/><title type='text'>Http Basic Authentication with Android</title><content type='html'>The Google App Engine infrastructure, I'm &lt;a href="http://dlinsin.blogspot.com/2009/08/cron-jobs-on-google-app-engine.html"&gt;developing&lt;/a&gt; in my spare time, is meant to be used by an &lt;a href="http://www.android.com/"&gt;Android&lt;/a&gt; client. To give our users at least a vague feeling of security, we decided to use a &lt;em&gt;&lt;a href="http://en.wikipedia.org/wiki/Basic_access_authentication"&gt;Basic Authentication&lt;/a&gt;&lt;/em&gt; together with &lt;a href="http://en.wikipedia.org/wiki/HTTP_Secure"&gt;HTTPS&lt;/a&gt;. Apparently, Android 1.5 is &lt;a href="http://linklens.blogspot.com/2009/06/android-multipart-upload.html"&gt;shipping with&lt;/a&gt; &lt;a href="http://hc.apache.org/httpcomponents-client/httpclient/"&gt;Apache's HttpClient 4.0 Beta2&lt;/a&gt;, which has a pitfall, when it comes to &lt;em&gt;Basic Authentication&lt;/em&gt;.&lt;br /&gt;&lt;br /&gt;When you search for &lt;em&gt;HttpClient&lt;/em&gt; and &lt;em&gt;Basic Authentication&lt;/em&gt;, Google will most definitely send you to the &lt;a href="http://hc.apache.org/httpclient-3.x/authentication.html#Preemptive_Authentication"&gt;official documentation of HttpClient 3.x&lt;/a&gt;, which shows you, how to do Basic Authentication in a preemptive way. That means, sending the client's credentials with every request, instead of waiting for a &lt;em&gt;&lt;a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html"&gt;401&lt;/a&gt; Unauthorized&lt;/em&gt; response and only then sending the credentials. That's probably what you want to in the first place, because it saves your client a request.&lt;br /&gt;&lt;pre&gt;HttpClient client = new HttpClient();&lt;br /&gt;client.getParams().setAuthenticationPreemptive(true);&lt;br /&gt;Credentials defaultcreds = new UsernamePasswordCredentials("username", "password");&lt;br /&gt;client.getState().setCredentials(new AuthScope("myhost", 80, AuthScope.ANY_REALM), defaultcreds);&lt;/pre&gt;&lt;br /&gt;This sample code won't compile with HttpClient version 4. The method called &lt;em&gt;setAuthenticationPreemptive&lt;/em&gt; is missing. The problem is, if you omit this very method call, the code still works, but the authentication is not preemptive. We missed this little detail and only noticed after a while, that every request was preceded by a &lt;em&gt;401 Unauthorized&lt;/em&gt; request/response cycle. That doubled the amount of requests we served.&lt;br /&gt;&lt;br /&gt;The &lt;a href="http://hc.apache.org/httpcomponents-client/tutorial/html/authentication.html"&gt;HttpClient 4 documentation&lt;/a&gt; shows how to do preemptive authentication with the new API. You need to implement a so called &lt;em&gt;HttpRequestInterceptor&lt;/em&gt;:&lt;br /&gt;&lt;pre&gt;HttpRequestInterceptor preemptiveAuth = new HttpRequestInterceptor() {&lt;br /&gt;    public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {&lt;br /&gt;        AuthState authState = (AuthState) context.getAttribute(ClientContext.TARGET_AUTH_STATE);&lt;br /&gt;        CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(&lt;br /&gt;                ClientContext.CREDS_PROVIDER);&lt;br /&gt;        HttpHost targetHost = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST);&lt;br /&gt;        &lt;br /&gt;        if (authState.getAuthScheme() == null) {&lt;br /&gt;            AuthScope authScope = new AuthScope(targetHost.getHostName(), targetHost.getPort());&lt;br /&gt;            Credentials creds = credsProvider.getCredentials(authScope);&lt;br /&gt;            if (creds != null) {&lt;br /&gt;                authState.setAuthScheme(new BasicScheme());&lt;br /&gt;                authState.setCredentials(creds);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }    &lt;br /&gt;};&lt;/pre&gt;&lt;br /&gt;It basically sets the Basic Authentication headers, before each requests and thus avoids the 401 response. In order for the interceptor to work, you need to add it to the request chain:&lt;br /&gt;&lt;pre&gt;DefaultHttpClient httpclient = new DefaultHttpClient();&lt;br /&gt;httpclient.addRequestInterceptor(preemptiveAuth, 0);&lt;/pre&gt;&lt;br /&gt;You might also run into the problem of using the old HttpClient 3.x way of doing &lt;em&gt;Basic Authentication&lt;/em&gt;. It does work, but it's not preemptive, which you might not notice right away. Save yourself some time and checkout the &lt;a href="https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/httpclient/src/examples/org/apache/http/examples/client/ClientPreemptiveBasicAuthentication.java"&gt;sample code&lt;/a&gt; provided by Apache.&lt;br /&gt;&lt;br /&gt;Furthermore, I wasn't abel to find any official site, which states the version of HttpClient, used in Android 1.5. There are various sources, you might encounter, when searching Google, but I would like to see an official site, that states the version. A reason for Google not to reveal this information might be, that they adopted HttpClient and thus are not compatible anymore. However, to avoid mistakes and confusion, it would be nice to know, on which version the Android HttpClient is based on.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-3289699145764093216?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/3289699145764093216/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=3289699145764093216' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/3289699145764093216'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/3289699145764093216'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/08/http-basic-authentication-with-android.html' title='Http Basic Authentication with Android'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-3842347694001736295</id><published>2009-08-10T06:15:00.000+02:00</published><updated>2009-08-10T06:16:57.646+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='google'/><category scheme='http://www.blogger.com/atom/ns#' term='gae'/><title type='text'>Cron Jobs on Google App Engine</title><content type='html'>I've been developing for the &lt;a href="http://code.google.com/appengine"&gt;Google App Engine&lt;/a&gt; (GAE) &lt;a href="http://dlinsin.blogspot.com/search/label/gae"&gt;for a couple of months&lt;/a&gt; now and there's a lot, I want to talk about in future blog posts. This installment is about &lt;a href="http://code.google.com/appengine/docs/java/config/cron.html"&gt;scheduled tasks&lt;/a&gt; on the GAE  - &lt;a href="http://en.wikipedia.org/wiki/Cron"&gt;cron jobs&lt;/a&gt;  - and a couple of pitfalls that you should be aware of.&lt;br /&gt;&lt;br /&gt;The configuration of a cron on GAE is pretty straightforward. You define your job in a file called &lt;em&gt;cron.xml&lt;/em&gt;, which goes to your &lt;em&gt;WEB-INF&lt;/em&gt; folder in your WAR file. &lt;br /&gt;&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;br /&gt;&amp;lt;cronentries&amp;gt;&lt;br /&gt;    &amp;lt;cron&amp;gt;&lt;br /&gt;        &amp;lt;url&amp;gt;/cron/clean&amp;lt;/url&amp;gt;&lt;br /&gt;        &amp;lt;description&amp;gt;job to clean tmp every 5 minutes&amp;lt;/description&amp;gt;&lt;br /&gt;        &amp;lt;schedule&amp;gt;every 5 minutes&amp;lt;/schedule&amp;gt;&lt;br /&gt;    &amp;lt;/cron&amp;gt;&lt;br /&gt;&amp;lt;/cronentries&amp;gt;&lt;/pre&gt;&lt;br /&gt;The file is easy to understand: &lt;em&gt;url &lt;/em&gt;denotes where the GAE is supposed to send a GET request to, when the cron is triggered. Whatever you place behind the denoted &lt;em&gt;url&lt;/em&gt; is your own choice. It can be a plain &lt;em&gt;Servlet&lt;/em&gt; or some RESTful resource. The &lt;em&gt;schedule&lt;/em&gt; tag contains a english-like syntax to define when the &lt;em&gt;url&lt;/em&gt; is supposed to be requested. In this example GAE makes a HTTP GET request to http://yourname.appspot.com/cron/clean every 5 minutes.&lt;br /&gt;&lt;br /&gt;One of GAE's subtle details is that your application is being shutdown, as soon as there are no requests coming in for a certain period of time. That's no a problem by itself, however, if you want to access the cached data of your application from within your cron, you are in trouble. It's not a severe problem, you just need prepared for it.&lt;br /&gt;&lt;br /&gt;Another pitfall, that you might encounter, is configuring the &lt;em&gt;url&lt;/em&gt; of the cron to be secured by SSL. You can define which &lt;em&gt;urls&lt;/em&gt; are supposed to be confidential in your &lt;em&gt;web.xml&lt;/em&gt;. As soon as you add your cron's &lt;em&gt;url&lt;/em&gt;, you'll encounter an error message in your admin console under "Cron Jobs", which says "Too many continues". This indicates, that GAE wants to execute cron jobs using http instead of https, which leads to a &lt;a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html"&gt;HTTP 302&lt;/a&gt; response. Browsers can easily interpret it as "don't use http, use https instead", but the GAE can't.&lt;br /&gt;&lt;br /&gt;This is not a security problem, since the urls of your cron job should only be accessible by administrators anyways. You can easily exclude the cron's &lt;em&gt;url&lt;/em&gt; from your SSL configuration and everything should work fine.&lt;br /&gt;&lt;br /&gt;There are more little gotchas, that I encountered developing for the GAE and I'm going to blog more about it soon.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-3842347694001736295?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/3842347694001736295/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=3842347694001736295' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/3842347694001736295'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/3842347694001736295'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/08/cron-jobs-on-google-app-engine.html' title='Cron Jobs on Google App Engine'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-3689680441155528956</id><published>2009-08-03T06:15:00.000+02:00</published><updated>2009-08-05T15:36:52.088+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='karlsruhe'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='jug-ka'/><title type='text'>JavaFX @ JUG-Ka</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://picasaweb.google.com/lh/photo/zDGbEVVB5ihDeoGRJltMvA?authkey=Gv1sRgCKXY3MiLxprXDQ&amp;feat=embedwebsite"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh4.ggpht.com/_myUhtr_A51Y/SnWVogdEjOI/AAAAAAAAPiQ/o9MjIPCDE_s/s144/jug_logo_original_transparent.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5191388009271083842" /&gt;&lt;/a&gt;With our new logo, designed by &lt;a href="http://samuel-mellert.de/"&gt;Samuel Mellert&lt;/a&gt;, who held &lt;a href="http://dlinsin.blogspot.com/2009/06/git-talk-jug-ka.html"&gt;last month's session&lt;/a&gt; on &lt;a href="http://en.wikipedia.org/wiki/Git_(software)"&gt;Git&lt;/a&gt;, we want to start into the second half of 2009. There are a couple of &lt;a href="http://www.xing.com/net/jug-karlsruhe/"&gt;exciting talks coming up&lt;/a&gt; at &lt;a href="http://jug-ka.de"&gt;Java User Group Karlsruhe&lt;/a&gt;, beginning with an introduction to &lt;a href="http://java.sun.com/javafx/"&gt;JavaFX&lt;/a&gt; this week. To get the latest updates of the JUG-Ka sign up for our &lt;a href="http://groups.google.com/group/jug-karlsruhe"&gt;Google Group&lt;/a&gt; or join us on &lt;a href="https://www.xing.com/net/jug-karlsruhe/"&gt;XING&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Stefan Schneider from Sun Micorsystem is going to give an introductory level session on JavaFX, with samples and time for QA. Stefan gave &lt;a href="http://dlinsin.blogspot.com/2007/08/jvm-memory-pool-sizing-principles.html"&gt;a couple&lt;/a&gt; &lt;a href="http://dlinsin.blogspot.com/2007/07/java-se-6-tech-talk.html"&gt;of talks&lt;/a&gt; here in Karlsruhe and I'm very pleased he is back with something new.&lt;br /&gt;&lt;br /&gt;The session takes place this Wednesday at University Karlsruhe and starts at 7:15pm. As usual, &lt;a href="http://www.jetbrains.net"&gt;JetBrains&lt;/a&gt; and &lt;a href="http://www.zeroturnaround.com"&gt;ZeroTurnaround&lt;/a&gt; are each giving away a free license of one of their products. There'll be a lottery, so if you are interested in getting a free copy of &lt;a href="http://www.jetbrains.net/confluence/display/IDEADEV/Home"&gt;IntelliJ&lt;/a&gt; or &lt;a href="http://www.zeroturnaround.com/javarebel/"&gt;JavaRebel&lt;/a&gt;, send me an email to participate in the draw. The winners are gonna be announced at the end of the talk.  &lt;br /&gt;&lt;br /&gt;I'm sure you all know the &lt;a href="http://www.dzone.com"&gt;DZone network&lt;/a&gt;, which provides all sorts of services and information for developers, like &lt;a href="http://java.dzone.com/"&gt;Javalobby&lt;/a&gt;, &lt;a href="http://www.jroller.com/"&gt;JRoller&lt;/a&gt; or &lt;a href="http://eclipse.dzone.com/"&gt;EclipseZone&lt;/a&gt;. They also have a service called &lt;a href="http://refcardz.dzone.com/"&gt;Refcardz&lt;/a&gt; - nice little cheat sheets on a variety of topcis. You can download those Refcardz and print them yourself or you can come by on Wednesday and get one for free. DZone was kind enough to provide a couple of professionally printed Refcardz, which I will give out at the end of the next talk.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-3689680441155528956?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/3689680441155528956/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=3689680441155528956' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/3689680441155528956'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/3689680441155528956'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/08/javafx-jug-ka.html' title='JavaFX @ JUG-Ka'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_myUhtr_A51Y/SnWVogdEjOI/AAAAAAAAPiQ/o9MjIPCDE_s/s72-c/jug_logo_original_transparent.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-1230213002804593952</id><published>2009-07-29T06:10:00.000+02:00</published><updated>2009-07-29T06:10:00.397+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='git'/><title type='text'>Git Stash</title><content type='html'>After a &lt;a href="http://dlinsin.blogspot.com/2009/06/git-talk-jug-ka.html"&gt;great talk&lt;/a&gt; on &lt;a href="http://en.wikipedia.org/wiki/Git_(software)"&gt;Git&lt;/a&gt; at the &lt;a href="http://jug-ka.de"&gt;Java User Group Karlsruhe&lt;/a&gt; and a couple of &lt;a href="http://dlinsin.blogspot.com/search/label/git"&gt;other experiences&lt;/a&gt;, I started to really dig the Distributed Source Control Management (DSCM) notion and its advantages. A neat and quite helpful feature of Git is &lt;em&gt;stashing&lt;/em&gt;.&lt;br /&gt;&lt;br /&gt;The &lt;a href="http://www.kernel.org/pub/software/scm/git/docs/user-manual.html#interrupted-work"&gt;Git manual&lt;/a&gt; has as an exceptionally helpful description of &lt;em&gt;stashing&lt;/em&gt;. They call it "Temporarily setting aside work in progress".&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;While you are in the middle of working on something complicated, you find an unrelated but obvious and trivial bug. You would like to fix it before continuing. You can use &lt;a href="http://www.kernel.org/pub/software/scm/git/docs/git-stash.html"&gt;git-stash(1)&lt;/a&gt; to save the current state of your work, and after fixing the bug (or, optionally after doing so on a different branch and then coming back), unstash the work-in-progress changes.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;$ git stash save "work in progress for foo feature"&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;This command will save your changes away to the stash, and reset your working tree and the index to match the tip of your current branch. Then you can make your fix as usual.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;... edit and test ...&lt;br /&gt;$ git commit -a -m "blorpl: typofix"&lt;br /&gt;&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;After that, you can go back to what you were working on with git stash pop:&lt;br /&gt;&lt;br /&gt;&lt;em&gt;$ git stash pop&lt;/em&gt;&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Git's &lt;em&gt;stashing&lt;/em&gt; feature saved me quite some trouble a couple of days ago, while working on a side project. I developed a feature on branch "B" and had quite a big change-set, while someone found a bug on the stable branch, let's call it "A". &lt;br /&gt;&lt;br /&gt;Coming from SVN, I thought I had to check-in my changes in order to switch to Branch "A" and fix the bug. That's quite ugly, because it screws with your history of commits and even might break your builds. I know that there are other possibilities with SVN, like copying your changed files or &lt;a href="http://blog.jayfields.com/2008/02/using-patch-as-subversion-stash.html"&gt;creating a patch file&lt;/a&gt;. However, to me those all look like workarounds rather than solutions.&lt;br /&gt;&lt;br /&gt;So I simply &lt;em&gt;stashed&lt;/em&gt; my changes from branch "B" and got a clean branch with HEAD. I switched to branch "A" and was able to fix and commit the bug. After switching to branch "B" again, I simply &lt;em&gt;poped&lt;/em&gt; the changes from the &lt;em&gt;stash&lt;/em&gt;, as if I had never stopped working on the feature.  &lt;br /&gt;&lt;br /&gt;After working with Git for some time, I would really recommend people to switch to a DSCM. I'm not alone with this. SpringSource's DM server team &lt;a href="http://blog.springsource.com/2009/07/15/git-migration/"&gt;migrated&lt;/a&gt; their repositories to Git a couple of weeks ago and they didn't hear any complaints from the community.  No matter if you choose Git or Mercurial, the advantages of a DSCM really outweigh the &lt;a href="http://dlinsin.blogspot.com/2009/06/github-pain.html"&gt;initial costs&lt;/a&gt; of getting started.  &lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-1230213002804593952?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/1230213002804593952/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=1230213002804593952' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/1230213002804593952'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/1230213002804593952'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/07/git-stash.html' title='Git Stash'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-3135061459129563318</id><published>2009-07-13T06:00:00.000+02:00</published><updated>2009-07-13T06:16:42.617+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='karlsruhe'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='jug-ka'/><title type='text'>Jug-Ka Summer Stammtisch</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_myUhtr_A51Y/SAuD46fTQ0I/AAAAAAAAGaU/dObCE4Jx8H8/s1600-h/jug-logo_124x77.png"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://bp3.blogger.com/_myUhtr_A51Y/SAuD46fTQ0I/AAAAAAAAGaU/dObCE4Jx8H8/s320/jug-logo_124x77.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5191388009271083842" /&gt;&lt;/a&gt;Since we haven't had a Stammtisch of the &lt;a href="http://jug-ka.de"&gt;Java Users Group Karlsruhe&lt;/a&gt; for a while, I thought it's time to get together &lt;a href="http://dlinsin.blogspot.com/2009/03/jug-ka-stammtisch-30.html"&gt;again&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;We are gonna meet on &lt;a href="https://www.xing.com/events/319637"&gt;Wednesday, July 15th - 20:00&lt;/a&gt;, at a nice pub called &lt;a href="http://tinyurl.com/blzfzn"&gt;Litfass&lt;/a&gt;, near Marktplatz. For further information you can send an email to our &lt;a href="http://groups.google.com/group/jug-karlsruhe"&gt;Google Group&lt;/a&gt; or join us on &lt;a href="https://www.xing.com/net/jug-karlsruhe/"&gt;Xing&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-3135061459129563318?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/3135061459129563318/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=3135061459129563318' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/3135061459129563318'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/3135061459129563318'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/07/jug-ka-summer-stammtisch.html' title='Jug-Ka Summer Stammtisch'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp3.blogger.com/_myUhtr_A51Y/SAuD46fTQ0I/AAAAAAAAGaU/dObCE4Jx8H8/s72-c/jug-logo_124x77.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-4151301823886425333</id><published>2009-07-06T05:53:00.000+02:00</published><updated>2009-07-06T20:46:42.995+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><title type='text'>When Unchecked Exceptions Go Wrong</title><content type='html'>In the application I'm developing right now, we found a bug right before going live. A colleague of mine forgot to catch an unchecked Exception in a loop, which led the loop to exit, before iterating over each element. Bugs like this are so subtle and hard to track, that's why I think it was kind of my fault for not using a checked instead of an unchecked Exception.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;// must work all jobs&lt;br /&gt;private void workAllJobs() {&lt;br /&gt;  for(MyJob job : allJobs) {&lt;br /&gt;    job.work(); // throws RuntimeException&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Let me be clear, I don't want to engage into the checked vs. unchecked Exception war. I made &lt;a href="http://dlinsin-area51.blogspot.com/2009/01/how-to-internationalize-exceptions-ii.html"&gt;my point&lt;/a&gt; clear a couple of times, however I simply think I need to revise &lt;a href="http://dlinsin.blogspot.com/2008/01/wonderful-checked-exceptions.html"&gt;my position&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;I &lt;a href="http://dlinsin.blogspot.com/2009/02/aspect-is-part-of-your-implementation.html"&gt;used to think&lt;/a&gt; forcing a client of an API to catch an Exception was evil! That's why I usually design custom Exceptions to inherit from &lt;em&gt;RuntimeException&lt;/em&gt; and &lt;a href="http://dlinsin.blogspot.com/2008/06/does-your-api-docs-leave-users-in-dark.html"&gt;declare each and every case&lt;/a&gt; carefully in the javadocs of my API methods. I simply followed the &lt;a href="http://www.amazon.de/gp/product/0321356683?ie=UTF8&amp;tag=mytakeonthing-21&amp;linkCode=as2&amp;camp=1638&amp;creative=6742&amp;creativeASIN=0321356683"&gt;expert's advice&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Always declare checked exceptions individually, and document precisely the condition under which each one is thrown using the Javadoc @throws tag. .... While the language does not require programmers to declare unchecked exceptions that a method is capable of throwing, it is wise to document them as carefully as the checked Exceptions.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Unfortunately, the expert's advice didn't work in my co-worker's case. Should I say in most cases? He is with the sad majority of developers, that don't ready javadocs - not even on public API methods. Maybe I should have listened to &lt;a href="http://dlinsin.blogspot.com/2008/06/does-your-api-docs-leave-users-in-dark.html?showComment=1214837340000#c4776475304015970181"&gt;people telling me&lt;/a&gt; this:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;I kind of disagree with your example; documenting everything in every tiny detail may be a requirement for widely used public APIs (like the JDK), but IMHO a more relaxed approach is useful for the rest of us. Like "configuration by exception" is considered a good idea, there should also be "documentation by exception". That is, don't document every tiny detail but instead follow some higher level conventions. Many people do that intuitively, but unfortunately forget to document those conventions.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Like &lt;a href="http://unmaintainable.wordpress.com/"&gt;Matthias&lt;/a&gt;, my co-worker argued, that he thinks conventions should define what happens if e.g. you pass &lt;em&gt;null&lt;/em&gt; to a method or which Exceptions are being thrown from an API method. I do agree, conventions are a great tool to define this kind of behavior. The problem with conventions are: How do you get the whole team to agree on certain conventions and more important - how do you manage those convetions?&lt;br /&gt;&lt;br /&gt;Getting a team of strongly opinionated individuals to even agree on minor details, e.g. how to name interfaces, is almost impossible. Believe me, I have been there! After more than a year, we still can't agree on wether to use Eclipse's notation of &lt;em&gt;IService&lt;/em&gt; or my preference - simply &lt;em&gt;Service&lt;/em&gt;. I cannot imagine how hard it will be to agree on really important topics like &lt;a href="http://dlinsin.blogspot.com/2008/10/thoughts-on-api-design.html"&gt;passing &lt;em&gt;null&lt;/em&gt; to methods&lt;/a&gt; and wether to throw &lt;em&gt;IllegalArgumentException&lt;/em&gt; or &lt;em&gt;NullPointerException&lt;/em&gt; in that case. &lt;br /&gt;&lt;br /&gt;The second issue with conventions is how to manage them. Who is responsible to maintain those agreements? Do you put those conventions on a wiki page, which nobody updates and nobody reads anyways? I argue, that for us developers code and eventually the IDE is where we live, thus I think that's the place those conventions should live as well. Code is the common single point of truth for us and hence javadocs are in my opinion the best way to document any code related conventions. &lt;br /&gt;&lt;br /&gt;I guess you can see where this is going. It's a trade-off, as always in software development. However, it's a trade-off with a catch. I think it's simply not enough to "just have conventions", they need to be documented and managed in the code and my weapon of choice for doing that is javadocs. &lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-4151301823886425333?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/4151301823886425333/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=4151301823886425333' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/4151301823886425333'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/4151301823886425333'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/07/when-unchecked-exceptions-go-wrong.html' title='When Unchecked Exceptions Go Wrong'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-3523813092165285099</id><published>2009-07-03T07:27:00.001+02:00</published><updated>2009-07-03T07:27:07.980+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='hibernate'/><category scheme='http://www.blogger.com/atom/ns#' term='out&apos;n about'/><title type='text'>Java Forum Stuttgart</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://picasaweb.google.com/lh/photo/RBMplqKi5UjZWGdYPLExKg?authkey=Gv1sRgCKXY3MiLxprXDQ&amp;feat=embedwebsite"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh3.ggpht.com/_myUhtr_A51Y/Sk2P0Fh15KI/AAAAAAAAPMk/qzJ79c42r_U/s288/logo.jpg" border="0" alt=""id="" /&gt;&lt;/a&gt;Yesterday, I was &lt;a href="http://picasaweb.google.com/dlinsin/JavaForumStuttgart2009#"&gt;attending&lt;/a&gt; the &lt;a href="http://www.java-forum-stuttgart.de/"&gt;12. Java Forum Stuttgart&lt;/a&gt; and compared to the &lt;a href="http://picasaweb.google.com/dlinsin/JavaforumStuttgart2005#"&gt;last time&lt;/a&gt; I went, which was &lt;a href="http://dlinsin-archives.blogspot.com/2005/07/java-forum-stuttgart.html"&gt;in 2005&lt;/a&gt;, the event has really fledged. &lt;br /&gt;&lt;br /&gt;The quality of the sessions was good and the hallway was filled with &lt;a href="http://www.olivergierke.de/"&gt;familiar faces&lt;/a&gt;. The theme of the conference for me, was JPA and O/R mappers. There were a couple of really &lt;a href="http://rockingcode.blogspot.com/"&gt;great talks&lt;/a&gt; about it and it seems like there's a lot of going on in that area at the moment. &lt;br /&gt;&lt;br /&gt;I'm definitely gone be there again next year and I would love for the conference to be extended to a 2-3 days event. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-3523813092165285099?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/3523813092165285099/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=3523813092165285099' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/3523813092165285099'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/3523813092165285099'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/07/java-forum-stuttgart.html' title='Java Forum Stuttgart'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_myUhtr_A51Y/Sk2P0Fh15KI/AAAAAAAAPMk/qzJ79c42r_U/s72-c/logo.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-8918968127412450371</id><published>2009-06-29T06:03:00.000+02:00</published><updated>2009-06-29T06:03:01.261+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='spring'/><category scheme='http://www.blogger.com/atom/ns#' term='out&apos;n about'/><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>Wrapping-up Jazoon09</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://picasaweb.google.com/lh/photo/3X27U9TvyYhi8rWZLnecAw?authkey=Gv1sRgCKXY3MiLxprXDQ&amp;feat=embedwebsite"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh4.ggpht.com/_myUhtr_A51Y/SjqfDd3Q9HI/AAAAAAAAN_Y/-cN2utCGfFg/s288/jazoon-logo.gif" border="0" alt=""id="" /&gt;&lt;/a&gt; I was &lt;a href="http://dlinsin.blogspot.com/2009/06/jazoon-2009.html"&gt;attending Jazoon09&lt;/a&gt; in Zurich, Switzerland last week. As with &lt;a href="http://dlinsin.blogspot.com/2009/05/springone-2009-recap.html"&gt;springOne 2009&lt;/a&gt;, I wasn't really satisfied with this content of the conference. &lt;br /&gt;&lt;br /&gt;The problem was, that there were only a few really good speakers and thus only a couple of great talks. The keynotes were average, only &lt;a href="http://jazoon.com/en/conference/presentationdetails.html?type=sid&amp;detail=9280"&gt;Adrian Colyer's presentation&lt;/a&gt; on the last day was standing out. Even James Gosling's presentation or Danny Cowards talk were both not that exciting.&lt;br /&gt;&lt;br /&gt;However, instead of telling you what sucked, I rather want to highlight, that the organization of the conference was great! The food was really good! There was an vegetarian alternative every day and the coffee was delicious. Jazoon took place in a movie theater, just like &lt;a href="http://devoxx.com/display/DV09/Home"&gt;Devoxx&lt;/a&gt;, thus it was easy to get there by train and the seats were nice and comfy. Kudos to the organizers.&lt;br /&gt;&lt;br /&gt;There were a couple of great talks I want to highlight: Neal Ford's "&lt;a href="http://jazoon.com/en/conference/presentations/istr/9440"&gt;Smithying in the 21st Century&lt;/a&gt;" as well as Ed Burns' "&lt;a href="http://jazoon.com/en/conference/presentationdetails.html?type=sid&amp;detail=8000"&gt;Secrets of the Rockstar Programmers&lt;/a&gt;" were really awesome and I can only commend listening to them, in case they come up on the schedule of another conference. &lt;br /&gt;&lt;br /&gt;I'm not too sure if I'll come back to Jazoon next year, although I think it could be a really cool conference and a great alternative to Devoxx, which takes place in Antwerp. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-8918968127412450371?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/8918968127412450371/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=8918968127412450371' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/8918968127412450371'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/8918968127412450371'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/06/wrapping-up-jazoon09.html' title='Wrapping-up Jazoon09'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_myUhtr_A51Y/SjqfDd3Q9HI/AAAAAAAAN_Y/-cN2utCGfFg/s72-c/jazoon-logo.gif' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-6950717746196154845</id><published>2009-06-21T10:58:00.000+02:00</published><updated>2009-06-21T11:02:21.163+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='out&apos;n about'/><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><category scheme='http://www.blogger.com/atom/ns#' term='scala'/><title type='text'>Jazoon 2009</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://picasaweb.google.com/lh/photo/3X27U9TvyYhi8rWZLnecAw?authkey=Gv1sRgCKXY3MiLxprXDQ&amp;feat=embedwebsite"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh4.ggpht.com/_myUhtr_A51Y/SjqfDd3Q9HI/AAAAAAAAN_Y/-cN2utCGfFg/s288/jazoon-logo.gif" border="0" alt=""id="" /&gt;&lt;/a&gt; I'll be at &lt;a href="http://jazoon.com/en.html"&gt;Jazoon 2009&lt;/a&gt;, which is starting next week on Monday with a &lt;a href="http://jazoon.com/en/workshop5.html"&gt;Glassfish Day&lt;/a&gt;. It's going to be my first time in Zurich this year and I'm really excited, because they have a great line up of &lt;a href="http://jazoon.com/en/conference/speakers.html"&gt;speakers&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;Among others, there's gonna be the father of Java himself &lt;a href="http://jazoon.com/en/conference/speakerdetails.html?type=author&amp;detail=James_Gosling"&gt;Mr. James Gosling&lt;/a&gt;. For me it's always something special to listen to a keynote of him. &lt;br /&gt;&lt;br /&gt;There's gonna be sessions on a wide range of topics, e.g. "&lt;a href="http://jazoon.com/en/conference/presentations/tl/8760"&gt;Concurrency and Performance Reloaded&lt;/a&gt;" with Kirk Pepperdine, "&lt;a href="http://jazoon.com/en/conference/presentations/tl/9140"&gt;Development for the iPhone&lt;/a&gt;" or "&lt;a href="http://jazoon.com/en/conference/presentations/tl/6421"&gt;Design Patterns in Dynamic Languages&lt;/a&gt;" with Neal Ford, to name only a few of the first conference day.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-6950717746196154845?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/6950717746196154845/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=6950717746196154845' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/6950717746196154845'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/6950717746196154845'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/06/jazoon-2009.html' title='Jazoon 2009'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_myUhtr_A51Y/SjqfDd3Q9HI/AAAAAAAAN_Y/-cN2utCGfFg/s72-c/jazoon-logo.gif' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-9065515322354979200</id><published>2009-06-15T05:52:00.000+02:00</published><updated>2009-06-15T05:52:04.596+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='karlsruhe'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='jug-ka'/><category scheme='http://www.blogger.com/atom/ns#' term='git'/><title type='text'>Git talk @ JUG-Ka</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_myUhtr_A51Y/SAuD46fTQ0I/AAAAAAAAGaU/dObCE4Jx8H8/s1600-h/jug-logo_124x77.png"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://bp3.blogger.com/_myUhtr_A51Y/SAuD46fTQ0I/AAAAAAAAGaU/dObCE4Jx8H8/s320/jug-logo_124x77.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5191388009271083842" /&gt;&lt;/a&gt;This coming Wednesday &lt;a href="https://www.xing.com/profile/Samuel_Mellert"&gt;Samuel Mellert&lt;/a&gt; (1&amp;1 Internet AG) is going talk about "&lt;em&gt;Distributed SCM with Git&lt;/em&gt;" at the &lt;a href="http://jug-ka.de/"&gt;Java User Group Karlsruhe&lt;/a&gt;. I'm a &lt;a href="http://dlinsin.blogspot.com/2009/06/github-pain.html"&gt;newbie&lt;/a&gt; when it comes to distributed version control and especially &lt;a href="http://en.wikipedia.org/wiki/Git_(software)"&gt;Git&lt;/a&gt;, that's why I'm really looking forward to this meet-up. The talk starts at 7:15pm and takes place at &lt;a href="http://maps.google.de/maps?f=q&amp;hl=de&amp;geocode=&amp;q=Am+Fasanengarten+5,+Karlsruhe&amp;jsv=107&amp;sll=49.031752,8.454051&amp;sspn=0.008666,0.020685&amp;ie=UTF8&amp;t=h&amp;z=16&amp;iwloc=addr"&gt;University of Karlsruhe&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.jetbrains.net"&gt;JetBrains&lt;/a&gt; and &lt;a href="http://www.zeroturnaround.com"&gt;ZeroTurnaround&lt;/a&gt; are each giving away a free license for their products. We are gonna have a lottery, so if you are interested in getting a free copy of &lt;a href="http://www.jetbrains.net/confluence/display/IDEADEV/Home"&gt;IntelliJ&lt;/a&gt; or &lt;a href="http://www.zeroturnaround.com/javarebel/"&gt;JavaRebel&lt;/a&gt;, send me an email to participate in the draw. The winners are gonna be announced at the end of the talk.  &lt;br /&gt;&lt;br /&gt;To get the latest updates of the JUG-Ka sign up for our &lt;a href="http://groups.google.com/group/jug-karlsruhe"&gt;Google Group&lt;/a&gt; or join us on &lt;a href="https://www.xing.com/net/jug-karlsruhe/"&gt;XING&lt;/a&gt;. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-9065515322354979200?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/9065515322354979200/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=9065515322354979200' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/9065515322354979200'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/9065515322354979200'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/06/git-talk-jug-ka.html' title='Git talk @ JUG-Ka'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp3.blogger.com/_myUhtr_A51Y/SAuD46fTQ0I/AAAAAAAAGaU/dObCE4Jx8H8/s72-c/jug-logo_124x77.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-6072110514304406252</id><published>2009-06-08T05:55:00.001+02:00</published><updated>2009-06-08T05:56:55.898+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='git'/><title type='text'>Git(Hub) Pain</title><content type='html'>This is a little bit of a rant post, so if you are not interested in my subjective experience with &lt;a href="http://www.github.com"&gt;GitHub&lt;/a&gt;, you should stop reading now.&lt;br /&gt;&lt;br /&gt;A couple of weeks ago I started &lt;a href="http://dlinsin.blogspot.com/2009/04/using-reflection-in-unit-tests.html"&gt;playing&lt;/a&gt; with &lt;a href="http://en.wikipedia.org/wiki/Git_(software)"&gt;Git&lt;/a&gt;. First of all, I love the notion of distributed version control, although it took me a while to wrap my head around it.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Every Git working directory is a full-fledged repository with complete history and full revision tracking capabilities, not dependent on network access or a central server.&lt;/blockquote&gt;&lt;br /&gt;I found it quite hard to explain Git or distributed version control in general. The best explanation I heard so far comes from &lt;a href="http://www.joelonsoftware.com/"&gt;Joel Spolsky&lt;/a&gt;, who talked about it on the &lt;a href="http://blog.stackoverflow.com/2009/01/podcast-36/"&gt;stackoverflow podcast&lt;/a&gt;. It goes something like this: &lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;You are working on a local clone of the full repository and a commit is basically a set of changes applied to a version of that repository.&lt;/blockquote&gt;&lt;br /&gt;I have a little side-project with 2 other developers and we decided to use Git as our version control system. One of the reasons why we choose Git, was the limitations we saw in our daily work with SVN and the more important reason - isn't it just cool to try something new?&lt;br /&gt;&lt;br /&gt;One way to let others participate in your development, is to have a &lt;a href="https://github.com/dlinsin"&gt;repository on GitHub&lt;/a&gt;. Others can come in and get a local copy of your work, contribute to it and merge it back into your original source tree later on. Everything is accessible and essentially open source. However, if you pay GitHub some money, you can have private repositories with contributors - that's what we did.&lt;br /&gt;&lt;br /&gt;The move from SVN to Git wasn't easy. We had to adopt our style of working with version control, because we ran into trouble pretty early. I guess we first needed to grok the mindset of branching and merging, before we could really leverage the advantages of distributed version control.&lt;br /&gt;&lt;br /&gt;Unfortunately GitHub didn't help getting started. I don't know if it's because of &lt;a href="http://code.google.com/p/egit/"&gt;broken tools&lt;/a&gt; or if it was GitHub's fault, but we managed to damage one of our branches. When I tried to pull from the branch I got the following error message:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;crusty:doublemill dlinsin$ git pull origin master &lt;br/&gt;error: Could not read 9b82bba67693db3b47189aefea07ec4e1d7be38e &lt;br/&gt;fatal: bad tree object 9b82bba67693db3b47189aefea07ec4e1d7be38e &lt;br/&gt;remote: Counting objects: 13, done. remote: Compressing objects: 100% (8/8), done. &lt;br/&gt;error: waitpid (async) failed &lt;br/&gt;fatal: git-upload-pack: aborting due to possible repository corruption on the remote side. &lt;br/&gt;remote: Total 8 (delta 0), reused 0 (delta 0) &lt;br/&gt;remote: aborting due to possible repository corruption on the remote side. &lt;br/&gt;Unpacking objects: 100% (8/8), done. &lt;br/&gt;error: waitpid (async) failed fatal: error in sideband demultiplexer&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Since I couldn't find clear information on the problem, I decided to open an &lt;a href="http://support.github.com/discussions/repos/804-i-cant-pull-from-my-repository-anymore"&gt;issue&lt;/a&gt; with GitHub support. Unfortunately after almost a week, I got a reply to my issue which was more than disappointing:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;The best thing to do would be to delete the repository and recreate it with a fresh git push.&lt;/blockquote&gt;&lt;br /&gt;I mean seriously? Is that the best they can do? Sure, it's the easiest way out for GitHub, but not the most customer friendliest way I can imagine. It's just so disappointing, because I like GitHub and I have no problem to pay for their service. In fact, I guess I would have &lt;a href="http://dlinsin.blogspot.com/2008/05/paying-for-software.html"&gt;paid them anyways&lt;/a&gt;, because they have a great product and deserve some support. I just don't like this kind of &lt;a href="http://www.imdb.com/title/tt0487831/quotes"&gt;"Have you tried turning it off and on again?"&lt;/a&gt; - attitude.&lt;br /&gt;&lt;br /&gt;However, even after all the trouble we had, I'm still glad we choose Git. I think distributed source control is the future and I would suggest anybody to have a look at it, before starting the next project with SVN.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-6072110514304406252?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/6072110514304406252/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=6072110514304406252' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/6072110514304406252'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/6072110514304406252'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/06/github-pain.html' title='Git(Hub) Pain'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-8198686122910162937</id><published>2009-05-28T13:07:00.001+02:00</published><updated>2009-05-28T13:07:30.302+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='scala'/><title type='text'>More JDK 7 Language Changes</title><content type='html'>A &lt;a href="http://dlinsin.blogspot.com/2009/03/jdk-7-language-changes-are-coined.html"&gt;couple of month ago&lt;/a&gt;, Joseph Darcy announced the first six new language features for JDK 7. Yesterday, he &lt;a href="http://blogs.sun.com/darcy/entry/project_coin_consideration_round_2"&gt;posted&lt;/a&gt; another 7 proposals, which Sun will take into further consideration:&lt;br /&gt;&lt;br /&gt;1. &lt;a href="http://mail.openjdk.java.net/pipermail/coin-dev/2009-March/000905.html"&gt;Byte and Short Integer Literal Suffixes&lt;/a&gt; by Bruce Chapman&lt;br /&gt;&lt;br /&gt;2. &lt;a href="http://mail.openjdk.java.net/pipermail/coin-dev/2009-March/000929.html"&gt;Binary Literals&lt;/a&gt; by Derek Foster&lt;br /&gt;&lt;br /&gt;3. &lt;a href="http://mail.openjdk.java.net/pipermail/coin-dev/2009-April/001628.html"&gt;Underscores in numbers&lt;/a&gt; by Derek Foster&lt;br /&gt;&lt;br /&gt;4. &lt;a href="http://mail.openjdk.java.net/pipermail/coin-dev/2009-March/001131.html"&gt;Language support for JSR 292&lt;/a&gt; by John Rose&lt;br /&gt;&lt;br /&gt;5. &lt;a href="http://mail.openjdk.java.net/pipermail/coin-dev/2009-March/001108.html"&gt;Indexing access syntax for Lists and Maps&lt;/a&gt; by Shams Mahmood Imam&lt;br /&gt;&lt;br /&gt;6. &lt;a href="http://mail.openjdk.java.net/pipermail/coin-dev/2009-March/001193.html"&gt;Collection Literals&lt;/a&gt; by Joshua Bloch&lt;br /&gt;&lt;br /&gt;7. &lt;a href="http://mail.openjdk.java.net/pipermail/coin-dev/2009-March/000869.html"&gt;Large arrays (revised)&lt;/a&gt; by James Lowden&lt;br /&gt;&lt;br /&gt;Joseph says:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;All the selected proposals were reviewed and judged to have favorable effort to reward ratios and to preserve the essential character of the language.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;I think &lt;em&gt;"Collection Literals"&lt;/em&gt; and &lt;em&gt;"Indexing access for Lists and Maps"&lt;/em&gt; will considerably improve my daily coding life as an average Joe developer. Furthermore, &lt;em&gt;"Underscores in numbers"&lt;/em&gt; could really improve working with numeric data. Here are a couple of code snippets from the proposals:&lt;br /&gt;&lt;br /&gt;&lt;em&gt;6. Collection Literals&lt;/em&gt;:&lt;br /&gt;&lt;pre&gt;final List&amp;lt;Integer&amp;gt; piDigits = [3, 1, 4, 1, 5];&lt;br /&gt;final Set&amp;lt;Integer&amp;gt; primes = { 2, 7, 31, 127, 8191};&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;compare that to the current way of creating a immutable List:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;final List&amp;lt;Integer&amp;gt; piDigits = &lt;br /&gt;Collections.unmodifiableList(Arrays.asList( 3, 1, 4, 1, 5));&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I really like this proposal, it'll make life so much easier. I use the notation above almost in every JUnit test to create some sample data in a &lt;em&gt;List&lt;/em&gt; or &lt;em&gt;Set&lt;/em&gt;. It's so much cleaner and more readable using the proposed syntax. &lt;br /&gt;&lt;br /&gt;&lt;em&gt;5. Indexing access for Lists and Maps&lt;/em&gt;&lt;br /&gt;&lt;pre&gt;List&amp;lt;String&amp;gt; l1 = ["a", "b", "c"];&lt;br /&gt;String firstElement = l1[0];&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I like this notation, it's concise and feels very natural. On the other hand, I don't really mind writing &lt;em&gt;List.get(0)&lt;/em&gt;. Actually, this reminds me of &lt;a href="http://dlinsin.blogspot.com/search/label/scala"&gt;Scala&lt;/a&gt;, where you can also say something like:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;val l1 = List("a", "b", "c")&lt;br /&gt;val firstElement = l1(0)&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;em&gt;3. Underscores in numbers&lt;/em&gt;&lt;br /&gt;&lt;pre&gt;int phoneNumber = 555_555_1212;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This proposal is supposed to increase readability and everything that accomplishes this should be added to Java.  However, I believe, you have to find the middle ground and only apply conciseness, where it makes sense and doesn't hurt readability, but that's another &lt;a href="http://dlinsin.blogspot.com/2007/12/reading-code-or-writing-code.html"&gt;story&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Most of these proposals are only nice additions and are not really essential. However, I think a lot of them are going to make my daily coding life more enjoyable. I'm excited to see them implemented and hope there will be a JSR soon.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-8198686122910162937?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/8198686122910162937/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=8198686122910162937' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/8198686122910162937'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/8198686122910162937'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/05/more-jdk-7-language-changes.html' title='More JDK 7 Language Changes'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-5036143898399658239</id><published>2009-05-25T06:03:00.000+02:00</published><updated>2009-08-08T14:13:40.590+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='google'/><category scheme='http://www.blogger.com/atom/ns#' term='gae'/><title type='text'>Make it Easy to Contribute</title><content type='html'>The past couple of weeks, I've been playing a lot with &lt;a href="http://www.restlet.org/"&gt;Restlet&lt;/a&gt;, a Java based &lt;a href="http://en.wikipedia.org/wiki/Representational_State_Transfer"&gt;REST&lt;/a&gt; framework. I like the it a lot, because it's easy to use and you can get it up and running in no time. &lt;br /&gt;&lt;br /&gt;The current version of Restlet is &lt;a href="http://www.restlet.org/documentation/1.1/firstResource"&gt;1.1&lt;/a&gt;, but when I compared it to the upcoming version &lt;a href="http://www.restlet.org/documentation/1.2/firstResource"&gt;1.2-M2&lt;/a&gt;. it was clear that I would start using that right away. It has built in &lt;a href="http://dlinsin.blogspot.com/2009/04/more-playing-with-gae.html"&gt;Google App Engine&lt;/a&gt; support and uses an Annotation driven approach, which increases testability and ease of use. This is one of the rare uses cases, where I think Annotations are perfectly suited, but I'm digressing. Overall, it was a natural choice to use the milestone build 1.2 right from the beginning. &lt;br /&gt;&lt;br /&gt;After a couple of weeks of coding against 1.2-M2, I started to encounter a couple of weird side effects. After testing the code against the current stable version, it was clear, that it must be a bug in the 1.2 milestone release. So I checked the &lt;a href="http://www.restlet.org/community/issues"&gt;Restlet site&lt;/a&gt;, which nicely describes the process of reporting bugs. &lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Before entering a new report, you should &lt;a href="http://restlet.tigris.org/issues/query.cgi"&gt;query&lt;/a&gt; the current issue database for similar open issues.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;As a decent developer, I followed the &lt;a href="http://restlet.tigris.org/issues/query.cgi"&gt;link to the issue tracker&lt;/a&gt; and saw this:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh3.ggpht.com/_myUhtr_A51Y/Shj_LaxJ4GI/AAAAAAAAN9k/9mCdZu_cHBg/srewed_tracker.png?imgmax=400" alt="srewed_tracker.png" border="0" align="center" /&gt;&lt;br /&gt;&lt;br /&gt;I was shocked to see this kind of interface! I know I'm a developer and I should love these kind of fine grained and detailed options to query an issues tracker, but that's too much! It's not the first time that I see this interface, &lt;a href="https://jugs-incubator.dev.java.net/issues/query.cgi"&gt;Java.net&lt;/a&gt; uses it too and I just think it's a &lt;a href="http://en.wiktionary.org/wiki/PITA"&gt;PITA&lt;/a&gt; to use! Compare it to &lt;a href="http://code.google.com/hosting/"&gt;google's code hosting&lt;/a&gt; issue tracker interface:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh5.ggpht.com/_myUhtr_A51Y/ShkCcFazSeI/AAAAAAAAN98/a2lFgyIzIIg/google_code_tracker.png?imgmax=400" alt="google_code_tracker.png" border="0" align="center" /&gt;&lt;br /&gt;&lt;br /&gt;It's worlds apart in terms of usability and ease of use! And the screenshot above even shows the "advanced" search mode. In most cases, you can find everything with a single search box - google like!&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;I think, if you want the community to interact and contribute to your project, make it as easy as possible for them. They are trying to help you!&lt;/strong&gt; We all know the problem of issues reported multiple times or reporters not taking time to correctly file an issue. However, every feedback is valuable and you can always ask for further and more detailed descriptions, if necessary.   &lt;br /&gt;&lt;br /&gt;Coming back to my bug in Restlet, after I managed to query the issue tracker, I saw that it had already been reported. So I decided to switch back to the current stable version. Unfortunately, I had to jump through a couple of hoops to make it work on the GAE, but that's another story altogether. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-5036143898399658239?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/5036143898399658239/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=5036143898399658239' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/5036143898399658239'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/5036143898399658239'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/05/make-it-easy-to-contribute.html' title='Make it Easy to Contribute'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_myUhtr_A51Y/Shj_LaxJ4GI/AAAAAAAAN9k/9mCdZu_cHBg/s72-c/srewed_tracker.png?imgmax=400' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-728055933961304526</id><published>2009-05-18T06:14:00.000+02:00</published><updated>2009-05-18T06:15:17.014+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='pics'/><title type='text'>Is JavaFX Really the Future?</title><content type='html'>&lt;img src="http://lh3.ggpht.com/_myUhtr_A51Y/Sg78Nu2mNNI/AAAAAAAAN80/qYcaVcqNWmY/javafx_java_net_poll.png?imgmax=800" alt="javafx_java_net_poll.png" border="0" width="516" height="261"/&gt;&lt;br /&gt;&lt;br /&gt;A &lt;a href="http://today.java.net/pub/pq/257"&gt;java.net poll&lt;/a&gt;, which took votes over the past week, shows that a lot of developers are not quite sure, if &lt;a href="http://javafx.com/"&gt;JavaFX&lt;/a&gt; is the way to go in Java UI space. The &lt;a href="http://weblogs.java.net/blog/editors/"&gt;"Editor's Daily Blog"&lt;/a&gt; tried to &lt;a href="http://weblogs.java.net/blog/editors/archives/2009/05/poll_result_how.html"&gt;blandish&lt;/a&gt; the numbers:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;The first thing to note is that more than 600 votes were cast. That means that this poll stimulated more community participation than any of the other recent polls. Does this matter? I think so. I think it suggests that, whatever may people think about the future of JavaFX, JavaFX is something that has captured their interest. People are following JavaFX.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;To be fair, I voted for &lt;em&gt;"It will endure as a secondary option"&lt;/em&gt;. I'm not too sure if JavaFX will take off in the future. There was a lot of fuzz about it at &lt;a href="http://dlinsin.blogspot.com/2009/01/java-7-jigsaw-and-missing-jsr.html"&gt;last year's Devoxx&lt;/a&gt;. I talked to a couple of people back then, but all of them had different opinions. Some of them liked it, other were sceptical. However, the general opinion was "We'll have to wait and see!".&lt;br /&gt;&lt;br /&gt;I rarely push out this kind of bash blog posts, but with &lt;a href="http://www.artima.com/weblogs/viewpost.jsp?thread=242077"&gt;Sun putting a lot of resources&lt;/a&gt; into JavaFX and this significant developer refusal, I think it's worth mentioning it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-728055933961304526?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/728055933961304526/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=728055933961304526' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/728055933961304526'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/728055933961304526'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/05/is-javafx-really-future.html' title='Is JavaFX Really the Future?'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_myUhtr_A51Y/Sg78Nu2mNNI/AAAAAAAAN80/qYcaVcqNWmY/s72-c/javafx_java_net_poll.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-8152933680638672410</id><published>2009-05-13T06:00:00.000+02:00</published><updated>2009-05-20T08:23:19.360+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><title type='text'>Handling Concurrency in Domain Models</title><content type='html'>I'm working on a project right now that has a rich domain model, which we plan to use in different environments. One group of consumers are mobile clients, the others are highly concurrent servers. We try to cater to both environments in terms of parallelization, as well as keeping the domain model free of technical details.&lt;br /&gt;&lt;br /&gt;When I design a domain model, I tend to keep it as technology independent as possible, that's probably the reason why I'm not the biggest fan of &lt;a href="http://dlinsin.blogspot.com/2007/04/guice-di-framework.html"&gt;Annotations&lt;/a&gt;. You kind of lock yourself into a certain type of technology and you probably introduce dependencies on 3rd party code. Overall I think you are polluting your core classes with details that don't belong there. &lt;br /&gt;&lt;br /&gt;One of those technical aspects I'm referring to is concurrency. You can either choose to prepare your domain model for a concurrent environment or leave it to the consumer of the core classes. Take a look at the following class as an example:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;public class Foo {&lt;br /&gt;  private Map&amp;lt;Long,Bar&amp;gt; bars;&lt;br/&gt;&lt;br /&gt;  public Foo() {&lt;br /&gt;    bars = new HashMap&amp;lt;Long,Bar&amp;gt;;&lt;br /&gt;  }&lt;br/&gt;&lt;br /&gt;  public void addBar(Long argId, Bar argBar) {&lt;br /&gt;    bars.put(argId, argBar);&lt;br /&gt;  }&lt;br/&gt;&lt;br /&gt;  public Map&amp;lt;Long,Bar&amp;gt; getBars() {&lt;br /&gt;    return bars;&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Obviously, this class is not meant to be used in multithreaded code. There are a couple of issues, which could lead to an unpredictable state of the &lt;em&gt;Collection&lt;/em&gt; bar. Here's another version:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;public class Foo {&lt;br /&gt;  private &lt;strong&gt;final&lt;/strong&gt; Map&amp;lt;Long,Bar&amp;gt; bars;&lt;br/&gt;&lt;br /&gt;  public Foo() {&lt;br /&gt;    bars = new HashMap&amp;lt;Long,Bar&amp;gt;;&lt;br /&gt;  }&lt;br/&gt;&lt;br /&gt;  public void addBar(Long argId, Bar argBar) {&lt;br /&gt;    bars.put(argId, argBar);&lt;br /&gt;  }&lt;br/&gt;&lt;br /&gt;  /**&lt;br /&gt;   * @return immutable Map implementation&lt;br /&gt;   */&lt;br /&gt;  public Map&amp;lt;Long,Bar&amp;gt; getBars() {&lt;br /&gt;    return &lt;strong&gt;Collections.immutableMap(bars)&lt;/strong&gt;;&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As you can see there are a few easy things, you can do to prepare this class for usage in a parallelized environment. In fact, &lt;a href="http://dlinsin.blogspot.com/2009/03/final-or-not-final.html"&gt;I think you should always do them&lt;/a&gt;, even if your code won't run in multiple threads. However, this code is not thread-safe either and you need to jump through a couple of more hoops until it is:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;public class Foo {&lt;br /&gt;  private final Map&amp;lt;Long,Bar&amp;gt; bars;&lt;br/&gt;&lt;br /&gt;  public Foo() {&lt;br /&gt;    bars = new HashMap&amp;lt;Long,Bar&amp;gt;;&lt;br /&gt;  }&lt;br/&gt;&lt;br /&gt;  public &lt;strong&gt;synchronized&lt;/strong&gt; void addBar(Long argId, Bar argBar) {&lt;br /&gt;    bars.put(argId, argBar);&lt;br /&gt;  }&lt;br/&gt;&lt;br /&gt;  /**&lt;br /&gt;   * @return immutable Map implementation&lt;br /&gt;   */&lt;br /&gt;  public &lt;strong&gt;synchronized&lt;/strong&gt; Map&amp;lt;Long,Bar&amp;gt; getBars() {&lt;br /&gt;    return Collections.unmodifiableMap(bars);&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I think this version gets pretty close and you could call it thread-safe. I say close, because a) I'm not &lt;a href="http://www.amazon.de/gp/product/0321349601?ie=UTF8&amp;tag=mytakeonthing-21&amp;linkCode=as2&amp;camp=1638&amp;creative=6742&amp;creativeASIN=0321349601"&gt;Brian Goetz&lt;/a&gt; and b) we don't know more about &lt;em&gt;Bar&lt;/em&gt;. However, we can be sure, that there shouldn't be any problems with the &lt;em&gt;Collection&lt;/em&gt; bars. You can add new &lt;em&gt;Bar&lt;/em&gt; instances and retrieve them concurrently without affecting the &lt;em&gt;Collection&lt;/em&gt; itself in any way.&lt;br /&gt;&lt;br /&gt;The price we paid for this is clarity. The code gets a bit more complicated. I know there are other ways to make the &lt;em&gt;Collection&lt;/em&gt; bar safely concurrent modifiable, e.g. using a &lt;em&gt;ConcurrentHashMap&lt;/em&gt;. The point is, that if you have a complex and fairly large domain class, making it thread-safe would mean littering it with all those technical details, &lt;a href="http://dlinsin.blogspot.com/2009/01/technical-and-business-aspects-in-one.html"&gt;mixing responsibilities&lt;/a&gt; and making it way more complicated to maintain.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;So where is the right place to handle concurrency?&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;While researching this topic, I found a great &lt;a href="http://stackoverflow.com/questions/234341/should-i-always-make-my-java-code-thread-safe-or-for-performance-reasons-do-it-o"&gt;discussion on stackoverflow&lt;/a&gt;, where &lt;a href="http://stackoverflow.com/users/7671/alex-miller"&gt;Alex Miller&lt;/a&gt; suggests the &lt;a href="http://stackoverflow.com/questions/234341/should-i-always-make-my-java-code-thread-safe-or-for-performance-reasons-do-it-o/235903#235903"&gt;following&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Start from the data. Decide which data is explicitly shared and protect it. If at all possible, encapsulate the locking with the data. Use pre-existing thread-safe concurrent collections. &lt;br /&gt;&lt;br /&gt;Whenever possible, use immutable objects. Make attributes final, set their values in the constructors. If you need to "change" the data consider returning a new instance. Immutable objects don't need locking.&lt;br /&gt;&lt;br /&gt;For objects that are not shared or thread-confined, do not spend time making them thread-safe.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;This doesn't answer my question directly, but it suggest to handle concurrency on the level of data. &lt;strong&gt;That means using the &lt;em&gt;java.util.concurrent&lt;/em&gt; classes in your domain model and only for data which is shared between threads.&lt;/strong&gt; &lt;br /&gt;&lt;br /&gt;So what I take away from all of this, is that, it isn't really a question of where to handle concurrency, but rather when. I think if you can minimize the technical pollution of your domain model to the data that needs it, you can contain the damage of clarity. With the &lt;em&gt;java.util.concurrent Collections&lt;/em&gt; you have an abstraction that hides the concurrency aspect quite nicely. I guess the combination of both is the way to go: &lt;strong&gt;Only optimize data that needs it, with a good abstraction, that hides the technical details.&lt;/strong&gt; &lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-8152933680638672410?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/8152933680638672410/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=8152933680638672410' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/8152933680638672410'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/8152933680638672410'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/05/handling-concurrency-domain-models.html' title='Handling Concurrency in Domain Models'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-1984650441697879584</id><published>2009-05-04T06:00:00.000+02:00</published><updated>2009-05-04T06:00:01.422+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='traveling'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='spring'/><title type='text'>springOne 2009 recap</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://lh5.ggpht.com/_myUhtr_A51Y/Ses-doM_DFI/AAAAAAAANq0/CsoTRBa6Ag0/springone.png?imgmax=800"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh5.ggpht.com/_myUhtr_A51Y/Ses-doM_DFI/AAAAAAAANq0/CsoTRBa6Ag0/springone.png?imgmax=400" border="0" alt=""id="" /&gt;&lt;/a&gt; &lt;a href="http://dlinsin.blogspot.com/2009/03/springone-2009.html"&gt;Last week&lt;/a&gt; I was at &lt;a href="http://europe.springone.com/europe-2009"&gt;springOne 2009&lt;/a&gt; in Amsterdam, Netherlands. Unfortunately the city left a bigger impression than the conference. There were a lot of people from SpringSource and quite a few &lt;a href="http://europe.springone.com/europe-2009/schedule/tuesday.jsp"&gt;interesting sessions&lt;/a&gt;, however I didn't get into the spring feeling that I was hoping for. &lt;br /&gt;&lt;br /&gt;The most outstanding announcement for me was something called &lt;a href="http://www.springsource.org/roo"&gt;"Spring Roo"&lt;/a&gt;. According to &lt;a href="http://blog.springsource.com/2009/05/01/roo-part-1/"&gt;Ben Alex&lt;/a&gt;, it's&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;...a sophisticated round-tripping code generator that makes it quicker and easier than you've ever imagined to create and evolve Spring applications&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;You can leverage it to create applications with technologies like Spring, JPA and Spring MVC, using a shell-based approach with tab completion, hints and great default behavior. A big part of the opening keynote was dedicated to Spring Roo, where Ben live coded a simple web-based voting application. You can already &lt;a href="http://dist.springframework.org/milestone/ROO/spring-roo-1.0.0.A1.zip"&gt;download&lt;/a&gt; an alpha release and check it out.&lt;br /&gt;&lt;br /&gt;Overall the keynotes were the most valuable sessions for me. Most of the others had a rather introductory character and were quite basic, which I think should not be the focus of such a conference. I'm a little disappointed, but nevertheless, I hope next year will be better. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-1984650441697879584?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/1984650441697879584/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=1984650441697879584' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/1984650441697879584'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/1984650441697879584'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/05/springone-2009-recap.html' title='springOne 2009 recap'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_myUhtr_A51Y/Ses-doM_DFI/AAAAAAAANq0/CsoTRBa6Ag0/s72-c/springone.png?imgmax=400' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-1875513552928976163</id><published>2009-04-27T10:35:00.000+02:00</published><updated>2009-04-28T08:51:14.951+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='quality'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>Using Reflection in Unit Tests II</title><content type='html'>A couple of days ago, I &lt;a href="http://dlinsin.blogspot.com/2009/04/using-reflection-in-unit-tests.html"&gt;blogged&lt;/a&gt; about the pros and cons of using Reflection in unit tests. Thanks to &lt;a href="http://dlinsin.blogspot.com/2009/04/using-reflection-in-unit-tests.html?showComment=1240402320000#c8890054256159297582"&gt;Samuel&lt;/a&gt;, who gave me a pointer to "&lt;a href="http://code.google.com/p/powermock"&gt;PowerMock&lt;/a&gt;", I don't have to deal with the Reflection API anymore. &lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;PowerMock is a Java framework that allows you to unit test code normally regarded as untestable. &lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;To be more precise, PowerMock takes away the annoyance of dealing with the Reflection API. In case of the &lt;a href="http://github.com/dlinsin/area51/blob/59dd5f8fc96799bf6d93539675f299e0a8ebec4c/ReflectionJUnitSample/src/test/java/de/linsin/sample/reflectionjunit/domain/ContractTest.java"&gt;example&lt;/a&gt; presented in my previous post, instead of this code:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;@Test&lt;br /&gt;public void test_Calculate_Quote() throws Exception {&lt;br /&gt;    Contract classUnderTest = new Contract(1L);&lt;br /&gt;   &lt;strong&gt; Method calcMethod = getMethodOfClass(Contract.class, "calculateQuote");&lt;br /&gt;    BigDecimal quote = (BigDecimal) calcMethod.invoke(classUnderTest, new DateMidnight(1982, 4, 27));&lt;/strong&gt;&lt;br /&gt;    assertEquals(new BigDecimal("7.79"), quote.setScale(2, RoundingMode.FLOOR));&lt;br /&gt;}&lt;br/&gt;&lt;br /&gt;&lt;strong&gt;private Method getMethodOfClass(Class&lt;?&gt; argClass, String argMethodName) {&lt;br /&gt;    Method[] methods = argClass.getDeclaredMethods();&lt;br /&gt;    for (Method method : methods) {&lt;br /&gt;        if (method.getName().equals(argMethodName)) {&lt;br /&gt;            method.setAccessible(true);&lt;br /&gt;            return method;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    throw new NoSuchMethodError("couldn't find " + argMethodName + " on class " + argClass);&lt;br /&gt;}&lt;br /&gt;&lt;/strong&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;you simply write this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;@Test&lt;br /&gt;public void test_Calculate_Quote() throws Exception {&lt;br /&gt;    Contract classUnderTest = new Contract(1L);&lt;br /&gt;   &lt;strong&gt; BigDecimal quote = (BigDecimal) Whitebox.invokeMethod(classUnderTest, "calculateQuote", new DateMidnight(1982, 4, 27));&lt;/strong&gt;&lt;br /&gt;    assertEquals(new BigDecimal("7.79"), quote.setScale(2, RoundingMode.FLOOR));&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I like the approach of hiding the complexity of the Reflection API behind an abstraction and in case of &lt;a href="http://en.wikipedia.org/wiki/White_box_testing"&gt;&lt;em&gt;Whitebox&lt;/em&gt;&lt;/a&gt; it's even named perfectly. But accessing private methods is not the only thing PowerMock lets you do. In addition to much more you can set private fields, invoke hidden constructors and mock static methods. A feature that I really like is &lt;a href="http://code.google.com/p/powermock/wiki/MockConstructor"&gt;mocking final classes or methods&lt;/a&gt;, it can be quite helpful, when you are dealing with JDK classes like &lt;a href="http://java.sun.com/javase/6/docs/api/java/io/File.html"&gt;java.io.File&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;I think PowerMock is a great extention to &lt;a href="http://easymock.org/"&gt;EasyMock&lt;/a&gt; and it should go into every developer's tool box. It's definitely going straight into mine.  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-1875513552928976163?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/1875513552928976163/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=1875513552928976163' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/1875513552928976163'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/1875513552928976163'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/04/using-reflection-in-unit-tests-ii.html' title='Using Reflection in Unit Tests II'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-1929405297185814632</id><published>2009-04-21T17:30:00.000+02:00</published><updated>2009-04-21T18:00:06.131+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='traveling'/><category scheme='http://www.blogger.com/atom/ns#' term='spring'/><title type='text'>SpringOne 2009</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://lh5.ggpht.com/_myUhtr_A51Y/Ses-doM_DFI/AAAAAAAANq0/CsoTRBa6Ag0/springone.png?imgmax=800"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh5.ggpht.com/_myUhtr_A51Y/Ses-doM_DFI/AAAAAAAANq0/CsoTRBa6Ag0/springone.png?imgmax=400" border="0" alt=""id="" /&gt;&lt;/a&gt; Next week, starting on the 27th of April there's gonna be &lt;a href="http://europe.springone.com/europe-2009"&gt;SpringOne 2009&lt;/a&gt;. That's 3 days straight and full blown intensive Spring!&lt;br /&gt;&lt;br /&gt;&lt;a href="http://dlinsin.blogspot.com/2007/06/back-from-springone.html"&gt;Unlike&lt;/a&gt; the &lt;a href="http://dlinsin.blogspot.com/2008/06/springone08exit.html"&gt;last&lt;/a&gt; 2 years, the conference is not going to be held in Antwerp, Belgium - instead it'll take place in Amsterdam, Netherlands. I think it's a nice change. I've never been to Amsterdam before and I even added a couple of days to my trip to explore the city with my wife.  &lt;br /&gt;&lt;br /&gt;Looking at the first day, there's a bunch of really interesting sessions like "&lt;a href="http://europe.springone.com/europe-2009/presentation/Performance+Tuning+for+Apache+Tomcat"&gt;Performance Tuning for Apache Tomcat&lt;/a&gt;", "&lt;a href=""&gt;New Features in Spring 3.0&lt;/a&gt;" or "&lt;a href="http://europe.springone.com/europe-2009/presentation/Extreme+Productivity+in+Application+Development"&gt;Extreme Productivity in Application Development&lt;/a&gt;". &lt;br /&gt;&lt;br /&gt;I'm really excited to feel some Spring spirit again... &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-1929405297185814632?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/1929405297185814632/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=1929405297185814632' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/1929405297185814632'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/1929405297185814632'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/03/springone-2009.html' title='SpringOne 2009'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_myUhtr_A51Y/Ses-doM_DFI/AAAAAAAANq0/CsoTRBa6Ag0/s72-c/springone.png?imgmax=400' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-4238213274970756148</id><published>2009-04-19T05:43:00.000+02:00</published><updated>2009-04-20T05:41:17.848+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='quality'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>Using Reflection in Unit Tests</title><content type='html'>A couple of days ago I watched a &lt;a href="http://www.infoq.com/news/2009/04/10-Ways-to-Better-Code-Neal-Ford"&gt;presentation&lt;/a&gt; on &lt;a href="http://www.nealford.com/my/bio.htm"&gt;Neal Ford&lt;/a&gt;'s book &lt;a href="http://www.amazon.de/gp/product/0596519788?ie=UTF8&amp;tag=mytakeonthing-21&amp;linkCode=as2&amp;camp=1638&amp;creative=6742&amp;creativeASIN=0596519788"&gt;"The Productive Programmer"&lt;/a&gt;. He talked about 10 ways to improve your code. One thing, which I found quite innovative, was leveraging &lt;a href="http://java.sun.com/docs/books/tutorial/reflect/index.html"&gt;Java Reflection &lt;/a&gt;to test non-accessible code. &lt;br /&gt;&lt;br /&gt;With non-accessible code, I mean private, package-private or protected methods and constructors of your domain or service classes. If you are a test addict like me, you've probably made a method public or protected every now and then, even though it shouldn't be, simply so that you could access it from your test code. Using Reflection instead, is a simple and easy solution, I have never given a thought before. &lt;br /&gt;&lt;br /&gt;I came up with a little sample, which you can &lt;a href="http://download.linsin.de/ReflectionJUnitSample.tar"&gt;download&lt;/a&gt; or check out on &lt;a href="http://github.com/dlinsin/area51/tree/aaaa45027fa8dd41171eed94a1a548d29f93abf5/ReflectionJUnitSample"&gt;github&lt;/a&gt;. Take a look at the following &lt;a href="http://github.com/dlinsin/area51/blob/aaaa45027fa8dd41171eed94a1a548d29f93abf5/ReflectionJUnitSample/src/main/java/de/linsin/sample/reflectionjunit/domain/Contract.java"&gt;class&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;public class Contract {&lt;br /&gt;    private static final Double RATE = 0.3;&lt;br /&gt;    private final Long contractId;&lt;br /&gt;    private BigDecimal quote;&lt;br /&gt;    private Customer owner;&lt;br/&gt;&lt;br /&gt;    public Contract(Long argContractId) {&lt;br /&gt;        contractId = argContractId;&lt;br /&gt;        quote = BigDecimal.ZERO;&lt;br /&gt;    }&lt;br/&gt;&lt;br /&gt;    /** package */void setOwner(Customer argOwner) {&lt;br /&gt;        owner = argOwner;&lt;br /&gt;        quote = calculateQuote(argOwner.getBirthday());&lt;br /&gt;    }&lt;br/&gt;&lt;br /&gt;    &lt;strong&gt;private BigDecimal calculateQuote(DateMidnight argBirtday) {&lt;br /&gt;        Years ageInYears = Years.yearsBetween(argBirtday, new DateMidnight());&lt;br /&gt;        return new BigDecimal(RATE).multiply(new BigDecimal(ageInYears.getYears()));&lt;br /&gt;    }&lt;/strong&gt;&lt;br /&gt;    // other stuff omitted&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In order to test the &lt;em&gt;calculateQuote&lt;/em&gt; method in isolation, I would have probably introduced a &lt;em&gt;QuoteCalculator&lt;/em&gt; class. It would be in charge of the business logic and used by the &lt;em&gt;Contract&lt;/em&gt; class. If you need the logic at multiple places in your code, that's a great solution. If you only need it in &lt;em&gt;Contract&lt;/em&gt;, it's quite tedious and I would have probably made &lt;em&gt;calculateQuote&lt;/em&gt; accessible from the test class. However, I used Reflection instead to solve the problem and it &lt;a href="http://github.com/dlinsin/area51/blob/aaaa45027fa8dd41171eed94a1a548d29f93abf5/ReflectionJUnitSample/src/test/java/de/linsin/sample/reflectionjunit/domain/ContractTest.java"&gt;works&lt;/a&gt; quite nicely:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;@Test&lt;br /&gt;public void test_Calculate_Quote() throws Exception {&lt;br /&gt;    Contract classUnderTest = new Contract(1L);&lt;br /&gt;   &lt;strong&gt; Method calcMethod = getMethodOfClass(Contract.class, "calculateQuote");&lt;br /&gt;    BigDecimal quote = (BigDecimal) calcMethod.invoke(classUnderTest, new DateMidnight(1982, 4, 27));&lt;/strong&gt;&lt;br /&gt;    assertEquals(new BigDecimal("7.79"), quote.setScale(2, RoundingMode.FLOOR));&lt;br /&gt;}&lt;br/&gt;&lt;br /&gt;private Method getMethodOfClass(Class&lt;?&gt; argClass, String argMethodName) {&lt;br /&gt;    Method[] methods = argClass.getDeclaredMethods();&lt;br /&gt;    for (Method method : methods) {&lt;br /&gt;        if (method.getName().equals(argMethodName)) {&lt;br /&gt;            method.setAccessible(true);&lt;br /&gt;            return method;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    throw new NoSuchMethodError("couldn't find " + argMethodName + " on class " + argClass);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As you can see the &lt;em&gt;method.setAccessible(true);&lt;/em&gt; is doing the trick to enable access from the test to the domain object. Instead of the actual instance of &lt;em&gt;Contract&lt;/em&gt;, we use the &lt;em&gt;Method&lt;/em&gt; instance to invoke the private call. Everything else is plain test code, as you know it. &lt;br /&gt;&lt;br /&gt;Of course, you could argue to make the &lt;em&gt;calculateQuote&lt;/em&gt; method package-private or protected and put the test class into the same package. In fact &lt;a href="http://www.artima.com/consulting.html"&gt;Bill Venners&lt;/a&gt; wrote about that in one of is &lt;a href="http://www.artima.com/suiterunner/privateP.html"&gt;articles&lt;/a&gt;, suggesting that testing private methods is bad practice anyways. I tend to agree and I guess the example above is an exception. However, using Reflection is not necessarily limited to testing private methods. You might need access to an object to manipulate it's internal state, in order to test it thoroughly and the best tool for that job is probably Reflection.&lt;br /&gt;&lt;br /&gt;I think it's a neat idea to use Reflection in unit tests. Used with caution, it can be really helpful and is definitely better than &lt;a href="http://www.amazon.de/gp/product/0974514012?ie=UTF8&amp;tag=mytakeonthing-21&amp;linkCode=as2&amp;camp=1638&amp;creative=6742&amp;creativeASIN=0974514012"&gt;breaking encapsulation for testing&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-4238213274970756148?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/4238213274970756148/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=4238213274970756148' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/4238213274970756148'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/4238213274970756148'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/04/using-reflection-in-unit-tests.html' title='Using Reflection in Unit Tests'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-4273703015510583715</id><published>2009-04-16T05:33:00.000+02:00</published><updated>2009-08-08T14:13:55.241+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='google'/><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><category scheme='http://www.blogger.com/atom/ns#' term='gae'/><title type='text'>More Playing with GAE</title><content type='html'>In my &lt;a href="http://dlinsin.blogspot.com/2009/04/playing-with-google-app-engine.html"&gt;previous blog&lt;/a&gt; about the Google App Engine (GAE), I wrote about the problems I had encountered using JPA and Maven. In this post I want to share how using Groovy on the GAE worked out and what my impressions are on the scalability of the platform.&lt;br /&gt;&lt;br /&gt;Inspired by SpringSource's &lt;a href="http://blog.springsource.com/2009/04/07/write-your-google-app-engine-applications-in-groovy/"&gt;blog&lt;/a&gt; on Groovy with the GAE and &lt;a href="http://dlinsin.blogspot.com/2008/05/does-ide-support-influence-your.html"&gt;after almost a year&lt;/a&gt;, I decided to do some Groovy coding. I wrote a small &lt;a href="http://groovy.codehaus.org/Groovlets"&gt;Groovlet&lt;/a&gt;, which uses the JPA to fetch entities from persistent storage. Again, I'm quite astonished of how smooth coding can be:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;import javax.persistence.*&lt;br /&gt;import java.util.List&lt;br /&gt;import de.linsin.sample.gae.domain.User &lt;br /&gt;import de.linsin.sample.gae.EMF&lt;br/&gt; &lt;br /&gt;EntityManager em = EMF.getInstance().createEntityManager()&lt;br /&gt;Query query = em.createQuery("SELECT u FROM " + User.class.getName() + " u")&lt;br /&gt;List&amp;lt;User&amp;gt; users = query.getResultList()&lt;br/&gt;&lt;br /&gt;for (User user : users) {&lt;br /&gt;  out.println(user.getName() + " " + user.getLastname() + "&amp;lt;br/&amp;gt;")&lt;br /&gt;}&lt;br&gt;&lt;br /&gt;em.close();&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;To be fair, I have no Exception handling in this code and there is no &lt;em&gt;finally&lt;/em&gt; making sure the &lt;em&gt;EntitManager&lt;/em&gt; is closed at the end. Yet, it feels like Java without the clutter.&lt;br /&gt;&lt;br /&gt;To get this working on the GAE, all you need to do is drop the &lt;em&gt;groovy-all.jar&lt;/em&gt; into the &lt;em&gt;WEB-INF/lib&lt;/em&gt; folder of your WAR file and add the &lt;em&gt;GroovyServlet&lt;/em&gt; to your &lt;em&gt;&lt;a href="http://github.com/dlinsin/area51/blob/b0be57eb58ac1d602c49aa544df84ba959eaea3c/gae/src/main/webapp/WEB-INF/web.xml"&gt;web.xml&lt;/a&gt;&lt;/em&gt;. You add your Groovlet to a groovy folder under &lt;em&gt;WEB-INF&lt;/em&gt; and access it like a good old Servlet in your browser e.g. &lt;a href="http://dlinsin-area51.appspot.com/HelloWorld.groovy"&gt;http://dlinsin-area51.appspot.com/HelloWorld.groovy&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Unfortunately there is one major drawback, which makes developing Groovy for GAE a real pain - local deployment of Groovlets is not working right now. It seems like Google has been &lt;a href="http://code.google.com/p/googleappengine/issues/detail?id=1219"&gt;notified&lt;/a&gt; about the issue, but even after a few days the bug is still in status "new", so I don't think there'll be a fix anytime soon. Since daily deployment is limited by a quota, I think this bug is almost a show-stopper and I wouldn't want to develop in Groovy for the GAE right now.&lt;br /&gt;&lt;br /&gt;Let's turn our attention to performance and scalability. First off I have a disclaimer: I didn't run any thorough or scientific tests! Although I have some numbers to back up my opinion, you might want to run further tests before you draw a final conclusion. &lt;br /&gt;&lt;br /&gt;To get us all on the same page, let's start with a little "theory". When I talk about &lt;a href="http://en.wikipedia.org/wiki/Computer_performance"&gt;performance&lt;/a&gt; it means something like this:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;... performance is characterized by the amount of useful work accomplished by a computer system compared to the time and resources used.&lt;br /&gt;&lt;br /&gt;...performance may involve one or more of the following:&lt;br /&gt;&lt;li&gt;Short response time&lt;br /&gt;&lt;li&gt;High throughput&lt;/blockquote&gt;&lt;br /&gt;The same goes for &lt;a href="http://en.wikipedia.org/wiki/Scalability"&gt;scalability&lt;/a&gt;, although in general it's more subtle to properly define it:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;...scalability is a desirable property of a system, a network, or a process, which indicates its ability to either handle growing amounts of work in a graceful manner, or to be readily enlarged.&lt;/blockquote&gt;&lt;br /&gt;With that out of the way let's head over to the test setup. It consists of a simple &lt;a href="http://jakarta.apache.org/jmeter/"&gt;Apache JMeter&lt;/a&gt; &lt;a href="http://github.com/dlinsin/area51/blob/b0be57eb58ac1d602c49aa544df84ba959eaea3c/gae/src/main/resources/gae_insert.jmx"&gt;test script&lt;/a&gt;, which puts my poor sample application under load. The script loads the start page &lt;a href="http://dlinsin-area51.appspot.com"&gt;http://dlinsin-area51.appspot.com&lt;/a&gt;, then inserts the name "JMeter" into the input field and submits the form. The submission results in a redirect to the start page again. I mainly wanted to test "writes" to the persistent storage, because in my experience that's the most hardest part to scale. &lt;br /&gt;&lt;br /&gt;&lt;div style="text-align:left;"&gt;&lt;a href="http://picasaweb.google.com/lh/photo/iMSIgRJvDWkc3NNN6UpnkA?authkey=Gv1sRgCKXY3MiLxprXDQ&amp;feat=embedwebsite"&gt;&lt;img src="http://lh3.ggpht.com/_myUhtr_A51Y/SeWF5BNxBII/AAAAAAAANog/tdH6vJwA3O0/s800/gae_summary_10_small.png" width=600/&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align:left;"&gt;&lt;a href="http://picasaweb.google.com/lh/photo/dcgwpfXIzuJjgGVTAb6Y_w?authkey=Gv1sRgCKXY3MiLxprXDQ&amp;feat=embedwebsite"&gt;&lt;img src="http://lh5.ggpht.com/_myUhtr_A51Y/SeWF9MifIrI/AAAAAAAANoo/k5stXYChHGs/s800/gae_summary_100_small.png" width=600/&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;I ran the test script with 10 threads (blue shaded) and with 100 threads hitting my sample app. Each thread runs the test procedure 10 times. The results were quite interesting. If you take a closer look at the average response time of both samplers you can see only a small difference between 10 users and 100 users (or threads) hitting the application at the same time. The response time for inserting a row into the persistent storage went up insignificantly, which I think is quite astonishing, considering the increase of load. &lt;br /&gt;&lt;br /&gt;However, you should take these results with a grain of salt. They merely exist to give me a hint of what the GAE is capable of. I would be interested in real benchmarks, because I think it's crucial to see how scalable the GAE is under field conditions.  &lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update: The test results are simply wrong! Thx for pointing it out Bernd, I'm working on it!&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update 2: Here comes the bugfix!&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align:left;"&gt;&lt;a href="http://picasaweb.google.com/lh/photo/Efy_6dV6PcvGMAk4Ckdw-Q?authkey=Gv1sRgCKXY3MiLxprXDQ&amp;feat=directlink"&gt;&lt;img src="http://lh6.ggpht.com/_myUhtr_A51Y/SedlXucU7sI/AAAAAAAANp8/a0ohuS9acLA/s800/gae_summary_revised_10.png" alt="gae_summary_revised_10.png" width=600/&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align:left;"&gt;&lt;a href="http://picasaweb.google.com/lh/photo/CNVK9mq1cSVHO3QwiETS5A?authkey=Gv1sRgCKXY3MiLxprXDQ&amp;feat=directlink"&gt;&lt;img src="http://lh3.ggpht.com/_myUhtr_A51Y/SedleqL5oTI/AAAAAAAANqE/ftILEE4AXr8/s800/gae_summary_revised_100.png" width=600/&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;You might ask what happened? Frankly, I mixed up the pictures and didn't double check, before pushing out the post. Sorry about that! I ran the tests with the same specs as mentioned above again and as you can see from the results, this time it looks reasonable. &lt;br /&gt;&lt;br /&gt;Based on the new numbers, I have to put my conclusion a little bit into perspective. It still think the GAE scales reasonably well, but the numbers are not that astonishing anymore. &lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-4273703015510583715?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/4273703015510583715/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=4273703015510583715' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/4273703015510583715'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/4273703015510583715'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/04/more-playing-with-gae.html' title='More Playing with GAE'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_myUhtr_A51Y/SeWF5BNxBII/AAAAAAAANog/tdH6vJwA3O0/s72-c/gae_summary_10_small.png' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-8911564559875525934</id><published>2009-04-13T07:45:00.000+02:00</published><updated>2009-08-08T14:14:03.114+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='google'/><category scheme='http://www.blogger.com/atom/ns#' term='gae'/><title type='text'>Playing with Google App Engine</title><content type='html'>I'm jumping on the hype wagon and checking out the Google App Engine (GAE) for Java. After writing a &lt;a href="http://dlinsin-area51.appspot.com"&gt;simple sample application&lt;/a&gt; using &lt;a href="http://java.sun.com/products/servlet/"&gt;Servlets&lt;/a&gt;, &lt;a href="http://groovy.codehaus.org/Groovlets"&gt;Groovlets&lt;/a&gt; and the &lt;a href="http://en.wikipedia.org/wiki/Java_Persistence_API"&gt;JPA&lt;/a&gt;, I want to share with you the bumpy ride I had. &lt;br /&gt;&lt;br /&gt;If you are interested in checking out the sample application you can either &lt;a href="http://download.linsin.de/gae_sample.tar"&gt;download&lt;/a&gt; it or check it out on &lt;a href="http://github.com/dlinsin/area51/tree/0cfdae4810c7d266e2f42fc1b2c9cd2224b4f287/gae"&gt;github&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Fankly, I only had a bumpy ride, because I didn't follow &lt;a href="http://code.google.com/appengine/docs/java/gettingstarted/installing.html"&gt;Google's suggestion&lt;/a&gt; to use the Eclipse IDE plug-in together with a set of &lt;a href="http://ant.apache.org/"&gt;ANT&lt;/a&gt; tasks, they provide. Instead I wanted to use &lt;a href="http://www.jetbrains.com/idea"&gt;IntelliJ&lt;/a&gt; and &lt;a href="http://maven.apache.org"&gt;Maven&lt;/a&gt;. Another suggestions of Google I didn't follow, is to use &lt;a href="http://en.wikipedia.org/wiki/Java_Data_Objects"&gt;Java Data Object (JDO)&lt;/a&gt; as a persistence API. Yes, that's right, Google suggests JDO as a preferred way to store data. If you are working on a somewhat up to date Java enterprise application, you are probably using the &lt;a href="http://en.wikipedia.org/wiki/Java_Persistence_API"&gt;Java Persistence API (JPA)&lt;/a&gt; instead. The difference between the two standards, according to wikipedia, are:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;... JPA, however, is an Object-relational mapping (ORM) standard, while JDO is an Object-relational mapping standard and a transparent object persistence standard. JDO, from an API point of view, is agnostic to the technology of the underlying datastore, whereas JPA is being oriented totally around RDBMS datastores.&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;Google's underlying persistence layer is called &lt;a href="http://googleappengine.blogspot.com/2009/02/back-to-future-for-data-storage.html"&gt;Bigtable&lt;/a&gt;. It's designed to scale across distributed systems, by being non-relation. That means you can dump your data into Bigtable, without brooding over your schema first. On the question why Google favors JDO over JPA, I think it's because you are not dealing with an RDBMS, but Bigtable. &lt;br /&gt;&lt;br /&gt;The first thing I did was to write a simple hello world JSP and Servlet and packaged it as a WAR file. Thanks to IntelliJ's built-in enterprise support, I was up and running in no more than 10 minutes. The only difference to a regular enterprise application is the file &lt;em&gt;appengine-web.xml&lt;/em&gt;. It contains your application identifier and version. The versioning schema is quite interesting. You can configure which version a user sees and simultaneously deploy a new version for testing. &lt;br /&gt;&lt;br /&gt;Google provides a couple of &lt;a href="http://code.google.com/appengine/docs/java/gettingstarted/uploading.html"&gt;command-line tools&lt;/a&gt;, which help you to start a local version of the app engine and upload your application to the cloud. Once you are online, you can access your server logs through a dashboard. Unfortunately I wasn't able to see the logs instantly, after encountering errors. I had to wait for a couple of seconds, which is pretty annoying and makes local testing much more important. &lt;br /&gt;&lt;br /&gt;The first hurdle I had to overcome was integrating JPA. Google uses an implementation called &lt;a href="http://www.datanucleus.org/"&gt;DataNucleus&lt;/a&gt;, which relies on post-compile bytecode manipulation of your entities. I had the hardest time integrating the bytecode manipulation with my Maven build. Fortunately, the &lt;a href="http://groups.google.com/group/google-appengine-java/browse_thread/thread/260c0307f039aac2/"&gt;App Engine Google Group&lt;/a&gt; was very helpful and the problem was solved quickly. After a couple of hours, I was able to run my first JPA enabled application, without thinking about deployment or database issues. No problems with getting a server to run or twiddling with database settings - the stuff just runs. &lt;br /&gt;&lt;br /&gt;As a developer, your probably don't want be bothered with infrastructure, which sometimes looks like black magic. I really like the completely transparent way of developing for the GAE, but I'm skeptical if I'd want to rely my business on this kind of hosting. With &lt;a href="http://aws.amazon.com/ec2/"&gt;Amazon's EC2&lt;/a&gt; you still have the configuration of the system under control and that's probably important, if you want to run a business on it. In terms of pricing, I can't really say much. To me it looks almost the same, except that with GAE you have a quota on everything. There's even a daily quota on datastore API calls, which can't be increased even by paying for it. That's a bit of a bummer! However, I think they chose the quotas reasonably and maybe the are being increased after the public release of the GAE for Java.&lt;br /&gt;&lt;br /&gt;In my next blog post, I will cover some more details on how to run Groovlets. I also did some simple load testing with &lt;a href="http://jakarta.apache.org/jmeter"&gt;Apache JMeter&lt;/a&gt; to get a feeling on how GAE scales. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-8911564559875525934?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/8911564559875525934/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=8911564559875525934' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/8911564559875525934'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/8911564559875525934'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/04/playing-with-google-app-engine.html' title='Playing with Google App Engine'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-5342272723647319631</id><published>2009-04-06T05:53:00.000+02:00</published><updated>2009-04-08T16:48:22.741+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><title type='text'>The State Pattern</title><content type='html'>A couple of weeks ago I had to come up with a design for a state machine at work. Even though a colleague mentioned the &lt;a href="http://en.wikipedia.org/wiki/State_pattern"&gt;State Pattern&lt;/a&gt;, I thought it was oversized and being the &lt;a href="http://www.amazon.de/gp/product/020161622X?ie=UTF8&amp;tag=mytakeonthing-21&amp;linkCode=as2&amp;camp=1638&amp;creative=6742&amp;creativeASIN=020161622X"&gt;pragmatic programmer&lt;/a&gt; I am, I decided to take the easy turn.&lt;br /&gt;&lt;br /&gt;I've never really used the State Pattern before and I have to admit, even after glancing at the UML diagram, I couldn't quite figure out how this is gonna help me solve my problem. In case you are like me and need a little recap on the State Pattern, let's look at the &lt;a href="http://en.wikipedia.org/wiki/State_pattern"&gt;wikipedia summary&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;The state pattern is a behavioral software design pattern, also known as the objects for states pattern. This pattern is used in computer programming to represent the state of an object. This is a clean way for an object to partially change its type at runtime.&lt;br /&gt;&lt;table style="width:auto;"&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://picasaweb.google.com/lh/photo/AJaLiAVKZbNWCS2fS5v82w?authkey=Gv1sRgCKXY3MiLxprXDQ&amp;feat=embedwebsite"&gt;&lt;img src="http://lh6.ggpht.com/_myUhtr_A51Y/SdhtECgVIZI/AAAAAAAANmE/QFxB58e6iW0/s288/State_Design_Pattern_UML_Class_Diagram.png" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="font-family:arial,sans-serif; font-size:11px; text-align:right"&gt;from &lt;a href="http://en.wikipedia.org/wiki/File:State_Design_Pattern_UML_Class_Diagram.png"&gt;wikipedia&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;Starting from this description, it seemed like there is a hammer, which looks for a nail. Having a concrete state class for &lt;strong&gt;each&lt;/strong&gt; state in my design? I'm going to end up with a truckload full of classes and they all need to be carefully designed and tested. &lt;br /&gt;&lt;br /&gt;With this mindset, I went down the switch/case-if/else road and designed the state machine in the most straight forward, but most unmaintainable way possible. Here's an imperative style code sample, which points out how not to implement a state machine:&lt;br /&gt;&lt;pre&gt;public void insertCoins() {&lt;br /&gt;  if (state == NO_COINS &amp;&amp; numberOfCoins &amp;gt;= MIN_COINS) {&lt;br/&gt;    state = COINS_OK;&lt;br /&gt;  } else if (state == NO_COINS &amp;&amp; numberOfCoins &amp;lt; MIN_COINS) {&lt;br/&gt;    state = NO_COINS;&lt;br/&gt;    System.out.println("more coins!");&lt;br /&gt;  } else if (state == SOLD_OUT) {&lt;br/&gt;    state = RETURN_COINS;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;// more transitions like dispenseFood(), printReceipt() and soldOut()&lt;/pre&gt;&lt;br /&gt;If you are like me and have never used the State Pattern, you probably think "What is wrong with this code?". The answer is nothing! It's just very imperative and highly unmaintainable. Imagine if you have to add a new state, you will need to adopt every if/else blocks in each transition method. In addition to that, you need to update each test case you wrote.&lt;br /&gt;&lt;br /&gt;After reading on the State Pattern in &lt;a href="http://www.amazon.de/gp/product/0596007124?ie=UTF8&amp;tag=mytakeonthing-21&amp;linkCode=as2&amp;camp=1638&amp;creative=6742&amp;creativeASIN=0596007124"&gt;Head First Design Patterns&lt;/a&gt;, which explains it very visually, I would change my code above into something like this:&lt;br /&gt;&lt;pre&gt;public class NoCoins implements State {&lt;br /&gt;  public void insertCoins() {&lt;br /&gt;    if (numberOfCoins &amp;gt;= MIN_COINS) {&lt;br/&gt;      context.setCoinsOk();&lt;br /&gt;    } else {&lt;br/&gt;      System.out.println("more coins!");&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  // more methods defined in State, like dispenseFood()...&lt;br /&gt;}&lt;br/&gt;&lt;br /&gt;public class SoldOut implements State {&lt;br /&gt;  public void insertCoins() {&lt;br /&gt;    context.returnCoins();&lt;br /&gt;  }&lt;br /&gt;  // more methods defined in State, like dispenseFood()...&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Each state gets a concrete class and common functionality can be aggregated in an abstract super class (I used an interface here instead). The member &lt;em&gt;context&lt;/em&gt; is the representation of the state machine, which is used by the client to transition between states. &lt;br /&gt;&lt;br /&gt;The main advantages of the State Pattern, according to Head First Design Patterns, are:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;li&gt; unlike a procedural state machine, it represents state as a full-blown class&lt;br /&gt;&lt;li&gt; the context object gets its behavior by delegating to the current state object&lt;br /&gt;&lt;li&gt; it allows a context to change its behavior as the state of the context changes&lt;/blockquote&gt;&lt;br /&gt;My state machine implementation was pretty simple, so I couldn't see any downside using the imperative approach. However, trying to test the beast ended up to be much more complicated than the implementation itself. A colleague pointed out the design smell (thanks Alexander). &lt;br /&gt;&lt;br /&gt;With the State Pattern, each state is represented by a class, it can be tested in isolation. I think that is one of the most compelling advantages. It outweighs the argument, that you have much more classes than with an imperative approach.&lt;br /&gt;&lt;br /&gt;I really enjoyed reading up on the State Pattern in &lt;a href="http://www.amazon.de/gp/product/0596007124?ie=UTF8&amp;tag=mytakeonthing-21&amp;linkCode=as2&amp;camp=1638&amp;creative=6742&amp;creativeASIN=0596007124"&gt;Head First Design Patterns&lt;/a&gt;. Each comes with a visually rich example, giving you everything, needed to use it in your code. It even compares a procedural/imparative implementation of a state machine with the much more improved version using the State Pattern. I find that one of the most effective ways to learn.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-5342272723647319631?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/5342272723647319631/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=5342272723647319631' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/5342272723647319631'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/5342272723647319631'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/04/state-pattern.html' title='The State Pattern'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_myUhtr_A51Y/SdhtECgVIZI/AAAAAAAANmE/QFxB58e6iW0/s72-c/State_Design_Pattern_UML_Class_Diagram.png' height='72' width='72'/><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-7669762738983762606</id><published>2009-03-27T07:02:00.001+01:00</published><updated>2009-04-01T05:50:24.434+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><title type='text'>JDK 7 Language Changes are Coined</title><content type='html'>Joe Darcy has posted a &lt;a href="http://blogs.sun.com/darcy/entry/project_coin_for_further_consideration"&gt;blog post&lt;/a&gt; on the status of project coin. According to Joe there are six proposals for small language changes, which are shortlisted by Sun. I put together an overview with some code samples.&lt;br /&gt;&lt;br /&gt;1. &lt;a href="http://mail.openjdk.java.net/pipermail/coin-dev/2009-February/000001.html"&gt;Strings in switch&lt;/a&gt; by Joe Darcy&lt;br /&gt;&lt;pre&gt;String s = ...&lt;br /&gt;switch(s) {&lt;br /&gt;  case "foo":&lt;br /&gt;    processFoo(s);&lt;br /&gt;    break;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;I really have no feelings about this change whatsoever! It's not a pain for me that it doesn't work right now and I think I won't need it if it's gonna be in JDK 7.&lt;br /&gt;&lt;br /&gt;2. &lt;a href="http://mail.openjdk.java.net/pipermail/coin-dev/2009-February/000003.html"&gt;Improved Exception Handling for Java&lt;/a&gt; by Neal Gafter&lt;br /&gt;&lt;pre&gt;try {&lt;br /&gt;    doWork(file);&lt;br /&gt;} catch (final IOException|SQLException ex) {&lt;br /&gt;    logger.log(ex);&lt;br /&gt;    throw ex;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;I &lt;a href="http://dlinsin.blogspot.com/2008/09/series-reflections-put-closures-into.html"&gt;wrote&lt;/a&gt; about how great it would be to have this feature as part of closures. As we all know, sadly, &lt;a href="http://dlinsin.blogspot.com/2009/01/java-7-jigsaw-and-missing-jsr.html"&gt;there won't&lt;/a&gt; be any closures in JDK 7, however I'm really glad that this part is considered to be in.&lt;br /&gt;&lt;br /&gt;3. &lt;a href="http://mail.openjdk.java.net/pipermail/coin-dev/2009-February/000011.html"&gt;Automatic Resource Management&lt;/a&gt; by Josh Bloch&lt;br /&gt;&lt;pre&gt;try (BufferedReader br = new BufferedReader(new FileReader(path)) {&lt;br /&gt;  return br.readLine();&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;As with catching multiple Exceptions at the same time, this is a language change that I would welcome with open arms! On the &lt;a href="http://mail.openjdk.java.net/pipermail/coin-dev/"&gt;mailing-list of project coin&lt;/a&gt; this proposal caused the most vibrant discussions. Josh Bloch was very open about any type of feedback, even when &lt;a href="http://mail.openjdk.java.net/pipermail/coin-dev/2009-March/000182.html"&gt;someone suggested&lt;/a&gt; a modifier for cleaning up resources instead of the try statement. That made following the discussion very interesting.&lt;br /&gt;&lt;br /&gt;4. &lt;a href="http://mail.openjdk.java.net/pipermail/coin-dev/2009-February/000009.html"&gt;Improved Type Inference for Generic Instance Creation&lt;/a&gt; by Jeremy Manson&lt;br /&gt;&lt;pre&gt;Map&amp;lt;String, List&amp;lt;String&amp;gt;&amp;gt; anagrams = new HashMap&amp;lt;&amp;gt;();&lt;/pre&gt;&lt;br /&gt;I asked for this in a &lt;a href="http://dlinsin.blogspot.com/2008/06/static-typing-with-less-keystrokes.html"&gt;blog post&lt;/a&gt; almost a year ago and it seems as if I have been finally heard. The first thing you might ask when you see this, is why Jeremy Manson proposes &lt;em&gt;new HashMap&amp;lt;&amp;gt;()&lt;/em&gt; instead of &lt;em&gt;new HashMap()&lt;/em&gt;. In his very well worked out proposal, he indicates, it's a matter of backward compatibility. However, he also mentions it is not impossible to remove the arrow brackets in future versions.&lt;br /&gt;&lt;br /&gt;5. &lt;a href="http://mail.openjdk.java.net/pipermail/coin-dev/2009-March/000047.html"&gt;Elvis and Other Null-Safe Operators&lt;/a&gt; written by Neal Gafter and submitted by Stephen Colebourne&lt;br /&gt;&lt;pre&gt;String s = mayBeNull?.toString() ?: "null";&lt;br /&gt;Integer ival = ...;  // may be null&lt;br /&gt;int i = ival ?: -1;  // no NPE from unboxing&lt;/pre&gt;&lt;br /&gt;The operator &lt;em&gt;?:&lt;/em&gt; is called Elvis, because the question mark looks like the Elvis smiley, as I've learned from the &lt;a href="http://groups.google.com/group/javaposse/browse_thread/thread/c51cf10f8e266879/524c350496e036da?lnk=gst&amp;q=elvis#524c350496e036da"&gt;Java Posse mailing-list&lt;/a&gt; yesterday. Jokes apart, I like this proposal the more I look at it. However, I've &lt;a href="http://dlinsin.blogspot.com/2008/07/why-is-static-analysis-uncommon.html"&gt;never had problems&lt;/a&gt; with adding null checks to my code and I'm not sure if the NPE problem would be solved better with annotations. JSR 303 - Bean Validation, provides a @NotNull annotation, which can be used to define your intend to prevent Null from being passed to your method. I tend to think the language level approach is not suitable here.&lt;br /&gt;&lt;br /&gt;6. &lt;a href="http://mail.openjdk.java.net/pipermail/coin-dev/2009-March/000217.html"&gt;Simplified Varargs Method Invocation&lt;/a&gt; by Bob Lee&lt;br /&gt;&lt;br /&gt;Before:&lt;br /&gt;&lt;pre&gt;static &amp;lt;T&amp;gt; List&amp;lt;T&amp;gt; asList(T... elements) { ... }&lt;br /&gt;static List&amp;lt;Callable&amp;lt;String&amp;gt;&amp;gt; stringFactories() {&lt;br /&gt;  Callable&amp;lt;String&amp;gt; a, b, c;&lt;br /&gt;    ...&lt;br /&gt;  *// Warning: **"uses unchecked or unsafe operations"*&lt;br /&gt;  return asList(a, b, c);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;After:&lt;br /&gt;&lt;pre&gt;*// Warning: **"enables unsafe generic array creation"*&lt;br /&gt;static &amp;lt;T&amp;gt; List&amp;lt;T&amp;gt; asList(T... elements) { ... }&lt;br /&gt;static List&amp;lt;Callable&amp;lt;String&amp;gt;&amp;gt; stringFactories() {&lt;br /&gt;  Callable&amp;lt;String&amp;gt; a, b, c;&lt;br /&gt;  ...&lt;br /&gt;  return asList(a, b, c);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;This proposal simply moves the warnings from the invocation of the generified method to the declaration. I know this warning is annoying and it would reduce the number of warnings in your code base, but for me, this has never been a real pain so far.&lt;br /&gt;&lt;br /&gt;The project coin &lt;a href="http://mail.openjdk.java.net/pipermail/coin-dev/"&gt;mailing-list&lt;/a&gt; was flooded with proposals, so if you wonder why Sun chose the afore mentioned here's why:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Sun believes that the proposals are &lt;a href="http://blogs.sun.com/darcy/entry/guidance_measure_language_change_size"&gt;small enough&lt;/a&gt;, have favorable estimated reward to effort ratios, and advance the &lt;a href="http://blogs.sun.com/darcy/entry/criteria_for_desirable_small_language"&gt;stated criteria&lt;/a&gt; of making things programmers do everyday easier or supporting platform changes in JDK 7.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;You can really tell that Sun is &lt;a href="http://openjdk.java.net/projects/jdk7/"&gt;on a schedule&lt;/a&gt; and that there is not a lot of time to gather more ideas. In fact Joe asks in his blog post to continue work on the before mentioned proposals, in the form of prototypes and refinements, instead of sending in new ones. &lt;br /&gt;&lt;br /&gt;I've been on the project coin mailing-list since day one and I have to commend Joe on how he is "handling" the list. No offense, but some of the folks sending in proposals should really try to get some writing lessons. My English is not perfect and trying to communicate something subtle as a language change is really hard, but some folks are just not able to produce proper English sentences. Joe did a pretty good job in keeping people like that on a short leash. &lt;br /&gt;&lt;br /&gt;The shortlisted proposals almost match with the winners of the &lt;a href="http://dlinsin.blogspot.com/2009/01/java-7-language-changes-poll-results.html"&gt;poll&lt;/a&gt; I conducted a couple of months ago. I think in general they are gonna be very welcome by a broad range of developers. However, the implementation of those proposals is a whole different story. Next week we will know more.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-7669762738983762606?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/7669762738983762606/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=7669762738983762606' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/7669762738983762606'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/7669762738983762606'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/03/jdk-7-language-changes-are-coined.html' title='JDK 7 Language Changes are Coined'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-718569878131970215</id><published>2009-03-23T07:01:00.000+01:00</published><updated>2009-03-23T07:01:00.533+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='karlsruhe'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='jug-ka'/><title type='text'>JUG-Ka Stammtisch 3.0</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_myUhtr_A51Y/SAuD46fTQ0I/AAAAAAAAGaU/dObCE4Jx8H8/s1600-h/jug-logo_124x77.png"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://bp3.blogger.com/_myUhtr_A51Y/SAuD46fTQ0I/AAAAAAAAGaU/dObCE4Jx8H8/s320/jug-logo_124x77.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5191388009271083842" /&gt;&lt;/a&gt;Version 3.0 of the &lt;a href="http://jug-ka.de"&gt;Java Users Group Karlsruhe&lt;/a&gt; Stammtisch is being release this coming &lt;a href="https://www.xing.com/events/319637"&gt;Thursday 26th of March&lt;/a&gt;. This time we are going to meet directly in the pub &lt;a href="http://tinyurl.com/blzfzn"&gt;Litfass&lt;/a&gt;, which is near Marktplatz, at 20:00 to get a good sip of Java. For further information check out the &lt;a href="http://groups.google.com/group/jug-karlsruhe"&gt;Google Group&lt;/a&gt; or drop me an email.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-718569878131970215?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/718569878131970215/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=718569878131970215' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/718569878131970215'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/718569878131970215'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/03/jug-ka-stammtisch-30.html' title='JUG-Ka Stammtisch 3.0'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp3.blogger.com/_myUhtr_A51Y/SAuD46fTQ0I/AAAAAAAAGaU/dObCE4Jx8H8/s72-c/jug-logo_124x77.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-4497111685882726889</id><published>2009-03-19T06:53:00.000+01:00</published><updated>2009-03-19T06:53:00.670+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='spring'/><title type='text'>Final or Not Final?</title><content type='html'>I recently implemented an extension to &lt;a href="http://www.springframework.org"&gt;Spring's&lt;/a&gt; &lt;em&gt;&lt;a href="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/jdbc/support/incrementer/HsqlSequenceMaxValueIncrementer.html"&gt;HsqlSequenceMaxValueIncrementer&lt;/a&gt;&lt;/em&gt;. When I digged into the Spring source code of &lt;em&gt;HsqlSequenceMaxValueIncrementer&lt;/em&gt; and its ancestors, I noticed something astonishing: almost all methods are public or at least protected and there is not one final class or field. Everything is extensible and somewhat accessible. &lt;br /&gt;&lt;br /&gt;Let's take a look at &lt;em&gt;AbstractSequenceMaxValueIncrementer&lt;/em&gt;, one of the parent classes of &lt;em&gt;HsqlSequenceMaxValueIncrementer&lt;/em&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public abstract class AbstractDataFieldMaxValueIncrementer implements DataFieldMaxValueIncrementer, InitializingBean {&lt;br /&gt;	private DataSource dataSource;&lt;br /&gt;	private String incrementerName;&lt;br/&gt;&lt;br /&gt;	public AbstractDataFieldMaxValueIncrementer() {&lt;br /&gt;	}&lt;br/&gt;&lt;br /&gt;	public AbstractDataFieldMaxValueIncrementer(DataSource dataSource, String incrementerName) {&lt;br /&gt;		Assert.notNull(dataSource, "DataSource must not be null");&lt;br /&gt;		Assert.notNull(incrementerName, "Incrementer name must not be null");&lt;br /&gt;		this.dataSource = dataSource;&lt;br /&gt;		this.incrementerName = incrementerName;&lt;br /&gt;	}&lt;br/&gt;&lt;br /&gt;	public void setDataSource(DataSource dataSource) {&lt;br /&gt;		this.dataSource = dataSource;&lt;br /&gt;	}&lt;br/&gt;&lt;br /&gt;	public DataSource getDataSource() {&lt;br /&gt;		return this.dataSource;&lt;br /&gt;	}&lt;br/&gt;&lt;br /&gt;	public void setIncrementerName(String incrementerName) {&lt;br /&gt;		this.incrementerName = incrementerName;&lt;br /&gt;	}&lt;br/&gt;&lt;br /&gt;	public String getIncrementerName() {&lt;br /&gt;		return this.incrementerName;&lt;br /&gt;	}&lt;br/&gt;&lt;br /&gt;	public void afterPropertiesSet() {&lt;br /&gt;		if (this.dataSource == null) {&lt;br /&gt;			throw new IllegalArgumentException("Property 'dataSource' is required");&lt;br /&gt;		}&lt;br /&gt;		if (this.incrementerName == null) {&lt;br /&gt;			throw new IllegalArgumentException("Property 'incrementerName' is required");&lt;br /&gt;		}&lt;br /&gt;	}&lt;br/&gt;&lt;br /&gt;	public int nextIntValue() throws DataAccessException {&lt;br /&gt;		return (int) getNextKey();&lt;br /&gt;	}&lt;br/&gt;&lt;br /&gt;	public long nextLongValue() throws DataAccessException {&lt;br /&gt;		return getNextKey();&lt;br /&gt;	}&lt;br/&gt;&lt;br /&gt;	public String nextStringValue() throws DataAccessException {&lt;br /&gt;		// removed implementation for brevity &lt;br /&gt;	}&lt;br/&gt;&lt;br /&gt;	protected abstract long getNextKey();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Since I was extending the framework, the degree of flexibility was great for me. All I had to do was override a few methods to plug in my code and that's it. &lt;br /&gt;&lt;br /&gt;If everything is accessible, the way it is in case of &lt;em&gt;AbstractDataFieldMaxValueIncrementer&lt;/em&gt;, users can do almost everything they want with the classes you published. They can override all the methods and subclass each and every class. As shown above, that was obviously intended. The class wouldn't be abstract and have public/protected methods if it wasn't intended to be subclassed. &lt;br /&gt;&lt;br /&gt;After &lt;a href="http://dlinsin.blogspot.com/2009/03/book-review-practical-api-design.html"&gt;reading&lt;/a&gt; the book &lt;a href="http://www.amazon.de/gp/product/1430209739?ie=UTF8&amp;tag=mytakeonthing-21&amp;linkCode=as2&amp;camp=1638&amp;creative=6742&amp;creativeASIN=1430209739"&gt;Practical API Design&lt;/a&gt;, which preaches backward compatibility, I wonder how you would be able to evolve such a class? Let's say you wanted to extend the meaning of the field &lt;em&gt;incrementerName&lt;/em&gt;. It should convey more information than simply the name of the sequence. Therefore you'd create a class &lt;em&gt;IncrementerName&lt;/em&gt;, which replaced the String field. With this simple change, you'd break all the clients relying on the field &lt;em&gt;incrementerName&lt;/em&gt;. I know this example might seem far-fetched, but I think you get the idea. &lt;br /&gt;&lt;br /&gt;Another thing that caught my eye, when looking at &lt;em&gt;AbstractDataFieldMaxValueIncrementer&lt;/em&gt;, is the field &lt;em&gt;dataSource&lt;/em&gt;. It is basically a public field and I'm not quite sure why. Is it really necessary to replace the &lt;em&gt;DataSource&lt;/em&gt; after you initialized the bean? Do you really want everyone to screw with the &lt;em&gt;DataSource&lt;/em&gt;?  &lt;br /&gt;&lt;br /&gt;If it was for me, I would have answered those questions with a plain "No!". Why not implement &lt;em&gt;AbstractDataFieldMaxValueIncrementer&lt;/em&gt; as follows:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public abstract class AbstractDataFieldMaxValueIncrementer implements DataFieldMaxValueIncrementer {&lt;br /&gt;	private final DataSource dataSource;&lt;br /&gt;	private final String incrementerName;&lt;br/&gt;&lt;br /&gt;	public AbstractDataFieldMaxValueIncrementer(DataSource dataSource, String incrementerName) {&lt;br /&gt;		Assert.notNull(dataSource, "DataSource must not be null");&lt;br /&gt;		Assert.notNull(incrementerName, "Incrementer name must not be null");&lt;br /&gt;		this.dataSource = dataSource;&lt;br /&gt;		this.incrementerName = incrementerName;&lt;br /&gt;	}&lt;br/&gt;&lt;br /&gt;	protected DataSource getDataSource() {&lt;br /&gt;		return this.dataSource;&lt;br /&gt;	}&lt;br/&gt;&lt;br /&gt;	public String getIncrementerName() {&lt;br /&gt;		return this.incrementerName;&lt;br /&gt;	}&lt;br/&gt;&lt;br /&gt;	public int nextIntValue() throws DataAccessException {&lt;br /&gt;		return (int) getNextKey();&lt;br /&gt;	}&lt;br/&gt;&lt;br /&gt;	public long nextLongValue() throws DataAccessException {&lt;br /&gt;		return getNextKey();&lt;br /&gt;	}&lt;br/&gt;&lt;br /&gt;	public String nextStringValue() throws DataAccessException {&lt;br /&gt;		// removed implementation for brevity &lt;br /&gt;	}&lt;br/&gt;&lt;br /&gt;	protected abstract long getNextKey();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt; &lt;br /&gt;&lt;br /&gt;I think this would not restrict the use of the class, except that you have to use &lt;a href="http://googletesting.blogspot.com/2009/02/constructor-injection-vs-setter.html"&gt;constructor injection&lt;/a&gt;, but that's a &lt;a href="http://dlinsin.blogspot.com/2008/07/how-to-fail-fast-on-faulty-injection.html"&gt;different story&lt;/a&gt;. It would, however, restrict the use of the fields &lt;em&gt;dataSource&lt;/em&gt; and &lt;a href="http://dlinsin.blogspot.com/2008/07/how-to-fail-fast-on-faulty-injection.html"&gt;incrementerName&lt;/a&gt;. I think those fields should be immutable anyways, since I cannot think of a reason why you would want to change them during the life-cycle of the object. I know &lt;em&gt;dataSource&lt;/em&gt; isn't truly immutable, but the access is limited to subclasses, which should confine the wrong usage.&lt;br /&gt;&lt;br /&gt;That leaves me with the question, why the authors of &lt;em&gt;AbstractDataFieldMaxValueIncrementer&lt;/em&gt; didn't go that route? I think it's simply a design decision of Spring authors, to be as open and extensible as possible. They have &lt;em&gt;InitializingBean&lt;/em&gt; and &lt;em&gt;&lt;a href="http://blog.springsource.com/2007/07/11/setter-injection-versus-constructor-injection-and-the-use-of-required/"&gt;@Required&lt;/a&gt;&lt;/em&gt; to enforce necessary dependencies, which work quite well. I guess they push the responsibility of handling immutables with care to the developer, which is reasonable, but in my opinion rather risky. I think using final in such cases is an easy way to remove unnecessary sources of errors.&lt;br /&gt;&lt;br /&gt;I'm not quite sure which approach is the best here. Does it all boil down to "it depends"? Or is all a matter of taste? I don't think so. In my day to day coding, I tend to consciously follow the advices of &lt;a href="http://www.amazon.de/gp/product/0321356683?ie=UTF8&amp;tag=mytakeonthing-21&amp;linkCode=as2&amp;camp=1638&amp;creative=6742&amp;creativeASIN=0321356683"&gt;Effective Java&lt;/a&gt;, which favors immutability and limiting accessibility. However, looking at Spring's source code is a truly inspiring. They are doing a phenomenal job of keeping it stable, considering how open and extensible their classes are. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-4497111685882726889?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/4497111685882726889/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=4497111685882726889' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/4497111685882726889'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/4497111685882726889'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/03/final-or-not-final.html' title='Final or Not Final?'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-3099985074994816839</id><published>2009-03-16T07:29:00.000+01:00</published><updated>2009-03-16T07:29:47.668+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='quality'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><title type='text'>Manifesto for Software Craftmanship</title><content type='html'>&lt;div style="text-align:center;"&gt;&lt;img src="http://lh4.ggpht.com/_myUhtr_A51Y/Sbv2lPwISuI/AAAAAAAANT0/Tdo760TWmc8/manifesto_for_software_craftmanship.png?imgmax=800" alt="manifesto_for_software_craftmanship.png" border="0" width="569" height="684" /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Go and &lt;a href="http://manifesto.softwarecraftsmanship.org"&gt;sign up&lt;/a&gt; yourself! &lt;a href="http://www.infoq.com/news/2009/03/software_craftsmanship"&gt;Support&lt;/a&gt; our craft and help to make better software!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-3099985074994816839?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/3099985074994816839/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=3099985074994816839' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/3099985074994816839'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/3099985074994816839'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/03/manifesto-for-software-craftmanship.html' title='Manifesto for Software Craftmanship'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_myUhtr_A51Y/Sbv2lPwISuI/AAAAAAAANT0/Tdo760TWmc8/s72-c/manifesto_for_software_craftmanship.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-373129668325770155</id><published>2009-03-09T07:14:00.000+01:00</published><updated>2009-03-09T07:14:01.125+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='karlsruhe'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='jug-ka'/><title type='text'>Heiko W. Rupp @ JUG-Ka</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_myUhtr_A51Y/SAuD46fTQ0I/AAAAAAAAGaU/dObCE4Jx8H8/s1600-h/jug-logo_124x77.png"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://bp3.blogger.com/_myUhtr_A51Y/SAuD46fTQ0I/AAAAAAAAGaU/dObCE4Jx8H8/s320/jug-logo_124x77.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5191388009271083842" /&gt;&lt;/a&gt;Heiko W. Rupp of Red Hat (author of the &lt;a href="http://www.amazon.de/gp/product/3898643182?ie=UTF8&amp;tag=mytakeonthing-21&amp;linkCode=as2&amp;camp=1638&amp;creative=6742&amp;creativeASIN=3898643182"&gt;first German JBoss book&lt;/a&gt;) is going to talk about "System management with RHQ and JBoss ON 2" at the &lt;a href="http://jug-ka.de"&gt;Java Users Group Karlsruhe&lt;/a&gt; this coming Wednesday. The &lt;a href="http://www.jboss.com/products/jbosson"&gt;JBoss Operations Network (ON)&lt;/a&gt; management platform is a centralized system for managing the JBoss middleware. &lt;br /&gt;&lt;br /&gt;The talk starts at 7:15pm and takes place at &lt;a href="http://maps.google.de/maps?f=q&amp;hl=de&amp;geocode=&amp;q=Am+Fasanengarten+5,+Karlsruhe&amp;jsv=107&amp;sll=49.031752,8.454051&amp;sspn=0.008666,0.020685&amp;ie=UTF8&amp;t=h&amp;z=16&amp;iwloc=addr"&gt;University of Karlsruhe&lt;/a&gt;. Get the latest updates of the JUG-Ka at our &lt;a href="http://groups.google.com/group/jug-karlsruhe"&gt;Google Group&lt;/a&gt; or sign up for &lt;a href="https://www.xing.com/net/jug-karlsruhe/"&gt;XING&lt;/a&gt;. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-373129668325770155?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/373129668325770155/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=373129668325770155' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/373129668325770155'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/373129668325770155'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/03/heiko-w-rupp-jug-ka.html' title='Heiko W. Rupp @ JUG-Ka'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp3.blogger.com/_myUhtr_A51Y/SAuD46fTQ0I/AAAAAAAAGaU/dObCE4Jx8H8/s72-c/jug-logo_124x77.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-7246825293801572420</id><published>2009-03-02T06:14:00.000+01:00</published><updated>2009-03-08T13:53:36.315+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='quality'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><title type='text'>Book Review: Practical API Design</title><content type='html'>&lt;span style="font-style:italic;"&gt;&lt;a href="http://www.apress.com"&gt;Apress&lt;/a&gt; was kind enough to pass me a copy of this book, which I agreed to review in return.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://picasaweb.google.com/lh/photo/kq_xf6NJLZlpt52VbxUYPg?authkey=Da5qMLkXLCU"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://lh3.ggpht.com/_myUhtr_A51Y/SOdWQ2EY1EI/AAAAAAAAJpE/VVnq_razI0w/s144/cover_practical_api-design.png" border="0" alt=""id="" /&gt;&lt;/a&gt;&lt;a href="http://www.amazon.de/gp/product/1430209739?ie=UTF8&amp;tag=mytakeonthing-21&amp;linkCode=as2&amp;camp=1638&amp;creative=6742&amp;creativeASIN=1430209739"&gt;Practical API Design: Confessions of a Java Framework Architect&lt;/a&gt; is the book I've always been waiting for. &lt;a href="http://www.apidesign.org"&gt;Jaroslav Tulach&lt;/a&gt;, the founder and architect of &lt;a href="http://www.netbeans.org"&gt;NetBeans&lt;/a&gt;, created a highly recommend read for everyone in charge of developing APIs. &lt;br /&gt;&lt;br /&gt;But even if it's not your job to define interfaces, you are somewhat alway on the other side consuming them and it's good to know what drives evolution of the APIs you work with.&lt;br /&gt;&lt;br /&gt;The book consists of 3 parts:&lt;br /&gt;&lt;br /&gt;Part 1 is called &lt;em&gt;"Theory and Justification"&lt;/em&gt;. It defines the terminology and background which gives you the necessary foundation to explain and justify API design. &lt;br /&gt;&lt;br /&gt;Let me give you and example: Have you ever had problems explaining your design to a colleague? You couldn't find the right words to reasonably highlight your decision, but you know it was right? The only justification was your intuition and the feeling that your design was the right choice. Does that sound familiar? &lt;br /&gt;&lt;br /&gt;That's where Part 1 of this book comes in and tries to give you a tool to justify and even measure the quality of your design decisions: &lt;strong&gt;Selective Cluelessness&lt;/strong&gt;. It's a principle which is based on the assumption that you can achieve more by knowing less: &lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;"The more good APIs we have, the bigger the systems we'll be able to build without understanding all their details."&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;&lt;em&gt;"Practical Design"&lt;/em&gt; is Part 2 and basically puts the theory highlighted in the first part to practice. Using Java, the author provides a set of what he calls &lt;strong&gt;API design patterns&lt;/strong&gt;. They are design patterns in the traditional sense, but with a focus on evolution. Most of them accompanied by an examples the NetBeans APIs. It doesn't matter if you are not familiar with NetBeans. Due to the author's years of experience with NetBeans, he does a great job explaining the problems it was facing and how they came with a solution, which evolved into an API design pattern.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;..., it's slightly more complicated to design a universe than building a house. As a result, we need an enhanced version of the design patters. We need patterns that help us building a "universe".&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Part 3 of the book, &lt;strong&gt;"Daily Life"&lt;/strong&gt;, is a collection of advices on how to bugfix, evolve and maintain an API. The author points out how important versioning is and how NetBeans solves such a problem. He highlights the importance of compatibility and how to keep the promises to the users of your APIs. &lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;The theory and initial design are just the tip of the iceberg. The rest is the real work.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;I'm really glad I read this book, although it took me quite a while. The reason is not the 365 pages. It was all the moments I had during reading it. So many times I was &lt;a href="http://dlinsin.blogspot.com/2008/10/thoughts-on-api-design.html"&gt;reminded&lt;/a&gt; of &lt;a href="http://dlinsin.blogspot.com/2009/01/technical-and-business-aspects-in-one.html"&gt;mistakes&lt;/a&gt; I made in APIs I previously designed. I often &lt;a href="http://dlinsin.blogspot.com/2009/02/aspect-is-part-of-your-implementation.html"&gt;pondered&lt;/a&gt; on whether a solution suggested in the book was better than the one I came up with. I think that's what makes a book interesting, if it makes you think and critically review your own  solutions.&lt;br /&gt;&lt;br /&gt;I enjoyed taking the journey through the API universe and learning how to design an API in a selective cluelessness way. As for the reasons why you should read this book:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;... when you understand the API world, its needs and its laws of evolution, you can shape it into a form containing all that the creators of the oldest and most perfect science always searched for - beauty, truth and elegance. A properly API universe ain't a bad place to be.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-7246825293801572420?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/7246825293801572420/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=7246825293801572420' title='14 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/7246825293801572420'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/7246825293801572420'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/03/book-review-practical-api-design.html' title='Book Review: Practical API Design'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_myUhtr_A51Y/SOdWQ2EY1EI/AAAAAAAAJpE/VVnq_razI0w/s72-c/cover_practical_api-design.png' height='72' width='72'/><thr:total>14</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-6628916908588499846</id><published>2009-02-23T06:12:00.000+01:00</published><updated>2009-02-23T06:12:00.165+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='karlsruhe'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='jug-ka'/><title type='text'>JUG-Ka Stammtisch 2.0</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_myUhtr_A51Y/SAuD46fTQ0I/AAAAAAAAGaU/dObCE4Jx8H8/s1600-h/jug-logo_124x77.png"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://bp3.blogger.com/_myUhtr_A51Y/SAuD46fTQ0I/AAAAAAAAGaU/dObCE4Jx8H8/s320/jug-logo_124x77.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5191388009271083842" /&gt;&lt;/a&gt;The second Stammtisch of &lt;a href="http://jug-ka.de"&gt;Java Users Group Karlsruhe&lt;/a&gt; will be held on &lt;a href="https://www.xing.com/events/302395"&gt;Thursday 26th of February&lt;/a&gt;. We will meet up at &lt;a href="http://tinyurl.com/6stlm6"&gt;Marktplatz&lt;/a&gt; around 8pm to find a suitable location. If you'd like to join the geek chat about Java, check out the &lt;a href="http://groups.google.com/group/jug-karlsruhe"&gt;Google Group&lt;/a&gt; for further details or drop me an email.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;NOTE: Over the next few weeks, I'm moving my feed from feedburner back to blogspot so please subscribe to &lt;a href="http://dlinsin.blogspot.com/feeds/posts/default/"&gt;http://dlinsin.blogspot.com/feeds/posts/default/&lt;/a&gt; instead!&lt;/b&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-6628916908588499846?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/6628916908588499846/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=6628916908588499846' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/6628916908588499846'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/6628916908588499846'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/02/jug-ka-stammtisch-20.html' title='JUG-Ka Stammtisch 2.0'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp3.blogger.com/_myUhtr_A51Y/SAuD46fTQ0I/AAAAAAAAGaU/dObCE4Jx8H8/s72-c/jug-logo_124x77.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-6091754065448017704</id><published>2009-02-18T06:10:00.000+01:00</published><updated>2009-02-18T06:10:00.280+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='quality'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><title type='text'>An Aspect is Part of Your Implementation</title><content type='html'>When implementing parts of your application with AOP, you are magically applying additional behavior to your code. Although technically cool and in terms of reusability great, it might lead to a maintenance nightmare. &lt;br /&gt;&lt;br /&gt;Think of an API, let's call it &lt;em&gt;CustomerService&lt;/em&gt;. It defines CRUD methods to load and save &lt;em&gt;Customer&lt;/em&gt; instances from some backend service. It might define methods like the following:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;/**&lt;br /&gt; * blah&lt;br /&gt; *&lt;br /&gt; * @throws CustomerServiceException with following causes:&lt;br /&gt; *    - IllegalArgumentException if anything is wrong with the passed Customer instance&lt;br /&gt; *    - NullPointerException if anything is wrong with a collaborator&lt;br /&gt; *    - FancyPersistenceException if anything is wrong your persistence service&lt;br /&gt; */&lt;br /&gt;public Customer save(Customer argToSave) throws CustomerServiceException;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Note the &lt;a href="http://www.amazon.de/gp/product/0321356683?ie=UTF8&amp;tag=mytakeonthing-21&amp;linkCode=as2&amp;camp=1638&amp;creative=6742&amp;creativeASIN=0321356683"&gt;standard&lt;/a&gt; Javadocs, which &lt;a href="http://dlinsin.blogspot.com/2008/06/does-your-api-docs-leave-users-in-dark.html"&gt;define precisely&lt;/a&gt; the causes of a &lt;em&gt;CustomerServiceException&lt;/em&gt;, which the client of this API can expect. These defined causes as well as the &lt;em&gt;CustomerServiceException&lt;/em&gt; are part of the contract defined by the interface. Clients can program against those causes e.g. display different error messages or inform another system. &lt;br /&gt;&lt;br /&gt;An implementation of this interface might look as the following:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;@Override&lt;br /&gt;public Customer save(Customer argToSave) throws CustomerServiceException {&lt;br /&gt;  try {&lt;br /&gt;    // do some fancy stuff&lt;br /&gt;  } catch (IllegalArgumentException e) {&lt;br /&gt;    &lt;br /&gt;    // do some logging here&lt;br /&gt;    throw new CustomerServiceException(e);  &lt;br /&gt;  &lt;br /&gt;  } catch (NullPointerException e) {&lt;br /&gt;    &lt;br /&gt;    // this is programming error&lt;br /&gt;    throw new CustomerServiceException(e, "Dude where's your head?");  &lt;br /&gt;  &lt;br /&gt;  } catch (FancyPersistenceException e) {&lt;br /&gt;    &lt;br /&gt;    // you might want to do some transaction handling stuff here&lt;br /&gt;    throw new CustomerServiceException(e);  &lt;br /&gt;  &lt;br /&gt;  } catch (Exception e) {&lt;br /&gt;    throw new CustomerServiceException();  &lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This is pretty ugly isn't it? Especially because of the verbose &lt;em&gt;Exception&lt;/em&gt; handling. However it's complying with the contract and a valid implementation. &lt;br /&gt;&lt;br /&gt;In my current project we followed this pattern in every service class throughout the application. This works fine and provides a lot of consistency for clients of those APIs. However making a commitment like this requires not only a lot of discipline, when implementing those contracts, but also a lot of test code. You have to make sure that each implementation complies with the contracts promised in its interface. &lt;br /&gt;&lt;br /&gt;A new member of the team immediately pointed out the drawbacks of our approach: a lot of test code to maintain, a lot of verbose &lt;em&gt;Exception&lt;/em&gt; handling polluting the implementation methods and no centralized way to manage the same recurring code. His idea was to use &lt;a href="http://dlinsin.blogspot.com/2008/12/introduction-to-aop-jug-ka.html"&gt;AOP&lt;/a&gt;. You can refactor the &lt;em&gt;Exception&lt;/em&gt; handling into an &lt;a href="http://en.wikipedia.org/wiki/Aspect"&gt;Aspect&lt;/a&gt; and thus get rid of all the drawbacks mentioned before.&lt;br /&gt;&lt;br /&gt;The implementation of the &lt;em&gt;CustomerService&lt;/em&gt; interface boils down to the following:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;@Override&lt;br /&gt;public Customer save(Customer argToSave) throws CustomerServiceException {&lt;br /&gt;     // do some fancy stuff&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;For a moment this might look like a perfect solution. The ugly technical &lt;em&gt;Exception&lt;/em&gt; handling is gone and we are left with the code that actually gets the job done. That makes reading the code so much easier and don't think &lt;a href="http://dlinsin.blogspot.com/2008/02/reading-code-or-writing-code-ii.html"&gt;I'm the only fan&lt;/a&gt; of that. &lt;br /&gt;&lt;br /&gt;Despite of all the drawbacks we go rid of, we silently introduced new ones. The fact that the implementation doesn't contain any hard reference to those &lt;em&gt;Exceptions&lt;/em&gt; we claim to throw in the interface, makes understanding the implementation of the contract a lot harder. Not to mention the code for the &lt;em&gt;Exception&lt;/em&gt; handling itself. Another drawback is refactoring the interface. Imagine someone unfamiliar with the code has to change one of the &lt;em&gt;Exception&lt;/em&gt; causes. It is merely impossible to understand what's going on. &lt;br /&gt;&lt;br /&gt;You have to consider the Aspect containing the logic to handle the Exception as of your implementation. Without it your implementation doesn't comply with the contract. &lt;br /&gt;&lt;br /&gt;So what to do now? We have a great solution, which relieves us from all the drawbacks but brings along another bag of drawbacks. Should we gauge which drawbacks are easier to deal with? I remembered a solution I read in &lt;a href="http://www.amazon.de/gp/product/0131489062?ie=UTF8&amp;tag=mytakeonthing-21&amp;linkCode=as2&amp;camp=1638&amp;creative=6742&amp;creativeASIN=0131489062"&gt;Applying UML and Patterns&lt;/a&gt;, which suggests a &lt;a href="http://dlinsin.blogspot.com/2007/04/lazy-loading-singleton.html"&gt;Singleton&lt;/a&gt; based centralized error handling. So I came up with the following solution:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public final class ExceptionHandler {&lt;br /&gt;  // private constructor&lt;br /&gt;  public static final &amp;lt;T extends BaseException&amp;gt; transform(Exception argCause, Class&amp;lt;T&amp;gt; argType) {&lt;br /&gt;    try {&lt;br /&gt;      if (argType.isAssignableFrom(argCause.getClass())) {&lt;br /&gt;        return (T) argCause;&lt;br /&gt;      } else if (argCause instanceof NullPointerException) {&lt;br /&gt;        return argType.getConstructor(Throwable.class, String.class).newInstance(argCause,"Dude where's your head?");&lt;br /&gt;      } else if (argCause instanceof FancyPersistenceException) {&lt;br /&gt;        return argType.getConstructor(Throwable.class).newInstance(argCause);&lt;br /&gt;      } else {&lt;br /&gt;        return argType.getConstructor(Throwable.class, String.class).newInstance(argCause,"Unkown!");&lt;br /&gt;      }     &lt;br /&gt;   } catch (Exception e) {/** do some reflection error handling **/}&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The implementation of the &lt;em&gt;CustomeService&lt;/em&gt; interface using the centralized &lt;em&gt;ExceptionHandler&lt;/em&gt; looks like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;@Override&lt;br /&gt;public Customer save(Customer argToSave) throws CustomerServiceException {&lt;br /&gt;  try {&lt;br /&gt;     // do some fancy stuff&lt;br /&gt;  } catch (Exception e) {&lt;br /&gt;    throw ExceptionHandler.transform(e, CustomerServiceException.class);  &lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I believe this is a great trade-off. You have a single point in your code responsible for handling Exceptions. It's easy to test in isolation, which means the test code for your services decrease. The overhead in your service methods is minimal. You have to implement a &lt;em&gt;try-catch&lt;/em&gt; block, but I think that's reasonable considering the benefits of this approach. &lt;br /&gt;&lt;br /&gt;However, I think the biggest advantage of this solution is the fact that it works without any framework magic whatsoever. It's just plain old Java!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;NOTE: Over the next few weeks, I'm moving my feed from feedburner back to blogspot so please subscribe to &lt;a href="http://dlinsin.blogspot.com/feeds/posts/default/"&gt;http://dlinsin.blogspot.com/feeds/posts/default/&lt;/a&gt; instead!&lt;/b&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-6091754065448017704?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/6091754065448017704/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=6091754065448017704' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/6091754065448017704'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/6091754065448017704'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/02/aspect-is-part-of-your-implementation.html' title='An Aspect is Part of Your Implementation'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-5748693049914941932</id><published>2009-02-16T05:16:00.000+01:00</published><updated>2009-02-16T05:16:00.362+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='spring'/><title type='text'>Spring Integration in 10 minutes</title><content type='html'>&lt;a href="http://www.springsource.com"&gt;SpringSource's Mark Fisher&lt;/a&gt; posted a &lt;a href="http://blog.springsource.com/2009/02/13/982/"&gt;tutorial&lt;/a&gt; on &lt;a href="http://www.springsource.org/spring-integration"&gt;Spring Integration&lt;/a&gt; a couple of days ago. I followed the tutorial step-by-step and thought I'd share &lt;a href="http://download.linsin.de/SpringIntegrationSample.zip"&gt;my IDEA project&lt;/a&gt; with you. I also added a &lt;a href="http://maven.apache.org"&gt;maven POM&lt;/a&gt; file so you can also import it into Eclipse or use the command line.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;NOTE: Over the next few weeks, I'm moving my feed from feedburner back to blogspot so please subscribe to &lt;a href="http://dlinsin.blogspot.com/feeds/posts/default/"&gt;http://dlinsin.blogspot.com/feeds/posts/default/&lt;/a&gt; instead!&lt;/b&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-5748693049914941932?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/5748693049914941932/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=5748693049914941932' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/5748693049914941932'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/5748693049914941932'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/02/spring-integration-in-10-minutes.html' title='Spring Integration in 10 minutes'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-2262600873117631127</id><published>2009-02-10T06:45:00.000+01:00</published><updated>2009-02-10T06:45:10.160+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='karlsruhe'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='jug-ka'/><title type='text'>Heinz Kabutz @ JUG-Ka</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_myUhtr_A51Y/SAuD46fTQ0I/AAAAAAAAGaU/dObCE4Jx8H8/s1600-h/jug-logo_124x77.png"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://bp3.blogger.com/_myUhtr_A51Y/SAuD46fTQ0I/AAAAAAAAGaU/dObCE4Jx8H8/s320/jug-logo_124x77.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5191388009271083842" /&gt;&lt;/a&gt;Im really excited about this Wednesday's meeting of the &lt;a href="http://jug-ka.de"&gt;Java Users Group Karlsruhe&lt;/a&gt;. Dr Heinz Kabutz is giving a to talk on "The Secrets of Concurrency". &lt;br /&gt;&lt;br /&gt;You might know the &lt;a href="http://www.javaspecialists.eu/"&gt;The Java(tm) Specialists' Newsletter&lt;/a&gt;, which is most famous for a series of laws, explaining how to best write concurrent code in Java. Heinz is the mastermind behind those laws and the newsletter.&lt;br /&gt;&lt;br /&gt;The talk takes place at &lt;a href="http://maps.google.de/maps?f=q&amp;hl=de&amp;geocode=&amp;q=Am+Fasanengarten+5,+Karlsruhe&amp;jsv=107&amp;sll=49.031752,8.454051&amp;sspn=0.008666,0.020685&amp;ie=UTF8&amp;t=h&amp;z=16&amp;iwloc=addr"&gt;University of Karlsruhe&lt;/a&gt; and it starts at 7:15pm. Sign up for our &lt;a href="http://groups.google.com/group/jug-karlsruhe"&gt;Google Group&lt;/a&gt; or join us on &lt;a href="https://www.xing.com/net/jug-karlsruhe/"&gt;XING&lt;/a&gt; to get the latest &lt;a href="http://it-republik.de/jaxenter/news/Vortrag-der-JUG-Karlsruhe-047208.html"&gt;updates&lt;/a&gt; on what's happing at the &lt;a href="http://jug-ka.de"&gt;Java Users Group Karlsruhe&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;NOTE: Over the next few weeks, I'm moving my feed from feedburner back to blogspot so please subscribe to &lt;a href="http://dlinsin.blogspot.com/feeds/posts/default/"&gt;http://dlinsin.blogspot.com/feeds/posts/default/&lt;/a&gt; instead!&lt;/b&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-2262600873117631127?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/2262600873117631127/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=2262600873117631127' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/2262600873117631127'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/2262600873117631127'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/02/heinz-kabutz-jug-ka.html' title='Heinz Kabutz @ JUG-Ka'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp3.blogger.com/_myUhtr_A51Y/SAuD46fTQ0I/AAAAAAAAGaU/dObCE4Jx8H8/s72-c/jug-logo_124x77.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-4415525776222077340</id><published>2009-02-09T06:03:00.000+01:00</published><updated>2009-02-09T06:03:00.976+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='blog'/><category scheme='http://www.blogger.com/atom/ns#' term='google'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Got Burned, Changing my feed!!</title><content type='html'>My current feed &lt;a href="http://dlinsin.blogspot.com/2007/04/feedburner.html"&gt;is run by FeedBurner&lt;/a&gt;. That means most of you are getting this feed from http://feeds.feedburner.com/dlinsin and thanks to &lt;a href="http://www.feedburner.com"&gt;FeedBurner&lt;/a&gt; you see stuff like "Digg This!" or "Submit to Reddit" at the bottom of each post. &lt;br /&gt;&lt;br /&gt;FeedBurner was acquired by Google and is moving all accounts to Google Adsense by the end of this month. Since Google doesn't want me to use Adsense, I can only switch back to my old feed. So if you want to keep reading my stuff, simply switch to &lt;a href="http://dlinsin.blogspot.com/feeds/posts/default/"&gt;http://dlinsin.blogspot.com/feeds/posts/default/&lt;/a&gt;. However, the FeedBurner feed will still be alive for the next couple of weeks.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-4415525776222077340?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/4415525776222077340/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=4415525776222077340' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/4415525776222077340'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/4415525776222077340'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/02/got-burned-changing-my-feed.html' title='Got Burned, Changing my feed!!'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-4632701058806515517</id><published>2009-02-02T06:04:00.000+01:00</published><updated>2009-02-02T06:34:34.080+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='quality'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>Do You Write Unit Tests For Yourself?</title><content type='html'>A couple of days ago I listened to the &lt;a href="http://blog.stackoverflow.com/2009/01/podcast-39/"&gt;stackoverflow podcast&lt;/a&gt; with &lt;a href="http://codinghorror.com/"&gt;Jeff Atwood&lt;/a&gt; and &lt;a href="http://www.joelonsoftware.com/AboutMe.html"&gt;Joel Spolsky&lt;/a&gt;. They were talking about when it is appropriate to write unit tests for your code. &lt;br /&gt;&lt;br /&gt;Joel:&lt;br /&gt;&lt;blockquote&gt;"Changing a parameter in some function call from 37 to a 9 is not really gonna fail or introduce a bug"&lt;br /&gt;"It's great if you use TDD for your encryption library or compiler, but that's a very finite number of apps that are like that"&lt;br /&gt;"For a team of good programmers the prescriptions are probably gonna be different than from a team of mediocre programmers"&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Jeff:&lt;br /&gt;&lt;blockquote&gt;"With stackoverflow there are very few cases where I feel like: okay I'm gonna sit down and write a unit test and this is gonna result in a measurably better experience."&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Of course I cannot &lt;a href="http://www.joelonsoftware.com/items/2009/01/31.html"&gt;quote&lt;/a&gt; the whole discussion here, so this might sound very harsh on testing for you. Jeff kind of put their statements into perspective by saying that he is pro testing, but just doesn't do it on stackoverflow. However, I still have this feeling that they turned their backs on unit testing too easily.&lt;br /&gt;&lt;br /&gt;First off, let me tell you that I love unit tests. I write a lot of them and I care about code coverage quite a bit. You might even say that I am a little religious or dogmatic about it. I only have good experience with unit testing, so I tend to believe that I am not that far off. On my current project we are about 8 people working on our code base, so you can imagine there is a lot going on. It's easy to break something and believe me we are breaking stuff all the time. Although each developer has &lt;a href="http://dlinsin.blogspot.com/2008/05/do-you-care-about-your-code.html"&gt;his domain&lt;/a&gt;, you are constantly working with code that was not written by yourself. &lt;br /&gt;&lt;br /&gt;Fortunately our test coverage is pretty good. In case a test fails and something seems to be broken, you are being notified by the continuous integration server. Personally that gives me the confidence to do refactorings and changes without the fear of introducing major bugs. The biggest downside, which was also mentioned by Joel, is the "bang for the bucks". You need a big investment to get to this degree of test coverage. You really need to gauge, whether it's worth the time invested to write a unit test for a piece of code. &lt;br /&gt;&lt;br /&gt;As mentioned before there is a lot of stuff breaking during development of new features. This is mainly due to &lt;a href="http://dlinsin.blogspot.com/2007/12/code-base-tourist.html"&gt;not knowing all of the code base&lt;/a&gt;. However, I think working with other developers, you are bound to have unit tests. At least for the parts of your code that might be extended or maintained by others. It is worth to invest in a unit test, if you want to make life easier for your fellow developers. At least they can change your code with confidence. You are basically giving them a guideline, which helps to get their work done faster and more reliable. In the long run, I think that will increase the quality of your application and hopefully leaves an impression of your code on your fellow team members.&lt;br /&gt;&lt;br /&gt;So what if you are working on a small team or almost alone on your project? Do you still need unit tests? That's when I want to come back to the discussion Joel and Jeff had. The &lt;a href="http://stackoverflow.com/about"&gt;stackoverflow team&lt;/a&gt; is quite small. I think Jeff probably thinks, that if you are working alone on a project it might not be worth the investment to write unit tests. &lt;br /&gt;&lt;br /&gt;I kind of disagree, because I've been there myself. As the only developer of an application and working on various features at the same time, I found myself breaking my own features over and over again. I started &lt;a href="http://dlinsin.blogspot.com/2008/09/how-to-do-continuous-integration-and.html"&gt;writing unit tests and setup a continuous integration&lt;/a&gt; server, which helped me automate my builds and improved quality dramatically. By the time a second developer started working on the project the test coverage was reasonable enough, so he could change my code without the fear of breaking anything. &lt;br /&gt;&lt;br /&gt;Maybe I'm just not that good of a developer and I need to write unit tests for myself. However, I do think it's worth the investment, because sooner or later another developer will have to work with your code. Go and make his &lt;a href="http://dlinsin.blogspot.com/2006/03/cowboy-coding.html"&gt;life a little easier&lt;/a&gt; and provide some unit tests. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-4632701058806515517?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/4632701058806515517/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=4632701058806515517' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/4632701058806515517'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/4632701058806515517'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/02/do-you-write-unit-tests-for-yourself.html' title='Do You Write Unit Tests For Yourself?'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-915614859717416300</id><published>2009-01-23T06:07:00.000+01:00</published><updated>2009-02-01T22:04:42.887+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='jug-ka'/><title type='text'>Java 7 Language Changes Poll Results</title><content type='html'>Inspired by &lt;a href="http://www.jroller.com/scolebourne"&gt;Stephen Colebourne&lt;/a&gt;'s &lt;a href="http://www.devoxx.com/display/JV08/Whiteboards"&gt;whiteboard&lt;/a&gt; initiative at &lt;a href="http://www.devoxx.com"&gt;Devoxx&lt;/a&gt;, I started a similar &lt;a href="http://dlinsin.blogspot.com/2008/12/java-7-language-changes-poll.html"&gt;poll&lt;/a&gt; on new Java language feature about a month ago. I sent an invite to the &lt;a href="http://www.jug-ka.de"&gt;local JUG&lt;/a&gt; and posted it on &lt;a href="http://dlinsin.blogspot.com/2008/12/java-7-language-changes-poll.html"&gt;DZone&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I'm really surprised, that over 200 fellow developers participated! Thanks to everyone for voting! &lt;br /&gt;&lt;br /&gt;Now let's check out the results. First of all I gave you the choice to let me know how you found this poll. &lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh3.ggpht.com/_myUhtr_A51Y/SXgMJpjM5BI/AAAAAAAAMOs/xPlZM6jPJI0/chart.png?imgmax=800" alt="chart.png" border="0" width="300" height="150" /&gt;&lt;br /&gt;&lt;br /&gt;There were 80 votes submitted through my blog and about 50% of all submissions came through various other channels like DZone or &lt;a href="http://java7.tumblr.com/post/65960563/david-linsin-java-7-language-changes-poll"&gt;Alex Miller's Java 7 blog&lt;/a&gt;. I'm a little disappointed about the participation of our JUG. Only 22 people voted, although there are about 173 members signed up in our &lt;a href="http://goups.google.com/group/jug-karlsruhe"&gt;Google Group&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Unfortunately about 10% of the votes are invalid. Maybe I should have put the instructions on the blog post directly instead of using &lt;a href="http://docs.google.com/Presentation?id=ajkb8rnkwjk5_165g8wpxhf5"&gt;Stephen's presentation&lt;/a&gt;. However, there are 181 correct votes and those are used in the following charts:&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;1. Map for-each&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://spreadsheets.google.com/pub?key=pmEYSbIMy5z8k2NlD4WguMQ&amp;oid=12&amp;output=image" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;2. For-each iteration control&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://spreadsheets.google.com/pub?key=pmEYSbIMy5z8k2NlD4WguMQ&amp;oid=3&amp;output=image" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;3. List / Map access&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://spreadsheets.google.com/pub?key=pmEYSbIMy5z8k2NlD4WguMQ&amp;oid=4&amp;output=image" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;4. Infer generics in declarations&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://spreadsheets.google.com/pub?key=pmEYSbIMy5z8k2NlD4WguMQ&amp;oid=5&amp;output=image" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;5. Multi-catch of Exceptions&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://spreadsheets.google.com/pub?key=pmEYSbIMy5z8k2NlD4WguMQ&amp;oid=6&amp;output=image" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;6. String Switch&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://spreadsheets.google.com/pub?key=pmEYSbIMy5z8k2NlD4WguMQ&amp;oid=8&amp;output=image" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;7. String interpolation&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://spreadsheets.google.com/pub?key=pmEYSbIMy5z8k2NlD4WguMQ&amp;oid=7&amp;output=image" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;8. Multi-line Strings&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://spreadsheets.google.com/pub?key=pmEYSbIMy5z8k2NlD4WguMQ&amp;oid=9&amp;output=image" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;9. Resource Management&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://spreadsheets.google.com/pub?key=pmEYSbIMy5z8k2NlD4WguMQ&amp;oid=10&amp;output=image" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;10. Null-handling&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://spreadsheets.google.com/pub?key=pmEYSbIMy5z8k2NlD4WguMQ&amp;oid=11&amp;output=image" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;It looks like most people wanna see an &lt;strong&gt;improved &lt;em&gt;Null&lt;/em&gt; handling (change no 10)&lt;/strong&gt; in the coming Java 7 release. The improved &lt;strong&gt;List/Map access (change no 3)&lt;/strong&gt; on the other hand is something that almost all the correct voters oppose. If you are interested in the cleansed results, I published as a separate &lt;a href="http://spreadsheets.google.com/pub?key=pmEYSbIMy5z8k2NlD4WguMQ&amp;gid=10"&gt;website&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I thought a lot about how I should interpret these votes. Frankly, I'm a little surprised about the results. I didn't think that so many fellow developers would like to see an &lt;strong&gt;improved &lt;em&gt;Null&lt;/em&gt; handling&lt;/strong&gt;. However, the &lt;a href="http://www.jroller.com/scolebourne/entry/devoxx_2008_whiteboard_votes"&gt;votes at Devoxx&lt;/a&gt; indicated the same and I do admit &lt;em&gt;Null&lt;/em&gt; handling can be a huge &lt;a href="http://en.wiktionary.org/wiki/PITA"&gt;PITA&lt;/a&gt;. What I am kind of shocked about is the huge resistance to closures-related features like &lt;strong&gt;resource management (change no 9)&lt;/strong&gt; or &lt;strong&gt;Map for-each (change no 1)&lt;/strong&gt;. Maybe this indicates that a lot of developers are &lt;a href="http://dlinsin.blogspot.com/2008/09/series-reflections-put-closures-into.html"&gt;not yet ready for closures&lt;/a&gt; in Java. After all, it might have been a reasonable decision of Sun to drop closures - for now.&lt;br /&gt;&lt;br /&gt;So, what am I gonna do with those votes? Well, I'll email them to &lt;a href="http://blogs.sun.com/darcy"&gt;Joe Darcy&lt;/a&gt; and hope Sun will include your votes into their decision-making process. I know these votes only speak for a very small fraction of the Java community, but I think every vote counts.&lt;br /&gt;&lt;br /&gt;Thanks again everyone for voting!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-915614859717416300?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/915614859717416300/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=915614859717416300' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/915614859717416300'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/915614859717416300'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/01/java-7-language-changes-poll-results.html' title='Java 7 Language Changes Poll Results'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_myUhtr_A51Y/SXgMJpjM5BI/AAAAAAAAMOs/xPlZM6jPJI0/s72-c/chart.png?imgmax=800' height='72' width='72'/><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-2107887540002633585</id><published>2009-01-13T07:06:00.000+01:00</published><updated>2009-01-13T07:06:05.111+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='karlsruhe'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='jug-ka'/><title type='text'>JUG-Ka Stammtisch(BETA)</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_myUhtr_A51Y/SAuD46fTQ0I/AAAAAAAAGaU/dObCE4Jx8H8/s1600-h/jug-logo_124x77.png"&gt;&lt;img style="float:left;margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://bp3.blogger.com/_myUhtr_A51Y/SAuD46fTQ0I/AAAAAAAAGaU/dObCE4Jx8H8/s320/jug-logo_124x77.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5191388009271083842" /&gt;&lt;/a&gt;This coming Thursday at 7pm, the &lt;a href="http://jug-ka.de"&gt;Java Users Group Karlsruhe&lt;/a&gt; will meet up for beer and chatting about Java at &lt;a href="http://tinyurl.com/6stlm6"&gt;Marktplatz&lt;/a&gt;. If you'd like to join, check out the &lt;a href="http://groups.google.com/group/jug-karlsruhe"&gt;Google Group&lt;/a&gt; for further details or drop me an email.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/17885082-2107887540002633585?l=dlinsin.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dlinsin.blogspot.com/feeds/2107887540002633585/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=17885082&amp;postID=2107887540002633585' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/2107887540002633585'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/17885082/posts/default/2107887540002633585'/><link rel='alternate' type='text/html' href='http://dlinsin.blogspot.com/2009/01/jug-ka-stammtischbeta.html' title='JUG-Ka Stammtisch(BETA)'/><author><name>David Linsin</name><uri>http://www.blogger.com/profile/12280104990941617395</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://1.bp.blogspot.com/_myUhtr_A51Y/TM8GhvWJUHI/AAAAAAAATk4/QzoBlmmlt8I/S220/1_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp3.blogger.com/_myUhtr_A51Y/SAuD46fTQ0I/AAAAAAAAGaU/dObCE4Jx8H8/s72-c/jug-logo_124x77.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-17885082.post-297986735362094484</id><published>2009-01-12T06:21:00.000+01:00</published><updated>2009-01-12T06:21:03.005+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><title type='text'>Technical and Business Aspects in one Domain Model</title><content type='html'>We recently had a discussion at work about mixing technical and business aspects in your domain model. In the following post, I want to elaborate on why we had this argument, how we solved it and what conclusion I drew from this discussion.&lt;br /&gt;&lt;br /&gt;In our domain model we have a &lt;em&gt;Customer&lt;/em&gt; class, which has multiple &lt;em&gt;Contracts&lt;/em&gt;. In order to keep it simple let's say the &lt;em&gt;Customer&lt;/em&gt; class has a member called &lt;em&gt;contracts&lt;/em&gt; of type &lt;em&gt;Collection&lt;/em&gt;. &lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh5.ggpht.com/_myUhtr_A51Y/SWWodUFKqzI/AAAAAAAAL_8/gpYOwf7ZakI/uml_cust_contract.png?imgmax=800" alt="uml_cust_contract.png" border="0" width="433" height="95" /&gt;&lt;br /&gt;&lt;br /&gt;Our application is an Eclipse RCP client with a Java EE backend. In order to support the "Eclipse way", we allow the user of our application to modify multiple contracts of a customer and save those in one batch. Since those &lt;em&gt;Contract&lt;/em&gt; instances only live within the context of a &lt;em&gt;Customer&lt;/em&gt;, we pass the &lt;em&gt;Customer&lt;/em&gt; instance for persistent storage to the server-side and that's where the fun begins. &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Customer alfons = customerServic
