<?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-20427072</id><updated>2012-01-19T13:48:10.351Z</updated><category term='facebook'/><category term='zoomaroundtown'/><category term='diy'/><category term='mendeley'/><category term='javascript'/><category term='lastfm'/><category term='photography'/><category term='php'/><category term='redis'/><category term='riot'/><category term='testbed'/><category term='hong kong'/><category term='freebie'/><category term='gwt'/><category term='music'/><category term='google gadget'/><category term='8tracks'/><category term='india'/><category term='chrome extension'/><category term='cloud'/><category term='django'/><category term='gae'/><category term='software development'/><category term='design pattern'/><category term='jquery'/><category term='africa'/><category term='climbing'/><category term='desert island'/><category term='osgi'/><category term='download'/><category term='travel'/><category term='google map'/><category term='python'/><category term='geeky'/><category term='spring'/><category term='central america'/><category term='twitter'/><category term='europe'/><category term='cycling'/><category term='xss'/><category term='unicode'/><category term='greenwich'/><category term='london'/><category term='json'/><title type='text'>random thoughts...</title><subtitle type='html'>on rockclimbing, cycling, writing software, photography, music...</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>see wah cheng</name><uri>https://profiles.google.com/106402076208124215773</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-NyHjUGqj-k4/AAAAAAAAAAI/AAAAAAAAAAA/OpTwOW0esiE/s512-c/photo.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>51</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-20427072.post-5656945829159625626</id><published>2011-12-29T19:12:00.002Z</published><updated>2011-12-30T19:47:09.766Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='software development'/><category scheme='http://www.blogger.com/atom/ns#' term='mendeley'/><category scheme='http://www.blogger.com/atom/ns#' term='geeky'/><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><title type='text'>ShinyField - a placeholder jQuery plugin</title><content type='html'>&lt;script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"&gt;&lt;/script&gt;&lt;script src="https://raw.github.com/seewah/ShinyField/master/jquery.shinyfield.js" type="text/javascript"&gt;&lt;/script&gt;&lt;style type="text/css"&gt;.shiny-field-wrapper { position: relative; height: 36px; width: 292px; border: 1px solid #a2a2a2; border-radius: 4px; -moz-box-shadow: inset 0 1px 2px rgba(0,0,0,0.1), 0 1px 0 rgba(255,255,255,0.2); -webkit-box-shadow: inset 0 1px 2px rgba(0,0,0,0.1), 0 1px 0 rgba(255,255,255,0.2); box-shadow: inset 0 1px 2px rgba(0,0,0,0.1), 0 1px 0 rgba(255,255,255,0.2); background: white;}.shiny-field-wrapper.in-focus { border: 1px solid #5897fb; -moz-box-shadow: 0 0 5px rgba(0,0,0,0.3); -webkit-box-shadow: 0 0 5px rgba(0,0,0,0.3); box-shadow: 0 0 5px rgba(0,0,0,0.3);}.shiny-field-wrapper input { outline: none; position: absolute; z-index: 2; width: 272px; padding: 0; margin: 8px 10px 8px 10px; border: 0; font-family: Arial; font-size: 16px; background:  none; -webkit-appearance: none; /* to stop ipad safari from applying a text shadow! */}.shiny-field-hint { display: none; position: absolute; z-index: 1; width: 272px; padding: 8px 10px 8px 10px; font-family: Arial; font-size: 16px; color: #aaa; cursor: text;}.shiny-field-wrapper.in-focus .shiny-field-hint { color: #c7c7c7;}/* example 1 */#example1 { width: 100px; height: 24px; }#example1 input { margin: 5px; width: 90px; font-size: 11px; }#example1 .shiny-field-hint { padding: 5px; width: 90px; font-size: 11px; line-height: normal; }/* example 2 */#example2.in-focus { border-color: red; }/* example 3 */#example3 { display: inline-block; }&lt;/style&gt;A simple jQuery plugin, originally developed for the &lt;a href="http://www.mendeley.com/research/essence-javascript/"&gt;Mendeley website&lt;/a&gt;, to&lt;br /&gt;&lt;br /&gt;&lt;div&gt;1) implement &lt;b&gt;consistent&lt;/b&gt; HTML5 input (text/password) placeholder hinting behaviour across all major browsers, including IE 7+! (Note that having a placeholder value is optional)&lt;/div&gt;&lt;br /&gt;&lt;div&gt;2) apply beautiful CSS3 styling such as border-radius and box-shadow on the inputs. Sorry, IE users will not get those lovely rounded corners.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;ShinyField &lt;a href="https://github.com/seewah/ShinyField/"&gt;source&lt;/a&gt; and &lt;a href="https://github.com/seewah/ShinyField/downloads"&gt;download&lt;/a&gt; on GitHub&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Live demo (&lt;a href="https://raw.github.com/seewah/ShinyField/master/demo.html"&gt;html source&lt;/a&gt;):&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Turns&lt;/div&gt;&lt;div&gt;&lt;input name="ugly-text" placeholder="Your name ..." /&gt;&lt;/div&gt;&lt;div&gt;into&lt;/div&gt;&lt;div&gt;&lt;input name="pretty-text" placeholder="Your name ..." /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;Turns&lt;/div&gt;&lt;div&gt;&lt;input name="ugly-password" placeholder="Password ..." type="password" /&gt;&lt;/div&gt;&lt;div&gt;into&lt;/div&gt;&lt;div&gt;&lt;input name="pretty-password" placeholder="Password ..." type="password" /&gt;&lt;/div&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="color: #274e13;"&gt;Customization examples&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Every look-and-feel aspect of this plugin can be customized via CSS. Here are some common examples:&lt;br /&gt;&lt;br /&gt;1) To change the dimensions of the input (default is 292px by 36px, with 10px by 8px "inner padding")&lt;br /&gt;&lt;br /&gt;&lt;input name="pretty-text-example1" placeholder="Example 1" /&gt;&lt;br /&gt;&lt;pre style="background-image: URL(http://2.bp.blogspot.com/_z5ltvMQPaa8/SjJXr_U2YBI/AAAAAAAAAAM/46OqEP32CJ8/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt; // JS  &lt;br /&gt; $("input[name=pretty-text-example1]").shinify({"wrapperId": "example1"});  &lt;br /&gt; // CSS  &lt;br /&gt; #example1 { width: 100px; height: 24px; }  &lt;br /&gt; #example1 input { margin: 5px; width: 90px; font-size: 11px; } /* note that you have to override MARGIN here */  &lt;br /&gt; #example1 .shiny-field-hint { padding: 5px; width: 90px; font-size: 11px; } /* note that you have to override PADDING here */  &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;2) To change "in focus" border colour&lt;br /&gt;&lt;br /&gt;&lt;input name="pretty-text-example2" placeholder="Example 2" /&gt;&lt;br /&gt;&lt;pre style="background-image: URL(http://2.bp.blogspot.com/_z5ltvMQPaa8/SjJXr_U2YBI/AAAAAAAAAAM/46OqEP32CJ8/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt; // JS  &lt;br /&gt; $("input[name=pretty-text-example2]").shinify({"wrapperId": "example2"});  &lt;br /&gt; // CSS  &lt;br /&gt; #example2.in-focus { border-color: red; }  &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;3) To make the wrapper an "inline block" element&lt;br /&gt;&lt;br /&gt;blah...&lt;input name="pretty-text-example3" placeholder="Example 3" /&gt;&lt;br /&gt;&lt;pre style="background-image: URL(http://2.bp.blogspot.com/_z5ltvMQPaa8/SjJXr_U2YBI/AAAAAAAAAAM/46OqEP32CJ8/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt; // JS  &lt;br /&gt; $("input[name=pretty-text-example3]").shinify({"wrapperId": "example3"});  &lt;br /&gt; // CSS  &lt;br /&gt; #example3 { display: inline-block; }  &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="color: #999999;"&gt;Special thanks to &lt;a href="http://twitter.com/#!/AndrewOfficer"&gt;@AndrewOfficer&lt;/a&gt; and &lt;a href="http://twitter.com/#!/subcide"&gt;@subcide&lt;/a&gt;, of the Mendeley UX team, for design input and advice. &lt;/span&gt;&lt;script&gt;$(function() {  $("input[name=pretty-text]").shinify();  $("input[name=pretty-password]").shinify();  $("input[name=pretty-text-example1]").shinify({"wrapperId": "example1"});  $("input[name=pretty-text-example2]").shinify({"wrapperId": "example2"});  $("input[name=pretty-text-example3]").shinify({"wrapperId": "example3"});});&lt;/script&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-5656945829159625626?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/5656945829159625626/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=5656945829159625626' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/5656945829159625626'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/5656945829159625626'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2011/12/shinyfield-placeholder-jquery-plugin.html' title='ShinyField - a placeholder jQuery plugin'/><author><name>see wah cheng</name><uri>https://profiles.google.com/106402076208124215773</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-NyHjUGqj-k4/AAAAAAAAAAI/AAAAAAAAAAA/OpTwOW0esiE/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-1982090067823674015</id><published>2011-12-28T20:55:00.001Z</published><updated>2011-12-31T00:59:08.494Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='cycling'/><category scheme='http://www.blogger.com/atom/ns#' term='diy'/><title type='text'>Bullhorn Conversion - Chop and Flop</title><content type='html'>I have been meaning to swap the &lt;a href="http://www.flickr.com/photos/seewah/4956832130/"&gt;dropbar&lt;/a&gt; on my commuter singlespeed for a bullhorn bar for a while. Over the festive period, after reading a few articles on the web, I finally decided to buy a £2.50 hacksaw and "chop and flop" the bar, i.e. chop off the "drops" and invert the bar.&lt;br /&gt;&lt;br /&gt;&lt;div style="color: #274e13;"&gt;&lt;b&gt;Why the chop?&lt;/b&gt;&lt;/div&gt;&lt;br /&gt;The combination of the old school dropbar and the old school Weinmann brake levers makes braking "on the hood" rather difficult, and I often resort to riding with my palms on the ramps of the bar and using my middle and my fourth fingers to push the &lt;a href="http://www.sheldonbrown.com/gloss_e-f.html#extension"&gt;suicide levers&lt;/a&gt; in order to brake, which is not ideal especially when you need that extra bit of braking power in the rain. As I rarely ride in the drop position when dashing around London anyway, I thought a bullhorn setup which allows me to bring the brakes closer to my normal gripping position would be more ideal.&lt;br /&gt;&lt;br /&gt;I could probably have got a brand-new bullhorn bar for just over a tenner, but in this age of insane consumerism, it certainly feels very refreshing to be able to create something new yourself using existing bits, in keeping with &lt;b&gt;a greener ethos&lt;/b&gt;. Hence I was very excited to undertake this little DIY project.&lt;br /&gt;&lt;div style="color: #274e13;"&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div style="color: #274e13;"&gt;&lt;b&gt;How to chop?&lt;/b&gt;&lt;/div&gt;&lt;br /&gt;The whole process was surprisingly easy (even for someone like me who had never sawed anything!) and only took a couple of hours. The following is a step-by-step guide to how to "chop and flop":&lt;br /&gt;&lt;br /&gt;Apart from the usual bike tools, such as allen keys, spanners, screw drivers, etc, all the tools that are needed specifically for this job include a &lt;b&gt;(junior) hacksaw&lt;/b&gt;, a roll of &lt;b&gt;tape&lt;/b&gt;, some &lt;b&gt;lubricating oil&lt;/b&gt; and a bit of &lt;b&gt;sandpaper&lt;/b&gt; (or a &lt;b&gt;metal file&lt;/b&gt;):&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-w1YB8vhGgm0/Tvuc8RoEpFI/AAAAAAAAABQ/JhQTB2E8uDU/s1600/IMAG0116.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="191" src="http://2.bp.blogspot.com/-w1YB8vhGgm0/Tvuc8RoEpFI/AAAAAAAAABQ/JhQTB2E8uDU/s320/IMAG0116.jpg" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;The chromed steel bar and the Weinmann brakes before the chop. Notice the protruding pivot bolts - I managed to remove the suicide levers with no problem with this pair of brakes, and I simply left the bolts sticking out, which is perfectly fine:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-57Y4Ld7x0Fs/Tvudsptu-QI/AAAAAAAAABc/-6IH74AL-bo/s1600/IMAG0105.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://3.bp.blogspot.com/-57Y4Ld7x0Fs/Tvudsptu-QI/AAAAAAAAABc/-6IH74AL-bo/s320/IMAG0105.jpg" width="191" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Now flip the bar upside down and put it back on the bike. Try out a few positions until you have found the most comfortable hand-gripping point. REMEMBER YOU MUST ALLOW ENOUGH EXTRA ROOM FOR ATTACHMENTING THE BRAKES. Simply mark the exact point where you want to cut the bar by wrapping some tape around it. Remember which side of the tape you want to cut:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-QT2TqIQTFRg/Tvuf-QhUesI/AAAAAAAAABo/QJDBf3G3uw8/s1600/IMAG0107.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="191" src="http://1.bp.blogspot.com/-QT2TqIQTFRg/Tvuf-QhUesI/AAAAAAAAABo/QJDBf3G3uw8/s320/IMAG0107.jpg" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;If you can clamp the bar against a worktop, good for you! Otherwise, some people suggest sawing the bar with it attached to the bike stem. I simply put it on the floor and started sawing. A couple of tips: 1) put it on top of some cloth/foam to stop your neighbours from wondering about the strange noise 2) frequently drop lubricating oil (or chain oil) onto the contact point to stop the blade from breaking:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-fhj8g3HKPjA/TvuiymKHDTI/AAAAAAAAAB0/yRYHGndqcpo/s1600/IMAG0110.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://4.bp.blogspot.com/-fhj8g3HKPjA/TvuiymKHDTI/AAAAAAAAAB0/yRYHGndqcpo/s320/IMAG0110.jpg" width="191" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Hopefully you will have achieved quite a clean cut. Use a bit of sandpaper to sand down any rough/sharp bits. Now use the removed end to help you mark out the cutting point for the other side of the bar. Again remember which side of the tape you want to cut:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-hZqWqOqH3h8/TvukdPkbNkI/AAAAAAAAACA/rLCtQsiQxJs/s1600/IMAG0111.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://4.bp.blogspot.com/-hZqWqOqH3h8/TvukdPkbNkI/AAAAAAAAACA/rLCtQsiQxJs/s320/IMAG0111.jpg" width="191" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;Voila:&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-OitBueNdyvI/TvumVzVk1TI/AAAAAAAAACk/Eoh8NgKSaSM/s1600/IMAG0112.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="191" src="http://1.bp.blogspot.com/-OitBueNdyvI/TvumVzVk1TI/AAAAAAAAACk/Eoh8NgKSaSM/s320/IMAG0112.jpg" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;On my 90s Concorde steel frame (Sturmey Archer Stem, Selle Turbo Saddle, Schwalbe Marathon Original Tyre, Vittoria Randonneur White Tyre, and a wine bottle cork bar end plug!)&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-mhwi7jT2RJ0/TvunNj28F_I/AAAAAAAAACw/Dj4Tb_xoo_o/s1600/IMAG0125.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="191" src="http://1.bp.blogspot.com/-mhwi7jT2RJ0/TvunNj28F_I/AAAAAAAAACw/Dj4Tb_xoo_o/s320/IMAG0125.jpg" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style="color: #274e13;"&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div style="color: #274e13;"&gt;&lt;b&gt;First impression:&lt;/b&gt;&lt;/div&gt;&lt;br /&gt;I went out for a 30-minutes ride after the chop, and the new bullhorn bar felt superb. I felt that &lt;b&gt;power transfer&lt;/b&gt; was much better especially when I was riding out of the saddle. I was able to really lean on the bar. At the same time, transitioning to a more relaxed riding position by moving my hands closer to the top of the bar was smooth and easy. The brake lever reach is still a little bit too far for my liking, but at least I can readily reach the levers now, and I suspect having some modern brake levers may help.&lt;br /&gt;&lt;br /&gt;&lt;object height="300" width="400"&gt; &lt;param name="flashvars" value="offsite=true&amp;lang=en-us&amp;page_show_url=%2Fphotos%2Fseewah%2Fsets%2F72157628608518175%2Fshow%2F&amp;page_show_back_url=%2Fphotos%2Fseewah%2Fsets%2F72157628608518175%2F&amp;set_id=72157628608518175&amp;jump_to="&gt;&lt;/param&gt;&lt;param name="movie" value="http://www.flickr.com/apps/slideshow/show.swf?v=109615"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;embed type="application/x-shockwave-flash" src="http://www.flickr.com/apps/slideshow/show.swf?v=109615" allowFullScreen="true" flashvars="offsite=true&amp;lang=en-us&amp;page_show_url=%2Fphotos%2Fseewah%2Fsets%2F72157628608518175%2Fshow%2F&amp;page_show_back_url=%2Fphotos%2Fseewah%2Fsets%2F72157628608518175%2F&amp;set_id=72157628608518175&amp;jump_to=" width="400" height="300"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-1982090067823674015?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/1982090067823674015/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=1982090067823674015' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/1982090067823674015'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/1982090067823674015'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2011/12/bullhorn-conversion-chop-and-flop.html' title='Bullhorn Conversion - Chop and Flop'/><author><name>see wah cheng</name><uri>https://profiles.google.com/106402076208124215773</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-NyHjUGqj-k4/AAAAAAAAAAI/AAAAAAAAAAA/OpTwOW0esiE/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-w1YB8vhGgm0/Tvuc8RoEpFI/AAAAAAAAABQ/JhQTB2E8uDU/s72-c/IMAG0116.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-3101249330224967731</id><published>2011-11-06T22:26:00.001Z</published><updated>2011-11-06T22:43:36.685Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='cycling'/><title type='text'>A trip to Mercian</title><content type='html'>&lt;a href="http://4.bp.blogspot.com/-EX86NDb__f4/TrcHUEHDw-I/AAAAAAAAADE/m65HjDUFUpo/s1600/IMAG0135.jpg" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="191" src="http://4.bp.blogspot.com/-EX86NDb__f4/TrcHUEHDw-I/AAAAAAAAADE/m65HjDUFUpo/s320/IMAG0135.jpg" width="320" /&gt;&lt;/a&gt;&lt;a href="http://1.bp.blogspot.com/-R2-650YQkvI/TrcHekl8geI/AAAAAAAAADM/YHYEj53VUpI/s1600/IMAG0136.jpg" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="191" src="http://1.bp.blogspot.com/-R2-650YQkvI/TrcHekl8geI/AAAAAAAAADM/YHYEj53VUpI/s320/IMAG0136.jpg" width="320" /&gt;&lt;/a&gt; &lt;br /&gt;Ever since seeing a picture of a &lt;a href="http://www.merciancycles.co.uk/"&gt;Mercian&lt;/a&gt; bike on an Internet forum, I have always been fascinated by their &lt;a href="http://www.classiclightweights.co.uk/mercian.html"&gt;beautiful lugged steel frames&lt;/a&gt;. A weekend trip to Derby presented an opportunity to visit the home of Mercian Cycles. Mercian Cycles is based in &lt;a href="http://en.wikipedia.org/wiki/Alvaston"&gt;Alvaston&lt;/a&gt;, a suburb just south of the Derby city centre. What struck me immediately was that the store bearing the name of these sought-after custom-built frames with such distinguished heritage is very much your &lt;b&gt;local bike shop&lt;/b&gt; (albeit a pretty pricy one :) ). Admittedly you are greeted by a wonderful display of Mercian bikes and frames as soon as you step inside the shop, which, after all, was what I personally wanted to see, but it does not seem to be obsessed with Mercian's fame. Instead of finding glossy pictures of Mercians from magazines or coffee table books, you get letters from their satisfied customers. They also sell a huge stock of non-Mercian bikes and accessories, and the man at the till was extremely friendly too.&lt;br /&gt;&lt;br /&gt;Not quite rich enough to be a Mercian rider, I am now, nonetheless, the happy owner of a &lt;a href="http://www.mercianblog.com/2009/08/mercian-mugs.html"&gt;Mercian coffee mug&lt;/a&gt;!&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-Il8yLBogFMM/TrcHFI8pXuI/AAAAAAAAAC0/acbuKJNrTS0/s1600/IMAG0133.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="191" src="http://3.bp.blogspot.com/-Il8yLBogFMM/TrcHFI8pXuI/AAAAAAAAAC0/acbuKJNrTS0/s320/IMAG0133.jpg" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-FYhe_mwnWcs/TrcHM6nq7sI/AAAAAAAAAC8/uz_yr25jIgw/s1600/IMAG0134.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://2.bp.blogspot.com/-FYhe_mwnWcs/TrcHM6nq7sI/AAAAAAAAAC8/uz_yr25jIgw/s320/IMAG0134.jpg" width="191" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-n2fYafrbw0k/TrcHmLBN-LI/AAAAAAAAADU/aLxnPo7vVLo/s1600/IMAG0132.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="191" src="http://1.bp.blogspot.com/-n2fYafrbw0k/TrcHmLBN-LI/AAAAAAAAADU/aLxnPo7vVLo/s320/IMAG0132.jpg" width="320" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Another surprise in Alvaston - an art deco pub (which does £3.75 carvery)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&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/20427072-3101249330224967731?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/3101249330224967731/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=3101249330224967731' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/3101249330224967731'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/3101249330224967731'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2011/11/trip-to-mercian.html' title='A trip to Mercian'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-EX86NDb__f4/TrcHUEHDw-I/AAAAAAAAADE/m65HjDUFUpo/s72-c/IMAG0135.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-7113333046047140409</id><published>2011-09-03T16:55:00.000+01:00</published><updated>2011-09-04T12:31:24.169+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='software development'/><category scheme='http://www.blogger.com/atom/ns#' term='mendeley'/><category scheme='http://www.blogger.com/atom/ns#' term='geeky'/><category scheme='http://www.blogger.com/atom/ns#' term='redis'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Redis Pipelining</title><content type='html'>At &lt;a href="http://www.mendeley.com/"&gt;Mendeley&lt;/a&gt;, we use a mixture of (my|no)sql technologies for handling different types of data. &lt;a href="http://www.redis.io/"&gt;Redis&lt;/a&gt; is a key-value store that we use for our user &lt;b&gt;feed&lt;/b&gt; and user &lt;b&gt;notification&lt;/b&gt; data.&lt;br /&gt;&lt;br /&gt;Being an &lt;b&gt;in-memory&lt;/b&gt; storage system with disk persistence, Redis has delivered exceptional performance in many of our applications. As the applications grow in complexity, it becomes apparent that &lt;b&gt;network delay&lt;/b&gt; acts as the main bottleneck. Redis is a TCP server based on the simple request-response model, which means if you are using a client library, such as &lt;a href="http://rediska.geometria-lab.net/"&gt;Rediska&lt;/a&gt;, and executing hundreds of &lt;i&gt;SET&lt;/i&gt; commands in one go over a single open socket (maintained over the script's execution), each one of those &lt;i&gt;SET&lt;/i&gt;s is going to result in a request being sent to Redis, and a response being expected. Each time, the socket will block until the response can be read. The effect of network delay associated with these request-response actions will simply multiply with the number of commands we try to execute per operation.&lt;br /&gt;&lt;br /&gt;One obvious answer to this performance issue is to use &lt;i&gt;&lt;a href="http://redis.io/commands/mset"&gt;MSET&lt;/a&gt;&lt;/i&gt; to set multiple key-value pairs in one single socket request. (Incidentally, &lt;i&gt;MSET&lt;/i&gt; is an atomic command, which may or may not be what you want, depending on your particular usecase.)&lt;br /&gt;&lt;br /&gt;But there is a better alternative - &lt;b&gt;Redis &lt;a href="http://redis.io/topics/pipelining"&gt;pipelining&lt;/a&gt;&lt;/b&gt;. The idea is dead simple: instead of waiting for a response per socket request, the client simply send through &lt;b&gt;multiple requests&lt;/b&gt; before reading the &lt;b&gt;aggregated responses&lt;/b&gt;. This hugely cuts down the effect of network delay on operation time. Client libraries like Rediska make using pipelining a bliss. &lt;i&gt;In Rediska's case, one simply has to start a pipeline, invoke the commands on the pipeline (instead of the usual Rediska object), and finally execute the pipeline to send through the batch of commands.&lt;/i&gt; Minimal code changes required!&lt;br /&gt;&lt;br /&gt;Not surprisingly, the performance gain by issuing 5k commands in a pipeline is remarkable. While I cannot provide you with precise benchmarking data, I can say that connecting from my local machine to a Redis server installed on an ec2 instance, the operation completed in &lt;b&gt;2 seconds as opposed to 20 seconds&lt;/b&gt; without using pipelining.&lt;br /&gt;&lt;br /&gt;There are a number of points worth noting (some more subtle than others):&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;pipelining is &lt;b&gt;NOT&lt;/b&gt;&amp;nbsp;&lt;a href="http://redis.io/commands#transactions"&gt;Redis transactions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;pipelining does not provide atomicity&lt;/li&gt;&lt;li&gt;pipelining really is nothing more complicated than the fact that Redis server is able to queue up responses in memory&lt;/li&gt;&lt;li&gt;it is entirely up to the client when to send the requests and when to wait on the responses&lt;/li&gt;&lt;li&gt;commands in a pipeline must be independent of each other (as individual responses don't get read till the end of the batch)&lt;/li&gt;&lt;li&gt;sending through too many commands in a single pipeline may cause memory issues and potentially socket timeout, so one may want to flush a pipeline after a number of commands (best to do some performance testing)&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;b&gt;&lt;span class="Apple-style-span" style="color: #38761d;"&gt;BufferedRedisPipeline&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Or "auto flushing pipeline"....&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I created a wrapper class for the &lt;i&gt;Rediska_Pipeline&lt;/i&gt; to introduce auto-flushing.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Code snippet of an example implementation of the class:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;pre style="background-image: URL(http://2.bp.blogspot.com/_z5ltvMQPaa8/SjJXr_U2YBI/AAAAAAAAAAM/46OqEP32CJ8/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt; /** @var Rediska_Pipeline */  &lt;br /&gt; private $pipeline;  &lt;br /&gt;   &lt;br /&gt; /** @var Rediska */&lt;br /&gt; private $rediska;  &lt;br /&gt;   &lt;br /&gt; /** @var int */  &lt;br /&gt; private $counter = 0;  &lt;br /&gt;   &lt;br /&gt; /** @var int */  &lt;br /&gt; private $flushThreshhold;  &lt;br /&gt;   &lt;br /&gt; /** @var array */  &lt;br /&gt; private $result = array();  &lt;br /&gt;   &lt;br /&gt; /**  &lt;br /&gt;  * @param Rediska $rediska  &lt;br /&gt;  * @param int $flushThreshhold set as -1 to stop the pipeline from autoflushing  &lt;br /&gt;  */  &lt;br /&gt; public function __construct(Rediska $rediska, $flushThreshhold = 5000) {  &lt;br /&gt;      $this-&amp;gt;rediska = $rediska;  &lt;br /&gt;      $this-&amp;gt;flushThreshhold = $flushThreshhold;  &lt;br /&gt;      $this-&amp;gt;pipeline = $rediska-&amp;gt;pipeline();  &lt;br /&gt; }  &lt;br /&gt;   &lt;br /&gt; /**  &lt;br /&gt;  * @return array  &lt;br /&gt;  */  &lt;br /&gt; public function execute() {  &lt;br /&gt;      if($this-&amp;gt;counter) {  &lt;br /&gt;           $this-&amp;gt;flushPipeline(false);  &lt;br /&gt;      }  &lt;br /&gt;      $this-&amp;gt;pipeline = null;  &lt;br /&gt;      return $this-&amp;gt;result;  &lt;br /&gt; }  &lt;br /&gt;   &lt;br /&gt; /**  &lt;br /&gt;  * Magic method to invoke command calls on the underlying pipeline object.  &lt;br /&gt;  *  &lt;br /&gt;  * @param string $name  &lt;br /&gt;  * @param array|null $arguments  &lt;br /&gt;  * @return void  &lt;br /&gt;  */  &lt;br /&gt; public function __call($name, $arguments) {  &lt;br /&gt;      $this-&amp;gt;counter++;  &lt;br /&gt;      call_user_func_array(array($this-&amp;gt;pipeline, $name), $arguments);  &lt;br /&gt;      // OR use other method to invoke command calls if one is concerned about the performance of call_user_func_array  &lt;br /&gt;      if($this-&amp;gt;flushThreshhold != -1 &amp;amp;&amp;amp; $this-&amp;gt;counter &amp;gt;= $this-&amp;gt;flushThreshhold) {  &lt;br /&gt;           $this-&amp;gt;flushPipeline(true);  &lt;br /&gt;      }  &lt;br /&gt; }  &lt;br /&gt;   &lt;br /&gt; /**  &lt;br /&gt;  * @param bool $createNewPipeline  &lt;br /&gt;  * @return void  &lt;br /&gt;  */  &lt;br /&gt; private function flushPipeline($createNewPipeline) {  &lt;br /&gt;      $this-&amp;gt;counter = 0;  &lt;br /&gt;      $this-&amp;gt;result = array_merge($this-&amp;gt;result, $this-&amp;gt;pipeline-&amp;gt;execute());  &lt;br /&gt;      $this-&amp;gt;pipeline = $createNewPipeline ? $this-&amp;gt;rediska-&amp;gt;pipeline() : null;  &lt;br /&gt; }  &lt;br /&gt;   &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;&lt;span class="Apple-style-span" style="color: #38761d;"&gt;Finally...&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Of course it is always worth considering handling heavy IO operations in an asynchronous manner to guarantee responsive user experience, e.g, either by leveraging a queue system or by using AJAX requests.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-7113333046047140409?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/7113333046047140409/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=7113333046047140409' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/7113333046047140409'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/7113333046047140409'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2011/09/redis-pipelining.html' title='Redis Pipelining'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-1643651666506248245</id><published>2011-08-13T21:33:00.014+01:00</published><updated>2011-08-14T12:13:43.332+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='riot'/><title type='text'>David Starkey on Newsnight</title><content type='html'>Just days after &lt;a href="http://seewah.blogspot.com/2011/08/nick-griffin-and-w-word.html"&gt;Mr Nick Griffin labeled the white rioters as wiggers&lt;/a&gt;, David Starkey, a well-known British historian, claimed on the BBC Newsnight programme that "the whites have become black" is to blame for the recent riots in England.&lt;br /&gt;&lt;br /&gt;&lt;iframe src="http://www.youtube.com/embed/gU5TcTSa9kk" allowfullscreen="" width="560" frameborder="0" height="349"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;There were so many ridiculously misinformed and racist views expressed by Mr Starkey, e.g.  his sweeping statement about what he called &lt;span style="font-weight: bold;"&gt;rap&lt;/span&gt; (unsurprisingly, when challenged, his lack of hiphop knowledge was quickly exposed, please Mr Starkey, go and listen to KRS-One, go and listen to Mos Def &amp;amp; Kweli), his rant about &lt;span style="font-weight: bold;"&gt;Jamaican patois&lt;/span&gt; as a "false" language, not to mention his insightful analysis of Enoch Powell's &lt;a href="http://en.wikipedia.org/wiki/Rivers_of_Blood_speech"&gt;River of Blood speech&lt;/a&gt;. The list would go on forever.&lt;br /&gt;&lt;br /&gt;What really made my jaw drop was his attempt to justify his view as one based on &lt;span style="font-weight: bold;"&gt;culture&lt;/span&gt;, and not &lt;span style="font-weight: bold;"&gt;colour&lt;/span&gt;. At around 1:55 in the video, he went, "&lt;span style="font-style: italic;"&gt;Listen to David Lammy. An archetypical successful black man. If you  turned the screen off, so you were listening to him on the radio  you’d think he was white&lt;/span&gt;". I had to think twice to make sure that I was not interpreting Mr Starkey's remark out of context, but this really is no different from saying, "look I am not a racist - I have a black mate (who happens to be culturally a bit white)".&lt;br /&gt;&lt;br /&gt;I don't claim to understand the intricate working of this society, and I certainly do not condone the riots nor the gang culture, but Mr Starkey, I can tell you that one of the undeniable causes of what has been happening is the widening gap between the greater society and the ruling class members - people, I dare say, not too dissimilar to yourself.&lt;br /&gt;&lt;br /&gt;Two twitter reactions the following day:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://twitter.com/#%21/DavidLammy/status/102361683321819137"&gt;David Lammy:&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/-QgfqfR28WYY/TkbywrVO8DI/AAAAAAAAACw/QptROwDRuxs/s1600/z.JPG"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 187px;" src="http://1.bp.blogspot.com/-QgfqfR28WYY/TkbywrVO8DI/AAAAAAAAACw/QptROwDRuxs/s320/z.JPG" alt="" id="BLOGGER_PHOTO_ID_5640462501407551538" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a href="http://twitter.com/#%21/nickgriffinmep/status/102343811765780480"&gt;Nick Griffin:&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/-j6ZZ-QuH3r4/TkbylJNY2kI/AAAAAAAAACo/mE6xbUEF1go/s1600/y.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 181px;" src="http://1.bp.blogspot.com/-j6ZZ-QuH3r4/TkbylJNY2kI/AAAAAAAAACo/mE6xbUEF1go/s320/y.jpg" alt="" id="BLOGGER_PHOTO_ID_5640462303269280322" border="0" /&gt;&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/20427072-1643651666506248245?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/1643651666506248245/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=1643651666506248245' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/1643651666506248245'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/1643651666506248245'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2011/08/david-starkey-on-newsnight.html' title='David Starkey on Newsnight'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://img.youtube.com/vi/gU5TcTSa9kk/default.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-2976363882854577987</id><published>2011-08-10T22:34:00.011+01:00</published><updated>2011-08-13T22:18:30.502+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='london'/><category scheme='http://www.blogger.com/atom/ns#' term='riot'/><title type='text'>Nick Griffin and the W Word</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/-mcLvv_KvdQY/TkL5o2PzeII/AAAAAAAAACg/9WrE-zxQ5dE/s1600/cunt.jpg"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 320px; height: 138px;" src="http://3.bp.blogspot.com/-mcLvv_KvdQY/TkL5o2PzeII/AAAAAAAAACg/9WrE-zxQ5dE/s320/cunt.jpg" alt="" id="BLOGGER_PHOTO_ID_5639344163573168258" border="0" /&gt;&lt;/a&gt;&lt;div&gt;After four terrible nights of rioting and looting, no doubt most Londoners are reflecting on what has easily been London's saddest chapter in recent history. I came across &lt;a href="http://twitter.com/#%21/nickgriffinmep/status/101241851255848960"&gt;this tweet&lt;/a&gt; (see screenshot) by none other than &lt;a href="http://en.wikipedia.org/wiki/Nick_Griffin"&gt;Mr Nick Griffin&lt;/a&gt;, the chairman of the racist British National Party. &lt;i&gt;Yes, I would quite openly label them as racists.&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;When I saw Mr Griffin using the word &lt;b&gt;"wiggers"&lt;/b&gt; in his tweet, I didn't really know whether to laugh or to cry. Is this the voice of the most confused man in Britain?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;A riot supposedly kick-started by the death of a black man, spreading like wildfire in predominantly black/multi-racial neighbourhoods in London (I am a proud &lt;b&gt;Lewisham&lt;/b&gt; resident myself), what a golden opinion for Mr Griffin to preach his twisted politics. Oh wait, there is a white face caught on camera carrying looted items. Ah, no problem, it is blatantly his black friend's fault that this white person decided to join in. I am just going to tweet about these wiggers. Shame on the blacks (and the wiggers).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Shame on you Mr Griffin (and shame on all you scumbag looters, troublemakers too). This is NOT a race war. This is a class war. We are talking about failure in parenting. We are talking about a failing system. Stop inflicting racial tension. Most Londoners are proud of its cultural diversity.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;P.S. Why did Mr Griffin not use the &lt;b&gt;N word&lt;/b&gt;? Why not &lt;b&gt;niggers and wiggers&lt;/b&gt;? Maybe he is making a very clever point here: why is it socially acceptable to use the word "wigger", but absolutely not the word "nigger"? Umm... perhaps not...&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-2976363882854577987?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/2976363882854577987/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=2976363882854577987' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/2976363882854577987'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/2976363882854577987'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2011/08/nick-griffin-and-w-word.html' title='Nick Griffin and the W Word'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-mcLvv_KvdQY/TkL5o2PzeII/AAAAAAAAACg/9WrE-zxQ5dE/s72-c/cunt.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-4696165887748684078</id><published>2011-07-25T00:32:00.018+01:00</published><updated>2011-07-30T12:12:42.167+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='cycling'/><title type='text'>Dunwich Dynamo 2011</title><content type='html'>&lt;img src="http://4.bp.blogspot.com/-I_dW2ZJC080/Tiyw4EFBy_I/AAAAAAAAACQ/JggYVZCiwUk/s400/IMAG0036.jpg" style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 400px; height: 239px;" alt="" id="BLOGGER_PHOTO_ID_5633071711147641842" border="0" /&gt;Two consecutive punctures only days before the ride was certainly not a good sign. Typically the forecast had been predicting heavy rain after a week of relatively fine weather. But then there is no room for common sense, nor cowardice, on the way to &lt;b&gt;Dunwich&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;I bought two rolls of &lt;a href="http://hogspeak.blogspot.com/2009/03/fond-de-jante.html"&gt;Velox&lt;/a&gt; to replace the crappy stock plastic rim tape, which I blame for the two punctures, and hoped for the best as my bottom prepared itself for the next 9 intimate hours with my &lt;a href="http://www.brooksengland.com/en/Shop_ProductPage.aspx?cat=saddles+-+road+%26+mtb&amp;amp;prod=Team+Pro+Classic"&gt;Brooks Team Pro&lt;/a&gt; saddle.&lt;br /&gt;&lt;br /&gt;The Dunwich Dynamo is a &lt;a href="http://southwarkcyclists.org.uk/content/dunwich-dynamo"&gt;semi-organised&lt;/a&gt; bike ride in the UK where cyclists from all walks of life pedal from &lt;b&gt;London&lt;/b&gt; to &lt;a href="http://en.wikipedia.org/wiki/Dunwich"&gt;Dunwich&lt;/a&gt; Beach 115 miles away overnight under the glorious July full moon. I arrived in Hackney at 8:30 pm for the 2011 edition of this popular annual event. &lt;b&gt;London Fields&lt;/b&gt; for once lived to to its name, after hours of torrential rain. Having collected the coach ticket and the route sheet from outside the &lt;b&gt;Pub On The Park&lt;/b&gt;, my friends and I joined the stream of hundreds (or &amp;gt; 1000?) of cyclists in our mass exodus to Essex and Beyond. The initial hour consisted mostly of red-light stops and getting strange looks from motorists and pedestrians. As the number of traffic lights started to decrease (so did the number of roadside street lamps which had been aiding us in our progress) the real adventure began. The fleet of red LEDs along the tree-lined roads out of London was really quite a sight. We started to pick up the pace.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Half an hour cycling into the night, the formidable red line was suddenly interrupted by the flashing light of an ambulance somewhere outside &lt;b&gt;Epping&lt;/b&gt;. Unfortunately there was an accident. (which I think was not too bad from what I have read on the internet afterwards) As we rode past, the sight of the participant being stretchered into the ambulance was a stark reminder that any moment of carelessness could lead to grave consequences in such situation.&lt;br /&gt;&lt;br /&gt;Past &lt;b&gt;Chelmsford&lt;/b&gt;, we reached the half-way point at &lt;b&gt;Sible Hedingham&lt;/b&gt; some time after midnight, and stopped for some food. We were extremely lucky with the weather. Despite the downpours during the day, the evening had been completely dry. The brief stop however made us release that the temperature had dropped quite significantly since we started at 9pm. Jacket on.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://1.bp.blogspot.com/--z625RM4b7U/TiyxXCqSvmI/AAAAAAAAACY/QHvWSFEenj0/s320/IMAG0039.jpg" style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 320px; height: 191px;" alt="" id="BLOGGER_PHOTO_ID_5633072243343015522" border="0" /&gt;As the great snake of red LEDs wound its way through the countryside of &lt;b&gt;Suffolk&lt;/b&gt;, I found the sound of gear grinding and free wheel clicking oddly soothing. Only the occasional calls of "car" echoing through the peloton to warn riders of coming vehicles reminded us that we were not the only insane people out on the road. Long sections of descents and short sections of ascents were the order of the night, and it was on one of these climbs that I had my share of misfortune. Misjudging the climb, I decided to stay on the big front chainring. I was cranking really hard, even on a big rear cog, until half-way up, I inevitably decided to shift to the smaller front chainring. As a more experienced climber would probably have warned me, the chain came off, and I ended up pedalling into the void. "Oh shit", I went. Somehow I managed to unclip my right foot from the SPD pedal and spared myself the embarrassment of kissing the tarmac. My friends helped me put the chain back on, and we were off into the darkness again.&lt;br /&gt;&lt;br /&gt;Apart from seeing a &lt;a href="http://twitter.com/#%21/leotong"&gt;Boris bike&lt;/a&gt; (good work!) by the roadside and stopping a couple of times for food and toilet, I honestly could not remember much from the time between 2am and 5am. My tiredness and a slight stomachache (probably from eating too much pasta during the day...) made me wish the whole thing would end soon. The sun started to come out, but I was still feeling cold from the wind that kept on penetrating my windproof. Finally we saw road-signs that seemed to lead us closer and closer to &lt;b&gt;Dunwich&lt;/b&gt;, but before we got anywhere near the coast, there was more rough riding in store. A "By Road" at 100 miles leading to &lt;b&gt;Darsham&lt;/b&gt; was in pretty bad conditions after the rain. While the guys in mountain bikes were happily rolling through the sand/mud, I was desperately trying to avoid skidding or getting a puncture. That was easily the hardest hour of the whole ride.&lt;br /&gt;&lt;br /&gt;As the sun started to warm up East Anglia, my legs started to spin more freely, and I was feeling less miserable. I started to take in the scenery and enjoy the riding more. The old Dunwich city wall was a welcoming sight. At around 7:00am we arrived at the beach. Having congratulated ourselves on finishing the ride, we were quick to get to the breakfast queue. During the breakfast, my mind was still pretty blank from the lack of sleep and the lack of energy in general. We decided to crash out on the beach. Bathing in the warm morning sun, I closed my eyes and immediately fell asleep. The commotion a couple of hours later of people starting to queue up for the return coaches woke us up and it was time to head home.&lt;br /&gt;&lt;br /&gt;It was only on the coach when I regained my sense that I thought back about the previous 12 hours. It was simply the strangest thing I had ever done, and no words could ever describe the excitement, the sheer emotional experience of riding through the night with hundreds of people who were there for no other reason than to do something slightly crazy.&lt;br /&gt;&lt;br /&gt;I will definitely do it again. Absolutely...&lt;br /&gt;&lt;br /&gt;&lt;i&gt;P.S. There are loads of excellent blog entries out there listing tips for surviving the Dunwich Dynamo. One thing I would say to anyone who wants to give it a go is that don't worry if you don't have the latest ultra-light roadbike or if you don't spend all of your leisure time on a bike saddle. Dunwich Dynamo is not a race. Just wear something comfortable and warm, and learn how to fix a puncture (if you don't already). You will be OK!&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;P.S.S. Big thanks for the &lt;b&gt;Southwark Cyclists&lt;/b&gt; for being the driving force behind this excellent event.&lt;/i&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-4696165887748684078?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/4696165887748684078/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=4696165887748684078' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/4696165887748684078'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/4696165887748684078'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2011/07/dunwich-dynamo-2011.html' title='Dunwich Dynamo 2011'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-I_dW2ZJC080/Tiyw4EFBy_I/AAAAAAAAACQ/JggYVZCiwUk/s72-c/IMAG0036.jpg' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-9167278609621323131</id><published>2011-02-13T23:12:00.008Z</published><updated>2011-02-14T17:04:55.141Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='greenwich'/><category scheme='http://www.blogger.com/atom/ns#' term='cycling'/><title type='text'>Where has the Thames Path gone?</title><content type='html'>This weekend, I decided to check out the North Greenwich stretch of the &lt;a href="http://www.sustrans.org.uk/what-we-do/national-cycle-network/route-numbering-system/route-1"&gt;National Cycle Route 1&lt;/a&gt; with my girlfriend for a bit of leisurely (and what I hoped to be scenic) cycling along the Thames, seeing that it is only a stone's throw away from where I live. Starting off from the Greenwich town centre, we were surprised to find that the cycle path came to an early abrupt end as the a property development (&lt;a href="http://www.liveatlovells.com/findus.htm"&gt;Lovell's Wharf&lt;/a&gt;) quite literally stood in the way of the Thames Path. After a minor detour, we got back onto the route following the signs down the side of the &lt;a href="http://maps.google.co.uk/maps?f=q&amp;source=s_q&amp;hl=en&amp;geocode=&amp;q=blackwall+tunnel+approach&amp;aq=&amp;sll=51.496808,0.012156&amp;sspn=0.003754,0.009645&amp;ie=UTF8&amp;hq=&amp;hnear=Blackwall+Tunnel+Approach,+Greenwich,+Greater+London+SE10+0,+United+Kingdom&amp;z=14"&gt;Blackwall Tunnel Approach (A102)&lt;/a&gt;. This bit of the route is on cycle path shared with pedestrians on pavement. Unfortunately it was very badly maintained and also full of broken glass. The route finally took us away from the busy A102. However, at this point, the cycle path also degenerated to a &lt;span style="font-weight:bold;"&gt;sandy dirt track&lt;/span&gt; (I believe the area is called Delta Wharf). It was so bad that we had to get off and push the bikes for a couple of minutes. The route did get better afterwards once we passed the O2, but for some reason, another development decided to stand in front of us on &lt;a href="http://maps.google.co.uk/maps?f=q&amp;source=s_q&amp;hl=en&amp;geocode=&amp;q=Mudlarks+Boulevard,+Greenwich&amp;aq=0&amp;sll=51.495524,0.003816&amp;sspn=0.030031,0.077162&amp;ie=UTF8&amp;hq=&amp;hnear=Mudlarks+Blvd,+Greenwich,+Greater+London+SE10+0,+United+Kingdom&amp;z=16"&gt;Mudlarks Boulevard&lt;/a&gt;. By that point, we got so fed up that we turned back and cycled home.&lt;br /&gt;&lt;br /&gt;P.S. I have since found a couple of very insightful articles on &lt;a href="http://853blog.wordpress.com"&gt;http://853blog.wordpress.com&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;See &lt;a href="http://853blog.wordpress.com/2010/11/03/greenwichs-secret%C2%A0cycle-superhighway/"&gt;http://853blog.wordpress.com/2010/11/03/greenwichs-secret%C2%A0cycle-superhighway/&lt;/a&gt; and more on &lt;a href="https://853blog.wordpress.com/tag/thames-path/"&gt;https://853blog.wordpress.com/tag/thames-path/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;P.S. I have also found this rather non-informative announcement on the Greenwich Council website: &lt;a href="http://www.greenwich.gov.uk/Greenwich/Travel/TravelNews/riverside-path-diversion.htm"&gt;http://www.greenwich.gov.uk/Greenwich/Travel/TravelNews/riverside-path-diversion.htm&lt;/a&gt; What about a simple map?&lt;br /&gt;&lt;br /&gt;Greenwich Council, please look after the Thames Path! It is one of (y)our biggest assets.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-9167278609621323131?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/9167278609621323131/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=9167278609621323131' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/9167278609621323131'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/9167278609621323131'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2011/02/where-has-thames-path-gone.html' title='Where has the Thames Path gone?'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-1903954129509273517</id><published>2011-02-13T22:14:00.010Z</published><updated>2011-12-29T00:21:37.048Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='cycling'/><title type='text'>Power Grips and CRUD Roadracer MK2</title><content type='html'>&lt;div&gt;Every now and then, you buy a piece of bike gear and it just works. You don't feel the need to spend hours surfing the net reading product comparisons / reviews trying to convince yourself that it was money well-spent. I have just bought two of these products.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Power Grips&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;img border="0" src="http://www.cambriabike.com/images/product/power_grip_performance_pedal_kit._T_.jpg" /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Frustrated by being left behind by fellow commuters who can seemingly accelerate at will in their clipless shoes up &lt;a href="http://maps.google.co.uk/maps?f=q&amp;amp;source=s_q&amp;amp;hl=en&amp;amp;geocode=&amp;amp;q=Blackfriars+Bridge+london&amp;amp;aq=&amp;amp;sll=51.498385,-0.06607&amp;amp;sspn=0.015015,0.038581&amp;amp;gl=uk&amp;amp;ie=UTF8&amp;amp;hq=&amp;amp;hnear=Blackfriars+Bridge,+City+of+London,+United+Kingdom&amp;amp;z=16"&gt;Blackfriars Bridge&lt;/a&gt; every morning, I want some kind of foot retention on my &lt;span style="font-weight: bold;"&gt;beater steel singlespeed&lt;/span&gt;. I am not a big fan of toeclips and straps as I never seem to be able get the adjustment right. Having come across the &lt;a href="http://powergrips.mrpbike.com/"&gt;Power Grips&lt;/a&gt; pedal straps a while ago, I decided to buy a pair of second hand ones from eBay. Each Power Grips strap is essential a big fat piece of laminated strap that goes from one corner of your pedal to the opposite diagonal corner. (Ah yes you do need pedals with toeclip mounting holes on them in order to fit the Power Grips) The idea behind this diagonal setup is that as you straighten up your foot inside the strap, the strap will naturally tighten around it, giving you a firm attachment to the pedal, and they really do work! They are surprisingly easy to get into and out of, and the leverage you get from these things far exceeds that I get from toeclips. They accomodate even my chunky walking shoes (for those miserable rainy days)...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;CRUD Roadracer MK2&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;img border="0" src="http://www.cyclingweekly.co.uk/imageBank/cache/c/crud-roadracer-mk2-med.jpg_e_21235a1c391f4b6e2ce013029b231756.jpg" /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Feeling much too impatient to wait until good weather to go riding on my &lt;span style="font-weight: bold;"&gt;new roadbike&lt;/span&gt;, I bought the &lt;a href="http://www.crudproducts.com/products/roadracer/roadracer_"&gt;CRUD Roadracer MK2&lt;/a&gt; mudguards from &lt;a href="http://www.condorcycles.com/"&gt;Condor London&lt;/a&gt; the other day. I guess, most people, upon opening the box, would be surprised to find that the product looks more like a bit of plastic &lt;a href="http://en.wikipedia.org/wiki/Airfix"&gt;Airfix&lt;/a&gt; kit than a pair of mudguards, and yes installing the mudguards involves "building" them from bits, and it took almost half and hour, but for me that was actually quite a bit of fun! The Roadracer is designed to fit roadbikes with up to 25mm 700c tyres with minimal (as little as 5mm) tyre clearance. The mudguards do not require eyelets, and they are impossibly lightweight. Once installed, they feel pretty firm and do not rattle annoyingly like some metal mudguards do. The MK2 version provides complete coverage for the rear wheel. The rear blade drops all the way down to the seatstay "wishbone". (I had to chop a bit off the bottom with a knife as it was a bit too long for my frame) In additional, it is sculpted very cleverly to stop any water from spraying onto right hand side of the bottom bracket - the chainset area. The really cool bit about the Roadracer is that it comes with little brushes that keep the sides of the guards from touching the tyres, keeping them reasonably centred at all time. I cannot imagine a better set of mudguards than the Roadracer MK2 in terms of coverage on any roadbikes. Last but not least, they sit so snuggly around the wheels that I do not find them ruining the aesthetics of the bike at all. You almost don't notice they are there!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I have not had a chance to see whether it will keep the backside and the bike clean in a proper deluge, but so far I can say it is a piece of really well-designed kit.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-1903954129509273517?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/1903954129509273517/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=1903954129509273517' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/1903954129509273517'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/1903954129509273517'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2011/02/power-grips-and-crud-roadracer-mk2.html' title='Power Grips and CRUD Roadracer MK2'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-6738567555037170843</id><published>2011-02-09T17:59:00.000Z</published><updated>2011-02-09T18:00:27.277Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='mendeley'/><title type='text'>Who says researchers are humourless creatures?</title><content type='html'>Now embeddable widgets are available for &lt;a href="http://www.mendeley.com"&gt;Mendeley&lt;/a&gt; groups too. Click on the Embed link at the bottom of the "About this group" box on any public group pages on &lt;a href="http://www.mendeley.com"&gt;Mendeley&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;By the way, who says researchers are humourless creatures?&lt;br /&gt;&lt;br /&gt;&lt;iframe src="http://www.mendeley.com/groups/536621/_/widget/31/5/" frameborder="0" allowTransparency="true" style="width:420px;height:650px;"&gt;&lt;/iframe&gt;&lt;p style="width:420px"&gt;&lt;a href="http://www.mendeley.com/groups/536621/creatively-named-research-papers/" title="Creatively named research papers on Mendeley"&gt;Creatively named research papers&lt;/a&gt; is a group in &lt;a href="http://www.mendeley.com/groups/economics/" title="Economics on Mendeley"&gt;Economics&lt;/a&gt;, &lt;a href="http://www.mendeley.com/groups/philosophy/" title="Philosophy on Mendeley"&gt;Philosophy&lt;/a&gt;, &lt;a href="http://www.mendeley.com/groups/psychology/" title="Psychology on Mendeley"&gt;Psychology&lt;/a&gt; on &lt;a href="http://www.mendeley.com/" title="Mendeley"&gt;Mendeley&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-6738567555037170843?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/6738567555037170843/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=6738567555037170843' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/6738567555037170843'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/6738567555037170843'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2011/02/who-says-researchers-are-humourless.html' title='Who says researchers are humourless creatures?'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-5309087719298326990</id><published>2011-01-26T16:00:00.007Z</published><updated>2011-02-02T15:19:30.606Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='mendeley'/><title type='text'>My new shiny Mendeley profile widget...</title><content type='html'>Log into your account on &lt;a href="http://www.mendeley.com"&gt;Mendeley&lt;/a&gt;, go to "edit profile" and create your own!&lt;br /&gt;&lt;br/&gt;&lt;br /&gt;&lt;iframe src="http://www.mendeley.com/profiles/see-wah-cheng/widget/7199/2592118260/c56460e80af08cf7bca6f100ad711bb01f2a18ca/" frameborder="0" allowTransparency="true" style="width:420px;height:600px;"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;p style="width:420px"&gt;&lt;a href="http://www.mendeley.com/profiles/see-wah-cheng/" title="See Wah Cheng on Mendeley"&gt;See Wah Cheng&lt;/a&gt; is a member of &lt;a href="http://www.mendeley.com/research-papers/computer-and-information-science/" title="Computer and Information Science on Mendeley"&gt;Computer and Information Science&lt;/a&gt; on &lt;a href="http://www.mendeley.com" title="Mendeley"&gt;Mendeley&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-5309087719298326990?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/5309087719298326990/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=5309087719298326990' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/5309087719298326990'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/5309087719298326990'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2011/01/new-shiny-mendeley-profile-widget.html' title='My new shiny Mendeley profile widget...'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-1096671744891289132</id><published>2010-02-25T12:14:00.005Z</published><updated>2010-02-25T16:07:36.399Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='music'/><category scheme='http://www.blogger.com/atom/ns#' term='8tracks'/><title type='text'>John Peel 8tracks mix</title><content type='html'>&lt;object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,28,0" width="100%" height="120"&gt;&lt;param name="movie" value="http://8tracks.com/mixes/91422/player_v2"&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;param name="FlashVars" value="bg_color=_000000"&gt;&lt;embed flashvars="bg_color=_000000" src="http://8tracks.com/mixes/91422/player_v2" pluginspage="http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash" type="application/x-shockwave-flash" width="100%" height="120" allowscriptaccess="always"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;i&gt;Teenage Kicks&lt;/i&gt;&lt;/b&gt; (The Undertones)&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;i&gt;Serpent Headed Mask&lt;/i&gt;&lt;/b&gt; (Nile)&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;i&gt;A New England&lt;/i&gt;&lt;/b&gt; (Billy Bragg)&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;i&gt;Two Sevens Clash&lt;/i&gt;&lt;/b&gt; (Culture)&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;i&gt;Leb'Wohl&lt;/i&gt;&lt;/b&gt; (Neu!)&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;i&gt;Totally Wired&lt;/i&gt;&lt;/b&gt; (The Fall)&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;i&gt;Love Will Tear Us Apart&lt;/i&gt;&lt;/b&gt; (Joy Division)&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;i&gt;Hell Hath No Fury&lt;/i&gt;&lt;/b&gt; (Klute)&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;i&gt;Electricity&lt;/i&gt;&lt;/b&gt; (Captain Beefheart)&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;i&gt;Throughout the Dark Months of April and May&lt;/i&gt;&lt;/b&gt; (Cocteau Twins)&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;i&gt;The Dead Flag Blues&lt;/i&gt;&lt;/b&gt; (Godspeed You! Black Emperor)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-1096671744891289132?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/1096671744891289132/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=1096671744891289132' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/1096671744891289132'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/1096671744891289132'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2010/02/john-peel-8tracks-mix.html' title='John Peel 8tracks mix'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-6892069938536157710</id><published>2010-02-20T17:16:00.003Z</published><updated>2010-02-20T17:20:34.230Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='software development'/><category scheme='http://www.blogger.com/atom/ns#' term='geeky'/><category scheme='http://www.blogger.com/atom/ns#' term='chrome extension'/><category scheme='http://www.blogger.com/atom/ns#' term='8tracks'/><title type='text'>8tracks Boombox Chrome Extension update!</title><content type='html'>&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/KqYVPH8I7cY&amp;amp;color1=0xb1b1b1&amp;amp;color2=0xcfcfcf&amp;amp;hl=en_US&amp;amp;feature=player_embedded&amp;amp;fs=1"&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;param name="allowScriptAccess" value="always"&gt;&lt;embed src="http://www.youtube.com/v/KqYVPH8I7cY&amp;amp;color1=0xb1b1b1&amp;amp;color2=0xcfcfcf&amp;amp;hl=en_US&amp;amp;feature=player_embedded&amp;amp;fs=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;I have just uploaded an update for my &lt;a href="https://chrome.google.com/extensions/detail/pmdfenjjbfliihnjlcpifgaaehankhnj"&gt;8tracks Boombox Chrome Extension&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;1.1.4 (Feb 20, 2010)&lt;br /&gt;&lt;br /&gt;* options (Options.html) to specify whether to automatically save the last search&lt;br /&gt;&lt;br /&gt;* allows playback "in the background" (i.e. now possible to listen to mixes while working!)&lt;br /&gt;&lt;br /&gt;Thanks!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-6892069938536157710?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/6892069938536157710/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=6892069938536157710' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/6892069938536157710'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/6892069938536157710'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2010/02/8tracks-boombox-chrome-extension-update.html' title='8tracks Boombox Chrome Extension update!'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-3258981346076488523</id><published>2010-02-20T11:04:00.005Z</published><updated>2010-02-20T11:24:15.950Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='london'/><category scheme='http://www.blogger.com/atom/ns#' term='cycling'/><title type='text'>Druid Street Cycles News</title><content type='html'>So as many of you would know, I was bigging up &lt;b&gt;Druid Street Cycles&lt;/b&gt; in a &lt;a href="http://seewah.blogspot.com/2009/12/druid-street-cycles-best-lbs-in-london.html"&gt;previous post&lt;/a&gt;. Last time I went to say hi to Thor, the project was really taking off. Workshop full of bikes getting repaired, frames getting resprayed...&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Finally, they have set up an &lt;a href="http://www.facebook.com/group.php?gid=288436138262"&gt;official Facebook group&lt;/a&gt;. (Note that there is an old group set up by someone else. To get to the official group, search for "druid cycles thor") And Thor is promising 10% discount for all group members!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Even more exciting news is they are looking to help Indian cyclist &lt;b&gt;Parmod Singh&lt;/b&gt; raise fund to take part in the 2o12 Olympics! Incidentally Parmod is taking part in the gruesome &lt;a href="http://mumbaicyclothon.topdoctorsonline.com/default.asp"&gt;Mumbai Cyclothon 2010&lt;/a&gt; later on today.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Best of luck to Thor and co.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-3258981346076488523?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/3258981346076488523/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=3258981346076488523' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/3258981346076488523'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/3258981346076488523'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2010/02/druid-street-cycles-news.html' title='Druid Street Cycles News'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-8779284816573380024</id><published>2010-02-18T22:04:00.005Z</published><updated>2010-02-18T22:34:11.939Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='software development'/><category scheme='http://www.blogger.com/atom/ns#' term='geeky'/><category scheme='http://www.blogger.com/atom/ns#' term='chrome extension'/><title type='text'>Dim My Chrome - my second Chrome extension</title><content type='html'>&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/v7gCYGHYiq8&amp;amp;color1=0xb1b1b1&amp;amp;color2=0xcfcfcf&amp;amp;hl=en_US&amp;amp;feature=player_embedded&amp;amp;fs=1"&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;param name="allowScriptAccess" value="always"&gt;&lt;embed src="http://www.youtube.com/v/v7gCYGHYiq8&amp;amp;color1=0xb1b1b1&amp;amp;color2=0xcfcfcf&amp;amp;hl=en_US&amp;amp;feature=player_embedded&amp;amp;fs=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;Hot on the heels of &lt;a href="https://chrome.google.com/extensions/detail/pmdfenjjbfliihnjlcpifgaaehankhnj"&gt;8tracks Boombox&lt;/a&gt;, I created yet another Chrome extension - &lt;span style="font-weight:bold;"&gt;Dim My Chrome: Energy Saver&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="https://chrome.google.com/extensions/detail/mfnlfgpmlnpgdbodbeaaobakimnbkjfi"&gt;https://chrome.google.com/extensions/detail/mfnlfgpmlnpgdbodbeaaobakimnbkjfi&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The extension dims down your Chrome pages after a specific amount of idle time (no mouse movement on the page), to save energy / save the planet, or just to stop nosy colleagues / bosses from being nosy! Simply move the mouse inside the page again to undim the page.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Dim My Chrome&lt;/span&gt; uses &lt;a href="http://code.google.com/chrome/extensions/background_pages.html"&gt;Background Page&lt;/a&gt;, &lt;a href="http://code.google.com/chrome/extensions/content_scripts.html"&gt;Content Scripts&lt;/a&gt; and &lt;a href="http://code.google.com/chrome/extensions/messaging.html"&gt;Message Passing&lt;/a&gt;. Check out the source on &lt;a href="http://github.com/seewah/Dim-My-Chrome"&gt;Github&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-8779284816573380024?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/8779284816573380024/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=8779284816573380024' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/8779284816573380024'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/8779284816573380024'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2010/02/dim-my-chrome-my-second-chrome.html' title='Dim My Chrome - my second Chrome extension'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-6511129369330399131</id><published>2010-02-16T00:40:00.001Z</published><updated>2010-02-16T00:47:17.446Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='london'/><category scheme='http://www.blogger.com/atom/ns#' term='cycling'/><title type='text'>The bike (not) to ride in London</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_cgyuO2_6pKQ/S3nqqwEhrRI/AAAAAAAAABs/wckh7nJtHfo/s1600-h/012-17.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 300px;" src="http://4.bp.blogspot.com/_cgyuO2_6pKQ/S3nqqwEhrRI/AAAAAAAAABs/wckh7nJtHfo/s400/012-17.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5438636045205875986" /&gt;&lt;/a&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_cgyuO2_6pKQ/S3nqqwEhrRI/AAAAAAAAABs/wckh7nJtHfo/s1600-h/012-17.jpg"&gt;&lt;/a&gt;:-)&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;NJS export frame - &lt;a href="http://njs-keirin.blogspot.com/2009/10/bomber-pro-black.html"&gt;http://njs-keirin.blogspot.com/2009/10/bomber-pro-black.html&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-6511129369330399131?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/6511129369330399131/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=6511129369330399131' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/6511129369330399131'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/6511129369330399131'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2010/02/bike-not-to-ride-in-london.html' title='The bike (not) to ride in London'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_cgyuO2_6pKQ/S3nqqwEhrRI/AAAAAAAAABs/wckh7nJtHfo/s72-c/012-17.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-3795196399328820435</id><published>2010-02-15T19:07:00.014Z</published><updated>2010-02-20T17:26:03.497Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='software development'/><category scheme='http://www.blogger.com/atom/ns#' term='geeky'/><category scheme='http://www.blogger.com/atom/ns#' term='chrome extension'/><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><category scheme='http://www.blogger.com/atom/ns#' term='8tracks'/><title type='text'>8tracks Boombox Chrome Extension</title><content type='html'>&lt;span style="font-weight:bold;"&gt;&lt;span class="Apple-style-span"  style="color:#996633;"&gt;Update: new version available &lt;/span&gt;&lt;a href="http://seewah.blogspot.com/2010/02/8tracks-boombox-chrome-extension-update.html"&gt;&lt;span class="Apple-style-span"  style="color:#996633;"&gt;http://seewah.blogspot.com/2010/02/8tracks-boombox-chrome-extension-update.html&lt;/span&gt;&lt;/a&gt;&lt;span class="Apple-style-span"  style="color:#996633;"&gt;, which allows playback in the background, meaning you can now listen to mixes while working away!&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;&lt;span class="Apple-style-span"  style="color:#996633;"&gt;Update: check out my second Chrome extension: Dim My Chrome &lt;/span&gt;&lt;a href="http://seewah.blogspot.com/2010/02/dim-my-chrome-my-second-chrome.html"&gt;&lt;span class="Apple-style-span"  style="color:#996633;"&gt;http://seewah.blogspot.com/2010/02/dim-my-chrome-my-second-chrome.html&lt;/span&gt;&lt;/a&gt;&lt;span class="Apple-style-span"  style="color:#996633;"&gt;, which uses more advanced features such as Content Scripts and Message Passing&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_cgyuO2_6pKQ/S3nsftFRlXI/AAAAAAAAAB0/wTzwjannoTM/s1600-h/scrn.gif"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 265px; height: 324px;" src="http://3.bp.blogspot.com/_cgyuO2_6pKQ/S3nsftFRlXI/AAAAAAAAAB0/wTzwjannoTM/s400/scrn.gif" border="0" alt="" id="BLOGGER_PHOTO_ID_5438638054448403826" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Inspired by my friend George's excellent &lt;a href="https://chrome.google.com/extensions/detail/ebipjbfcgphjbnkhbijmnpnpcgjolked"&gt;Twitter Reaction Google Chrome extension&lt;/a&gt;, I decided to take a proper look at &lt;a href="http://code.google.com/chrome/extensions/"&gt;Google Chrome extension&lt;/a&gt;. Unlike &lt;b&gt;Firefox add-ons&lt;/b&gt;, Chrome extensions are written in pure HTML, Javascript and CSS which everyone already knows, although &lt;a href="http://en.wikipedia.org/wiki/NPAPI"&gt;NPAPI&lt;/a&gt; is also supported. It also has &lt;b&gt;cross-domain XHR&lt;/b&gt; support as well as &lt;b&gt;HTML5&lt;/b&gt; support such as &lt;a href="http://www.webreference.com/authoring/languages/html/HTML5-Client-Side/"&gt;local storage&lt;/a&gt;. Performance-wise, Chrome extensions are reputed to be faster and more stable (as each runs in its own process). In a way, Chrome extensions are more similar to &lt;a href="http://code.google.com/apis/gadgets/"&gt;Google Gadgets&lt;/a&gt;.&lt;div&gt;&lt;br /&gt;&lt;div&gt;Anyway I don't mean to write a Firefox add-on vs Chrome extension comparison here :-) So back to business...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;A site/service I have been digging big time recently is &lt;a href="http://8tracks.com/"&gt;http://8tracks.com&lt;/a&gt;. So I thought I could try out their &lt;a href="http://developer.8tracks.com/"&gt;API&lt;/a&gt; and see what I can come up with in a form of a Chrome extension.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span class="Apple-style-span"  style="color:#006600;"&gt;The result:&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So voila! &lt;b&gt;8tracks Boombox&lt;/b&gt;: &lt;a href="https://chrome.google.com/extensions/detail/pmdfenjjbfliihnjlcpifgaaehankhnj"&gt;https://chrome.google.com/extensions/detail/pmdfenjjbfliihnjlcpifgaaehankhnj&lt;/a&gt;, an extension to let you search and play mixes. No more, no less...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Source can be found on &lt;a href="http://github.com/seewah/8tracks-Boombox/"&gt;GitHub&lt;/a&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span class="Apple-style-span"  style="color:#006600;"&gt;Just some thoughts to share&lt;/span&gt;&lt;/b&gt;:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;- Chrome extensions are dead easy to develop and deploy. I started writing the extension only after spending less than 30 minutes reading the doc. It really is just that simple!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;- I used &lt;b&gt;JQuery&lt;/b&gt; for all the DOM manipulation stuff, but relied on good old &lt;b&gt;XMLHttpRequest&lt;/b&gt; for AJAX requests. I supposed I could have used JQuery for those as well.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;- I used &lt;b&gt;HTML5 localStorage&lt;/b&gt; for saving "last search" criteria - piece of cake to use.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;- The extension popup closes as soon as the user clicks elsewhere, which is a bit annoying as that means users cannot really open a mix in the extension itself and work at the same time. The fact that the popup closes automatically is, I suppose, by design. I will see if I can figure something out. Any ideas welcomed! &lt;b&gt;(See update at the top of the page)&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;- Finally, remember the extension will only run on Chrome, and go crazy and use all sorts of cool &lt;b&gt;WebKit CSS styles&lt;/b&gt;!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Try out &lt;a href="https://chrome.google.com/extensions/detail/pmdfenjjbfliihnjlcpifgaaehankhnj"&gt;8tracks Boombox&lt;/a&gt; and enjoy 8tracks :-)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Follow me on &lt;a href="http://twitter.com/seewahcheng"&gt;Twitter&lt;/a&gt; on get the latest updates.&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-3795196399328820435?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/3795196399328820435/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=3795196399328820435' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/3795196399328820435'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/3795196399328820435'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2010/02/8tracks-boombox-chrome-extension.html' title='8tracks Boombox Chrome Extension'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_cgyuO2_6pKQ/S3nsftFRlXI/AAAAAAAAAB0/wTzwjannoTM/s72-c/scrn.gif' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-864020429932960594</id><published>2010-02-04T10:36:00.003Z</published><updated>2010-02-04T10:45:22.009Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='music'/><category scheme='http://www.blogger.com/atom/ns#' term='8tracks'/><title type='text'>Retro soul / funk</title><content type='html'>Hearing a tune, thinking that it was produced during the golden age of funk &amp; soul, only to find out that it is very much a contemporary track, what a pleasant surprise :-)&lt;br /&gt;&lt;br /&gt;&lt;a href="http://8tracks.com/seewah/retro-revival"&gt;http://8tracks.com/seewah/retro-revival&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,28,0" width="100%" height="120" &gt;&lt;param name="movie" value="http://8tracks.com/mixes/83616/player_v2"&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;param name="FlashVars" value="bg_color=_000000"&gt;&lt;embed FlashVars="bg_color=_000000" src="http://8tracks.com/mixes/83616/player_v2" pluginspage="http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash" type="application/x-shockwave-flash" width="100%" height="120" allowscriptaccess="always" &gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;Lee Fields &amp; The Expressions, The Menahan Street Band, Mayer Hawthorne, Sharon Jones &amp; The Dab-Kings, Hypnotic Brass Ensemble, Naomi Shelton &amp; The Gospel Queens&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-864020429932960594?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/864020429932960594/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=864020429932960594' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/864020429932960594'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/864020429932960594'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2010/02/retro-soul-funk.html' title='Retro soul / funk'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-5529094097329350793</id><published>2010-02-03T13:46:00.008Z</published><updated>2010-02-03T16:18:07.726Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='software development'/><category scheme='http://www.blogger.com/atom/ns#' term='geeky'/><category scheme='http://www.blogger.com/atom/ns#' term='facebook'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>HipHop for PHP - running PHP at C++ speed</title><content type='html'>The Facebook folks have been at it again. After giving the world &lt;a href="http://incubator.apache.org/cassandra/"&gt;Cassandra&lt;/a&gt;, &lt;a href="http://incubator.apache.org/thrift/"&gt;Thrift&lt;/a&gt; and &lt;a href="http://www.tornadoweb.org/"&gt;Tornado&lt;/a&gt;, they are now on the verge of giving to the open-source world another piece of exciting technology - &lt;span style="font-weight:bold;"&gt;HipHop for PHP&lt;/span&gt; (a bit of a silly name, IMHO :-) )&lt;br /&gt;&lt;br /&gt;In short, &lt;a href="http://developers.facebook.com/news.php?blog=1&amp;story=358"&gt;HipHop for PHP&lt;/a&gt; is a platform consisting of a PHP to C++ &lt;span style="font-weight:bold;"&gt;transformer&lt;/span&gt;, a C++ &lt;span style="font-weight:bold;"&gt;runtime library&lt;/span&gt; (instead of the Zend Engine) and a &lt;span style="font-weight:bold;"&gt;simple web server&lt;/span&gt; (with Apache webserver support on the way) to allow developers to develop/test code in PHP but run the final code as C++ code on the server. This means developers can work with a simple, dynamic-typed language such as PHP, without sacrificing runtime performance.&lt;br /&gt;&lt;br /&gt;There is also a custom PHP interpreter called &lt;span style="font-weight:bold;"&gt;HPHPi&lt;/span&gt; to facilitate debugging. Of course, a developer can always use the standard PHP interpreter since he / she will be writing pure PHP code.&lt;br /&gt;&lt;br /&gt;Despite some syntactic similarities, PHP and C++ are essentially two very different languages. PHP is dynamically + weakly typed, whereas C++ is statically + strongly typed. Effective transformation of PHP code to C++ code would inevitably involve &lt;span style="font-weight:bold;"&gt;global code analysis&lt;/span&gt; and &lt;span style="font-weight:bold;"&gt;type inference&lt;/span&gt; to achieve early binding and specific typing, etc.&lt;br /&gt;&lt;br /&gt;The Facebook guys explains this thousands times better than I can, so check out this excellent video to see how this all works:&lt;br /&gt;&lt;br /&gt;&lt;object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="480" height="386" id="utv183556" name="utv_n_538703"&gt;&lt;param name="flashvars" value="loc=%2F&amp;amp;autoplay=false&amp;amp;vid=4409735" /&gt;&lt;param name="allowfullscreen" value="true" /&gt;&lt;param name="allowscriptaccess" value="always" /&gt;&lt;param name="src" value="http://www.ustream.tv/flash/video/4409735" /&gt;&lt;embed flashvars="loc=%2F&amp;amp;autoplay=false&amp;amp;vid=4409735" width="480" height="386" allowfullscreen="true" allowscriptaccess="always" id="utv183556" name="utv_n_538703" src="http://www.ustream.tv/flash/video/4409735" type="application/x-shockwave-flash" /&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;One point I have picked up from the video in particular is the &lt;span style="font-weight:bold;"&gt;tradeoff&lt;/span&gt; between the dynamicness in your original PHP code and the performance of the final C++ code. CPU / Memory performance gain seems to come primarily from early binding and specific typing (using primitive C++ types, for example). While HipHop for PHP seems to be very clever, the fact remains that if your PHP code results in loads of dynamic function calls and dynamic variable lookups, or even the abundant use of "variant" types in the final C++ code, you may not see such a big gain.&lt;br /&gt;&lt;br /&gt;Whether this will prove beneficial to the PHP community, only time will tell... But it certainly looks good!&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;P.S. Funny thing is I have just spent the last year or so working on a &lt;span style="font-weight:bold;"&gt;GWT&lt;/span&gt; project, writing Javascript frontend in Java. (and of course we ended up using a bit of &lt;span style="font-weight:bold;"&gt;Jython&lt;/span&gt; for some of the backend stuff...) I seem to be living a life in translation :-)&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-5529094097329350793?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/5529094097329350793/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=5529094097329350793' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/5529094097329350793'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/5529094097329350793'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2010/02/hiphop-for-php-php-at-c-speed.html' title='HipHop for PHP - running PHP at C++ speed'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-1899418894826998768</id><published>2010-02-02T12:21:00.010Z</published><updated>2010-02-04T00:26:43.977Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='africa'/><category scheme='http://www.blogger.com/atom/ns#' term='music'/><category scheme='http://www.blogger.com/atom/ns#' term='8tracks'/><title type='text'>West African mini-mix</title><content type='html'>Having watched the &lt;a href="http://www.bbc.co.uk/programmes/b00pv1m4"&gt;Lost Kingdoms of Africa - West Africa&lt;/a&gt; on BBC iPlayer over the weekend (in which somewhat tenuous links between Mali's Tellem art and Benin's bronze casting were mentioned), I was inspired to create another &lt;a href="http://8tracks.com/"&gt;8tracks&lt;/a&gt; mix:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://8tracks.com/seewah/west-african-boom-box"&gt;http://8tracks.com/seewah/west-african-boom-box&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,28,0" width="100%" height="120" &gt;&lt;param name="movie" value="http://8tracks.com/mixes/82740/player_v2"&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;param name="FlashVars" value="bg_color=_000000"&gt;&lt;embed FlashVars="bg_color=_000000" src="http://8tracks.com/mixes/82740/player_v2" pluginspage="http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash" type="application/x-shockwave-flash" width="100%" height="120" allowscriptaccess="always" &gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; font-style: italic;"&gt;Kayes-Ba&lt;/span&gt; Boubacar Traore&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic; font-weight: bold;"&gt;Trouble Sleep Yanga Wake Am&lt;/span&gt; Baaba Maal/ Taj Mahal/ Kaouding Cissoko/ Antibalas x Antibalas Afrobeat Orchestra&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; font-style: italic;"&gt;Ndéleng Ndéleng&lt;/span&gt; Orchestra Baobab&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; font-style: italic;"&gt;Nous avons gagne&lt;/span&gt; Orchestre Poly-Rythmo De Cotonou&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; font-style: italic;"&gt;Savane&lt;/span&gt; ali farka toure&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; font-style: italic;"&gt;Tapha Niang&lt;/span&gt; Toumani Diabaté's Symmetric Orchestra&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; font-style: italic;"&gt;Minsato Le, Mi Dayihome&lt;/span&gt; Orchestre Poly-Rythmo de Cotonou&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; font-style: italic;"&gt;Mi Ni Non Kpo&lt;/span&gt; Orchestre Poly-Rythmo De Cotonou&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; font-style: italic;"&gt;Suffering &amp;amp; Smiling&lt;/span&gt; Fela Kuti&lt;br /&gt;&lt;br /&gt;Purists may be disgusted by how I'm bunging all these different styles together under one umbrella, but I think this is exactly what makes African music great - the fact that the sounds are so different across regions (or countries, as drawn up by colonialists) , and yet they all so successfully convey this extraordinary sense of emotion, whether it is the simple guitar finger-picking of Boubacar Traore from &lt;span style="font-weight: bold;"&gt;Mali&lt;/span&gt;, or the out-of-this-world psychedelic funk of Orchestre Poly-Rythmo de Cotonou from &lt;span style="font-weight: bold;"&gt;Benin&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-1899418894826998768?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/1899418894826998768/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=1899418894826998768' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/1899418894826998768'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/1899418894826998768'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2010/02/west-african-mini-mix.html' title='West African mini-mix'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-3359171627143666545</id><published>2010-02-02T11:59:00.012Z</published><updated>2010-02-02T17:30:08.031Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='london'/><category scheme='http://www.blogger.com/atom/ns#' term='climbing'/><title type='text'>The Reach - new climbing wall in Woolwich, south east London</title><content type='html'>The word on the street is that there is a new climbing wall in &lt;span style="font-weight: bold;"&gt;South East London&lt;/span&gt;. Yes, in &lt;span style="font-weight: bold;"&gt;Woolwich&lt;/span&gt;! This is great news for me as it is only a 20 mins bus or bike ride from lonely Lewisham. In a way, the &lt;a href="http://www.archclimbingwall.com/"&gt;Arch&lt;/a&gt; in London Bridge is ideally situated as it is on my work commute route, but trying to be nicer to my not-so-happy fingers, I have decided to stay away from bouldering walls, and a lead wall south of the river is exactly what I need.&lt;br /&gt;&lt;br /&gt;So I decided to suss out this place called the &lt;a href="http://www.thereach.org.uk/"&gt;Reach&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Tucked away inside an unassuming, if slightly post-apocalyptic, industrial estate close to the Thames Barriers, it is basically a massive warehouse unit. If you are taking a bus from the direction of Greenwich, e.g. on &lt;a href="http://www.tfl.gov.uk/tfl/gettingaround/maps/buses/?route=180"&gt;number 180&lt;/a&gt;, get off at the &lt;span style="font-weight: bold;"&gt;third roundabouts&lt;/span&gt; on Woolwich Road after the Blackwall Tunnel Approach Flyover. Otherwise, the &lt;span style="font-weight: bold;"&gt;Woolwich Dockyard rail station&lt;/span&gt; seems to be the closest train station, and the &lt;span style="font-weight: bold;"&gt;Woolwich Arsenal DLR&lt;/span&gt; is just a bit further away. The climbing wall itself is located inside the Mellish industrial estate towards the river. From the aorundabout, walk down &lt;span style="font-weight: bold;"&gt;Warspite Road&lt;/span&gt;, follow signs and try not to get lost. When you finally reach the &lt;span style="font-weight: bold;"&gt;Reach&lt;/span&gt; (terrible pun!), you will be greeted by a bright, clean entrance (something of an oddity inside this depressing industrial estate) with bike parking space on the right. Registration is straightforward. However, do note that when I went (27th Jan, 2010) they did not have a card machine, so &lt;span style="font-weight: bold;"&gt;bring along some cash&lt;/span&gt;!&lt;br /&gt;&lt;br /&gt;The warehouse is huge and spacious, and it currently consists of a steep overhang lead wall (with easy very slab 50/60-degree toproping on the other side), another slightly overhanging lead wall, and a number of toproping/lead vertical/slab wall, including a wonderfully named 80/85-degree tall slab - the &lt;span style="font-weight: bold;"&gt;"Black Slabbath"&lt;/span&gt;. They seem to be in the process of building more walls at the back of the warehouse. There is also an upstairs bit, with a traverse bouldering wall, sofas, tea and coffee, etc.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 102, 0);"&gt;The climbing&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;First of all, if you like to show off your toned climber body by wearing the bear minimum, the Reach is not the place for you as it does not seem to have any heating. I suppose it would cost the earth (in every sense) to heat up a warehouse! So &lt;span style="font-weight: bold;"&gt;put some layers on&lt;/span&gt; and stop showing off.&lt;br /&gt;&lt;br /&gt;The lead grades seem to be bunched around the &lt;span style="font-weight: bold;"&gt;F5c - F6c&lt;/span&gt; range (my estimate is there are about 40/50 routes within this range), so if you are new to leading, or if you are a F7 climber, you will find the leading a bit limited. On the toproping front, there seem to be quite a lot of easy slabby routes for beginners. Personally I feel that the grading is slightly inconsistent, but hey when do climbers ever not moan about grade inconsistency?&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 102, 0);"&gt;Will I go there again?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;Yes&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Being close to Lewisham/Greenwich, for me this beats all the other London walls. If you do not live around the area, it is a bit out of the way, but there are buses, trains, and well, DLRs going to that part of Woolwich. Price-wise, at &lt;span style="font-weight: bold;"&gt;8/9 quid a pop&lt;/span&gt;, it is OK - not cheap, not extortionally expensive.&lt;br /&gt;&lt;br /&gt;Personally, I like the wall because it is spacious, and unlike the Castle or the Westway, you won't have to queue up for climbs or get into each other's way while climbing, but this may obviously change if the Reach eventually becomes popular.&lt;br /&gt;&lt;br /&gt;I think there is still loads of room for improvement, and they seem to have big plans in the future. For now, being a new place, it is not bad if you are more into climbing than socialising! If you are from SE London, pop along with a fleece and check it out for yourself (no, I don't work for them, in case you are wondering!)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-3359171627143666545?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/3359171627143666545/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=3359171627143666545' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/3359171627143666545'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/3359171627143666545'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2010/02/reach-new-climbing-wall-in-woolwich.html' title='The Reach - new climbing wall in Woolwich, south east London'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-460927617627642073</id><published>2010-01-26T18:41:00.007Z</published><updated>2010-02-04T00:27:53.177Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='geeky'/><category scheme='http://www.blogger.com/atom/ns#' term='music'/><category scheme='http://www.blogger.com/atom/ns#' term='8tracks'/><title type='text'>8tracks.com</title><content type='html'>Came across &lt;a href="http://8tracks.com/"&gt;http://8tracks.com&lt;/a&gt; today. It is basically a site where you can create your "mp3 mix tapes". You can add songs to your mix by selecting from their existing collection of individual mp3's or by uploading your own individual mp3's. Note that you cannot do "advanced" mixing with this app - no knob-twisting for all you bedroom DJs :-)&lt;br /&gt;&lt;br /&gt;To cover their arses, they have enforced a set of interesting rules which would apparently ensure that they don't get sued by record companies. These include:&lt;br /&gt;&lt;br /&gt;- This mix has to "include at least 8 tracks, no more than 2 of which are from the same artist or album"&lt;br /&gt;&lt;br /&gt;- 8tracks will randomize "playback the 2nd time a person listens to your mix"&lt;br /&gt;&lt;br /&gt;- You can only listen to 30 seconds of each track when creating your mix&lt;br /&gt;&lt;br /&gt;- When listening to of mixes created by others, you can only skip 3 tracks per hour.&lt;br /&gt;&lt;br /&gt;Check out my mix (dub, dubstep, electronic)&lt;br /&gt;&lt;a href="http://8tracks.com/seewah/turn-up-the-sub-bass"&gt;http://8tracks.com/seewah/turn-up-the-sub-bass&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,28,0" width="100%" height="120" &gt;&lt;param name="movie" value="http://8tracks.com/mixes/80595/player_v2"&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;param name="FlashVars" value="bg_color=_000000"&gt;&lt;embed FlashVars="bg_color=_000000" src="http://8tracks.com/mixes/80595/player_v2" pluginspage="http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash" type="application/x-shockwave-flash" width="100%" height="120" allowscriptaccess="always" &gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; font-style: italic;"&gt;Anti War Dub ( Feat. Spen G)&lt;/span&gt; Digital Mystikz&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic; font-weight: bold;"&gt;Aztec Warrior&lt;/span&gt; Mad Professor&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; font-style: italic;"&gt;Negions Fail&lt;/span&gt; Wisp&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; font-style: italic;"&gt;Drop The Other (Scuba's Vulpine Remix)&lt;/span&gt; Emika&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; font-style: italic;"&gt;Love Cry (Joy Orbison Remix)&lt;/span&gt; Four Tet&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; font-style: italic;"&gt;Chronograph&lt;/span&gt; Sketch Show&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; font-style: italic;"&gt;Exhibit A (Transformations)&lt;/span&gt; Jay Electronica&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; font-style: italic;"&gt;Eastern Jam&lt;/span&gt; Chase &amp;amp; Status&lt;br /&gt;&lt;br /&gt;Yes I like the idea! No doubt I will be creating more mini-mixes soon...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-460927617627642073?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/460927617627642073/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=460927617627642073' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/460927617627642073'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/460927617627642073'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2010/01/8trackscom.html' title='8tracks.com'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-7002310358650779376</id><published>2010-01-11T09:46:00.008Z</published><updated>2010-01-12T21:57:06.458Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='desert island'/><category scheme='http://www.blogger.com/atom/ns#' term='software development'/><category scheme='http://www.blogger.com/atom/ns#' term='geeky'/><category scheme='http://www.blogger.com/atom/ns#' term='lastfm'/><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><category scheme='http://www.blogger.com/atom/ns#' term='json'/><title type='text'>jQuery autocomplete + JSONP + Wikipedia OpenSearch service</title><content type='html'>My second &lt;a href="http://desert-island.appspot.com/"&gt;Desert Island&lt;/a&gt;-related post...&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="color:#006600;"&gt;A bit of background...&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;em&gt;When a user creates his / her list in the app, he / she has to somehow specific a list of albums.&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;There were a number of issues which I had to consider when designing this part of the app:&lt;br /&gt;&lt;br /&gt;1) where do I get artist name /album names from?&lt;br /&gt;2) do users browse and select or do they simply type into a freetext field with auto-completion / suggestion?&lt;br /&gt;3) when users have to specify new artist names / album names unknown to my app, how do I ensure that they type in the "universally-accepted" names for the artists / albums?&lt;br /&gt;&lt;br /&gt;It is not realistic for me to keep a database of all known artists and albums - it would be a maintenance nightmare. New albums come out everyday!&lt;br /&gt;&lt;br /&gt;So I came up with the following idea: (why not &lt;a href="http://desert-island.appspot.com/"&gt;add your list&lt;/a&gt; and see it work in practice!)&lt;br /&gt;&lt;br /&gt;When adding an album to the list, the user would&lt;br /&gt;&lt;br /&gt;1) specify the artist using a freetext field with auto-completion (using data provided by the &lt;a href="http://en.wikipedia.org/w/api.php"&gt;Wikipedia OpenSearch service&lt;/a&gt;)&lt;br /&gt;2) click on "list albums" to see a list of popular albums by this artist (using &lt;a href="http://www.last.fm/api/show?service=287"&gt;Last.fm's artist.gettopalbums service&lt;/a&gt;)&lt;br /&gt;3) either select an album from the list or, if the album is not in the list, create a new album, again using another freetext field with auto-completion (again using the &lt;a href="http://en.wikipedia.org/w/api.php"&gt;Wikipedia OpenSearch service&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;This way, I don't really differentiate between new or old artists / albums (i.e. whether a certain entry has been chosen by somehow else before) when dealing with the list creation UI.&lt;br /&gt;&lt;br /&gt;In this blog I would like to focus on the use of jQuery autocomplete plugin along with the (not very well-known but IMHO very useful) Wikipedia OpenSearch service.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="color:#006600;"&gt;jQuery autocomplete plugin with Wikipedia Opensearch&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;Links to &lt;a href="http://bassistance.de/jquery-plugins/jquery-plugin-autocomplete/"&gt;jQuery autocomplete plugin&lt;/a&gt; and &lt;a href="http://docs.jquery.com/Plugins/Autocomplete"&gt;documentation&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Unfortunately, the documentation above fails to mention how to use a third-party &lt;strong&gt;cross-domain&lt;/strong&gt; JSONP service (the Wikipedia service in my case) to provide data for the autocomplete plugin. After a bit of googling, I came across this &lt;a href="http://1300grams.com/2009/08/17/jquery-autocomplete-with-json-jsonp-support-and-overriding-the-default-search-parameter-q/"&gt;excellent article&lt;/a&gt; which shows the autocomplete plugin does really with JSONP! Do go ahead an read this article.&lt;br /&gt;&lt;br /&gt;The following is my implementation using the Wikipedia Opensearch JSONP service, where &lt;em&gt;expression&lt;/em&gt; is the CSS selector of the html textbox to which the autocomplete widget is to be attached:&lt;br /&gt;&lt;br /&gt;&lt;pre style="WIDTH: 440px; HEIGHT: 450px; OVERFLOW: auto"&gt;&lt;span style="color:blue;"&gt;function attachWikiAutoComplete(expression) {&lt;br /&gt; $(expression).autocomplete("http://en.wikipedia.org/w/api.php",  {&lt;br /&gt;    dataType: "jsonp", &lt;br /&gt;    parse: function(data) { &lt;br /&gt;      var rows = new Array(); &lt;br /&gt;      var matches = data[1];&lt;br /&gt;      for( var i = 0; i &lt; matches.length; i++){ &lt;br /&gt;        rows[i] = { data:matches[i], value:matches[i], result:matches[i] }; &lt;br /&gt;      } &lt;br /&gt;      return rows;&lt;br /&gt;    },&lt;br /&gt;    formatItem: function(row) { return row; }, &lt;br /&gt;    extraParams: {&lt;br /&gt;      action: "opensearch", &lt;br /&gt;      format: "json", &lt;br /&gt;      search: function () { return $(expression).val() } }, &lt;br /&gt;     max: 10 &lt;br /&gt;   }&lt;br /&gt; );&lt;br /&gt;}&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;For those of you interested, an example HTTP request sent by the autocomplete plugin to Wikipedia could be (user's just typed in "metallic"):&lt;br /&gt;&lt;a href="http://en.wikipedia.org/w/api.php?callback=jsonp1263175070864&amp;_=1263175085048&amp;q=metallic&amp;limit=10&amp;timestamp=1263175085048&amp;action=opensearch&amp;format=json&amp;search=metallic"&gt;http://en.wikipedia.org/w/api.php?callback=jsonp1263175070864&amp;_=1263175085048&amp;q=metallic&amp;limit=10&amp;amp;timestamp=1263175085048&amp;action=opensearch&amp;format=json&amp;search=metallic&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;As you can see, the autocomplete plugin automatically adds the parameters "q" and "limit" to the url, which Wikipedia simply ignores. If you are to create your own REST data service for the plugin, you may want to create your service to understand "q" as the query parameter and "limit" for result limit parameter. However, if you are using a third-party service, just add the required query pararmeter ("search" in the case of the Wikipedia Opensearch service) as part of the "extraParams" option, as I have done here.&lt;br /&gt;&lt;br /&gt;To see this Wiki jQuery autocomplete &lt;strong&gt;in action&lt;/strong&gt;, log into &lt;a href="http://desert-island.appspot.com"&gt;http://desert-island.appspot.com&lt;/a&gt; via &lt;strong&gt;Facebook Connect&lt;/strong&gt;, click on &lt;strong&gt;"Create"&lt;/strong&gt; in the welcome box and try adding albums to the empty list!&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_cgyuO2_6pKQ/S0zv_10vIPI/AAAAAAAAABk/i1oAk0NTrV8/s1600-h/autocomplete.gif"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 227px;" src="http://1.bp.blogspot.com/_cgyuO2_6pKQ/S0zv_10vIPI/AAAAAAAAABk/i1oAk0NTrV8/s400/autocomplete.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5425975531133673714" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-7002310358650779376?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/7002310358650779376/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=7002310358650779376' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/7002310358650779376'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/7002310358650779376'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2010/01/jquery-autocomplete-jsonp-wikipedia.html' title='jQuery autocomplete + JSONP + Wikipedia OpenSearch service'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_cgyuO2_6pKQ/S0zv_10vIPI/AAAAAAAAABk/i1oAk0NTrV8/s72-c/autocomplete.gif' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-1131547860957100789</id><published>2010-01-05T13:50:00.001Z</published><updated>2010-01-05T13:52:07.502Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='google gadget'/><title type='text'>ONIX Quick Search iGoogle Gadget prototype</title><content type='html'>Prototype of the ONIX iGoogle Gadget:&lt;br /&gt;&lt;br /&gt;&lt;script src="http://www.gmodules.com/ig/ifr?url=http://hosting.gmodules.com/ig/gadgets/file/111513885136272339065/onix_qs_0.xml&amp;amp;synd=open&amp;amp;w=420&amp;amp;h=500&amp;amp;title=ONIX+Quick+Search&amp;amp;border=%23ffffff%7C3px%2C1px+solid+%23999999&amp;amp;output=js"&gt;&lt;/script&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-1131547860957100789?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/1131547860957100789/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=1131547860957100789' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/1131547860957100789'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/1131547860957100789'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2010/01/onix-quick-search-igoogle-gadget.html' title='ONIX Quick Search iGoogle Gadget prototype'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-5802190520061610031</id><published>2009-12-23T16:34:00.010Z</published><updated>2009-12-23T23:29:49.160Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='desert island'/><category scheme='http://www.blogger.com/atom/ns#' term='software development'/><category scheme='http://www.blogger.com/atom/ns#' term='geeky'/><category scheme='http://www.blogger.com/atom/ns#' term='facebook'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='gae'/><title type='text'>Google App Engine Datastore "IN" operator</title><content type='html'>This is the first &lt;a href="http://desert-island.appspot.com/"&gt;Desert Island&lt;/a&gt;-related technical post. In this article, I am outlining a couple of &lt;span style="font-weight: bold;"&gt;gotchas&lt;/span&gt; regarding the use of the "IN" operator in GQL.&lt;br /&gt;&lt;br /&gt;In &lt;a href="http://desert-island.appspot.com/"&gt;http://desert-island.appspot.com&lt;/a&gt;, I created a class &lt;span style="font-family:courier new;"&gt;DesertIslandList&lt;/span&gt;, which represents a user's list of his/her 8 desert island albums. It, amongst other properties, contains &lt;span style="font-family:courier new;"&gt;user_id&lt;/span&gt; and &lt;span style="font-family:courier new;"&gt;id_source&lt;/span&gt;. For a user who's logged in via &lt;span style="font-weight: bold;"&gt;Facebook Connect&lt;/span&gt;, the &lt;span style="font-family:courier new;"&gt;user_id&lt;/span&gt; of his/her created list would simply be his Facebook profile id and &lt;span style="font-family:courier new;"&gt;id_source&lt;/span&gt; would be "f" (for Facebook).&lt;br /&gt;&lt;br /&gt;In order to display lists created by his/her friends, I, first of all, get his/her friend's id list using &lt;span style="font-family:courier new;"&gt;friends.get&lt;/span&gt; method in the &lt;a href="http://wiki.developers.facebook.com/index.php/API"&gt;Facebook RESTful API&lt;/a&gt; with a version of &lt;a href="http://blog.patrickcrosby.com/2008/04/20/Write-Facebook-apps-using-Google-AppEngine.html"&gt;minifb hacked up for Google App Engine&lt;/a&gt;. I then created a GQL query for all &lt;span style="font-family:courier new;"&gt;DesertIslandList WHERE user_id IN :1 AND id_source = :2&lt;/span&gt; with &lt;span style="font-family:courier new;"&gt;:1&lt;/span&gt; being the Python list containing all the friend ids as strings, and &lt;span style="font-family:courier new;"&gt;:2&lt;/span&gt; being "f". Straightforward enough? Well it all seemed so until I stumbled across of a couple of gotchas.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 153, 0);"&gt;Only 30 items allowed&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;First of all, I soon got a "&lt;span style="font-weight: bold;"&gt;too many subqueries (max: 30...&lt;/span&gt;" exception.  As it turns out, only 30 items are allowed in the list:&lt;br /&gt;&lt;br /&gt;(from &lt;a href="http://code.google.com/appengine/docs/python/datastore/gqlreference.html"&gt;http://code.google.com/appengine/docs/python/datastore/gqlreference.html&lt;/a&gt;) &lt;span style="font-style: italic; font-weight: bold;"&gt;Note: The IN and != operators use multiple queries behind the scenes. For example, the IN operator executes a separate underlying datastore query for every item in the list. The entities returned are a result of the cross-product of all the underlying datastore queries and are de-duplicated. A maximum of 30 datastore queries are allowed for any single GQL query.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;So, my bad, not reading the documentation properly. To get around this, you could either somehow limit the list to 30 items or chop your list up into 30-item chunks and union the results (sorting offline if necessary).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 153, 0);"&gt;What if the "IN" list is empty?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;What if he or she has no friends?&lt;br /&gt;&lt;br /&gt;Convention SQL wisdom would suggest that the query should return no &lt;span style="font-family:courier new;"&gt;DesertIslandList&lt;/span&gt; objects. However, in the world of Google App Engine datastore, "IN" means cross-product of all the separate individual queries, so in my GQL query above, an empty "IN" list effectively means get all &lt;span style="font-family:courier new;"&gt;DesertIslandList WHERE id_source = "f"&lt;/span&gt; i.e. not filtering on the &lt;span style="font-family:courier new;"&gt;user_id&lt;/span&gt; field at all!&lt;br /&gt;&lt;br /&gt;Not only is this slight counter-intuitive, this can also throw a surprise "&lt;span style="font-weight: bold;"&gt;no matching &lt;/span&gt;&lt;a style="font-weight: bold;" href="http://code.google.com/appengine/docs/python/datastore/queriesandindexes.html"&gt;index&lt;/a&gt;&lt;span style="font-weight: bold;"&gt; found&lt;/span&gt;" exception. The fact that an empty "IN" list does not filter on the &lt;span style="font-family:courier new;"&gt;user_id&lt;/span&gt; field means my GQL instance with an empty "friend ids" list has to access an &lt;span style="font-weight: bold;"&gt;additional &lt;/span&gt;&lt;span style="font-family:courier new;"&gt;DesertIslandList&lt;/span&gt; index with only the &lt;span style="font-family:courier new;"&gt;id_source&lt;/span&gt; field, and not the usual index with both the &lt;span style="font-family:courier new;"&gt;user_id&lt;/span&gt; and the &lt;span style="font-family:courier new;"&gt;id_source&lt;/span&gt; fields.  Imagine this, if you have not run a GQL instance with an empty "IN" list in your local dev environment for this particular query, no index (with only the &lt;span style="font-family:courier new;"&gt;id_source&lt;/span&gt; field) will be added to your index.yaml. Up in the cloud, if a person happens to have no Facebook friends, he or she is going to see an ugly stacktrace complaining the the index does not exist!&lt;br /&gt;&lt;br /&gt;Of course, the simple solution to this problem is to always check the list to see if it is empty before running the query.&lt;br /&gt;&lt;br /&gt;Just another thing to bear in mind!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-5802190520061610031?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/5802190520061610031/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=5802190520061610031' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/5802190520061610031'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/5802190520061610031'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2009/12/google-app-engine-datastore-in-operator.html' title='Google App Engine Datastore &quot;IN&quot; operator'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-9093371158585085343</id><published>2009-12-23T14:39:00.007Z</published><updated>2009-12-23T23:32:37.069Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='photography'/><title type='text'>First time film processing...</title><content type='html'>Finally got round to getting my hands dirty (not literally) and processing my first film rolls (&lt;span style="font-weight: bold;"&gt;Ilford HP5 Plus ISO 400&lt;/span&gt;) with the assistance of a kind colleague of mine.&lt;br /&gt;&lt;br /&gt;Using a &lt;a href="http://en.wikipedia.org/wiki/Changing_bag"&gt;changing bag&lt;/a&gt;, I tranferred the two rolls of film from the canisters onto the plastic reel, which was then placed into the &lt;a href="http://en.wikipedia.org/wiki/Developing_tank"&gt;developing tank&lt;/a&gt;. After 30 mins or so of pouring various fluids (developer, fixer, warm water) in and out of the tank, the images finally appeared on the negative films. With the films being hung up by the door to dry overnight, I was extremely eager to scan them into Photoshop to get a proper look at them.&lt;br /&gt;&lt;br /&gt;The following day, I gingerly placed the negs onto the glass of my Canon flatbed scanner (using the film adapters that came with the scanner), started up Photoshop and got my first thumbnail previews of the shots. They looked OK, so I went ahead and scanned a few of them at maximum (3200 dpi) resolution. Much to my surprise, they came out extremely grainy, bordering on being rubbish. I love film grains, but what I was seeing was definitely not the level of grains that you would expect from an ISO 400 film. Worse still, one of the rolls had a vertical streak running down every single frame, and I am still not sure whether it was due to a mistake during processing or from the camera itself (I used different cameras for the two different rolls). Since the other roll does not exhibit this defect, I suspect it is more likely to be the latter.&lt;br /&gt;&lt;br /&gt;Anyway three of the "better shots" (which I actually quite like) that came out - the first two I took using the my &lt;a style="font-weight: bold;" href="http://www.flickr.com/photos/seewah/3713755250/"&gt;Canonet 19QL&lt;/a&gt; (£15 from eBay!) and the last one using my &lt;span style="font-weight: bold;"&gt;Cosina 35E&lt;/span&gt; (£2 from eBay...) - notice the vertical streak in this last one:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.flickr.com/photos/seewah/4202140254/" title="Aunt Sally by See Wah, on Flickr"&gt;&lt;img src="http://farm3.static.flickr.com/2566/4202140254_dfe2753501.jpg" alt="Aunt Sally" width="500" height="326" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.flickr.com/photos/seewah/4202138206/" title="Welcome to Kat's party! by See Wah, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4046/4202138206_646bc04213.jpg" alt="Welcome to Kat's party!" width="500" height="312" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Notice the vertical streak in the following photo:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.flickr.com/photos/seewah/4204977182/" title="Music to walk by by See Wah, on Flickr"&gt;&lt;img src="http://farm3.static.flickr.com/2765/4204977182_f7751d36df.jpg" alt="Music to walk by" width="500" height="329" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-9093371158585085343?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/9093371158585085343/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=9093371158585085343' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/9093371158585085343'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/9093371158585085343'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2009/12/first-time-film-processing.html' title='First time film processing...'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://farm3.static.flickr.com/2566/4202140254_dfe2753501_t.jpg' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-906023178336940334</id><published>2009-12-11T13:05:00.021Z</published><updated>2010-02-20T11:08:13.171Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='cycling'/><title type='text'>Druid Street Cycles - best LBS in London?</title><content type='html'>&lt;div&gt;&lt;b&gt;&lt;span class="Apple-style-span"  style="color:#CC0000;"&gt;Update: Thor and co. have recently set up an &lt;/span&gt;&lt;/b&gt;&lt;a href="http://www.facebook.com/group.php?gid=288436138262"&gt;&lt;b&gt;&lt;span class="Apple-style-span"  style="color:#CC0000;"&gt;official Facebook group&lt;/span&gt;&lt;/b&gt;&lt;/a&gt;&lt;b&gt;&lt;span class="Apple-style-span"  style="color:#CC0000;"&gt;. And he is promising 10% discount for all group members :-)&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;Their address: &lt;strong&gt;18 Druid Street, London SE1 2EY&lt;/strong&gt; &lt;a href="http://maps.google.co.uk/maps?q=druid%2Bstreet%2Bbike&amp;amp;ie=UTF8&amp;amp;hl=en&amp;amp;view=map&amp;amp;cid=15648102263483823901&amp;amp;ved=0CBEQpQY&amp;amp;ei=uDEkS_abEo7MjAeM9biQCQ&amp;amp;hq=druid%2Bstreet%2Bbike&amp;amp;hnear=&amp;amp;ll=51.503186,-0.079393&amp;amp;spn=0.006585,0.01929&amp;amp;z=16&amp;amp;iwloc=A"&gt;gmap&lt;/a&gt;&lt;br /&gt;Phone: &lt;strong&gt;07551016380&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.flickr.com/photos/seewah/4180210198/" title="Vintage Sun Solo Racer by See Wah, on Flickr"&gt;&lt;img src="http://farm3.static.flickr.com/2666/4180210198_51cbc7a5d6.jpg" width="500" height="333" alt="Vintage Sun Solo Racer" /&gt;&lt;/a&gt;Having recently bought an old &lt;strong&gt;Sun Solo racer&lt;/strong&gt; from Gumtree (80s Raleigh "&lt;a href="http://www.sheldonbrown.com/gloss_g.html"&gt;gaspipe&lt;/a&gt;" steel frame, 27" x 1 1/4 rims, Weinmann single-pivot brakes, Sturmey Archer stem, Sturmey Archer hub, 5-speed), I took it to the little known &lt;strong&gt;Druid Street Cycles&lt;/strong&gt; near &lt;strong&gt;London Bridge&lt;/strong&gt; for a quick service.&lt;br /&gt;&lt;br /&gt;Not a lot has been mentioned on the web about this &lt;strong&gt;social business project&lt;/strong&gt;, apart from a couple of youtube videos, a user comment in a Timeout article and an &lt;a href="http://www.lcc.org.uk/index.asp?PageID=1478"&gt;LCC article&lt;/a&gt; (they themselves don't have a website):&lt;br /&gt;&lt;br /&gt;&lt;object width="560" height="340"&gt;&lt;param name="movie" value="http://www.youtube.com/v/K4wTTHBpRYQ&amp;amp;hl=en_GB&amp;amp;fs=1&amp;amp;"&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;embed src="http://www.youtube.com/v/K4wTTHBpRYQ&amp;amp;hl=en_GB&amp;amp;fs=1&amp;amp;" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="560" height="340"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Thor&lt;/strong&gt;, who used to repair tanks in his native Germany, started this project to provide affordable (and yes, really affordable) bike repair service for the local community in &lt;strong&gt;Southwark&lt;/strong&gt;. To say that he, along with the rest of Druid Street Cycles, is passionate about building/fixing bikes is an understatement.&lt;br /&gt;&lt;br /&gt;I have been there twice now, and on both occasions, the jobs they did on the bike far exceeded my expectation. The first time round, they did a full checkup of the bike, fixed the rear brake, and replaced the dodgy tyres. He ended up only charging me 30 pounds (the tyres along cost 20 quid!) - he even crimped the cable ends for me, such is his attention to details! Second time round, I went in to see if they have any mudguard that would fit over my 27" wheel, which I knew was very hard to come by. In typical Thor's fashion, he said he would find something, and surely he did, somehow he managed to fit a full-length mudguard beautifully on the 27" wheel frame, which has minimal clearance. Initially he refused to take any money from me, but in the end I managed to convince him to take the money, which no doubt would be put to good use to benefit the local biking community.&lt;br /&gt;&lt;br /&gt;I really cannot recommend them enough, and I hope more people will get to know about this secret little place hidden underneath one of London Bridge's dark arches.&lt;br /&gt;&lt;strong&gt;&lt;/strong&gt;&lt;br /&gt;Ah how can I forget the mention the fact that they provide &lt;strong&gt;free courtesy bikes&lt;/strong&gt; to customers, so you can continue your commute to work or your cycle home while Thor and co. are busying fixing yours! Beat that!&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Looking for a commute route, or wanting to share your route with other cyclists? Visit my cycle route database: &lt;/span&gt;&lt;a style="font-style: italic;" href="http://zoomaroundtown.appspot.com/"&gt;http://zoomaroundtown.appspot.com&lt;/a&gt;&lt;span style="font-style: italic;"&gt;!&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-906023178336940334?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/906023178336940334/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=906023178336940334' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/906023178336940334'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/906023178336940334'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2009/12/druid-street-cycles-best-lbs-in-london.html' title='Druid Street Cycles - best LBS in London?'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://farm3.static.flickr.com/2666/4180210198_51cbc7a5d6_t.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-1371157692141964270</id><published>2009-12-08T12:20:00.008Z</published><updated>2010-01-11T01:14:33.025Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='desert island'/><category scheme='http://www.blogger.com/atom/ns#' term='cloud'/><category scheme='http://www.blogger.com/atom/ns#' term='geeky'/><category scheme='http://www.blogger.com/atom/ns#' term='lastfm'/><category scheme='http://www.blogger.com/atom/ns#' term='facebook'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='music'/><category scheme='http://www.blogger.com/atom/ns#' term='gae'/><title type='text'>Desert Island Discs for the non-celebs</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_cgyuO2_6pKQ/SyVuQila5kI/AAAAAAAAABU/E9PwXFnX_j4/s1600-h/logo.gif"&gt;&lt;img style="MARGIN: 0pt 10px 10px 0pt; WIDTH: 120px; FLOAT: left; HEIGHT: 60px; CURSOR: pointer" id="BLOGGER_PHOTO_ID_5414855357423609410" border="0" alt="" src="http://4.bp.blogspot.com/_cgyuO2_6pKQ/SyVuQila5kI/AAAAAAAAABU/E9PwXFnX_j4/s400/logo.gif" /&gt;&lt;/a&gt;I have just finished my second Google App Engine project: &lt;strong&gt;"Desert Island Discs for the non-celebs"&lt;/strong&gt; &lt;a href="http://desert-island.appspot.com/"&gt;http://desert-island.appspot.com/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This is just a little fun project to test out the powerful &lt;a href="http://developers.facebook.com/connect.php"&gt;Facebook Connect API&lt;/a&gt;, and to further explore Google App Engine (to find yet another Datastore oddity/feature, which I will blog about soon).&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_cgyuO2_6pKQ/SyVuZELr9bI/AAAAAAAAABc/_iKQEMiJJBQ/s1600-h/screen.gif"&gt;&lt;img style="MARGIN: 0pt 10px 10px 0pt; WIDTH: 300px; FLOAT: left; HEIGHT: 200px; CURSOR: pointer" id="BLOGGER_PHOTO_ID_5414855503881434546" border="0" alt="" src="http://3.bp.blogspot.com/_cgyuO2_6pKQ/SyVuZELr9bI/AAAAAAAAABc/_iKQEMiJJBQ/s400/screen.gif" /&gt;&lt;/a&gt; Try it out and add you list!&lt;br /&gt;&lt;br /&gt;Thx&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-1371157692141964270?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/1371157692141964270/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=1371157692141964270' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/1371157692141964270'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/1371157692141964270'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2009/12/desert-island-discs-for-non-celebs.html' title='Desert Island Discs for the non-celebs'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_cgyuO2_6pKQ/SyVuQila5kI/AAAAAAAAABU/E9PwXFnX_j4/s72-c/logo.gif' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-7088396327045607617</id><published>2009-11-24T12:48:00.012Z</published><updated>2009-11-25T10:06:50.406Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='software development'/><category scheme='http://www.blogger.com/atom/ns#' term='geeky'/><category scheme='http://www.blogger.com/atom/ns#' term='unicode'/><category scheme='http://www.blogger.com/atom/ns#' term='xss'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='gae'/><title type='text'>Django template urlencode unicode characters (Google App Engine)</title><content type='html'>I have just started work on my next &lt;strong&gt;Google App Engine&lt;/strong&gt; project, and I have been using the &lt;a href="http://docs.djangoproject.com/en/dev/ref/templates/builtins/#urlencode"&gt;Django Template urlencode filter&lt;/a&gt; to urlencode strings before displaying them as components inside an URL like this: &lt;span style="font-family:courier new;"&gt;&amp;lt;a href="/?{{ unsafe_string|urlencode }}" title=".."&amp;gt;..&amp;lt;/a&amp;gt;&lt;/span&gt;, to prevent &lt;strong&gt;XSS (Cross Site Scripting) attack&lt;/strong&gt;, etc. This is all good as long as &lt;span style="font-family:courier new;"&gt;unsafe_string&lt;/span&gt; does not contain unicode characters. Underneath, the Django urlencode filter calls &lt;a href="http://docs.python.org/library/urllib.html"&gt;urllib.quote method&lt;/a&gt; to do the encoding. Unfortunately, this method does not like unicode characters, and a string containing unicode (utf-8) characters has to be utf-8 encoded before it can be put through &lt;span style="font-family:courier new;"&gt;urllib.quote&lt;/span&gt; (and hence the Django urlencode filter).&lt;br /&gt;&lt;br /&gt;So I created a &lt;a href="http://docs.djangoproject.com/en/dev/howto/custom-template-tags/"&gt;custom Django template filter&lt;/a&gt; to circumvent this issue:&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Note: I have only tested it in Google App Engine (GAE) using its Django 0.96 template support. Also, the registration mechanism described below is specific to Google App Engine's own templating engine. However, there is no reason why this custom filter would not work in non-GAE Python apps using Django 0.96 or 1.0 templates. Please follow instructions &lt;/strong&gt;&lt;a href="http://docs.djangoproject.com/en/dev/howto/custom-template-tags/"&gt;&lt;strong&gt;here&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt; to configure your app if you are not using GAE.&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;The custom tag (which handles both unicode and "non-unicode" characters) itself is very straightforward to create.&lt;br /&gt;&lt;br /&gt;I created a module &lt;span style="font-family:courier new;"&gt;customtags.xss&lt;/span&gt; (you can name it something else) as follows:&lt;br /&gt;&lt;br /&gt;1) under the root folder of my GAE application, I created a folder "&lt;span style="font-family:courier new;"&gt;customtags&lt;/span&gt;"&lt;br /&gt;&lt;br /&gt;2) inside &lt;span style="font-family:courier new;"&gt;customtags&lt;/span&gt;, I created two empty files &lt;span style="font-family:courier new;"&gt;xss.py&lt;/span&gt; and &lt;span style="font-family:courier new;"&gt;__init__.py&lt;/span&gt; (&lt;span style="font-family:courier new;"&gt;__init__.py&lt;/span&gt; will remain empty)&lt;br /&gt;&lt;br /&gt;3) inside &lt;span style="font-family:courier new;"&gt;xss.py&lt;/span&gt;, I put&lt;br /&gt;&lt;pre style="WIDTH: 440px; HEIGHT: 240px; OVERFLOW: auto"&gt;import types&lt;br /&gt;import urllib&lt;br /&gt;from django import template&lt;br /&gt;&lt;br /&gt;register = template.Library()&lt;br /&gt;&lt;br /&gt;@register.filter&lt;br /&gt;def unicode_urlencode(value):&lt;br /&gt;    if type(value) is types.UnicodeType:&lt;br /&gt;        return urllib.quote(value.encode("utf-8"))&lt;br /&gt;    else:&lt;br /&gt;        return urllib.quote(value)&lt;br /&gt;&lt;/pre&gt;So, the filter utf-8 encodes the string first if it is of type &lt;span style="font-family:courier new;"&gt;UnicodeType&lt;/span&gt;. (You may want to handle exceptions more gracefully though - at the moment, I am not catching exceptions...)&lt;br /&gt;&lt;br /&gt;Let's look at how to register this template library so that you can use the filter inside your Django templates.&lt;br /&gt;&lt;br /&gt;In your application script, for example, &lt;span style="font-family:courier new;"&gt;YOUR_APP_NAME.py&lt;/span&gt;, simply insert the line:&lt;br /&gt;&lt;pre style="WIDTH: 440px; HEIGHT: 50px; OVERFLOW: auto"&gt;webapp.template.register_template_library("customtags.xss")&lt;/pre&gt;after the &lt;span style="font-family:courier new;"&gt;import&lt;/span&gt; statements at the top of the script. Obviously, if you are calling your module something else, you have to change the line above accordingly.&lt;br /&gt;&lt;br /&gt;Now you can write &lt;span style="font-family:courier new;"&gt;&amp;lt;a href="/?{{ unsafe_string|unicode_urlencode }}" title=".."&amp;gt;..&amp;lt;/a&amp;gt;&lt;/span&gt; in your template!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-7088396327045607617?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/7088396327045607617/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=7088396327045607617' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/7088396327045607617'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/7088396327045607617'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2009/11/django-template-urlencode-unicode.html' title='Django template urlencode unicode characters (Google App Engine)'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-4332911382018416543</id><published>2009-11-13T00:33:00.008Z</published><updated>2009-11-13T09:55:44.985Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='software development'/><category scheme='http://www.blogger.com/atom/ns#' term='zoomaroundtown'/><category scheme='http://www.blogger.com/atom/ns#' term='geeky'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='google map'/><title type='text'>GPolyline decoding in Python</title><content type='html'>I am currently working on providing GPX data for routes on &lt;a href="http://zoomaroundtown.appspot.com/"&gt;http://zoomaroundtown.appspot.com&lt;/a&gt;. As the routes are stored as &lt;span style="font-weight: bold;"&gt;Google Map GPolyline&lt;/span&gt; &lt;a href="http://code.google.com/apis/maps/documentation/overlays.html#Encoded_Polylines"&gt;encoded strings&lt;/a&gt; in the datastore, I need a way to decode them back into latitude and longitude points before I can create the respective GPX files. Mark McClure has an &lt;a href="http://facstaff.unca.edu/mcmcclur/GoogleMaps/EncodePolyline/"&gt;excellent page&lt;/a&gt; on this topic. Apart from his &lt;a href="http://facstaff.unca.edu/mcmcclur/GoogleMaps/EncodePolyline/decode.js"&gt;Javascript implementation&lt;/a&gt;, I have also found a &lt;a href="http://unitstep.net/blog/2008/08/02/decoding-google-maps-encoded-polylines-using-php/"&gt;PHP port&lt;/a&gt; of the &lt;span style="font-weight: bold;"&gt;decoder function&lt;/span&gt;. However, there does not seem to the any Python port, so I created a straightforward port:&lt;br /&gt;&lt;br /&gt;You can donwload the Python file &lt;a href="http://www.fotowuj.com/blog_stuff/gpolyline_decoder.py"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;pre style="overflow: auto; width: 440px; height: 600px;"&gt;&lt;span style="color:blue;"&gt;&lt;br /&gt;&lt;br /&gt;def decode_line(encoded):&lt;br /&gt;&lt;br /&gt;    """Decodes a polyline that was encoded using the Google Maps method.&lt;br /&gt;&lt;br /&gt;    See http://code.google.com/apis/maps/documentation/polylinealgorithm.html&lt;br /&gt;    &lt;br /&gt;    This is a straightforward Python port of Mark McClure's JavaScript polyline decoder&lt;br /&gt;    (http://facstaff.unca.edu/mcmcclur/GoogleMaps/EncodePolyline/decode.js)&lt;br /&gt;    and Peter Chng's PHP polyline decode&lt;br /&gt;    (http://unitstep.net/blog/2008/08/02/decoding-google-maps-encoded-polylines-using-php/)&lt;br /&gt;    """&lt;br /&gt;&lt;br /&gt;    encoded_len = len(encoded)&lt;br /&gt;    index = 0&lt;br /&gt;    array = []&lt;br /&gt;    lat = 0&lt;br /&gt;    lng = 0&lt;br /&gt;&lt;br /&gt;    while index &lt; encoded_len:&lt;br /&gt;&lt;br /&gt;        b = 0&lt;br /&gt;        shift = 0&lt;br /&gt;        result = 0&lt;br /&gt;&lt;br /&gt;        while True:&lt;br /&gt;            b = ord(encoded[index]) - 63&lt;br /&gt;            index = index + 1&lt;br /&gt;            result |= (b &amp; 0x1f) &lt;&lt; shift&lt;br /&gt;            shift += 5&lt;br /&gt;            if b &lt; 0x20:&lt;br /&gt;                break&lt;br /&gt;&lt;br /&gt;        dlat = ~(result &gt;&gt; 1) if result &amp; 1 else result &gt;&gt; 1&lt;br /&gt;        lat += dlat&lt;br /&gt;&lt;br /&gt;        shift = 0&lt;br /&gt;        result = 0&lt;br /&gt;&lt;br /&gt;        while True:&lt;br /&gt;            b = ord(encoded[index]) - 63&lt;br /&gt;            index = index + 1&lt;br /&gt;            result |= (b &amp; 0x1f) &lt;&lt; shift&lt;br /&gt;            shift += 5&lt;br /&gt;            if b &lt; 0x20:&lt;br /&gt;                break&lt;br /&gt;&lt;br /&gt;        dlng = ~(result &gt;&gt; 1) if result &amp; 1 else result &gt;&gt; 1&lt;br /&gt;        lng += dlng&lt;br /&gt;&lt;br /&gt;        array.append((lat * 1e-5, lng * 1e-5))&lt;br /&gt;&lt;br /&gt;    return array&lt;br /&gt;&lt;br /&gt;if __name__ == "__main__":&lt;br /&gt;    latlngs = decode_line("grkyHhpc@B[[_IYiLiEgj@a@q@yEoAGi@bEyH_@aHj@m@^qAB{@IkHi@cHcAkPSiMJqEj@s@CkFp@sDfB}Ex@iBj@S_AyIkCcUWgAaA_JUyAFk@{D_]~KiLwAeCsHqJmBlAmFuXe@{DcByIZIYiBxBwAc@eCcAl@y@aEdCcBVJpHsEyAeE")&lt;br /&gt;    for latlng in latlngs:&lt;br /&gt;        print str(latlng[0]) + "," + str(latlng[1])&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;Hope you Python GMap coders out there will find this useful!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-4332911382018416543?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/4332911382018416543/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=4332911382018416543' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/4332911382018416543'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/4332911382018416543'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2009/11/gpolyline-decoding-in-python.html' title='GPolyline decoding in Python'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-1806214029123864554</id><published>2009-11-04T16:30:00.016Z</published><updated>2009-11-27T15:59:59.883Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='freebie'/><category scheme='http://www.blogger.com/atom/ns#' term='software development'/><category scheme='http://www.blogger.com/atom/ns#' term='geeky'/><category scheme='http://www.blogger.com/atom/ns#' term='testbed'/><category scheme='http://www.blogger.com/atom/ns#' term='google gadget'/><category scheme='http://www.blogger.com/atom/ns#' term='json'/><title type='text'>Javascript Tester iGoogle gadget</title><content type='html'>Have been looking at iGoogle gadgets for work (I know they are so 2008, but still a good idea...) Anyway I created a simple Javascript Tester gadget for myself, which some of you may find useful: (&lt;a href="http://www.google.co.uk/ig/directory?hl=en&amp;amp;url=hosting.gmodules.com%2Fig%2Fgadgets%2Ffile%2F111513885136272339065%2Fjs_test_1_0.xml"&gt;See this gadget in the iGoogle directory&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Tip: Handy for testing (parsing) JSON objects!&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;Being an iGoogle gadget, you can add it to you iGoogle homepage, but you can always just use the embedded gadget right here!&lt;br /&gt;&lt;br /&gt;&lt;script src="http://www.gmodules.com/ig/ifr?url=http://hosting.gmodules.com/ig/gadgets/file/111513885136272339065/js_test_1_0.xml&amp;amp;synd=open&amp;amp;w=320&amp;amp;h=400&amp;amp;title=Javascript+Tester&amp;amp;border=%23ffffff%7C3px%2C1px+solid+%23999999&amp;amp;output=js"&gt;&lt;/script&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-1806214029123864554?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/1806214029123864554/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=1806214029123864554' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/1806214029123864554'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/1806214029123864554'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2009/11/javascript-tester-igoogle-gadget.html' title='Javascript Tester iGoogle gadget'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-4482242582665115687</id><published>2009-11-02T19:20:00.010Z</published><updated>2009-11-03T13:13:07.034Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='cloud'/><category scheme='http://www.blogger.com/atom/ns#' term='software development'/><category scheme='http://www.blogger.com/atom/ns#' term='zoomaroundtown'/><category scheme='http://www.blogger.com/atom/ns#' term='geeky'/><category scheme='http://www.blogger.com/atom/ns#' term='twitter'/><category scheme='http://www.blogger.com/atom/ns#' term='gae'/><title type='text'>Twitter Search API and Google App Engine</title><content type='html'>So in my &lt;a href="http://seewah.blogspot.com/2009/11/using-twitter-as-commenting-engine-for.html"&gt;previous post&lt;/a&gt;, I talked about using Twitter as a commenting engine in &lt;a href="http://zoomaroundtown.appspot.com/"&gt;http://zoomaroundtown.appspot.com/&lt;/a&gt;, and, towards the end, I briefly mentioned that the Twitter Search API (&lt;a href="http://apiwiki.twitter.com/Twitter-API-Documentation"&gt;http://apiwiki.twitter.com/Twitter-API-Documentation&lt;/a&gt;) does not really work on Google App Engine (and most other cloud environments).&lt;br /&gt;&lt;br /&gt;The reason why making Twitter Search API requests on Google App Engine often fails is simple. Twitter rate-limits requests per IP, and on Google App Engine, you share IPs with loads of other apps, a lot of which are probably trying to do the same thing as you i.e. making Twitter Search requests. Unfortunately Twitter Search API is still not incorporated into the Twitter REST API, which means there is no way of identifying requests via your username, so Twitter can only really rate-limit according to IPs.&lt;br /&gt;&lt;br /&gt;To circumvent this, I created a simple PHP proxy on my other website (hosted on a "real" machine") :&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;$url = $_GET['url'];&lt;br /&gt;$session = curl_init($url);&lt;br /&gt;curl_setopt($session, CURLOPT_HEADER, false);&lt;br /&gt;curl_setopt($session, CURLOPT_RETURNTRANSFER, true);&lt;br /&gt;$json = curl_exec($session);&lt;br /&gt;header("Content-Type: application/json");&lt;br /&gt;echo $json;&lt;br /&gt;curl_close($session);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;and in my GAE Python app, instead of sending the requests straight to Twitter using &lt;a href="http://code.google.com/appengine/docs/python/urlfetch/overview.html"&gt;URL Fetch&lt;/a&gt;, I send the requests to this proxy, which does a simple straightforward relay job for me.&lt;br /&gt;&lt;br /&gt;Of course, this is less than ideal, as this goes in the face of the philosophy of deploying in a scalable cloud environment such as Google App Engine. But until Twitter allows username-based identification in Twitter Search requests, or Google sorts something out with Twitter (and other service API providers, for that matter), this seems to be the best way to get round this problem!&lt;br /&gt;&lt;br /&gt;(I have heard that you are get your own IP on Amazon AWS, but I don't know enough about Amazon AWS to comment on it. Anyway no doubt more and more cloud infrastructures relying on shared IPs will be developed, and I can only see it heading that way...)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-4482242582665115687?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/4482242582665115687/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=4482242582665115687' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/4482242582665115687'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/4482242582665115687'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2009/11/twitter-search-api-and-google-app.html' title='Twitter Search API and Google App Engine'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-3405033680863966493</id><published>2009-11-02T18:41:00.007Z</published><updated>2009-11-02T23:32:23.152Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='cloud'/><category scheme='http://www.blogger.com/atom/ns#' term='zoomaroundtown'/><category scheme='http://www.blogger.com/atom/ns#' term='geeky'/><category scheme='http://www.blogger.com/atom/ns#' term='twitter'/><category scheme='http://www.blogger.com/atom/ns#' term='gae'/><title type='text'>Using Twitter as a commenting engine for Zoom around Town</title><content type='html'>So with the number of routes in &lt;a href="http://zoomaroundtown.appspot.com/"&gt;http://zoomaroundtown.appspot.com/&lt;/a&gt; increasing daily, the time has come for me to start considering implementing some kind of commenting functionality for the added cycle routes. I have not had the time to implement a user login system (I am still hoping to see more routes in the database first), let alone a commenting system. However one day, a simple idea came to me. What about getting people to comment on routes by creating &lt;strong&gt;tweets&lt;/strong&gt; and then all I have to do is to display these tweets using &lt;strong&gt;Twitter Search&lt;/strong&gt;. I was quite keen on exploring this idea, and I implemented the following in Zoom around Town:&lt;br /&gt;&lt;br /&gt;For each route, I programmatically create a &lt;a href="http://bit.ly/"&gt;bit.ly&lt;/a&gt; link for its unique page url e.g. (&lt;a href="http://bit.ly/39oF3z"&gt;http://bit.ly/39oF3z&lt;/a&gt; for &lt;a href="http://zoomaroundtown.appspot.com/findRoutes?id=24001"&gt;http://zoomaroundtown.appspot.com/findRoutes?id=24001&lt;/a&gt;). On the page, I then display a message telling people to comment on the route by sending a tweet starting with &lt;strong&gt;@zoomaroundtown http://bit.ly/39oF3z&lt;/strong&gt; (I'm even including a link that automatically populates the starting text for the user: &lt;a href="http://twitter.com/home?status=%40zoomaroundtown%20http%3A%2F%2Fbit.ly%2F39oF3z%20"&gt;http://twitter.com/home?status=%40zoomaroundtown%20http%3A%2F%2Fbit.ly%2F39oF3z%20&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;To display comments, I use the Search API (&lt;a href="http://apiwiki.twitter.com/Twitter-API-Documentation"&gt;http://apiwiki.twitter.com/Twitter-API-Documentation&lt;/a&gt;) to search for all tweets sent to zoomaroundtown containing the string &lt;a href="http://bit.ly/39oF3z"&gt;http://bit.ly/39oF3z&lt;/a&gt; i.e. using the search string &lt;strong&gt;to:zoomaroundtown http://bit.ly/39oF3z&lt;/strong&gt;.&lt;br /&gt;&lt;br /&gt;Whether this is a viable way of enabling commenting Zoom around Town, only time can tell. However there are obvious pros and cons:&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;PROs:&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;Extremely easy to implement (especially when I don't have a user login system yet)&lt;br /&gt;&lt;br /&gt;Increased site exposure on Twitter&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;CONs:&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;Twitter Search only seems to return tweets no more than 2 weeks old&lt;br /&gt;&lt;br /&gt;I have no control over content (but since these are public tweets, do I really care?)&lt;br /&gt;&lt;br /&gt;Last but not least, something unfortunately I only managed to discover after deployment to the live cloud environment - Twitter Search's rate limiter does not seem to like Google App Engine, and it is this point that will lead us to my &lt;a href="http://seewah.blogspot.com/2009/11/twitter-search-api-and-google-app.html"&gt;next post&lt;/a&gt;, but for now it suffices to say that Twitter Search rate-limits requests per IP, so it is not surprising that in a cloud environment where one is sharing IPs with loads of other apps, some requests are bound to get rejected!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-3405033680863966493?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/3405033680863966493/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=3405033680863966493' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/3405033680863966493'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/3405033680863966493'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2009/11/using-twitter-as-commenting-engine-for.html' title='Using Twitter as a commenting engine for Zoom around Town'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-2313346575958265176</id><published>2009-10-07T01:09:00.009+01:00</published><updated>2009-10-07T02:40:11.698+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='software development'/><category scheme='http://www.blogger.com/atom/ns#' term='zoomaroundtown'/><category scheme='http://www.blogger.com/atom/ns#' term='geeky'/><category scheme='http://www.blogger.com/atom/ns#' term='google map'/><title type='text'>Circle overlay on Google Map</title><content type='html'>This is my first "&lt;a style="font-weight: bold;" href="http://zoomaroundtown.appspot.com/"&gt;Zoom around Town&lt;/a&gt;"-related technical post. This first post looks at the use of &lt;a href="http://code.google.com/apis/maps/documentation/overlays.html#Ground_Overlays"&gt;GGroundOverlay&lt;/a&gt; for rendering circles on Google Maps.&lt;br /&gt;&lt;br /&gt;To represent the current search areas in &lt;span style="font-weight: bold;"&gt;Zoom around Town&lt;/span&gt;, I decided to overlay two radar-style circles, on the start point map and on the end point map. These circles have to increase and decrease in diameter depending on the user's range selections. See this &lt;a href="http://zoomaroundtown.appspot.com/findRoutes/GB/Greater%20London/260-302%20A200,%20Greenwich,%20Greater%20London%20SE10%209,%20UK/?fromAddress=51.48149,-0.012617&amp;amp;toAddress=51.51606,-0.118189"&gt;page&lt;/a&gt; in &lt;span style="font-weight: bold;"&gt;Zoom around Town&lt;/span&gt; for a demonstration. When you change the values of the +/- distance dropdowns, and update the maps, you should see the two circles change in size. The radius of the circle corresponds to the selected +/- value.&lt;br /&gt;&lt;br /&gt;While the Google Map API (v2) provides &lt;a href="http://code.google.com/apis/maps/documentation/overlays.html#Polylines_Overview"&gt;GPolyline&lt;/a&gt; and &lt;a href="http://code.google.com/apis/maps/documentation/overlays.html#Polygons_Overview"&gt;GPolygon&lt;/a&gt; for drawing lines and polygons on Google Map, it does not provide an overlay class for drawing circles.&lt;br /&gt;&lt;br /&gt;One solution is to create a multi-point GPolygon to approximate a circle. Check out this &lt;a href="http://dawsdesign.com/drupal/google_maps_circle_overlay"&gt;article&lt;/a&gt; for an explanation on how this can be done.&lt;br /&gt;&lt;br /&gt;A better (in my opinion) solution is to instead use a &lt;a href="http://code.google.com/apis/maps/documentation/overlays.html#Ground_Overlays"&gt;GGroundOverlay&lt;/a&gt;. GGroundOverlay provides a means to overlay an image on top of the map. The image will be scaled automatically as the user zooms the map, so that the image will always cover the same geography area on the map. So, to create a radar-style circle, one only has to find a &lt;span style="font-weight: bold;"&gt;transparent circle png&lt;/span&gt; file, locate the point on the map where the circle should be &lt;span style="font-weight: bold;"&gt;centred upon&lt;/span&gt;, and specify the &lt;span style="font-weight: bold;"&gt;southwest corner coordinate&lt;/span&gt; and the &lt;span style="font-weight: bold;"&gt;northeast corner coordinate&lt;/span&gt; of the imaginery square that bounds this circle (i.e. the GLatLngBounds). Sounds simple, does'nt it? Well almost, to work out the GLatLngBounds coordinates, we need a bit of good old trigonometry.&lt;br /&gt;&lt;br /&gt;Pythagoras told us that, for a 2km-radius circle, the four corners of this bounding square will be sqrt(2*2^2) km from the centre of the circle. So, to draw a 2km-radius circle centred upon a specific point, we need the southwest corner coordinate, which is &lt;span style="font-style: italic; font-weight: bold;"&gt;sqrt(2*2^2)&lt;/span&gt;&lt;span style="font-weight: bold;"&gt; at 225 degrees&lt;/span&gt; from the original point and the northeast corner coordinate, which is the &lt;span style="font-weight: bold;"&gt;same distance at 45 degrees&lt;/span&gt; from the original point.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0); font-weight: bold;"&gt;Coordinate of a destination point given distance and bearing from a start point&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Unfortunately, as the globe is a sphere, working out a destination point coordinate given the distance and the bearing from a start point is no straightforward maths. Fortunately, the good people at Movable Type Ltd. (not to be confused with &lt;a href="http://www.movabletype.org/"&gt;http://www.movabletype.org&lt;/a&gt;) published this informative &lt;a href="http://www.movable-type.co.uk/scripts/latlong.html"&gt;page&lt;/a&gt;. So based on the formula and the Javascript given on the page, I created the Javascript function:&lt;br /&gt;&lt;pre style="overflow: auto; width: 440px; height: 200px;"&gt;&lt;span style="color:blue;"&gt;function getDestLatLng(latLng, bearing, distance) {&lt;br /&gt; var lat1 = latLng.latRadians();&lt;br /&gt; var lng1 = latLng.lngRadians();&lt;br /&gt; var brng = bearing*Math.PI/180; &lt;br /&gt; var dDivR = distance/EARTH_RADIUS;&lt;br /&gt; var lat2 = Math.asin( Math.sin(lat1)*Math.cos(dDivR) + Math.cos(lat1)*Math.sin(dDivR)*Math.cos(brng) );&lt;br /&gt; var lng2 = lng1 + Math.atan2(Math.sin(brng)*Math.sin(dDivR)*Math.cos(lat1), Math.cos(dDivR)-Math.sin(lat1)*Math.sin(lat2));&lt;br /&gt; return new GLatLng(lat2/ Math.PI * 180, lng2/ Math.PI * 180);&lt;br /&gt;}&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;Now, the rest is easy:&lt;br /&gt;&lt;pre style="overflow: auto; width: 440px; height: 280px;"&gt;&lt;span style="color:blue;"&gt;var EARTH_RADIUS = 6378.137; //in kilometres&lt;br /&gt;&lt;br /&gt;function drawCircle(map, centrePt, rangeValue) {&lt;br /&gt; var boundaries = getBoundaries(centrePt, rangeValue);&lt;br /&gt; var circle = new GGroundOverlay("/images/map_overlays/circle.png", boundaries);&lt;br /&gt; map.addOverlay(circle);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function getBoundaries(centrePt, radius) {&lt;br /&gt; var hypotenuse = Math.sqrt(2 * radius * radius);&lt;br /&gt; var sw = getDestLatLng(centrePt, 225, hypotenuse);&lt;br /&gt; var ne = getDestLatLng(centrePt, 45, hypotenuse);&lt;br /&gt; return new GLatLngBounds(sw, ne);&lt;br /&gt;}&lt;/span&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-2313346575958265176?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/2313346575958265176/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=2313346575958265176' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/2313346575958265176'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/2313346575958265176'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2009/10/circle-overlay-on-google-map.html' title='Circle overlay on Google Map'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-6941951516575491564</id><published>2009-10-04T00:41:00.007+01:00</published><updated>2009-11-13T01:10:24.447Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='software development'/><category scheme='http://www.blogger.com/atom/ns#' term='zoomaroundtown'/><category scheme='http://www.blogger.com/atom/ns#' term='geeky'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='cycling'/><category scheme='http://www.blogger.com/atom/ns#' term='google map'/><category scheme='http://www.blogger.com/atom/ns#' term='gae'/><title type='text'>Zoom around Town is live!</title><content type='html'>Hi, just been spending many late evenings working on my pet project &lt;span style="FONT-WEIGHT: bold"&gt;Zoom around Town&lt;/span&gt;. It is now live on &lt;a href="http://zoomaroundtown.appspot.com/"&gt;http://zoomaroundtown.appspot.com/&lt;/a&gt;!&lt;br /&gt;&lt;br /&gt;&lt;span style="FONT-WEIGHT: bold"&gt;Zoom around Town&lt;/span&gt; is a global cycle route database, based on user contribution, to help city cyclists get around town.&lt;br /&gt;&lt;br /&gt;Please help by adding your commuting routes and by spreading the word!&lt;br /&gt;&lt;br /&gt;This project makes heavy use of the &lt;span style="FONT-WEIGHT: bold"&gt;Google Map API&lt;/span&gt; and has been developed in &lt;span style="FONT-WEIGHT: bold"&gt;Python&lt;/span&gt; on the excellent and "free" &lt;span style="FONT-WEIGHT: bold"&gt;Google App Engine&lt;/span&gt; platform. I will no doubt be blogging about some of the interesting technical encounters during the project in the near future.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-6941951516575491564?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/6941951516575491564/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=6941951516575491564' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/6941951516575491564'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/6941951516575491564'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2009/10/zoom-around-town-is-live.html' title='Zoom around Town is live!'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-1983839204382819987</id><published>2009-08-05T01:07:00.011+01:00</published><updated>2009-08-06T10:00:34.406+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='software development'/><category scheme='http://www.blogger.com/atom/ns#' term='geeky'/><category scheme='http://www.blogger.com/atom/ns#' term='gae'/><title type='text'>Datastore Bulk Upload + ReferenceProperty in Google App Engine</title><content type='html'>I have been working on my first Google App Engine app (a vintage camera database), and I have just got to the point when I need to upload some real data to the Datastore. Essentially, I have an CSV containing all the camera information, including the manufacturer's information and I want to use &lt;span style="FONT-STYLE: italic"&gt;google.appengine.tools.bulkloader.Loader&lt;/span&gt; to upload all this information to Datastore. I have two separate entities representing "camera" and "manufacturer", and I would like to normalize the data as such.&lt;br /&gt;&lt;br /&gt;Did someone shout, "Normalization + Bigtable = Bad"? Well, I have yet to make up my mind on this, and for now, I have decided to go with a more traditional relational database model. This may change in the future, but I decided I should still share my experience with uploading relational data as I feel that many people are probably trying to do the same thing.&lt;br /&gt;&lt;br /&gt;Before I carry on, these are the two (simplified) classes:&lt;br /&gt;&lt;pre style="OVERFLOW: auto; WIDTH: 440px; HEIGHT: 300px"&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;&lt;br /&gt;class Manufacturer(db.Model):&lt;br /&gt;    name = db.StringProperty(required=True)&lt;br /&gt;&lt;br /&gt;class Camera(db.Model):&lt;br /&gt;    name = db.StringProperty(required=True)&lt;br /&gt;    weight = db.IntegerProperty()&lt;br /&gt;    introduction_year = db.IntegerProperty()&lt;br /&gt;    max_aperture_value = db.FloatProperty()&lt;br /&gt;    e_mode_p = db.BooleanProperty(default=False)&lt;br /&gt;    e_mode_sp = db.BooleanProperty(default=False)&lt;br /&gt;    e_mode_ap = db.BooleanProperty(default=False)&lt;br /&gt;    e_mode_m = db.BooleanProperty(default=False)&lt;br /&gt;    note = db.TextProperty()&lt;br /&gt;    manufacturer = db.ReferenceProperty(Manufacturer)&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and the CSV:&lt;br /&gt;&lt;pre style="OVERFLOW: auto; WIDTH: 440px; HEIGHT: 150px"&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;&lt;br /&gt;"Canonet G-III QL17",Canon,620,1972,1.7,FALSE,TRUE,FALSE,TRUE,&lt;br /&gt;"Canonet G-III QL19",Canon,620,1972,1.9,FALSE,TRUE,FALSE,TRUE,&lt;br /&gt;Demi,Canon,380,1963,2.8,FALSE,TRUE,FALSE,TRUE,"Demi is a half-frame camera"&lt;br /&gt;"TRIP 35",Olympus,390,1967,2.8,TRUE,FALSE,FALSE,FALSE,&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Effectively, I want four instances of camera and two instances of manufacturer created, with the cameras correctly referencing the manufacturers.&lt;br /&gt;&lt;br /&gt;Google App Engine comes with the &lt;span style="FONT-STYLE: italic"&gt;google.appengine.tools.bulkloader.Loader&lt;/span&gt; class which facilitates the process of bulk uploading data to both the development server (local) datastore and the real (cloud) datastore. It supports basic mapping of CSV fields to object attributes. However, dealing with ReferenceProperty turns out to be not so straightforward. A quick Google search led to a few articles suggesting overridding the &lt;span style="FONT-STYLE: italic"&gt;HandleEntity&lt;/span&gt; (or &lt;span style="FONT-STYLE: italic"&gt;handle_entity&lt;/span&gt;?) function when subclassing &lt;span style="FONT-STYLE: italic"&gt;google.appengine.tools.bulkloader.Loader&lt;/span&gt;. Unfortunately, for some reason, I did not manage to get that to work. After some experimentation, I stumbled across my own solution, which actually is quite simple :-)&lt;br /&gt;&lt;br /&gt;&lt;span style="COLOR: rgb(255,0,0)"&gt;DISCLAIMER: I HAVE ONLY TRIED UPLOADING DATA TO MY LOCAL DEVELOPMENT SERVER (GOOGLE APP ENGINE 1.2.3) USING THIS METHOD, AND NOT TO THE REAL CLOUD YET...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Instead of injecting the reference in the &lt;span style="FONT-STYLE: italic"&gt;HandleEntity &lt;/span&gt;function, I create a function called get_manufacturer which looks in the datastore for an existing manufacturer based on the manufacturer's name, and returns its key (or the instance itself) if found, or creates a new instance in the datastore before returning the newly assigned key otherwise. I then make that the transformation function for the manufacturer &lt;span style="FONT-STYLE: italic"&gt;ReferenceProperty&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;The following is my loader class with the magic function:&lt;br /&gt;&lt;pre style="OVERFLOW: auto; WIDTH: 440px; HEIGHT: 500px"&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;&lt;br /&gt;from google.appengine.ext import db&lt;br /&gt;from google.appengine.tools import bulkloader&lt;br /&gt;import model&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;def get_manufacturer(name):&lt;br /&gt;    manufacturers = db.GqlQuery("select * from Manufacturer where name = :1", name)&lt;br /&gt;    if manufacturers.count() == 0:&lt;br /&gt;        newManufacturer = model.Manufacturer(name=name)&lt;br /&gt;        db.put(newManufacturer)&lt;br /&gt;        return newManufacturer&lt;br /&gt;    else:&lt;br /&gt;        return manufacturers[0]&lt;br /&gt;&lt;br /&gt;class CameraLoader(bulkloader.Loader):&lt;br /&gt;    def __init__(self):&lt;br /&gt;        bulkloader.Loader.__init__(self, "Camera",&lt;br /&gt;                                   [("name", str),&lt;br /&gt;                                    ("manufacturer", get_manufacturer),&lt;br /&gt;                                    ("weight", int),&lt;br /&gt;                                    ("introduction_year", int),&lt;br /&gt;                                    ("max_aperture_value", float),&lt;br /&gt;                                    ("e_mode_p", bool),&lt;br /&gt;                                    ("e_mode_sp", bool),&lt;br /&gt;                                    ("e_mode_ap", bool),&lt;br /&gt;                                    ("e_mode_m", bool),&lt;br /&gt;                                    ("note", str)])&lt;br /&gt;&lt;br /&gt;      &lt;br /&gt;loaders = [CameraLoader]&lt;br /&gt;if __name__ == '__main__':&lt;br /&gt;    bulkload.main(CameraLoader)&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Please refer to &lt;a href="http://code.google.com/appengine/docs/python/tools/uploadingdata.html"&gt;http://code.google.com/appengine/docs/python/tools/uploadingdata.html&lt;/a&gt; for details on how to do the actual bulk upload.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-1983839204382819987?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/1983839204382819987/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=1983839204382819987' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/1983839204382819987'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/1983839204382819987'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2009/08/datastore-bulk-upload-referenceproperty.html' title='Datastore Bulk Upload + ReferenceProperty in Google App Engine'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-3683387455528364469</id><published>2009-06-06T01:21:00.003+01:00</published><updated>2009-06-06T01:25:24.577+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gwt'/><category scheme='http://www.blogger.com/atom/ns#' term='software development'/><category scheme='http://www.blogger.com/atom/ns#' term='geeky'/><title type='text'>GWT Tips 3 - Popups and IE8</title><content type='html'>If you are like me and find that your GWT popups start killing your beautiful application in IE8, do not despair. I found this really useful article: &lt;a href="http://www.mooreds.com/wordpress/archives/000513"&gt;http://www.mooreds.com/wordpress/archives/000513&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Many thanks to Dan!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-3683387455528364469?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/3683387455528364469/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=3683387455528364469' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/3683387455528364469'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/3683387455528364469'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2009/06/gwt-tips-3-popups-and-ie8.html' title='GWT Tips 3 - Popups and IE8'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-3379245409272677212</id><published>2009-06-05T23:52:00.008+01:00</published><updated>2009-08-06T10:54:05.472+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='download'/><category scheme='http://www.blogger.com/atom/ns#' term='gwt'/><category scheme='http://www.blogger.com/atom/ns#' term='software development'/><category scheme='http://www.blogger.com/atom/ns#' term='geeky'/><category scheme='http://www.blogger.com/atom/ns#' term='spring'/><title type='text'>GWT and Spring Security - sample demo download</title><content type='html'>As requested, I have put together a sample app to demonstrate the GWT and Spring Security (formerly ACEGI) integration technique discussed in &lt;a href="http://seewah.blogspot.com/2009/02/gwt-spring-integration-using.html"&gt;http://seewah.blogspot.com/2009/02/gwt-spring-integration-using.html&lt;/a&gt; and &lt;a href="http://seewah.blogspot.com/2009/02/gwt-and-spring-security.html"&gt;http://seewah.blogspot.com/2009/02/gwt-and-spring-security.html&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;You can download the demo project here: &lt;a href="http://www.fotowuj.com/gwt_spring_security_demo.zip"&gt;http://www.fotowuj.com/gwt_spring_security_demo.zip&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Essentially this app demonstrates how a GWT client can access a Spring-managed authentication service (via AuthenticationServiceServlet) to authenticate against Spring Security, and subsequently access a secured operation (getNumOfPrivatePublications) in another Spring-managed service (via DocumentServiceServlet). Please refer to the aforementioned blog articles for detailed discussion on how to integrate GWT and Spring Security.&lt;br /&gt;&lt;br /&gt;You may also want to download a version of GWT.&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#ff0000;"&gt;NOTE THAT I HAVE ONLY TESTED THE DEMO WITH VERSION 1.5.3 OF GWT.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The zip contains a complete eclipse project consisting of:&lt;br /&gt;&lt;br /&gt;1) source (both client and server)&lt;br /&gt;2) all dependent jars&lt;br /&gt;3) WebContent/WEB-INF/web.xml, which gets added to the final WAR&lt;br /&gt;4) tomcat/webapps/ROOT/WEB-INF/web.xml, which is required to run in hosted mode&lt;br /&gt;5) build.xml for running app in hosted mode (gwt-shell) and creating war (war)&lt;br /&gt;6) .launch file for running app in hosted mode inside eclipse&lt;br /&gt;&lt;br /&gt;You can run the demo in a number of ways:&lt;br /&gt;&lt;br /&gt;1) WITH ECLIPSE - having imported the project into eclipse, right-click the .launch file inside eclipse and select "run as...", which will start the app in hosted mode. Note that you will probably have to change the gwt-dev-windows.jar location specified in the .launch file first.&lt;br /&gt;&lt;br /&gt;2) RUNNING "gwt-shell" USING ANT - simply run "ant gwt-shell", which will start the app in hosted mode. Note that you will probably will have change the gwt-dev-windows.jar location specified in the build.xml file first.&lt;br /&gt;&lt;br /&gt;3) RUNNING "war" USING ANT AND DEPLOYING IN TOMCAT - simply run "ant war", and deploy in Tomcat. Note that you will probably will have change the gwt-dev-windows.jar location specified in the build.xml file first.&lt;br /&gt;&lt;br /&gt;Thanks&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#006600;"&gt;PS, a reader recently asked the question, "How come that you declare the services (DocumentService for example) both in the client and in the server package? Why not having the DocumentServiceImpl in the server package implement the DocumentService in the client package?"&lt;br /&gt;&lt;br /&gt;And this is my anwser:&lt;br /&gt;&lt;br /&gt;1) my design is based on the premises that the Spring layer and the GWT layer are very loosely-coupled e.g. in your company, you may already have all the Spring service beans already developed and you are just bolting a GWT frontend on top of this service layer. There are other reasons why you may want your GWT service interface to be slightly different from your Spring service interface as well.&lt;br /&gt;&lt;br /&gt;2) The two interfaces &lt;strong&gt;ARE DIFFERENT&lt;/strong&gt;! The GWT service interface &lt;em&gt;getNumberOfPrivatePublications()&lt;/em&gt; method throws a checked &lt;em&gt;ServiceSecurityException&lt;/em&gt; while the corresponding method in the Spring service interface does not. There may be alternatives that can potentially circumvent this, but to be honest, I have not really thought hard about this :-)&lt;br /&gt;&lt;br /&gt;Anyway as I mentioned before, this is by no means the definitive way to integrate Spring Security in GWT. This method works great for me, but depending on your particular circumstances, this may not work so well. I am just hoping to give you guys some ideas :-)&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-3379245409272677212?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/3379245409272677212/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=3379245409272677212' title='16 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/3379245409272677212'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/3379245409272677212'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2009/06/gwt-and-spring-security-sample-demo.html' title='GWT and Spring Security - sample demo download'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>16</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-5839748390751725526</id><published>2009-05-13T13:35:00.015+01:00</published><updated>2009-05-14T09:36:57.595+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='software development'/><category scheme='http://www.blogger.com/atom/ns#' term='geeky'/><category scheme='http://www.blogger.com/atom/ns#' term='osgi'/><title type='text'>OSGi - the next big thing? (a beginner's view)</title><content type='html'>&lt;p&gt;Eclipse has been using it for years, and a number of open-source and non open-source Java application servers, such as SpringSource dm server and JBoss, are now providing server-side OSGi support.&lt;/p&gt;&lt;p&gt;I have been hearing the term OSGi for quite some time now. I have always known it to be some kind of modularization framework in Java. It is often mentioned alongside with the other wonderful buzzword that is SOA (Service Oriented Architecture). But what is it really? I decided that I should get a full understanding straight from the horse's mouth. So I went to &lt;a href="http://www.osgi.org/About/HowOSGi"&gt;http://www.osgi.org/About/HowOSGi&lt;/a&gt;, read the articles and tried out some examples. After a couple of hours, I learnt (and saw for myself) how using OSGi:&lt;/p&gt;&lt;p&gt;&lt;span style="color:#006600;"&gt;&lt;strong&gt;- you can divide up your ugly monolithic application into self-contained, loosely-coupled deployment units / modules known as bundles.&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color:#006600;"&gt;&lt;strong&gt;- a single bundle exists as a single JAR file.&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color:#006600;"&gt;&lt;strong&gt;- a bundle can access functionality provided by other bundles, but each bundle inherent provides a strict level of encapsulation. The creator of a bundle decides what functionality to expose. Everything else inside the bundle is private otherwise.&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color:#006600;"&gt;&lt;strong&gt;- bundles can be installed, updated, uninstalled without restarting the application.&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;span style="color:#006600;"&gt;- different versions of a single bundle can be deployed concurrently&lt;/span&gt;.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color:#006600;"&gt;&lt;strong&gt;- the current OSGi model assumes that all bundles run in the same JVM, so no distributed SOA ("Don’t distribute your objects." - Martin Fowler’s “First Rule of Distributed Object Design" :-) )&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;The idea of modularization of course has existed since the dawn of software engineering, and OSGi is not the first framework to provide modularization in Java. Yes, this idea of being able to hotswap/upgrade bundles during runtime sounds cool. But does an average IT manager REALLY want to be able to swap in a FTSE-lookup service for a DOW JONES-lookup service during runtime? (Oops I think I have just sparked controvery) My point is while OSGi provides an excellent framework for dynamically linking bundles, developers still has to write extra code to handle when a bundle goes offline, for example. For most projects, this kind of flexibility probably spells more trouble than it is worth. And if non-runtime-configurable dependency injection is what you are after, then just stick with simple Spring!&lt;/p&gt;&lt;p&gt;So I was beginning to think whether OSGi is just yet another "grand" idea.&lt;/p&gt;&lt;p&gt;It then suddenly dawned on me that, forgetting about all this hotswappable bundle magic, the basic idea of self-contained deployment units is the answer to the single biggest cause of Java developers' stress - &lt;strong&gt;jar hell&lt;/strong&gt;. I am sure a lot of you have used or are using Maven, Ivy to manage your hundreds of jars. While they do a decent job of managing dependencies and jar versioning, the simple fact remains - without writing custom classloading code, in Java, you cannot have two versions of a same jar in the same classpath and expect one bit of your application to use the 1st version and another bit of your application (maybe you are using some third-party classes) to use the 2nd version. Now, implemented as OSGi bundles, your different "bits" of the application can be packaged with the required versions of the jars (inside their respective OSGi bundle jar) without interfering with each other. Essentially each bundle has its own classpath/classloader. Is that not great?&lt;/p&gt;&lt;p&gt;OK nothing in this world is free. There is obviously the overhead in having to create these bundles, which is something I have yet to try in a real project. Nonetheless, knowing that there is a chance of forever emancipation from jar hell, there is more of a reason now why I should at least consider trying out OSGi for real! Umm it may be a long road, but perhaps a road worth travelling. Well, at least I know it is possible to embed Equinox in Tomcat. That is a start...&lt;/p&gt;&lt;p&gt;Don't get me wrong. I am not knocking OSGi's ability to dynamically manage and link bundles. I am just saying that the more immediate benefit of adopting OSGi may lie in the fact that you can have many versions of the same jar in your project! Yes, a simple problem, but a real problem. &lt;/p&gt;&lt;p&gt;Any comments welcome!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-5839748390751725526?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/5839748390751725526/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=5839748390751725526' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/5839748390751725526'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/5839748390751725526'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2009/05/osgi-next-big-thing-beginners-view.html' title='OSGi - the next big thing? (a beginner&apos;s view)'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-408813764648365502</id><published>2009-03-05T10:09:00.035Z</published><updated>2009-03-12T10:49:41.480Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='travel'/><category scheme='http://www.blogger.com/atom/ns#' term='india'/><title type='text'>Bombay Dreams</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_cgyuO2_6pKQ/SbhXxsgh1dI/AAAAAAAAAAU/aVl9KRxu6Ao/s1600-h/4799.JPG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5312092271756301778" style="MARGIN: 0pt 10px 10px 0pt; WIDTH: 400px; CURSOR: pointer; HEIGHT: 400px" alt="" src="http://2.bp.blogspot.com/_cgyuO2_6pKQ/SbhXxsgh1dI/AAAAAAAAAAU/aVl9KRxu6Ao/s400/4799.JPG" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="FONT-STYLE: italic"&gt;"13.7 million people call Mumbai home, but not everyone in Mumbai is lucky enough to have a home."&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Recently I made a 36-hour stopover in Mumbai on my way from London to Hong Kong.&lt;br /&gt;&lt;br /&gt;&lt;span style="FONT-WEIGHT: bold"&gt;28th February 2009&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Following advice in the guide book, I ordered a "pre-paid" taxi inside the Chhatrapati Shivaji International Airport. In just under 40 minutes, I was dropped off at the &lt;a href="http://www.ymcabombay.com/"&gt;YMCA&lt;/a&gt; near Mumbai Central train station. The YMCA is an excellent hotel if you don't mind the fact that it is not located in the touristy South Mumbai. In fact, it turned out that the location was pretty convenient for visiting a number of interesting sites in the city. And how can I not mention the tasty buffet dinner, which is included in the very reasonable room price.&lt;br /&gt;&lt;br /&gt;Having filled up my water bottle, I was ready to brave the 37C heat. Initially, it did not feel as scorching as I had expected. However, the combination of heat and constant car-horning was quickly doing my head in. Instead of wandering in the general direction which I had expected to take me through the Kalbadevi neighbourhood, past Crawford Market and onto Chhatrapati Shivaji Terminus, formerly known as Victoria Terminus (CST), I decided that I should take out my map and follow a more precise route. Having previously been to Delhi and several Rajasthan cities, I had an idea of how tough life can be in cities for the less privileged. Walking down the side streets, I soon realised that life in Mumbai for the marginalized was no better than what I had witnessed before, if not worse. The clusters of corrugated iron-roof huts (for the more lucky ones) hiding timidly behind the main thoroughfares were a constant reminder of the millions of slum dwellers in Mumbai, who ironically serve as the unsung powerhouse of the city's economy.&lt;br /&gt;&lt;br /&gt;I soon arrived at the first stop, &lt;span style="FONT-WEIGHT: bold; COLOR: rgb(0,0,102)"&gt;Mumbaidevi Temple&lt;/span&gt; in Kalbadevi. Mumbaidevi is the patron goddess after which the city took its name. The entrance to the temple is notoriously hard to find, but the guidebook clearly said there would be a long queue of faithfuls, which I could join to enter the temple. People were busy running around in all directions, but no queue of any description was to be found. I referred to the guidebook again, it also clearly said it is closed between noon and 4pm. Of course I had just managed to overlook this minor detail and had arrived during their siesta. Doh.&lt;br /&gt;&lt;br /&gt;Finding &lt;span style="FONT-WEIGHT: bold; COLOR: rgb(0,0,102)"&gt;Crawford Market&lt;/span&gt; was easy enough as it stood right by an unsightly flyover. A lot of the stores were closed when I arrived, but the cover market provided a much needed shelter from the increasingly tormenting sun. After bidding farewell to puppies housed in tiny cages (which presumably would one day be sold to someone with a stronger belief in animal wellfare), I carried on walking along the flyover to reach &lt;span style="FONT-WEIGHT: bold; COLOR: rgb(0,0,102)"&gt;Chhatrapati Shivaji Terminus&lt;/span&gt;. I had once read that the train station was home to hundreds of street kids. I did not see any when I was there. Part of the exterior of the station was undergoing renovation, but the grandeur of the place still made it a magnificent sight to behold. From the grandeur of Chhatrapati Shivaji Terminus, I crossed a busy road Indian-style to reach the convenience that is universally known as McDonald's. In an attempt to not feel completely shameful visiting McD in India, I ordered a &lt;span style="FONT-WEIGHT: bold; COLOR: rgb(0,0,102)"&gt;McAloo Tikki&lt;/span&gt;. Sadly, it tasted not much different from any of the other McInventions. It did the job nonetheless.&lt;br /&gt;&lt;br /&gt;With a full stomach and a pair of less tired legs, I ventured further south to visit the &lt;span style="FONT-WEIGHT: bold; COLOR: rgb(0,0,102)"&gt;Horniman Circle&lt;/span&gt; and the &lt;span style="FONT-WEIGHT: bold; COLOR: rgb(0,0,102)"&gt;Ballard Estate&lt;/span&gt; / &lt;span style="FONT-WEIGHT: bold; COLOR: rgb(0,0,102)"&gt;Dockyard&lt;/span&gt; districts. The broad tarmac roads provided perfect venues for many impromptu cricket games. Luckily for all those aging colonial buildings, tennis balls were used. And of course passing cars have to give way to the cricketers. I was disappointed to find that the whole of the waterfront was a restricted area due to national security, so no glorious view of the Gateway of India from across the water.&lt;br /&gt;&lt;br /&gt;It was time to make my way back to the YMCA. I passed &lt;span style="FONT-WEIGHT: bold; COLOR: rgb(0,0,102)"&gt;Mohammed Ali Road&lt;/span&gt; on the way. The religious divide (or harmony, depending on how you look at it) in India cannot be better illustrated than by the two surrounding neighbourhoods: the Islamic neighbourhood to the east and the Hindu neighbourhood to the west.&lt;br /&gt;&lt;br /&gt;&lt;span style="FONT-WEIGHT: bold"&gt;1st March 2009&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_cgyuO2_6pKQ/SbhYiYjMKKI/AAAAAAAAAAc/aGMWk0TFVUI/s1600-h/4762.JPG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5312093108212344994" style="FLOAT: left; MARGIN: 0pt 10px 10px 0pt; WIDTH: 200px; CURSOR: pointer; HEIGHT: 134px" alt="" src="http://4.bp.blogspot.com/_cgyuO2_6pKQ/SbhYiYjMKKI/AAAAAAAAAAc/aGMWk0TFVUI/s200/4762.JPG" border="0" /&gt;&lt;/a&gt;After a hearty breakfast at the YMCA, I decided to walk to &lt;span style="FONT-WEIGHT: bold; COLOR: rgb(0,0,102)"&gt;Malabar Hill&lt;/span&gt; and to the &lt;span style="FONT-WEIGHT: bold; COLOR: rgb(0,0,102)"&gt;Hanging Garden&lt;/span&gt;. The garden was not all that, but any excuse to sit around in the shade was a good one. Visiting the excellent &lt;span style="FONT-WEIGHT: bold; COLOR: rgb(0,0,102)"&gt;Jain Temple&lt;/span&gt; on the way, I descended the hill taking &lt;span style="FONT-WEIGHT: bold; COLOR: rgb(0,0,102)"&gt;Walkeshwar Road&lt;/span&gt; to reach &lt;span style="FONT-WEIGHT: bold; COLOR: rgb(0,0,102)"&gt;Chowpatty Beach&lt;/span&gt;. Looking out onto the Manhattan of Mumbai across the bay, the beach does not only provide a weekend escape for millions of Mumbai residents but also another plot of land for the marginalized to sleep under the stars.&lt;br /&gt;&lt;br /&gt;I finally gave up walking and took the train from Charni Road to &lt;span style="FONT-WEIGHT: bold; COLOR: rgb(0,0,102)"&gt;Churchgate&lt;/span&gt;. The guidebook did not paint a very appealing picture of this mode of transport, which is an integral part of the daily lives of the millions of Mumbai commuters. The journey was painless enough and in no time I arrived at Churchgate. With students playing crickets in a quintessential English setting, Churchgate would most certainly get Michael Palin's approval.&lt;br /&gt;&lt;br /&gt;Not far from Churchgate was the &lt;span style="FONT-WEIGHT: bold; COLOR: rgb(0,0,102)"&gt;Gateway of India&lt;/span&gt;, which had been built to commemorate the visit and King George V and Queen Mary to Mumbai, and the &lt;span style="FONT-WEIGHT: bold; COLOR: rgb(0,0,102)"&gt;Taj Mahal Palace and Tower Hotel&lt;/span&gt;. A sombre mood surrounded the imposing hotel, which was still undergoing renovation following the terrorist attack on the 26th of November, 2008. There was a sense of defiance as local and visiting Indians gathered along the waterfront to reflect upon what had happened.&lt;br /&gt;&lt;br /&gt;My stay in Mumbai was quickly coming to an end, but not just yet. It was 4:45pm and seeing that I had to be back at the YMCA by 7pm to catch a taxi for the airport, I had to decide whether to take a taxi from Churchgate back to the hotel or to take the train. Taxi would be the more comfortable option but there might be a chance of being stuck in the horrendous traffic. Somehow, the train seemed to make more sense at that time. I thought, if I ran to the train station quickly, I would just manage to beat the rush hours.&lt;br /&gt;&lt;br /&gt;It turned out I was very wrong. I got onto the train without any problem as Churchgate was the right at the end of the train line, but when more and more people poured inside the carriage as the train made its way north, I came to realise that taking the train was essentially a very bad idea. I had to get off at the next stop, Mumbai Central, but I was stuck in the middle of the carriage, naively hoping that people would somehow make way for me to exit the train. The fact that I was not sure which side to leave the carriage did not help. It became obvious that I was going nowhere as the train ground to a halt. People were already literally fighting their way onto the train. Next thing I knew, the train was in motion again. While my predicament provided entertainment for some fellow passengers, others took sympathy and urged me to push my way towards the door (incidentally there were no actual doors) in preparation to do battle at the following station. I was in pole position, but the whole world collapsed on me when a tsunami of bodies came crushing in before the train even came to a stop. I did finally make it off the train, but not without getting an elbow in the face. It was simply the most traumatic experience. I had to take the train in the opposite direction to get back to Mumbai Central. Luckily, the train going south was quite a lot less packed. Standing by the door as the train zoomed past industrial estates and residential high-rises, I was still struggling to get over the previous shock.&lt;br /&gt;&lt;br /&gt;The taxi journey to the airport was blissful by comparison. I tried to absorb the smell of this truly overwhelming city. I tried to understand the contrast between the rich and the poor that I was seeing outside the taxi window. Under a bridge, I saw a family sitting around a dimly-lit fire. 13.7 million people call Mumbai home, but not everyone in Mumbai is lucky enough to have a home.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-408813764648365502?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/408813764648365502/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=408813764648365502' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/408813764648365502'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/408813764648365502'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2009/03/bombay-dreams.html' title='Bombay Dreams'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_cgyuO2_6pKQ/SbhXxsgh1dI/AAAAAAAAAAU/aVl9KRxu6Ao/s72-c/4799.JPG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-824742865333708401</id><published>2009-03-04T23:32:00.007Z</published><updated>2009-03-10T15:41:00.509Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='travel'/><category scheme='http://www.blogger.com/atom/ns#' term='climbing'/><category scheme='http://www.blogger.com/atom/ns#' term='hong kong'/><title type='text'>Shek O 石澳 bouldering</title><content type='html'>Spring is here. Hong Kong is WET!&lt;br /&gt;&lt;br /&gt;Weather forecast was predicting at least five days of rain, which meant any hope of bolt-clipping in Tung Long during my short stay in HK was effectively gone, so before it started getting properly wet, I decided to give Shek O a go.&lt;br /&gt;&lt;br /&gt;Taking my rock shoes and the excellent &lt;a href="http://www.hongkongclimbing.com/boulderingguide/shekobouldering.pdf"&gt;guide&lt;/a&gt; provide free by the kind people at &lt;a href="http://www.hongkongclimbing.com/"&gt;http://www.hongkongclimbing.com/&lt;/a&gt; (and no, I did not have a crash mat!), I took bus no.9 from the bus terminal just outside the Shau Kei Wan (&lt;span lang="zh-Hant"&gt;筲箕灣&lt;/span&gt;) MTR station. The journey took about 15 minutes, the normally scenic route was sadly not very scenic in the light drizzle and the heavy mist.&lt;br /&gt;&lt;br /&gt;Arriving at the village, I followed the aforementioned guide and found my way to the boulder spots easy enough. The laid-back setting of the village itself provided the perfect antidote to my last few days of being constantly bombarded by car-horns in Mumbai and by people talking about the latest news on the fund-raising plan by HSBC on their mobiles in Hong Kong. Even though, I suspect if it had been a weekend and if it had not been raining, the place would have been heaving like the rest of Hong Kong!&lt;br /&gt;&lt;br /&gt;I warmed up on a couple of V1 problems on "Block 3" in the Village Boulders area just before the footbridge. The holds were positive enough and the friction was certainly good on the granite, but I was constantly being reminded by the scattered blocks underneath my feet of the certain possibility of my ankles breaking if I was going to fall. I did not have the nerve to try the harder problems which consisted mostly of slopers for hands and flat-smearing on the slightly overhanging boulder.&lt;br /&gt;&lt;br /&gt;I then crossed the footbridge to look for "The Corridor" but did not hang around long as, again, technical climbing was the order of the day and I was not getting over the fear of potentially breaking my ankles. Some problems certainly looked interesting and I wished I had a crash mat.&lt;br /&gt;&lt;br /&gt;Following the path, I made my way to the "Headlands Boulders" area. "Lone Block" was definitely fun. The V0 problem was a well-deserved 3-star, albeit being a bit short. I also managed the 360 traverse but could not do it in one go. Unfortunately the area stank of piss (or maybe it was something coming from the concrete hut next to it), which was a shame as the atmosphere was perfect otherwise (despite the constant drizzling, which I had kind of forgotten about by that point). I then moved on and tried out the three aretes on the "Cliff Top" boulders. The V0 was easy enough and it was kind of fun, but the V1's did not have much in terms of hand holds on them and, again, technical climbing on hard landing was not quite what I was prepared to put myself through. I backed down both climbs.&lt;br /&gt;&lt;br /&gt;The drizzle was getting more and more annoying, and being disheartened by my failure to climb the two v1's, I decided to call it a day.&lt;br /&gt;&lt;br /&gt;Pretty crap climbing (not a reflection on the quality of the area, but rather a reflection on my own incompetence and the fact that a crash mat is pretty essential), but 30 mins from my doorsteps to a bit of rockclimbing above crashing waves, I could not really complain!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-824742865333708401?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/824742865333708401/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=824742865333708401' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/824742865333708401'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/824742865333708401'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2009/03/shek-o-bouldering.html' title='Shek O 石澳 bouldering'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-19290587640458452</id><published>2009-02-24T13:05:00.020Z</published><updated>2009-04-15T18:49:56.819+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gwt'/><category scheme='http://www.blogger.com/atom/ns#' term='software development'/><category scheme='http://www.blogger.com/atom/ns#' term='geeky'/><title type='text'>GWT Tips 2 - nocache.js getting cached in browser</title><content type='html'>I have recently discovered that, after version deployments, our GWT application does not work in some users' browsers unless they do a hard refresh on the browser.&lt;br /&gt;&lt;br /&gt;A bit of investigation revealed that this is because their browsers are caching the all-important &lt;em&gt;YOUR_MODULE_NAME.nocache.js&lt;/em&gt; file. This file is GWT's bootstrapping file and its content (not its name) is likely to change as you introduce changes and then gwt-compile your Java code. If a user has an older version of this js file cached in the browser, the application will not be able to properly communicate with your server code. &lt;strong&gt;One should make sure that this file does not get cached by the browser nor any proxy servers along the way.&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;You may wonder what is the significance the of the "nocache" bit in the name. What does it actually do? Well, it does not do anything! But having this identifiable string in the filename does allow you to configure your server to send HTTP header information to the client browser to stop it from caching the file.&lt;br /&gt;&lt;br /&gt;If you are using Apache to serve your static files. You can simply edit your &lt;em&gt;.htaccess&lt;/em&gt; file to send the correct HTTP header information to the client for this &lt;em&gt;nocache.js&lt;/em&gt; file.&lt;br /&gt;&lt;br /&gt;However, if you are using a Java application server such as Tomcat to server your static files, you have to create a servlet &lt;em&gt;Filter&lt;/em&gt; to dispatch the necessary HTTP header information. Below is a filter I have written to do exactly this:&lt;br /&gt;&lt;br /&gt;&lt;pre style="width:440px;height:400px;overflow:auto;"&gt;&lt;br /&gt;   &lt;span style="color:blue;"&gt;&lt;br /&gt;import java.io.IOException;&lt;br /&gt;import java.util.Date;&lt;br /&gt;&lt;br /&gt;import javax.servlet.Filter;&lt;br /&gt;import javax.servlet.FilterChain;&lt;br /&gt;import javax.servlet.FilterConfig;&lt;br /&gt;import javax.servlet.ServletException;&lt;br /&gt;import javax.servlet.ServletRequest;&lt;br /&gt;import javax.servlet.ServletResponse;&lt;br /&gt;import javax.servlet.http.HttpServletRequest;&lt;br /&gt;import javax.servlet.http.HttpServletResponse;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * {@link Filter} to add cache control headers for GWT generated files to ensure&lt;br /&gt; * that the correct files get cached.&lt;br /&gt; * &lt;br /&gt; * @author See Wah Cheng&lt;br /&gt; * @created 24 Feb 2009&lt;br /&gt; */&lt;br /&gt;public class GWTCacheControlFilter implements Filter {&lt;br /&gt;&lt;br /&gt; public void destroy() {&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public void init(FilterConfig config) throws ServletException {&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException,&lt;br /&gt;   ServletException {&lt;br /&gt;&lt;br /&gt;  HttpServletRequest httpRequest = (HttpServletRequest) request;&lt;br /&gt;  String requestURI = httpRequest.getRequestURI();&lt;br /&gt;&lt;br /&gt;  if (requestURI.contains(".nocache.")) {&lt;br /&gt;   Date now = new Date();&lt;br /&gt;   HttpServletResponse httpResponse = (HttpServletResponse) response;&lt;br /&gt;   httpResponse.setDateHeader("Date", now.getTime());&lt;br /&gt;   // one day old&lt;br /&gt;   httpResponse.setDateHeader("Expires", now.getTime() - 86400000L);&lt;br /&gt;   httpResponse.setHeader("Pragma", "no-cache");&lt;br /&gt;   httpResponse.setHeader("Cache-control", "no-cache, no-store, must-revalidate");&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  filterChain.doFilter(request, response);&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;   &lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Essentially, this filter checks whether the requested file name contains the string ".nocache.", and if it does, it sets the header to tell the browser and any proxy servers along the way to not cache the file. For an excellent explanation of the different header options, check out &lt;a href="http://code.google.com/p/doctype/wiki/ArticleHttpCaching"&gt;http://code.google.com/p/doctype/wiki/ArticleHttpCaching&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Of course, you would have to add this filter to your web.xml (host-mode as well as standalone mode):&lt;br /&gt;&lt;pre style="width:440px;height:200px;overflow:auto;"&gt;&lt;br /&gt;   &lt;span style="color:blue;"&gt;&lt;br /&gt;&amp;lt;filter&amp;gt;&lt;br /&gt; &amp;lt;filter-name&amp;gt;gwtCacheControlFilter&amp;lt;/filter-name&amp;gt;&lt;br /&gt; &amp;lt;filter-class&amp;gt;com.seewah.blog.GWTCacheControlFilter&amp;lt;/filter-class&amp;gt;&lt;br /&gt;&amp;lt;/filter&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;filter-mapping&amp;gt;&lt;br /&gt; &amp;lt;filter-name&amp;gt;gwtCacheControlFilter&amp;lt;/filter-name&amp;gt;&lt;br /&gt; &amp;lt;url-pattern&amp;gt;/*&amp;lt;/url-pattern&amp;gt;&lt;br /&gt;&amp;lt;/filter-mapping&amp;gt;&lt;br /&gt;   &lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-19290587640458452?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/19290587640458452/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=19290587640458452' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/19290587640458452'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/19290587640458452'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2009/02/gwt-tips-2-nocachejs-getting-cached-in.html' title='GWT Tips 2 - nocache.js getting cached in browser'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-5122156928692066852</id><published>2009-02-23T01:14:00.004Z</published><updated>2009-02-23T01:30:09.668Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='software development'/><category scheme='http://www.blogger.com/atom/ns#' term='geeky'/><category scheme='http://www.blogger.com/atom/ns#' term='spring'/><title type='text'>Spring Tips 1 - reading encrypted passwords in properties file</title><content type='html'>Have you ever wondered how you can use encrypted passwords in Java properties files that get loaded into your Spring ApplicationContext via a &lt;span style="font-style: italic;"&gt;org.springframework.beans.factory.config.PropertyPlaceholderConfigurer&lt;/span&gt;?&lt;br /&gt;&lt;br /&gt;Here is the answer - &lt;a href="http://forum.springframework.org/showpost.php?s=6d689f6afe0143a9ab3a77bfbb8e017c&amp;amp;p=7965&amp;amp;postcount=6"&gt;http://forum.springframework.org/showpost.php?s=6d689f6afe0143a9ab3a77bfbb8e017c&amp;amp;p=7965&amp;amp;postcount=6&lt;/a&gt; (thanks to none other than Juergen Hoeller)&lt;br /&gt;&lt;br /&gt;Pretty slick!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-5122156928692066852?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/5122156928692066852/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=5122156928692066852' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/5122156928692066852'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/5122156928692066852'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2009/02/spring-tips-1-reading-encrypted.html' title='Spring Tips 1 - reading encrypted passwords in properties file'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-1231455761211241586</id><published>2009-02-20T13:02:00.014Z</published><updated>2009-04-15T19:17:03.940+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gwt'/><category scheme='http://www.blogger.com/atom/ns#' term='software development'/><category scheme='http://www.blogger.com/atom/ns#' term='geeky'/><category scheme='http://www.blogger.com/atom/ns#' term='design pattern'/><title type='text'>GWT Tips 1 - Chain of Responsibility</title><content type='html'>GWT application development (and AJAX programming in general) can sometimes prove problematic for web developers who has not yet adopted the mindset of asynchronous programming. In GWT, you make a RPC as follows:&lt;br /&gt;&lt;pre style="width:440px;height:200px;overflow:auto;"&gt;&lt;br /&gt;   &lt;span style="color:blue;"&gt;&lt;br /&gt;foo.bar(param, new AsycnCallback&amp;lt;object&amp;gt;) {&lt;br /&gt; public void onFailure(Throwable throwable) {&lt;br /&gt;        // handle exception&lt;br /&gt;    }&lt;br /&gt;    public void onSuccess(Object result) {&lt;br /&gt;        // successful execution of asynchronous RPC&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;   &lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;What if you have to make a series of RPC calls such that an RPC call only gets invoked after the previous call has succeeded. Of course we can simply wrap this series of RPC calls inside one single RPC on the server side so that the client does not have to worry about synchronising the calls, but you maybe you want to update the user interface between calls, or you may want to execute some client-side logic before making the second call.&lt;br /&gt;&lt;br /&gt;You may start off writing code like this:&lt;br /&gt;&lt;pre style="width:440px;height:300px;overflow:auto;"&gt;&lt;br /&gt;   &lt;span style="color:blue;"&gt;&lt;br /&gt;foo.bar(param, new AsycnCallback&amp;lt;object&amp;gt;) {&lt;br /&gt;    public void onFailure(Throwable throwable) {&lt;br /&gt;        ...&lt;br /&gt;    }&lt;br /&gt;    public void onSuccess(Object result) {&lt;br /&gt;        // some client-side logic before invoking the second RPC&lt;br /&gt;        foo2.bar2(param, new AsycnCallback&amp;lt;object&amp;gt;) {&lt;br /&gt;            public void onFailure(Throwable throwable) {&lt;br /&gt;                ....&lt;br /&gt;            }&lt;br /&gt;            public void onSuccess(Object object) {&lt;br /&gt;                ....&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;   &lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This is hard to follow and error-prone. A way to avoid nested RPC calls is to leverage the &lt;a href="http://en.wikipedia.org/wiki/Chain-of-responsibility_pattern"&gt;Chain of Responsibility&lt;/a&gt; design pattern. The main class in the design pattern is the &lt;em&gt;AbstractHandler&lt;/em&gt;. The following is a basic implementation of this class:&lt;br /&gt;&lt;pre style="width:440px;height:400px;overflow:auto;"&gt;&lt;br /&gt;   &lt;span style="color:blue;"&gt;&lt;br /&gt;/**&lt;br /&gt; * An abstract handler in the Chain or Responsibility design pattern.&lt;br /&gt; *&lt;br /&gt; * @author See Wah Cheng&lt;br /&gt; * @param &amp;lt;T&amp;gt;&lt;br /&gt; *     type of request object to handle&lt;br /&gt; */&lt;br /&gt;public abstract class AbstractHandler&amp;lt;T&amp;gt; {&lt;br /&gt;&lt;br /&gt;  private Handler&amp;lt;T&amp;gt; successor;&lt;br /&gt;&lt;br /&gt;  public void setSuccessor(Handler&amp;lt;T&amp;gt; successor) {&lt;br /&gt;      this.successor = successor;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public Handler&amp;lt;T&amp;gt; getSuccessor() {&lt;br /&gt;      return successor;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public void handOverToSuccessor(T request) {&lt;br /&gt;      if (successor != null) {&lt;br /&gt;          successor.handleRequest(request);&lt;br /&gt;      }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public abstract void handleRequest(T request);&lt;br /&gt;}&lt;br /&gt;   &lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Given this class, to execute a series of RPC calls, one simply has to create a concrete implementation of this &lt;em&gt;AbstractHandler&lt;/em&gt; for each RPC call and chain them together as follows: (where InvocationContext is some kind of class encapsulating the details needed for the various RPC invocation)&lt;br /&gt;&lt;pre style="width:440px;height:400px;overflow:auto;"&gt;&lt;br /&gt;   &lt;span style="color:blue;"&gt;&lt;br /&gt;public class FooInvoker extends AbstractHandler&amp;lt;Invocationcontext&amp;gt; {&lt;br /&gt;   public void handleRequest(InvocationContext request) {&lt;br /&gt;       Object param = request.getFooParam();&lt;br /&gt;       foo.bar(param, new AsycnCallback&amp;lt;object&amp;gt;) {&lt;br /&gt;           public void onFailure(Throwable throwable) { // handle exception }&lt;br /&gt;           public void onSuccess(Object result) {&lt;br /&gt;               // some client-side logic before invoking the next RPC&lt;br /&gt;               // updating request object if necessary&lt;br /&gt;               handOverToSuccessor(request);&lt;br /&gt;           }&lt;br /&gt;        }&lt;br /&gt;   }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public class Foo2Invoker extends AbstractHandler&amp;lt;InvocationContext&amp;gt; {&lt;br /&gt;   public void handleRequest(InvocationContext request) {&lt;br /&gt;       Object param = request.getFoo2Param();&lt;br /&gt;       foo2.bar2(param, new AsycnCallback&amp;lt;object&amp;gt;) {&lt;br /&gt;           public void onFailure(Throwable throwable) { // handle exception }&lt;br /&gt;           public void onSuccess(Object result) {&lt;br /&gt;               // some client-side logic before invoking the next RPC&lt;br /&gt;               // updating request object if necessary&lt;br /&gt;               handOverToSuccessor(request);&lt;br /&gt;           }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; InvocationContext request;&lt;br /&gt; ...&lt;br /&gt; FooInvoker fooInvoker = new FooInvoker();&lt;br /&gt; Foo2Invoker foo2Invoker = new Foo2Invoker();&lt;br /&gt; fooInvoker.setSuccessor(foo2Invoker);&lt;br /&gt; fooInvoker.handleRequest(request);&lt;br /&gt;   &lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;OK you may be shouting, "Isn't this a lot of code for something pretty simple? Er, agile? Hello?" Yes, it is more verbose, and whether this approach offers any benefit depends on the situation. If you are making three or more RPC calls in series, I definitely think this will lead to better quality code. If, inside each callback's onSuccess method, you are doing more than just invoking the next RPC, this will certainly lead to better code. If the individual stages are needed elsewhere, this again will lead to more reusable code.&lt;br /&gt;&lt;br /&gt;Finally, an added benefit of this is that the order of execution can be altered quite easy just by shuffling the handler order in the chain (assuming that the order of execution does not matter!)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-1231455761211241586?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/1231455761211241586/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=1231455761211241586' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/1231455761211241586'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/1231455761211241586'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2009/02/gwt-tips-1-chain-of-responsibility.html' title='GWT Tips 1 - Chain of Responsibility'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-1674324747358627489</id><published>2009-02-18T23:12:00.031Z</published><updated>2009-06-06T22:05:29.216+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gwt'/><category scheme='http://www.blogger.com/atom/ns#' term='software development'/><category scheme='http://www.blogger.com/atom/ns#' term='geeky'/><category scheme='http://www.blogger.com/atom/ns#' term='spring'/><title type='text'>GWT and Spring Security</title><content type='html'>&lt;strong&gt;&lt;span style="color: rgb(102, 0, 0);"&gt;Update: I have put together a sample app to demonstrate the integration technique: &lt;/span&gt;&lt;/strong&gt;&lt;a href="http://seewah.blogspot.com/2009/06/gwt-and-spring-security-sample-demo.html"&gt;&lt;strong&gt;&lt;span style="color: rgb(102, 0, 0);"&gt;http://seewah.blogspot.com/2009/06/gwt-and-spring-security-sample-demo.html&lt;/span&gt;&lt;/strong&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;As promised, I am posting this article to share my experience with using Spring Security (formerly ACEGI) with GWT. Apologies for the delay. I guess I have just been busy + lazy!&lt;br /&gt;&lt;br /&gt;First of all, for those of you who haven't, please read my previous blogs &lt;a href="http://seewah.blogspot.com/2008/07/gwt-and-spring.html"&gt;GWT Spring Integration&lt;/a&gt; and &lt;a href="http://seewah.blogspot.com/2009/02/gwt-spring-integration-using.html"&gt;GWT Spring Integration - using ContextLoaderListener&lt;/a&gt; to see how to use Spring-managed service beans in GWT.&lt;br /&gt;&lt;br /&gt;Again it is not my intention to provide a complete solution for GWT / Spring Security integration as there are so many ways of doing it, and which method to choose depend hugely on how you want to actually use Spring Security inside GWT. By sharing my limited experience here, I hope to give some ideas to those of you who are trying to come up with a solution that fits your needs.&lt;br /&gt;&lt;br /&gt;So how do I want to use Spring Security in my GWT application?&lt;br /&gt;&lt;br /&gt;I want to:&lt;br /&gt;&lt;br /&gt;1) leverage Spring Security's &lt;span style="font-style: italic;"&gt;SecurityContext&lt;/span&gt; mechanism to store authenticated token, which I can then access anywhere in my code&lt;br /&gt;2) be able to annotate my service classes, so that Spring can apply &lt;span style="font-style: italic;"&gt;method security&lt;/span&gt; using AOP to my service beans&lt;br /&gt;&lt;br /&gt;I DO NOT want to:&lt;br /&gt;&lt;br /&gt;1) let Spring Security automatically handle form authentication for me&lt;br /&gt;2) rely on Spring Security to automatically redirect user to login page&lt;br /&gt;&lt;br /&gt;In other words, using Spring Security, I want to develop some form of RPC service which my GWT client can call to authenticate the username and password supplied by the user at the login screen. Once authentication has succeeded, a token which identifies the current user as being authenticated will be available to all subsequent RPC requests, so that I can either manually inspect the token inside the RPC request to enforce some form of authorization, or I can rely on Spring Security's off-the-shelf &lt;span style="font-style: italic;"&gt;method security&lt;/span&gt; mechanism to allow or disallow user call certain methods in my Spring managed service beans.&lt;br /&gt;&lt;br /&gt;I am going to break up the rest of the article into three different sections: &lt;span style="font-weight: bold; color: rgb(0, 102, 0);"&gt;An authentication service&lt;/span&gt;, &lt;span style="font-weight: bold; color: rgb(0, 102, 0);"&gt;HTTP Session Integration&lt;/span&gt; and &lt;span style="font-weight: bold; color: rgb(0, 102, 0);"&gt;Method Security&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 102, 0);"&gt;An Authentication service&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The following is a simple implementation of such a service:&lt;br /&gt;&lt;pre style="overflow: auto; width: 440px; height: 400px;"&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;&lt;br /&gt;import org.springframework.security.Authentication;&lt;br /&gt;import org.springframework.security.context.SecurityContext;&lt;br /&gt;import org.springframework.security.context.SecurityContextHolder;&lt;br /&gt;import org.springframework.security.context.SecurityContextImpl;&lt;br /&gt;import org.springframework.security.providers.UsernamePasswordAuthenticationToken;&lt;br /&gt;import org.springframework.security.userdetails.User;&lt;br /&gt;&lt;br /&gt;public class AuthenticationServiceImpl implements AuthenticationService {&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * Authenticates against the supplied credentials,&lt;br /&gt;  * populating the Spring SecurityContext if&lt;br /&gt;  * credentials are OK.&lt;br /&gt;  *&lt;br /&gt;  * @param username&lt;br /&gt;  * @param password&lt;br /&gt;  * @return&lt;br /&gt;  *     whether authentication is successful&lt;br /&gt;  */&lt;br /&gt; public boolean authenticate(String username, String password) {&lt;br /&gt; &lt;br /&gt;     // check credentials, e.g., by querying a database...&lt;br /&gt;     boolean authenticated = checkCredentials();&lt;br /&gt;&lt;br /&gt;     // look up authorities by, e.g., querying the database...&lt;br /&gt;     GrantedAuthority[] authorities = getGrantedAuthorities(username);&lt;br /&gt; &lt;br /&gt;     if (authenticated) {&lt;br /&gt;         User user = new User(username, password, true, true, true, true, authorities);&lt;br /&gt;         Authentication auth = new UsernamePasswordAuthenticationToken(user, password, authorities);&lt;br /&gt;         SecurityContext sc = new SecurityContextImpl();&lt;br /&gt;         sc.setAuthentication(auth);&lt;br /&gt;         SecurityContextHolder.setContext(sc);&lt;br /&gt;     }&lt;br /&gt;&lt;br /&gt;     return authenticated;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public void logout() {&lt;br /&gt;     SecurityContextHolder.clearContext();&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This of course is a very simple implementation, but the point to note is within the &lt;em&gt;authenticate&lt;/em&gt; method, an Spring &lt;em&gt;Authentication&lt;/em&gt; object is created and inserted into the &lt;em&gt;SecurityContext&lt;/em&gt; programmatically.&lt;br /&gt;&lt;br /&gt;We can simply turn this into a RPC service by creating a &lt;em&gt;com.google.gwt.user.server.rpc.RemoteServiceServlet&lt;/em&gt; implementation in which we either access &lt;em&gt;AuthenticationServiceImpl&lt;/em&gt; directly or via a Spring ApplicationContext as described in my previous blogs &lt;a href="http://seewah.blogspot.com/2008/07/gwt-and-spring.html"&gt;GWT Spring Integration&lt;/a&gt; and &lt;a href="http://seewah.blogspot.com/2009/02/gwt-spring-integration-using.html"&gt;GWT Spring Integration - using ContextLoaderListener&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Now we have a service that effectively let us "log into" the system. However, this on its own is rather useless, as the token is not available to subsequent request threads. If we have a RPC service for retrieving documents, and we would like to protect the documents, we really would want to be able to, inside the service, inspect the SecurityContext to see whether an authenticated token exists. We want be able to do the following, which we cannot do... yet:&lt;br /&gt;&lt;pre style="overflow: auto; width: 440px; height: 200px;"&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;&lt;br /&gt;if (SecurityContextHolder.getContext() == null&lt;br /&gt; SecurityContextHolder.getContext().getAuthentication() == null&lt;br /&gt; !SecurityContextHolder.getContext().getAuthentication().isAuthenticated) {&lt;br /&gt;&lt;br /&gt; // no authenticated token found, stop user from retrieving documents&lt;br /&gt;}&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 102, 0);"&gt;HTTP Session Integration&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Luckily, Spring Security provides a filter, &lt;em&gt;org.springframework.security.context.HttpSessionContextIntegrationFilter&lt;/em&gt;, which automatically synchronises the current SecurityContext with a http session object, thus, making the token available to all subsequent request threads.&lt;br /&gt;&lt;br /&gt;We can enable this filter in a number of ways. We can create the filter directly in web.xml (host-mode or standalone mode). Or we can create a &lt;em&gt;org.springframework.security.util.FilterToBeanProxy&lt;/em&gt; in web.xml and define a &lt;em&gt;FilterChainProxy&lt;/em&gt; in the Spring ApplicationContext. Or we can, as most people do nowadays, create a &lt;em&gt;org.springframework.web.filter.DelegatingFilterProxy&lt;/em&gt; called &lt;em&gt;springSecurityFilterChain&lt;/em&gt; (note: the naming is crucial) in web.xml and use &lt;em&gt;Spring Security Namespace Configuration&lt;/em&gt; in the ApplicationContext to create the necessary beans. We will have a look at this last approach.&lt;br /&gt;&lt;br /&gt;In web.xml&lt;br /&gt;&lt;pre style="overflow: auto; width: 440px; height: 200px;"&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;&lt;br /&gt;&amp;lt;filter&amp;gt;&lt;br /&gt; &amp;lt;filter-name&amp;gt;springSecurityFilterChain&amp;lt;/filter-name&amp;gt;&lt;br /&gt; &amp;lt;filter-class&amp;gt;org.springframework.web.filter.DelegatingFilterProxy&amp;lt;/filter-class&amp;gt;&lt;br /&gt;&amp;lt;/filter&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;filter-mapping&amp;gt;&lt;br /&gt; &amp;lt;filter-name&amp;gt;springSecurityFilterChain&amp;lt;/filter-name&amp;gt;&lt;br /&gt; &amp;lt;url-pattern&amp;gt;/*&amp;lt;/url-pattern&amp;gt;&lt;br /&gt;&amp;lt;/filter-mapping&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;listener&amp;gt;&lt;br /&gt; &amp;lt;listener-class&amp;gt;org.springframework.web.context.ContextLoaderListener&amp;lt;/listener-class&amp;gt;&lt;br /&gt;&amp;lt;listener&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;listener&gt;&lt;br /&gt; &amp;lt;listener-class&gt;org.springframework.security.ui.session.HttpSessionEventPublisher&amp;lt;/listener-class&amp;gt;&lt;br /&gt;&amp;lt;/listener&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In Application Context:&lt;br /&gt;&lt;pre style="overflow: auto; width: 440px; height: 200px;"&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;&lt;br /&gt;&amp;lt;beans xmlns="http://www.springframework.org/schema/beans"&lt;br /&gt;xmlns:security="http://www.springframework.org/schema/security"....&amp;gt;&lt;br /&gt;&lt;br /&gt; &amp;lt;security:http entry-point-ref="dummyEntryPoint" create-session="always" /&amp;gt;&lt;br /&gt;&lt;br /&gt; &amp;lt;bean id="dummyEntryPoint" class="com.seewah.blog.DummyEntryPoint" /&amp;gt;&lt;br /&gt;...&lt;br /&gt;&amp;lt;beans&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This is pretty neat, except for the fact that we have to create a "dummy" &lt;em&gt;org.springframework.security.ui.AuthenticationEntryPoint&lt;/em&gt;, even though we are not replying on Spring Security to redirect user to login page. Unfortunately, by design, the &lt;em&gt;security:http&lt;/em&gt; tag requires an &lt;em&gt;AuthenticationEntryPoint&lt;/em&gt;, so I created this class:&lt;br /&gt;&lt;pre style="overflow: auto; width: 440px; height: 200px;"&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;&lt;br /&gt;public class DummyEntryPoint implements AuthenticationEntryPoint {&lt;br /&gt;&lt;br /&gt; public void commence(ServletRequest request, ServletResponse response, AuthenticationException e) throws IOException, ServletException {&lt;br /&gt; &lt;br /&gt;     throw new IllegalStateException("This implementation is a dummy class, created purely so that spring security namespace tags can be used in application context, and this method should never be called");&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;OK, so now we have a way to "log into" the system, and we can inspect the &lt;em&gt;SecurityContext&lt;/em&gt; in subsequent RPC requests to grant or deny users access to certain services. However, we would like to go one step further. We would like to leverage the excellent &lt;em&gt;method security&lt;/em&gt; feature provided by Spring Security because we like our security layer to be non-invasive, and we like AOP.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 102, 0);"&gt;Method Security&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;We configure &lt;em&gt;method security&lt;/em&gt; as follows:&lt;br /&gt;&lt;br /&gt;In Application Context:&lt;br /&gt;&lt;br /&gt;&lt;pre style="overflow: auto; width: 440px; height: 200px;"&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;&lt;br /&gt;&amp;lt;security:global-method-security secured-annotations="enabled" jsr250-annotations="disabled" /&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;bean id="dummyAuthenticationProvider" class="com.seewah.blog.DummyAuthenticationProvider"&amp;gt;&lt;br /&gt; &amp;lt;security:custom-authentication-provider /&amp;gt;&lt;br /&gt;&amp;lt;/bean&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Unfortunately, again, because of the way &lt;em&gt;Spring Security Namespace Configuration&lt;/em&gt; works, we have to introduce another dummy class, even though it will never be used. Remember our &lt;em&gt;AuthenticationServiceImpl&lt;/em&gt; creates authenticated token programmatically and &lt;em&gt;method security&lt;/em&gt; will simply grant / deny access based on the presence of this token. Hence the &lt;em&gt;authencationProvider&lt;/em&gt; here becomes redundant. We implement the dummy class as follows:&lt;br /&gt;&lt;pre style="overflow: auto; width: 440px; height: 200px;"&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;&lt;br /&gt;public class DummyAuthenticationProvider implements AuthenticationProvider {&lt;br /&gt;&lt;br /&gt; public Authentication authenticate(Authentication authentication) throws AuthenticationException {&lt;br /&gt; &lt;br /&gt;     throw new IllegalStateException("This implementation is a dummy class, created purely so that spring security namespace tags can be used in application context, and this method should never be called");&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; @SuppressWarnings("unchecked")&lt;br /&gt; public boolean supports(Class clazz) {&lt;br /&gt; &lt;br /&gt;     throw new IllegalStateException("This implementation is a dummy class, created purely so that spring security namespace tags can be used in application context, and this method should never be called");&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Of course, as method security is implemented using AOP in Spring, only Spring-managed beans are protected, which means in order to use this mechanism to protect our service layer, we MUST access these services via the Spring ApplicationContext (&lt;a href="http://seewah.blogspot.com/2008/07/gwt-and-spring.html"&gt;GWT Spring Integration&lt;/a&gt; and &lt;a href="http://seewah.blogspot.com/2009/02/gwt-spring-integration-using.html"&gt;GWT Spring Integration - using ContextLoaderListener&lt;/a&gt;) and NOT directly.&lt;br /&gt;&lt;br /&gt;When a protected method is accessed without an authenticated token being present in the &lt;em&gt;SecurityContext&lt;/em&gt;, Spring throws a &lt;em&gt;SpringSecurityException&lt;/em&gt;. If we let this exception propagate all the way to the GWT client code, we are most certainly going to be greeted with the dreaded &lt;em&gt;SerializableException&lt;/em&gt; error. Instead we should catch this exception and rethrow an exception that GWT can safely serialize and pass to the client side. We define such an exception, &lt;em&gt;ServiceSecurityException&lt;/em&gt;, in our GWT client package, and now we have a mechanism that translates a Spring Security method security exception into a GWT client exception:&lt;br /&gt;&lt;br /&gt;On the server&lt;br /&gt;&lt;pre style="overflow: auto; width: 440px; height: 200px;"&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;&lt;br /&gt;public class DocumentServiceImpl implements DocumentService {&lt;br /&gt;&lt;br /&gt; @org.springframework.security.annotation.Secured("ROLE_ADMIN")&lt;br /&gt; public void delete(Long id) {&lt;br /&gt;     ...&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class DocumentServiceServlet extends DependencyInjectionRemoteServiceServlet implements ... {&lt;br /&gt;&lt;br /&gt; @Autowired&lt;br /&gt; private DocumentService documentService;&lt;br /&gt;&lt;br /&gt; public void delete(Long id) throws ServiceSecurityException {&lt;br /&gt;     try {&lt;br /&gt;         documentService.delete(id);&lt;br /&gt;     } catch (SpringSecurityException e) {&lt;br /&gt;         throw new ServiceSecurityException(e.getMessage());&lt;br /&gt;     }&lt;br /&gt; }&lt;br /&gt; ....&lt;br /&gt;}&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Inside the client&lt;br /&gt;&lt;pre style="overflow: auto; width: 440px; height: 200px;"&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;&lt;br /&gt;documentServiceAsync.delete(id, new AsyncCallback&amp;lt;Object&amp;gt;() {&lt;br /&gt; public void onFailure(Throwable throwable) {&lt;br /&gt;     if (throwable instanceof ServiceSecurityException) {&lt;br /&gt;         // client side logic to redirect user to login page, for example&lt;br /&gt;     } else {&lt;br /&gt;          ...&lt;br /&gt;     }&lt;br /&gt; }&lt;br /&gt; ...&lt;br /&gt;});&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Of course, seeing that we need client-side exception handling whenever we invoke an "secured" RPC call, we may as well create an &lt;em&gt;AutoErrorHandlingAsyncCallback&lt;/em&gt; and invoke all our RPC calls passing through an instance of this callback instead of the normal &lt;em&gt;AsyncCallback&lt;/em&gt; object:&lt;br /&gt;&lt;pre style="overflow: auto; width: 440px; height: 200px;"&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;&lt;br /&gt;public abstract class AutoErrorHandlingAsyncCallback&amp;lt;T&amp;gt; implements AsyncCallback&amp;lt;T&amp;gt; {&lt;br /&gt;&lt;br /&gt; final public void onFailure(Throwable throwable) {    &lt;br /&gt;     if (throwable instanceof ServiceSecurityException) {&lt;br /&gt;         // client side logic to redirect user to login page, for example&lt;br /&gt;     } else {&lt;br /&gt;          ...&lt;br /&gt;     }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;OK this way of using Spring Security &lt;em&gt;method security&lt;/em&gt; is not as non-invasive as we have perhaps hoped, but IMHO this is far better than writing code to check for authenticated token in &lt;em&gt;SecurityContext&lt;/em&gt; in the services themselves.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-1674324747358627489?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/1674324747358627489/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=1674324747358627489' title='22 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/1674324747358627489'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/1674324747358627489'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2009/02/gwt-and-spring-security.html' title='GWT and Spring Security'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>22</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-5538095486745008786</id><published>2009-02-18T22:30:00.011Z</published><updated>2009-06-06T00:54:49.681+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gwt'/><category scheme='http://www.blogger.com/atom/ns#' term='software development'/><category scheme='http://www.blogger.com/atom/ns#' term='geeky'/><category scheme='http://www.blogger.com/atom/ns#' term='spring'/><title type='text'>GWT Spring Integration -  using ContextLoaderListener</title><content type='html'>&lt;strong&gt;&lt;span style="color:#660000;"&gt;Update: I have put together a sample app to demonstrate the integration technique: &lt;/span&gt;&lt;/strong&gt;&lt;a href="http://seewah.blogspot.com/2009/06/gwt-and-spring-security-sample-demo.html"&gt;&lt;strong&gt;&lt;span style="color:#660000;"&gt;http://seewah.blogspot.com/2009/06/gwt-and-spring-security-sample-demo.html&lt;/span&gt;&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;&lt;span style="color:#660000;"&gt; Just take out applicationContext-security.xml if you are not interested in the Spring Security aspect!&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;Just a quick followup post to my last post &lt;a href="http://seewah.blogspot.com/2008/07/gwt-and-spring.html"&gt;http://seewah.blogspot.com/2008/07/gwt-and-spring.html&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;In the last post I demostrated how to use the SpringApplicationContextLoader to manually load an application context from an xml file and subsequently to access Spring-managed beans via this class inside DependencyInjectionRemoteServiceServlet.&lt;br /&gt;&lt;br /&gt;For one reason or another, I have moved away from this approach and gone back to the traditional means of using the &lt;span style="FONT-STYLE: italic"&gt;org.springframework.web.context.ContextLoaderListener&lt;/span&gt; in &lt;span style="FONT-STYLE: italic"&gt;web.xml&lt;/span&gt; to load application contexts. So this is what to do if you prefer this approach.&lt;br /&gt;&lt;br /&gt;First of all, create the listener as you would with a normal Spring web application&lt;br /&gt;&lt;pre style="OVERFLOW: auto; WIDTH: 440px; HEIGHT: 130px"&gt;&lt;br /&gt;   &lt;span style="color:blue;"&gt;&lt;br /&gt;&amp;lt;listener&amp;gt;&lt;br /&gt;&amp;lt;listener-class&gt;org.springframework.web.context.ContextLoaderListener&amp;lt;/listener-class&amp;gt;&lt;br /&gt;&amp;lt;/listener&amp;gt;&lt;br /&gt;   &lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;in &lt;span style="FONT-STYLE: italic"&gt;web.xml&lt;/span&gt;. If you are running in host mode, this is found under the &lt;span style="FONT-STYLE: italic"&gt;tomcat/webapps/ROOT/WEB-INF&lt;/span&gt; folder inside your project. Otherwise, just edit the usual &lt;span style="FONT-STYLE: italic"&gt;web.xml&lt;/span&gt; inside your &lt;span style="FONT-STYLE: italic"&gt;webapps&lt;/span&gt; folder in your tomcat installation.&lt;br /&gt;&lt;br /&gt;Now, inside DependencyInjectionRemoteServiceServlet, simply use the static &lt;span style="FONT-STYLE: italic"&gt;org.springframework.web.context.support.WebApplicationContextUtils.getWebApplicationContext(ServletContext sc)&lt;/span&gt; method to access the loaded application context. This following is the revised class:&lt;br /&gt;&lt;pre style="OVERFLOW: auto; WIDTH: 440px; HEIGHT: 800px"&gt;&lt;br /&gt;   &lt;span style="color:blue;"&gt;&lt;br /&gt;import java.lang.reflect.Field;&lt;br /&gt;import java.util.HashSet;&lt;br /&gt;import java.util.Set;&lt;br /&gt;import org.apache.log4j.Logger;&lt;br /&gt;import org.springframework.beans.factory.NoSuchBeanDefinitionException;&lt;br /&gt;import org.springframework.beans.factory.annotation.Autowired;&lt;br /&gt;import org.springframework.web.context.support.WebApplicationContextUtils;&lt;br /&gt;import com.google.gwt.user.server.rpc.RemoteServiceServlet;&lt;br /&gt;/**&lt;br /&gt; * {@link RemoteServiceServlet} that automatically injects IoC dependency.&lt;br /&gt; * "org.springframework.beans.factory.annotation.Autowired" annotation is used&lt;br /&gt; * for marking which fields to inject into. Uses&lt;br /&gt; * {@link SpringApplicationContextLoader} to retrieve beans by name.&lt;br /&gt; * &amp;lt;p&amp;gt;&lt;br /&gt; * Note that the current implementation will only inject "declared" fields, and&lt;br /&gt; * not inherited fields. Fields can be private, protected, package or public.&lt;br /&gt; *&lt;br /&gt; * @author See Wah Cheng&lt;br /&gt; * @created 27 Jun 2008&lt;br /&gt; */&lt;br /&gt;@SuppressWarnings("serial")&lt;br /&gt;public class DependencyInjectionRemoteServiceServlet extends RemoteServiceServlet {&lt;br /&gt;&lt;br /&gt; protected static Logger logger = Logger.getLogger(DependencyInjectionRemoteServiceServlet.class);&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt; public void init() throws ServletException {&lt;br /&gt; super.init();&lt;br /&gt;      doDependencyInjection();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * Carries out dependency injection. This implementation uses Spring IoC&lt;br /&gt;  * container.&lt;br /&gt;  *&lt;br /&gt;  * @exception NoSuchBeanDefinitionException&lt;br /&gt;  *            if a suitable bean cannot be found in the Spring application&lt;br /&gt;  *            context. The current implementation looks up beans by name&lt;br /&gt;  */&lt;br /&gt; protected void doDependencyInjection() {&lt;br /&gt;&lt;br /&gt;     for (Field field : getFieldsToDependencyInject()) {&lt;br /&gt;         try {&lt;br /&gt;             boolean isFieldAccessible = field.isAccessible();&lt;br /&gt;             if (!isFieldAccessible) {&lt;br /&gt;                 field.setAccessible(true);&lt;br /&gt;             }&lt;br /&gt;             field.set(this, WebApplicationContextUtils.getWebApplicationContext(getServletContext()).getBean(field.getName()));&lt;br /&gt;             if (!isFieldAccessible) {&lt;br /&gt;                 field.setAccessible(false);&lt;br /&gt;             }&lt;br /&gt;             logger.debug("Dependency injection successful: " + this.getClass().getName() + "." + field.getName());&lt;br /&gt;         } catch (IllegalArgumentException e) {&lt;br /&gt;             throw new RuntimeException(e);&lt;br /&gt;         } catch (IllegalAccessException e) {&lt;br /&gt;             throw new RuntimeException(e);&lt;br /&gt;         }&lt;br /&gt;     }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * Find annotated fields to inject.&lt;br /&gt;  *&lt;br /&gt;  * @return a list of all the annotated fields&lt;br /&gt;  */&lt;br /&gt; private Set&amp;lt;Field&amp;gt; getFieldsToDependencyInject() {&lt;br /&gt;     Set&amp;lt;Field&amp;gt; fieldsToInject = new HashSet&amp;lt;Field&amp;gt;();&lt;br /&gt;     Field[] fields = this.getClass().getDeclaredFields();&lt;br /&gt;     for (Field field : fields) {&lt;br /&gt;         if (field.getAnnotation(Autowired.class) != null) {&lt;br /&gt;             fieldsToInject.add(field);&lt;br /&gt;         }&lt;br /&gt;     }&lt;br /&gt;     return fieldsToInject;&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;   &lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Note that there are two changes to this class. First of all, &lt;span style="FONT-STYLE: italic"&gt;doDependencyInjection()&lt;/span&gt; now accesses the application context using the &lt;span style="FONT-STYLE: italic"&gt;org.springframework.web.context.support.WebApplicationContextUtils&lt;/span&gt; class as described above. Secondly, &lt;span style="FONT-STYLE: italic"&gt;doDependencyInjection&lt;/span&gt;&lt;span style="FONT-STYLE: italic"&gt;()&lt;/span&gt; is now invoked from inside the servlet's &lt;span style="FONT-STYLE: italic"&gt;init()&lt;/span&gt; method and not its constructor. This is because, when &lt;span style="FONT-STYLE: italic"&gt;org.springframework.web.context.support.WebApplicationContextUtils.getWebApplicationContext(ServletContext sc)&lt;/span&gt;&lt;span style="font-size:+0;"&gt; is invoked inside &lt;/span&gt;&lt;span style="FONT-STYLE: italic"&gt;doDependencyInjection&lt;/span&gt;&lt;span style="FONT-STYLE: italic"&gt;()&lt;/span&gt;, a reference to the servlet's &lt;span style="FONT-STYLE: italic"&gt;ServletContext&lt;/span&gt;&lt;span style="font-size:+0;"&gt; must be obtained, and this is only available after the servlet object has been constructed.&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-5538095486745008786?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/5538095486745008786/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=5538095486745008786' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/5538095486745008786'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/5538095486745008786'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2009/02/gwt-spring-integration-using.html' title='GWT Spring Integration -  using ContextLoaderListener'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-8566751392380336772</id><published>2008-07-26T15:40:00.033+01:00</published><updated>2009-06-06T01:10:27.469+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gwt'/><category scheme='http://www.blogger.com/atom/ns#' term='software development'/><category scheme='http://www.blogger.com/atom/ns#' term='geeky'/><category scheme='http://www.blogger.com/atom/ns#' term='spring'/><title type='text'>GWT Spring Integration</title><content type='html'>&lt;strong&gt;Update: I have recently posted a follow-up blog &lt;a href="http://seewah.blogspot.com/2009/02/gwt-spring-integration-using.html"&gt;http://seewah.blogspot.com/2009/02/gwt-spring-integration-using.html&lt;/a&gt; which demostrates how to combine this approach with the more conventional method of loading ApplicationContext xml using ContextLoaderListener.&lt;/strong&gt;&lt;br /&gt;&lt;p&gt;&lt;span style="color:#660000;"&gt;&lt;strong&gt;Update 2: I have put together a sample app to demonstrate the integration technique (based on the more conventional method of loading ApplicationContext.xml using ContextLoaderListner): &lt;/strong&gt;&lt;/span&gt;&lt;a href="http://seewah.blogspot.com/2009/06/gwt-and-spring-security-sample-demo.html"&gt;&lt;span style="color:#660000;"&gt;&lt;strong&gt;http://seewah.blogspot.com/2009/06/gwt-and-spring-security-sample-demo.html&lt;/strong&gt;&lt;/span&gt;&lt;/a&gt;&lt;span style="color:#660000;"&gt;&lt;strong&gt; Just take out applicationContext-security.xml if you are not interested in the Spring Security aspect!&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;This is my first technical post. Sorry about the code formatting. I will fix it later!&lt;br /&gt;&lt;br /&gt;A couple of months ago, I started working on a GWT (Google Web Toolkit) project, using Spring as a IoC container to manage service beans on the server-side. An initial Google search for "spring gwt integration" led to a few ideas. Check out &lt;a href="http://www.jroller.com/galina/entry/google_web_toolkit_integration_with"&gt;http://www.jroller.com/galina/entry/google_web_toolkit_integration_with&lt;/a&gt; for a summary of some of the notable contributions out there.&lt;br /&gt;&lt;br /&gt;While there are a lot of working (and sometimes quite clever) solutions out there, most tend to tackle the problem in a rather round-about way. So I came up with my own solution, which, in my humble opinion, is quite a bit simpler. It is by no means perfect, but it may serve as an interesting reference for those who are interested.&lt;br /&gt;&lt;br /&gt;First of all, I want to leverage the built-in Tomcat provided by GWT, and I want to stick to the GWT model of letting &lt;em&gt;com.google.gwt.dev.shell.GWTShellServlet &lt;/em&gt;handle all requests in &lt;strong&gt;hosted mode &lt;/strong&gt;- i.e absolutely no changes to the standard GWT application setup are required to run the app in hosted mode.&lt;br /&gt;&lt;br /&gt;I created two classes: &lt;em&gt;DependencyInjectionRemoteServiceServlet&lt;/em&gt; and &lt;em&gt;SpringApplicationContextLoader&lt;/em&gt;:&lt;br /&gt;&lt;br /&gt;&lt;em&gt;DependencyInjectionRemoteServiceServlet&lt;/em&gt; extends the standard&lt;br /&gt;&lt;em&gt;com.google.gwt.user.server.rpc.RemoteServiceServlet&lt;/em&gt;, and this is going to be the class that all RPC servlet implementions will be extending. For example, if you have a service for authenticating users, you will have two interfaces:&lt;br /&gt;&lt;/p&gt;&lt;pre style="OVERFLOW: auto; WIDTH: 440px; HEIGHT: 150px"&gt;&lt;br /&gt;   &lt;span style="color:blue;"&gt;&lt;br /&gt;public interface AuthenticationService extends RemoteService {...}&lt;br /&gt;&lt;br /&gt;public interface AuthenticationServiceAsync {...}&lt;br /&gt;   &lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;and at least one implementation class:&lt;br /&gt;&lt;br /&gt;&lt;pre style="OVERFLOW: auto; WIDTH: 440px; HEIGHT: 100px"&gt;&lt;br /&gt;   &lt;span style="color:blue;"&gt;&lt;br /&gt;public class AuthenticationServiceImpl extends DependencyInjectionRemoteServiceServlet implements AuthenticationService {...}&lt;br /&gt;   &lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Before looking at what &lt;em&gt;DependencyInjectionRemoteServiceServlet&lt;/em&gt; does, let's take a quick look at &lt;em&gt;SpringApplicationContextLoader&lt;/em&gt; first:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre style="OVERFLOW: auto; WIDTH: 440px; HEIGHT: 600px"&gt;&lt;br /&gt;   &lt;span style="color:blue;"&gt;&lt;br /&gt;import org.springframework.beans.factory.NoSuchBeanDefinitionException;&lt;br /&gt;import org.springframework.context.ApplicationContext;&lt;br /&gt;import org.springframework.context.support.ClassPathXmlApplicationContext;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * Loads Spring application context from the classpath, and provides static&lt;br /&gt; * method to retrieve Spring-managed beans from the loaded context by name.&lt;br /&gt; *&lt;br /&gt; * @author See Wah Cheng&lt;br /&gt; * @created 27 Jun 2008&lt;br /&gt; */&lt;br /&gt;public class SpringApplicationContextLoader {&lt;br /&gt;&lt;br /&gt;    private static final String CONTEXT_LOC = "classpath:applicationContext.xml";&lt;br /&gt;&lt;br /&gt;    private static ApplicationContext applicationContext = new ClassPathXmlApplicationContext(new String[] { CONTEXT_LOC });&lt;br /&gt;&lt;br /&gt;    /**&lt;br /&gt;     * Returns a Spring-managed bean by name.&lt;br /&gt;     *&lt;br /&gt;     * @param name&lt;br /&gt;     * name of bean in the application context&lt;br /&gt;     * @return the Spring-managed bean&lt;br /&gt;     * @exception NoSuchBeanDefinitionException&lt;br /&gt;     * if a suitable bean cannot be found in the Spring application&lt;br /&gt;     * context. The current implementation looks up beans by name&lt;br /&gt;     */&lt;br /&gt;    public static Object getBean(String name) {&lt;br /&gt;        return applicationContext.getBean(name);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;   &lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So nothing clever here - just a class to load the Spring applicationContext and to retrieve beans by name.&lt;br /&gt;&lt;br /&gt;Now let's look at the &lt;em&gt;DependencyInjectionRemoteServiceServlet&lt;/em&gt; code:&lt;br /&gt;&lt;br /&gt;&lt;pre style="OVERFLOW: auto; WIDTH: 440px; HEIGHT: 600px"&gt;&lt;br /&gt;   &lt;span style="color:blue;"&gt;&lt;br /&gt;import java.lang.reflect.Field;&lt;br /&gt;import java.util.HashSet;&lt;br /&gt;import java.util.Set;&lt;br /&gt;import org.apache.log4j.Logger;&lt;br /&gt;import org.springframework.beans.factory.NoSuchBeanDefinitionException;&lt;br /&gt;import org.springframework.beans.factory.annotation.Autowired;&lt;br /&gt;import com.google.gwt.user.server.rpc.RemoteServiceServlet;&lt;br /&gt;/**&lt;br /&gt; * {@link RemoteServiceServlet} that automatically injects IoC dependency.&lt;br /&gt; * "org.springframework.beans.factory.annotation.Autowired" annotation is used&lt;br /&gt; * for marking which fields to inject into. Uses&lt;br /&gt; * {@link SpringApplicationContextLoader} to retrieve beans by name.&lt;br /&gt; * &amp;lt;p&amp;gt;&lt;br /&gt; * Note that the current implementation will only inject "declared" fields, and&lt;br /&gt; * not inherited fields. Fields can be private, protected, package or public.&lt;br /&gt; *&lt;br /&gt; * @author See Wah Cheng&lt;br /&gt; * @created 27 Jun 2008&lt;br /&gt; */&lt;br /&gt;@SuppressWarnings("serial")&lt;br /&gt;public class DependencyInjectionRemoteServiceServlet extends RemoteServiceServlet {&lt;br /&gt;&lt;br /&gt;    protected static Logger logger = Logger.getLogger(DependencyInjectionRemoteServiceServlet.class);&lt;br /&gt;&lt;br /&gt;    public DependencyInjectionRemoteServiceServlet() {&lt;br /&gt;        super();&lt;br /&gt;        doDependencyInjection();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    /**&lt;br /&gt;     * Carries out dependency injection. This implementation uses Spring IoC&lt;br /&gt;     * container.&lt;br /&gt;     *&lt;br /&gt;     * @exception NoSuchBeanDefinitionException&lt;br /&gt;     *            if a suitable bean cannot be found in the Spring application&lt;br /&gt;     *            context. The current implementation looks up beans by name&lt;br /&gt;     */&lt;br /&gt;    protected void doDependencyInjection() {&lt;br /&gt;&lt;br /&gt;        for (Field field : getFieldsToDependencyInject()) {&lt;br /&gt;            try {&lt;br /&gt;                boolean isFieldAccessible = field.isAccessible();&lt;br /&gt;                if (!isFieldAccessible) {&lt;br /&gt;                    field.setAccessible(true);&lt;br /&gt;                }&lt;br /&gt;                field.set(this, SpringApplicationContextLoader.getBean(field.getName()));&lt;br /&gt;                if (!isFieldAccessible) {&lt;br /&gt;                    field.setAccessible(false);&lt;br /&gt;                }&lt;br /&gt;                logger.debug("Dependency injection successful: " + this.getClass().getName() + "." + field.getName());&lt;br /&gt;            } catch (IllegalArgumentException e) {&lt;br /&gt;                throw new RuntimeException(e);&lt;br /&gt;            } catch (IllegalAccessException e) {&lt;br /&gt;                throw new RuntimeException(e);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    /**&lt;br /&gt;     * Find annotated fields to inject.&lt;br /&gt;     *&lt;br /&gt;     * @return a list of all the annotated fields&lt;br /&gt;     */&lt;br /&gt;    private Set&amp;lt;Field&amp;gt; getFieldsToDependencyInject() {&lt;br /&gt;        Set&amp;lt;Field&amp;gt; fieldsToInject = new HashSet&amp;lt;Field&amp;gt;();&lt;br /&gt;        Field[] fields = this.getClass().getDeclaredFields();&lt;br /&gt;        for (Field field : fields) {&lt;br /&gt;            if (field.getAnnotation(Autowired.class) != null) {&lt;br /&gt;                fieldsToInject.add(field);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        return fieldsToInject;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;   &lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So what is happening here? When the servlet instantiates, it calls &lt;em&gt;doDependencyInjection&lt;/em&gt;, which uses reflection to find out which fields to inject Spring beans into based on the &lt;em&gt;@Autowired &lt;/em&gt;annotation. It will attempt to find the bean in the Spring application context &lt;strong&gt;by name&lt;/strong&gt; using &lt;em&gt;SpringApplicationContextLoader&lt;/em&gt;. Note that I am only using &lt;em&gt;@Autowired &lt;/em&gt;to mark out fields. It does not do anything else. I could have devised my own annotation, but I have opted to use this Spring annotation for convenience's sake. So all you have to do now, when creating your RPC service implementation, is to extend &lt;em&gt;DependencyInjectionRemoteServiceServlet&lt;/em&gt; and annotate your fields:&lt;br /&gt;&lt;br /&gt;&lt;pre style="OVERFLOW: auto; WIDTH: 440px; HEIGHT: 200px"&gt;&lt;br /&gt;   &lt;span style="color:blue;"&gt;&lt;br /&gt;public class AuthenticationServiceImpl extends DependencyInjectionRemoteServiceServlet implements AuthenticationService {&lt;br /&gt;&lt;br /&gt;    @Autowired&lt;br /&gt;    private UserAccountService userAccountService;&lt;br /&gt;&lt;br /&gt;    public boolean authenticate(String userName, String password) {&lt;br /&gt;        userAccountService.authenticate(userName, password);&lt;br /&gt;        ....&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;   &lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;and of course in your applicationContext.xml, you have to define a bean whose id is userAccountService.&lt;br /&gt;&lt;br /&gt;&lt;pre style="OVERFLOW: auto; WIDTH: 440px; HEIGHT: 100px"&gt;&lt;br /&gt;   &lt;span style="color:blue;"&gt;&lt;br /&gt;&amp;lt;bean class=".....UserAccountServiceImpl" id="userAccountService"/&amp;gt;&lt;br /&gt;   &lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-8566751392380336772?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/8566751392380336772/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=8566751392380336772' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/8566751392380336772'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/8566751392380336772'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2008/07/gwt-and-spring.html' title='GWT Spring Integration'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-116749168791014054</id><published>2006-12-30T15:11:00.001Z</published><updated>2009-02-20T13:05:20.649Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='africa'/><category scheme='http://www.blogger.com/atom/ns#' term='travel'/><title type='text'>Tagines, Grand Taxis and Chris Sharma in flip-flops</title><content type='html'>&lt;span style="font-family:Verdana;font-size:85%;"&gt;Check out my Morocco photo set on &lt;a href="http://www.flickr.com/photos/seewah"&gt;http://www.flickr.com/photos/seewah&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;a href="http://photos1.blogger.com/x/blogger/4056/2046/1600/992384/veils.jpg"&gt;&lt;img style="FLOAT: left; MARGIN: 0px 10px 10px 0px; CURSOR: hand" alt="" src="http://photos1.blogger.com/x/blogger/4056/2046/320/433740/veils.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;19/Dec/06 Tues (London -&gt; Marrakech) - first impression...&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;By the time Shaon and I collected our backpacks, it was already 9pm. We were expecting some kind of information service, or even just a sign to the bus stop, but there was none. Eventually we came across a few dodgy-looking blokes smoking cigarettes by their taxis. After a bit of obligatory haggling (from 200 dirhams down to 100 dirhams, i.e. 12 GBP down to 6GBP), we set off for Jemma El Fna. The Jemma is the main square inside the Marrakech Medina, which is the walled old town of Marrakech.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;Apart from a minor altercation between the driver and a pony-cart owner after a near crash, the drive, mostly along the main boulevard, was uneventful. With the Koutoubia minaret, the most recognisable landmark of Marrakech, beckoning in the distance, I was looking out of the taxi window looking for clues, anticipating what surprises Morocco has in store for us, but Marrakech refused to lift its veil. We finally arrived at a broad and busy pedestrian area. The driver pointed out of the window and lazily indicated where our hotel was. Just when we were about to pay, he cheekily demanded another 20 dirhams. After a bit of futile debate, we managed to stuff the 100 dirham note in his hand and walked away.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;a href="http://photos1.blogger.com/x/blogger/4056/2046/1600/299107/jemma1.jpg"&gt;&lt;img style="FLOAT: left; MARGIN: 0px 10px 10px 0px; CURSOR: hand" alt="" src="http://photos1.blogger.com/x/blogger/4056/2046/320/130894/jemma1.jpg" border="0" /&gt;&lt;/a&gt;Our hotel, l'Hotel Afriquia, was tucked away in a side street by the pedestrian area leading up to the the Jemma. Straight after checking in, we headed to the food stalls in the Jemma and I had my first tagine, along with my first fresh pure orange juice in Morocco. The Jemma was just as I had imagined - bright white lamps, white steam coming out of the countless cooking utensils adding to the hectic ambience of the place. After our quick dinner, we had a wander around the dimly lit souq to the north of the Jemma and was promptly approached by various locals who claimed to have "the number one morocco hashish".&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;Chaos and yummy food, we loved it.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;&lt;strong&gt;20/Dec/06 Tues (Marrakech -&gt; Agadir -&gt; Taroudant) - it sucks...&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;In the morning we got to the coach station (La Gare Routiere) to buy a couple of overnight bus tickets to Taroudant for this evening. We was told that there would be no direct coaches to Taroudant at the time of the night, so we got a couple of tickets to Agadir, from where you could apparently catch a coach to Taroudant.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;From the coach station, we decided to take a shortcut through the souq in order to get back to the hotel in time for checkout. MISTAKE! The souq in the Marrakech Medina is a wonderful place, but not if you are in a hurry. As we went deeper and deeper into the souq, the network of alleyways became more and more chaotic until it turned into a wicked maze. As the clock was ticking down, the arabic banters between the shoppers, between the store owners seemed to be turning into jeers, and the straw hatch roof seemed to be letting less and less light in, until we had to concede and asked for direction. Just as we had expected, as soon as a friendly local showed us the way to the Jemma, a kid decided to be our guide and walked in front of us. Of course, walking in the same direction and being in a hurry, we could not exactly lose him. Eventually, bright day light came streaming through one of the gates leading to the Jemma. As I was to find out later throughout the week, tourist hustlers could be very forceful in Morocco. They would jump in front of you and offer to help you just to earn a few dirhams. Granted, without all these "helpful" hustlers, it would have had taken us much longer to work our way through the many labyrinths in Morocco, to decipher the chaotic transport (bus and grand taxis) system. I guess, in an alien environment, I wanted to feel in control, and having decisions made for you, having to blindly follow strangers, made me feel insecure.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;a href="http://photos1.blogger.com/x/blogger/4056/2046/1600/91148/birds.jpg"&gt;&lt;/a&gt;&lt;a href="http://photos1.blogger.com/x/blogger/4056/2046/1600/218536/palace_badii.jpg"&gt;&lt;img style="FLOAT: left; MARGIN: 0px 10px 10px 0px; CURSOR: hand" alt="" src="http://photos1.blogger.com/x/blogger/4056/2046/320/403527/palace_badii.jpg" border="0" /&gt;&lt;/a&gt;In the afternoon, we walked down the busy Avenue Hassan el Fetouaki to reach the Mellah (Jewish area) and the Kasbah in the south east corner of the Medina. Despite effort by various shopowners to assure us that the palaces were closed and to conveniently lure us into what we assume to be a bit of a tourist trap inside the Mellah, we visited the not really worth visiting Palais Bahia, and Le Palais Baadi, which, in the sinking north African sun, stood majestically in defiance of its own decay.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;After dinner, at around 10:30pm, we made our way to the coach station. The stand from which our coach was supposed to depart was eerily empty. The coach was there, but the driver, with his mates, was obviously not too bothered about his job, despite telling me again and again that we would depart in 15 minutes, until he eventually walked away to be replaced by another driver, who told us the same thing and also walked away. We waited and waited, assuming that the second driver was trying to gather more passengers. He eventually reemerged at 1:00am , and was genuinely surprised that we were still waiting in the freezing cold. Apparently earlier on, he shouted "mon amis" to us and signalled us to board an Agadir coach across the other side of the station, which we obviously did not paid much attention to. Luckily there was another coach at 1:30pm. We got on the coach and felt asleep straightaway (I did anyway). The coach arrived in Agadir at 5:00am. In a state of pure confusion, we got onto another coach for Taroudant. It was absolutely freezing and I was shivering like mad. Tiredness got the better of me, and I felt asleep again.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;We arrived at Taroudant at 7am. Found our hotel and went straight underneath the blanket on our beds.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;strong&gt;21/Dec/06 Wed (Taroudant) - relaxing...&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;a href="http://photos1.blogger.com/x/blogger/4056/2046/1600/578764/bab.jpg"&gt;&lt;img style="FLOAT: left; MARGIN: 0px 10px 10px 0px; CURSOR: hand" alt="" src="http://photos1.blogger.com/x/blogger/4056/2046/320/935332/bab.jpg" border="0" /&gt;&lt;/a&gt;We got our deserved sleep after the epic journey, and did not get to get out until midday. Taroudant was quite a relaxed little city with a beatiful city wall, a couple of souqs, a kasbah and a number of mosques. It was a nice change of scenery after a manic day in Marrakech. We wandered lazily through the meandering alleyways of the kasbah. The mud red kasbah wall was juxtoposed against modern shops, including an internet cafe called Cyber @ Kasba, and air-conditining units. We walked around the city wall and got back to the main square, Place Assarag, in time for dinner.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;Our hotel, l'hotel Taroudant, also functioned as a local drinking establishment. Being an Islamic country, outside big modern cities, alcohol was hard to come by. In fact, back in Marrakech, alcohol is pretty much forbidden in the Medina. We had a couple of state-produced Flag beers. &lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;Sadly, the Moroccan brew left a lot to desire...&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;Had an early night.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;strong&gt;22/Dec/06 Thurs (Taroudant -&gt; Inezgane -&gt; Tiznit -&gt; Tafraoute) - grand taxis...&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;We were told by the owner of the hotel that we could take a grand taxi to Inezgane and from there we could catch a bus to Tafraoute. With that mind, we squeezed ourselved into an old Mercedes taxi with four other passengers, excluding the driver, and endured our first grand taxi experience - two in the front seat and four in the back sites. I was pushed right up against the door. Trying not to think too much about what would happen to me if the door swang open (don't be silly, why would there be seat belts... in sh'Allah), I tried to absorb the scenery along way. As we were still quite a way from the Anti-Atlas, the area was flat, and moreover, most of it was quite arid. With the Sahara constantly expanding, desertification must be a real issue in many parts of Morocco. The repetitive barrenness and the heat eventually sent me to sleep. Incidentally, later on, I caught a glimpse of another passenger reading the newspaper, which happened to have a special report on water suuply problem in Morocco.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;After an hour in the taxi, we arrived in Inezgane only to find that there was no buses to Tafraoute. Instead we had to take another grand taxi to Tzinit, from where we would have to take another grand taxi to Tafraoute....&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;a href="http://photos1.blogger.com/x/blogger/4056/2046/1600/946314/girl.jpg"&gt;&lt;img style="FLOAT: left; MARGIN: 0px 10px 10px 0px; CURSOR: hand" alt="" src="http://photos1.blogger.com/x/blogger/4056/2046/320/950804/girl.jpg" border="0" /&gt;&lt;/a&gt;Another hour spent in another grand taxi, we arrived at the city of Tiznit. After a bit of asking around, we found a grand taxi for Tafraoute. Donning a blue head scarf, the driver was, I believe, of Touareg descent. Prior to this, I had only really seen Touareg people riding camels, living in nomad tents in the Sahara in travel photos, and I was glad our modern day blue man was equally skilled at negotiating the ultra-bendy mountain roads in his old Mercedes. Part of the experience of taking grand taxis is that you get to listen to whatever music the driver wants to listen to. While in our two previous rides, the drivers had had Moroccan radio on - an eclectic mix of arabic chanting, Rihanna and French hiphop, our Touareg driver this time was listening to some old cassettes of again what I assumed to be Touareg music. Pumping through his half-broken soundsystem, which was randomly adding delay, echo, stereo panning and all kinds of weird and wonderful effects, the music was amongst the most psychedelic I had ever heard. Syd Barrett himself would have approved. Driving into the golden setting sun, totally immersed in Touareg psychedelia, the meandering ride was simply magical.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;We checked into our hotel. Hotel Salama, a pretty plush one, for a change. The damage was 120 dirhams pppn (7 GBP!!!). We found a shop, Coin de Nomad, where we could hire mountain bikes for tomorrow. The shopkeeper, again in Touareg outfit, showed me photos of him with Chris Bonnington and Joe Brown. Respect! Not having had lunch, we were pretty hungry by 6pm, and we went down to the local restaurant to sample some Kefta Tagines. There we met a trio of English dudes. They were in Morocco on a surfing trip. The lack of waves had taken them away from the coast into the Anti-Atlas region. A couple of them were into African music, and I was happily talking to them about Seckou Keita, Tinawiren, Tounami Diabate, Fela Kuti and all those legendary musicians.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;strong&gt;23/Dec/06 Fri (Tafraoute) - Chris Sharma in flip-flops...&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;a href="http://photos1.blogger.com/x/blogger/4056/2046/1600/908500/boulders.jpg"&gt;&lt;img style="FLOAT: left; MARGIN: 0px 10px 10px 0px; CURSOR: hand" alt="" src="http://photos1.blogger.com/x/blogger/4056/2046/320/388187/boulders.jpg" border="0" /&gt;&lt;/a&gt;Shortly after breakfast, we went to Coin de Nomad to hire a couple of bikes. Following a hand-sketched map, we rode out into the wilderness. The landscape, dotted with weird, stacked boulders was simple incredible. The remoteness of the place, the eery silence. This is it! This is why we had endured hours of grand taxis to come to Tafraoute! We stopped at a cluster of boulders to shelter ourselves from the strong sun. I promptly put my climbing shoes on and had a go climbing some of these fine rounded granite boulders. They were pretty featureless, but quite frictiony, not dissimilar to the English gritstone. A kid from the local village, who must have been no more than 15 years old, saw me climbing and came over our way. Either my French was really bad or he could not speak any French, the kid and I could not really communicate. Nonetheless, driven by the universal urge to conquer and climb, he effortlessly soloed up a crimpy 10 metres route up to the top of a stack of boulders in flip-flops. My jaw dropped. He signalled me to come up. The route seemed to me to be about F5(+) / F6a, but there was obviously no protection whatsoever, no rope, no crashmat. With the kid staring down high above me, I got up 4 metres before coming to the crux move, which involved standing on a tiny flake, which was threatening to break off. The kid looked puzzled and did not really why I was having so much trouble. Feeling useless and inadequate, I told myself the flake was not going to break, my feet were not going to slip and I tried for different (non-existent) crimpy handholds until I decided that I valued my life more.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;Even though I could not get to talk to the kid, I felt as if I knew his whole life-story, such was the awe that he had inspired. With time running out, we had to ride off again, and we bid farewell to this amazing kid. He will probably never leave his village, never leave Morocco, but in my mind, he is always going to be the rockclimbing superstar.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;a href="http://photos1.blogger.com/x/blogger/4056/2046/1600/725572/rochers.jpg"&gt;&lt;img style="FLOAT: left; MARGIN: 0px 10px 10px 0px; CURSOR: hand" alt="" src="http://photos1.blogger.com/x/blogger/4056/2046/320/950584/rochers.jpg" border="0" /&gt;&lt;/a&gt;After 20 mins of cycling, we got to the painted rocks (Les Rochers Peintes). In 1986, driven by whatever urge he was feeling at the time, the Belgium artist, Jean Verame, decided to come to Morocco and draped a number of gigantic boulders in 18 tonnes of white, blue and red paint. With the colour fading, the colours were not so impressive now. Nonetheless, against this strange, otherworldly landscape, the effect was still extremely mesmerising. While Shaon went scrambling up to the top of some hill, I set off and climbed up some more (painted and non-painted) boulders in the middle of no man's land.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;Such an amazing place...&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;&lt;strong&gt;24/Dec/06 (Tafaroute -&gt; Agadir -&gt; Inezgand -&gt; Marrakech) - boredom...&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;If the previous day was the high point of our trip, the 24th was the all-time low. Due to bad judgement and incorrect information, we spent the whole day travelling. I tried various methods to keep myself entertained, including changing films in my Holga camera and playing mobile phone games, but nothing would cure my boredom. After three grand taxis and a four-hour coach journey, we eventually made it back to Marrakech at 10:00pm.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;&lt;strong&gt;25/Dec/06 (Marrakech) - new town...&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;We spent the whole day walking around Gueliz, the new town of Marrakech. Full of relatively overpriced eating places and western shops, there really was not much to see, apart from &lt;a href="http://www.jardinmajorelle.com/"&gt;Le Jardin Majorelle&lt;/a&gt;, which was a very pretty garden designed by Jacques Majorelle in 1886.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;&lt;a href="http://photos1.blogger.com/x/blogger/4056/2046/1600/913304/t2.jpg"&gt;&lt;img style="FLOAT: left; MARGIN: 0px 10px 10px 0px; CURSOR: hand" alt="" src="http://photos1.blogger.com/x/blogger/4056/2046/320/633943/t2.jpg" border="0" /&gt;&lt;/a&gt;Walking through the new town, you just could not help noticing the wealth gap in Marrakech. Up until now, I had never really been struck by the sight of poverty in Morocco, primarily because we had not walked through any areas showing the rich and the poor side by side, but now the contrast became really apparent. Interestingly, western influence in the new town was a lot stronger, for example, with a lot of females not wearing hijabs or veils, and bars selling alcohol becoming quite prevalent. Do people naturally yearn for western lifestyles once they have more money? Does sticking to you tradition render you an underdog in this global fight for power and wealth? Or is it simpler than that?&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;In the evening, we met up with a couple of friends from the UK and had dinner in a place called "Trio Latinos", which, despite all their effort to make the place look Latin American, decided to blast out French chanson versions of cheesy love songs.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;"&gt;&lt;span style="font-size:85%;"&gt;&lt;strong&gt;26/Dec/06 (Marrakech -&gt; London) - homebound...&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;a href="http://photos1.blogger.com/x/blogger/4056/2046/1600/196254/souq.jpg"&gt;&lt;span style="font-family:verdana;"&gt;&lt;img style="FLOAT: left; MARGIN: 0px 10px 10px 0px; CURSOR: hand" alt="" src="http://photos1.blogger.com/x/blogger/4056/2046/320/132997/souq.jpg" border="0" /&gt;&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family:verdana;"&gt;We spent the morning wandering around the Marrakech souq, with a compass in my pocket this time. To be honest, neither Shaon nor I was that fascinated by all the little things they sell. I did spot a pair of traditional Moroccan slippers with Louis Vuitton logos all over though. We also figured out that in the Moroccan's equivalent of pound shops, souvenirs like tea glasses costed half the price as they would do in the souq.&lt;br /&gt;&lt;br /&gt;We left early for the airport, thinking that we could leave our backpacks in the airport and visit the Menara garden near the airport. Unfortunately, there was no left-luggage, and we could not be bothered to trek to the garden with our heavy backpacks, so we simply sat outside the airport, soaking up the last bit of north African sun, kicking around an unfortunate orange, talking about films, religions, politics and all those topics you always find yourself talking about in foreign countries. &lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;Funny enough, I did not feel any of that languishing sensation that I had had when I had previously arrived at the departure lounges of the Havana airport, the Lima airport, or even the Delhi airport. Could it be because of the geographical proximity? Perhaps because I did not spend enough time talking to the people over there to understand the culture, the country? Perhaps because I felt that people were after my money all the time? Maybe it was because I knew at the back of mind that very soon I would be coming back to trek up Jebel Toubkal, or to climb the Todra Gorge?&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-116749168791014054?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/116749168791014054/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=116749168791014054' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/116749168791014054'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/116749168791014054'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2006/12/tagines-grand-taxis-and-chris-sharma.html' title='Tagines, Grand Taxis and Chris Sharma in flip-flops'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-114474551082877877</id><published>2006-04-11T09:49:00.001+01:00</published><updated>2009-02-20T13:05:42.020Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='europe'/><category scheme='http://www.blogger.com/atom/ns#' term='travel'/><category scheme='http://www.blogger.com/atom/ns#' term='climbing'/><title type='text'>Climbing in Catalunya, Spain</title><content type='html'>&lt;a href="http://photos1.blogger.com/blogger/4056/2046/1600/DSCF0072%20(WinCE).jpg"&gt;&lt;img style="CURSOR: hand" alt="" src="http://photos1.blogger.com/blogger/4056/2046/320/DSCF0072%20%28WinCE%29.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Just got back from an amazing climbing trip in Catalunya (Vilanova de Meia, Terradets and Montserrat)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Photos on &lt;/span&gt;&lt;a href="http://www.flickr.com/photos/seewah/sets/72057594103717835/"&gt;&lt;span style="font-size:85%;"&gt;http://www.flickr.com/photos/seewah/sets/72057594103717835/&lt;/span&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;I have also put some of my favourite Cuba photos on &lt;/span&gt;&lt;a href="http://www.flickr.com/photos/seewah/sets/72057594104317053/"&gt;&lt;span style="font-size:85%;"&gt;http://www.flickr.com/photos/seewah/sets/72057594104317053/&lt;/span&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-114474551082877877?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/114474551082877877/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=114474551082877877' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/114474551082877877'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/114474551082877877'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2006/04/climbing-in-catalunya-spain.html' title='Climbing in Catalunya, Spain'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-113628319128394867</id><published>2006-01-03T10:04:00.002Z</published><updated>2009-02-20T13:05:52.695Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='music'/><title type='text'>Mix CD</title><content type='html'>&lt;p&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;I compiled a mix CD of some of my favourite tunes yesterday. Here is the tracklisting.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;00:00 Cinematic Orchestra Feat Roots Manuva - All Things To All Men&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;09:34 Baaba Maal/Taj Mahal/Kaouding Cissoko/Antibalas Afrobeat Orchestra - Trouble Sleep Yanga Wake Am (tribute to Fela Kuti)&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;17:36 Cymande - Anthracite&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;23:02 Masters at Work - Watching Windows&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;28:04 Abyssinians - Mandela&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;31:41 Burning Spear - Black Wa-Da-Da (Invasion)&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;35:00 Sinead O'Connor - Curly Locks (Junior Byles cover)&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;38:45 Saul Williams - Niggas Way&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;41:56 Jay Dee and Madlib - The Jam&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;44:52 Mos Def and Talib Kweli - Thieves in the Night&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;49:41 The Marxmen (MOP) - Here Today Gone Tomorrow&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;52:56 Anti-Pop Consortium - Disorientation&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;56:37 Sketch Show - Cronograph&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;61:36 Explosions in the Sky - A Song for Our Fathers&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;65:28 Johnny Cash - Hurt (NIN cover)&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;I did not manage to include any of my favourite electronic or rock tunes. I may make another CD in the future.&lt;/span&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-113628319128394867?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/113628319128394867/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=113628319128394867' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/113628319128394867'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/113628319128394867'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2006/01/mix-cd.html' title='Mix CD'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-20427072.post-113620443175477787</id><published>2006-01-02T12:10:00.001Z</published><updated>2009-02-20T13:06:19.183Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='travel'/><category scheme='http://www.blogger.com/atom/ns#' term='central america'/><title type='text'>Cuban Diary</title><content type='html'>&lt;span style="font-family:verdana;font-size:85%;"&gt;Feliz Ano Nuevo!&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;Here is an account of my recent trip to Cuba. Enjoy...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;Links to my Cuban photos:&lt;/span&gt;&lt;br /&gt;&lt;a href="http://www.imagestation.com/album/pictures.html?id=2115677329&amp;amp;code=19894190&amp;amp;mode=invite&amp;amp;DCMP=isc-email-AlbumInvite"&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;album 1&lt;/span&gt;&lt;/a&gt; &lt;a href="http://www.imagestation.com/album/pictures.html?id=2115676041&amp;amp;code=19894335&amp;amp;mode=invite&amp;amp;DCMP=isc-email-AlbumInvite"&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;album 2&lt;/span&gt;&lt;/a&gt; &lt;a href="http://www.imagestation.com/album/pictures.html?id=2115675628&amp;amp;code=19894602&amp;amp;mode=invite&amp;amp;DCMP=isc-email-AlbumInvite"&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;album 3&lt;/span&gt;&lt;/a&gt; &lt;a href="http://www.imagestation.com/album/pictures.html?id=2115674998&amp;amp;code=19894816&amp;amp;mode=invite&amp;amp;DCMP=isc-email-AlbumInvite"&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;album 4&lt;/span&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://photos1.blogger.com/blogger/4056/2046/1600/DSCF0028.jpg"&gt;&lt;img style="CURSOR: hand" alt="" src="http://photos1.blogger.com/blogger/4056/2046/320/DSCF0028.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;strong&gt;11/Dec/05 Sun (London -&gt; La Habana)&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;We landed in La Habana pretty late. After an uneventful taxi trip, we arrived at our casa particular in Habana Centro. &lt;a href="http://www.casaparticular.info/"&gt;Casas particulares&lt;/a&gt; are private houses given permission by the State to accomodate foreign tourists. Prices are usually lower than those of hotels. We knocked on the door and a woman on the balcony lowered the key on a piece of string. It turned out that the owner of the house was away and we could not stay there. I thought, "Here we go - the joy of Cuban organisation." Luckily the woman knew someone round the corner who could rent out rooms to us.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;strong&gt;12/Dec/05 Mon (La Habana)&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;Woke up early. Went to check out La Habana Vieja. Full of buildings desperately in need of renovation, this old part of La Habana was nice, but I guess I was expecting something that transcended normality almost. We went to el Museo de la Revolucion, which was interesting, if only for the way the revolution was being portraited. Also visited the &lt;a href="http://www.havana-club.com/"&gt;Havana Club&lt;/a&gt; Rum Factory, and the Bacardi Building. The latter has nothing to do with rum nowadays, but the top of the building did offer a great view of La Habana. Incidentally, being a state-owned company produce, Havana Club is the preferred rum in Cuba.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://photos1.blogger.com/blogger/4056/2046/1600/DSCF0015%20(WinCE).jpg"&gt;&lt;img style="MARGIN: 0px 10px 10px 0px; CURSOR: hand" alt="" src="http://photos1.blogger.com/blogger/4056/2046/320/DSCF0015%20%28WinCE%29.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;strong&gt;13/Dec/05 Tue (La Habana -&gt; Santa Clara -&gt; Camaguey)&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;The start of our road trip. As expected, we got terribly lost. Map was useless and road signs were pretty much non-existent. On Cuban roads, revolution slogan signs are deemed more important than road signs. In the end, we figured out that the only way to get around Cuba was to ask people. Hitchhiking is a very common practice in Cuba. They even have officers, known as amarillos (yellow) after the colour of their jackets, organising the hitchhiker queue. These hitchhikers proved to be extremely resourceful and friendly even after we had told them we would not be able to take them. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;We arrived in Santa Clara a couple of hours after schedule. After visiting the &lt;a href="http://en.wikipedia.org/wiki/Che_Guevara"&gt;Che&lt;/a&gt;'s Mausoleum, we promptly left for Camaguey. The sun was quickly going down. Having to tackle potholes, upcoming traffic and cyclists, driving in the dark in Cuba is nothing but suicidual. My hand held firmly to the handle on the car door until we made it to our casa in Camaguey at around 10pm. The owner of the casa recommended a palador (private restaurant) called La Terraza. Much to our amusement, in the corner of the restaurant sit a fat guy, who would wake up every now and then from his dozing to play a tune on his guitar. We had a pretty good and cheap meal.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;em&gt;Revolutionary slogan of the day: El genio esta en las mases, el genio es masivo - Fidel (The genius is in the masses, the genius is massive - Fidel)&lt;/em&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://photos1.blogger.com/blogger/4056/2046/1600/DSCF0026.jpg"&gt;&lt;img style="CURSOR: hand" alt="" src="http://photos1.blogger.com/blogger/4056/2046/320/DSCF0026.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;strong&gt;14/Dec/05 Wed (Camaguey -&gt; Holguin)&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;After breakfast, we chatted with M, son of the casa owner, for a whole hour. This was our first proper extended conversation with any Cuban. It all started with me casually asking him what time he usually started work. It turned out that his working hours were quite "flexible". He spoke good English, and he went on to explain to us how the government was strangling private businesses, which included casas particulares and paladores, through heavy taxing, and how working in State-run companies would earn you very little money unless you were working for the government itself. Apparently, an average Cuban gets about 20 USD a month. (However, since a lot of basic necessities are being provided at extremely low cost by the government, people generally have enough to lead a pretty decent life.) We also went on to talk about America, the embargo and how Fidel was blaming everything on America. He was very uncertain about the future of Cuba as, on the one hand, a lot of young people believe the end of the current dictatorship will usher in a new taste of freedom they have been longing for, on the other hand, the introduction of an American-style capitalistic economy may destroy the social safety net that so many people depend on. Just like a lot of Cubans we were about to meet during the next week or so, M was very prepared to talk about the government. However, they would accept the reality and they would decide to have a happy, if sometimes slightly frustrating, life. The conversation left me questioning whether the socialistic system in Cuba would have survived up to this day, if Fidel and co had not done such a good job in putting up revolutionary signs.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;We spent the morning wandering around the pretty town of Camaguey. It was bustling, with people getting on with their everyday business. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;We decided to set off for Holguin early in the afternoon. On the way to our car, we caught M leisurely strolling through town with his girlfriend.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;em&gt;Revolutionary slogan of the day: Socialismo o muerte (Socialism or death)&lt;/em&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://photos1.blogger.com/blogger/4056/2046/1600/DSCF0030.jpg"&gt;&lt;img style="CURSOR: hand" alt="" src="http://photos1.blogger.com/blogger/4056/2046/320/DSCF0030.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;strong&gt;15/Dec/05 Thurs (Holguin -&gt; Santiago de Cuba)&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;Holguin was quite quiet. We went up to the top of a small hill, which offered a panoramic view of this little town. On the way to the centre, we came across a band rehearsal in an empty building on the main "high street", blasting out funky Cuban &lt;a href="http://en.wikipedia.org/wiki/Son_(music)"&gt;son&lt;/a&gt;-style music in the Cuban heat.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;In the afternoon, we set off for Santiago de Cuba, which was the furthest point of our road trip. We arrived in good time, and after dumping our stuff at the casa, we drove to La Castilla de Morro. It was a castle on the coast, overlooking the Caribbean Sea, and we stayed there till sunset.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;In the evening, we ventured into the centre, and after fighting off numerous jinateros (tourist hustlers, offering to take tourists to restaurants, bars...) we finally ended up in a basic state-run no-nonsense restaurants and had our first taste of "real" Cuban food - disgusting-tasting pizzas. We had 5 pizzas (we were real hungry) and two beers, and the bill came to less than 3 USD. If Cuba has failed miserably in the area of culinary arts, it has succeeded in giving the world good music. After the atrocious meal, we went to a music bar known as La Casa De La Trova. The band was excellent, but it was the audience's dancing that totally took my breath away. The rhythm, the spontaneity, the pure passion. Cubans are born to dance.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;em&gt;Revolutionary slogan of the day: Patrio o muerte (Patriotism or death)&lt;/em&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://photos1.blogger.com/blogger/4056/2046/1600/DSCF0068.jpg"&gt;&lt;img style="CURSOR: hand" alt="" src="http://photos1.blogger.com/blogger/4056/2046/320/DSCF0068.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;strong&gt;16/Dec/05 Fri (Santiago de Cuba)&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;The sun was blazing and there was not a single bit of wind. The heat was quite unbearable. We spent the day visiting different museums, including a santeria museum (&lt;a href="http://en.wikipedia.org/wiki/Santeria"&gt;santeria&lt;/a&gt; is a mix of catholicism and African belief, principally yoruba belief, widely practised amongst the black population in Cuba. An important deity is Orisha), a history of film/photography museum, and another not so interesting museum housed in what had had previously been a Bacardi family's building. One thing that struck me about the last museum was the sheer number of workers inside, who happened to be not doing much. We came to the conclusion that many jobs in Cuba must have been created for no other reason than to keep the unemployment rate low. We then spent the rest of the afternoon sitting by the coast of Santiago bay discussing the need for social integration, and amongst all things, the morality of prostitution.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;After sunset, we wandered back into town. As we were approaching the Bacardi building, we heard loud reggaeton beats pumping out of a soundsystem (more like a pair of old giant speakers) just round the corner. Some people have decided to throw a street party right in front of the Bacardi buillding. How cool is that! For those who have not heard of the term &lt;a href="http://www.reggaetonline.net/"&gt;reggaeton&lt;/a&gt;, it is a very popular form of "urban" music amongst Latin American youths. With a very distinctive beat, it seems to have drawn influence from American HipHop, Jamaican dancehall as well as more traditional Latin music. After dinner, we watched some students perform modern dance in the main square. At around midnight, we went to another music bar called Los Dos Abuelos to listen to more traditional Cuban &lt;a href="http://en.wikipedia.org/wiki/Son_(music)"&gt;son&lt;/a&gt; music. I was told later that I had slept through most of the performance, apparently.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;em&gt;Revolutionary slogan of the day (my favourite): Sin propaganda, no hay movimento de masas - Fidel (Without propaganda, there won't be mass movement - Fidel)&lt;/em&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://photos1.blogger.com/blogger/4056/2046/1600/DSCF0089.jpg"&gt;&lt;img style="CURSOR: hand" alt="" src="http://photos1.blogger.com/blogger/4056/2046/320/DSCF0089.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;strong&gt;17/Dec/05 Sat (Santiago de Cuba -&gt; Bayamo -&gt; Camaguey)&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;We left early to drive to Bayamo, which was a tiny town. After being refused entrance to a few restaurants on the basis that we were wearing shorts (how fascist!), we once again ventured into a no-nonsense "real cuban food" restaurant. No pizzas this time, and we were rewarded with a rather tasty meal consisting of pork chops with rice and beans, which came to just over 1 USD for three people. There was some hope in Cuban cuisine after all.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;We arrived in Camaguey just before sunset. Being a Saturday night, the town was very much alive, with people trying to outplay each other on their home stereos (in terms of volumn). We ended up talking to some friendly Camaguey locals in a bar. There we met a former Cuban Karate champion (1984 I think). The guy was a 4-dan black belt, and as soon as we learnt that I practised a bit of Kung Fu, he got very excited. Unfortunately, my poor Spanish did not allow me to have a very constructive conversation with him. Nonetheless, we exchanged addresses, and I intend to write to him soon, in better Spanish. Afterwards, we followed the music, and ended up in a massive street party, where they were playing more reggaeton tunes. There were loads of people, but it was not much of a party as most people were just standing still!&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;em&gt;Revolutionary slogan of the day: En caso barrio, revolucion (In every district, revolution)&lt;/em&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;strong&gt;18/Dec/05 Sun (Camaguey -&gt; Valle de los Ingenios -&gt; Trinidad)&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;Trinidad is in the south of Cuba. It is a small but pretty, well-preserved colonial town, not far from a gorgeous Caribbean beach, la Playa Ancon. Having come across very few tourists so far in our trip, the amount of non-Cubans in Trinidad took us by surprise. We spent a couple of hours on the beach until sunset, which unfortunately coincided with the sand fleas' dinner time.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;In the evening, we went looking for food. Despite the amount of tourists, it took us a while before we found an eating place. There we met the clumsiest waiter ever. He managed to drop beers on the floor on two separate occasions (once pouring it all over Gavin). In the end, they had to stop him from serving us food!&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;em&gt;Revolutionary slogan of the day: Venceremos (We shall overcome)&lt;/em&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;strong&gt;19/Dec/05 Mon (Trinidad)&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;We were defeated by the heat. The whole afternoon was pretty much spent doing nothing. In the end, we decided to go to the sea again. We had a most delicious dinner at the casa. The owner, C, made us a lavish meal of fish, shrimps and lobsters. We were later told that the comsumption of lobster was illegal in Cuba, and he had to buy the lobster from the black market. After dinner, we chatted for ages with C, his wife and two swiss girls who were also staying in the casa. The conversation was not particularly inspiring (again my poor Spanish was to blame), but we were very much taken by the genuine hospitality of C.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;em&gt;Revolutionary slogan of the day: Did not come across any in Trinidad!&lt;/em&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://photos1.blogger.com/blogger/4056/2046/1600/DSCF0008.jpg"&gt;&lt;img style="CURSOR: hand" alt="" src="http://photos1.blogger.com/blogger/4056/2046/320/DSCF0008.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;strong&gt;20/Dec/05 Tue (Trinidad -&gt; La Habana)&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;Felt pretty sad to have to say goodbye to C. Long drive back to La Habana. The weather was turning bad, and the Gulf of Mexico was relentlessly battering the Malecon. One could only imagine what it must be like during hurricanes. We had dinner in the most famous restaurant in Cuba, La Guardia, where they filmed some of the scenes for the movie La fresa y el chocolate. The decor was very quaint. The food was amazing.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;Most Cubans would never get to step inside.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;em&gt;Revolutionary slogan of the day: Senores Imperialistas, no les tenemos absolutemente ningun miedo (Mr Imperialists, we have absolutely no fear of you)&lt;/em&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;strong&gt;21/Dec/05 Wed (La Habana -&gt; Vinales)&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;Personally, Vinales was the town I had wanted to visit most. I had previously seen photos of this countryside town dotted with limestone hills (mogotes). And as soon as I caught the first glimpse of the place from inside the coach, I knew I was not going to be disappointed. We found our casa, and the owner J, being ultra friendly like the rest of Cuba, found us a mountain guide straighaway. Our chain-smoking guide took us trekking/rockclimbing through the forest straight to the heart of one of the little mountains around the town centre. The rock formations inside the mountain were breathtaking. The trek was much more challenging that we had expected as there were no foodpaths whatsoever, and it turned out to be a truly exhilarating experience.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;In the evening, we had dinner at the casa. Once again, we ended up chatting with the owner's family, and gained more and more insights into the very unique way of life of los Cubanos.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;em&gt;Revolutionary slogan of the day: too busy appreciating the beauty of Vinales; I have managed to forget about the Revolution/Struggle&lt;/em&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://photos1.blogger.com/blogger/4056/2046/1600/DSCF0034.jpg"&gt;&lt;img style="CURSOR: hand" alt="" src="http://photos1.blogger.com/blogger/4056/2046/320/DSCF0034.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;strong&gt;22/Dec/05 Thurs (Vinales)&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;Went to Los Cuevos de Santa Tomas, which was the biggest cave system in Cuba. It was impressive and the guide was very funny. Walking through the caves was like walking on another planet altogether. On the way back to town, we stopped by a mountainside mural, which was huge yet disappointing. In the afternoon, we went riding around the countryside. I had never been on a horse before, and I felt slightly uneasy at first, but excitement soon overcame fear. Riding through the fields, with the magnificent mogotes all around, as cliche as it may sound, I felt a very strong sense of freedom, the kind of freedom you can only achieve through solitude. Unfortunately, all good things had to come to an end, and after two hours on a horseback, my arse was starting to hurt.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;Afterwards, we went up to the Hotel Jasmine to catch supposedly the best view of Vinales. If Vinales offer the best view in Cuba, Hotel Jasmine offers the best view in Vinales. It was beauty beyond description.&lt;/span&gt; &lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="http://photos1.blogger.com/blogger/4056/2046/1600/DSCF0004.jpg"&gt;&lt;img style="CURSOR: hand" alt="" src="http://photos1.blogger.com/blogger/4056/2046/320/DSCF0004.jpg" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;strong&gt;23/Dec/05 Friday (Vinales -&gt; La Habana)&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;Arrived back in La Habana. We spent one hour waiting for our turn to exchange money in the bank. It was exactly one hour as they had a ridiculously tacky eletronic cuckoo clocks and the cuckoos came out twice. In Cuban banks, instead of queuing up, people just sit around. Coming into the bank, they would shout, "who is last?", to join the "queue". In theory, this is a great system. People just have to remember the person in front, but like all great ideas, implementations sometimes fail.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;We visited Las Partagas, a famous cigar factory in Habana Centro. Here we saw the biggest production line (all manual of course). Apparently, people got paid depending on the amount of cigars they could produce per day. A bit counter-revolutionary, dare I say? Cigars are expensive things considering that each is literally no more than just four tobago leaves rolled together, plus a little label stuck on it. Went to El Museo de las Bellas Artes in the afternoon, but our tiredness was starting to take its toll, and we were not really in the mood to enjoy the fine art.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;Gavin and Arry went to Club Tropicana in the evening. I did not go. Instead I spent the evening chatting/watching TV with the casa owner and her brother, who happened to have fought against the Somalians for the Ethiopians (then communist) 20 years ago! He told me amazing stories about Ethiopia. During the course of the evening, we watched a baseball game (boring...), two documentaries and Phantom of the Opera on TV. In Cuba, they like their commentaries. Some dude would come on TV and discuss the artistic merits and social messages of a movie or a documentary before it starts. Both the brother and I thought it was a waste of time.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;em&gt;Revolutionary slogan of the day: No al fascismo (no to fascism) - with a face of someone looking uncannily like the son of Hilter and G W Bush next to it&lt;/em&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;span style="font-family:Verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;strong&gt;&lt;a href="http://photos1.blogger.com/blogger/4056/2046/1600/DSCF0012.jpg"&gt;&lt;img style="CURSOR: hand" alt="" src="http://photos1.blogger.com/blogger/4056/2046/320/DSCF0012.jpg" border="0" /&gt;&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;strong&gt;24/Dec/05 Saturday (Las Habana -&gt; London)&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;Last day in Cuba. We decided that we had totally fallen in love with the country, and he would make the most of the final day. To get to Vedado, we walked along the Malecon, where we saw a lone old man sitting by the gulf (of mexico) playing his trumpet. Where else would you see that! We visited the Necropolis Cristobal Colon, which was basically a cemetery housing some of the most ridiculously pimped up grave mounments on the face of this earth. Very impressive, but not really my cup of tea!&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;For lunch, we had the blandest spaghetti we have ever tasted in our lives. We were totally baffled.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;We then visited the grand Hotel Nacional before having our last supper in Cuba in our favourite pizza restaurant in town, El Prado y El Neptuno. (They do surprisingly nice pizzas!) The waiters were all dressed up as clowns, pirates, arabs and china men as it was Christmas eve. Advertising was pretty much non-existent in Cuba, and we had almost forgotten about Christmas. It was really nice to see how Christmas in Cuba was one big fiesta and not a huge marketing campaign.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;We said goodbye to the casa family and got into a taxi to head to the airport. The radio was playing what must have been their christmas number one. It was a tune with a cheesy classical music intro, a pathetic reggaeton beat and a duet vocal by two old-sounding men. I had heard this tune many times before in La Habana. Previously I had been cursing this song, but this time I did not want the song to stop....&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;At the airport, we were brought back to reality by the rude staff.&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="http://photos1.blogger.com/blogger/4056/2046/1600/DSCF0023.jpg"&gt;&lt;img style="CURSOR: hand" alt="" src="http://photos1.blogger.com/blogger/4056/2046/320/DSCF0023.jpg" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;strong&gt;What do I think of Cuba?&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;I love the place for its natural beautiful. I love the people for their genuine friendliness, their laid-back attitude, their passion to express themselves through chit-chats, through music, through dancing. I look forward to going back to learn more about the country, to climb the mountains in Vinales and to learn how to dance like los cubanos.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;Politics is a funny topic. Even though most people seem to be pretty happy to discuss socio-political issues in private, there is a huge amount of information control and propaganda going on. Even travelling to other countries have been made difficult by the State. After 47 years of revolution, Fidel's view does not seem to have changed a bit. (With all the brainwashing, people seem surprisingly clued up about what is going on outside Cuba. I am sure Fidel would take credit for having created an excellent educational system :-) ) On the other hand, I am glad to have seen a working socialistic system where people are generally quite happy, well-cared and proud, where life is not just about earning the most money, where there is comparatively little visible poverty. (Incidentally, struggling to survive after the collapse of USSR, the Cuban government have had to legalise the dollars, effectively creating a two-tier economy, where people who have access to the dollars, such as people in the tourist industry e.g. taxi drivers, guides, restaurant workers, prostitutes, are much richer than those who don't.) Can true socialism ever lead to democracy, or even, can true democracy ever lead to socialism? What is just anyway? The freedom to speak against the State? The freedom to not have to starve? The freedom to write this very paragraph I am writing now? The freedom to visit Cuba (which incidentally is illegal for Americans)? My experience in Cuba has left me asking a lot of questions, most of which I do not have answers for. There is one thing I can say for sure though. Cuba is going to undergo a dramatic political shift after Fidel, and Cuba will never be the same again. Viva Cuba!&lt;/span&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/20427072-113620443175477787?l=seewah.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seewah.blogspot.com/feeds/113620443175477787/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=20427072&amp;postID=113620443175477787' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/113620443175477787'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/20427072/posts/default/113620443175477787'/><link rel='alternate' type='text/html' href='http://seewah.blogspot.com/2006/01/cuban-diary.html' title='Cuban Diary'/><author><name>See Wah Cheng</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry></feed>
