<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>gerard.au</title>
    <link>https://gerard.au/</link>
    <description>Recent content on gerard.au</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Fri, 22 Aug 2025 00:00:00 +0000</lastBuildDate>
    
	<atom:link href="https://gerard.au/index.xml" rel="self" type="application/rss+xml" />
    
    
    
    <item>
      <title>Magic Mirror Update - Tram Tracker</title>
      <link>https://gerard.au/2025-08-22_tram_tracker/</link>
      <pubDate>Fri, 22 Aug 2025 00:00:00 +0000</pubDate>
      
      <guid>https://gerard.au/2025-08-22_tram_tracker/</guid>
      <description>&lt;p&gt;As an extension to my &lt;a href=&#34;https://gerard.au/mirror-mirror-on-the-wall/&#34;&gt;MagicMirror&lt;/a&gt; project, I decided to
add a tram tracker to lazily assist my communte to the nearest train station.
Years ago, I had a &lt;a href=&#34;https://gerard.au/2018-01-14_it-s-about-the-little--internet-of--things-fba724e2124a/&#34;&gt;LIFX globe&lt;/a&gt; that would flash when the tram was about to arrive, but
I haven&amp;rsquo;t used it in a while. I did however, still have the code floating around on my private Bitbucket instance. The finest Node 6.10 code around.&lt;/p&gt;
&lt;p&gt;The code for the light was based off the excellent, and apparently still running &lt;a href=&#34;https://ws.tramtracker.com.au/pidsservice/pids.asmx&#34;&gt;TramTracker SOAP&lt;/a&gt; service.
Even way back in 2018 when I put this together, it was a bit of an oddity to use SOAP from a Node application, but with a few upgrades here and there to
the various libraries, the thing roared back to life. I have no idea how I got a license key for this service originally, but it&amp;rsquo;s still up, and it&amp;rsquo;s still
mostly working. You can&amp;rsquo;t seem to get the type of Tram any more from it (booooooo), but the arrival times are still there.&lt;/p&gt;
&lt;p&gt;I ended up using &lt;a href=&#34;https://github.com/Dennis-Rosenbaum/MMM-Template&#34;&gt;Dennis Rosenbaum&amp;rsquo;s&lt;/a&gt; excellent MagicMirror template to load my new module up into
MagicMirror. Effectively, the module has code which runs on the frontend, and then a mechanism to have code run on the backend (see node_helper.js),
with an event-based mechanism for communication between them. If the frontend reloads, or a 30 second timer fired on the backend, then
the new arrival information is sent down to the display.&lt;/p&gt;
&lt;p&gt;The finished product looks like this. Nice and simple, with terrible colours and alignment, but I sort of love it.
Even has the old &lt;a href=&#34;https://en.wikipedia.org/wiki/Metropolitan_Transit_Authority_(Victoria)&#34;&gt;Met logo&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;./images/magic_mirror_tram_display.png&#34;
    alt=&#34;MagicMirror Tram Arrival Plugin&#34;&gt;&lt;figcaption&gt;
      &lt;h4&gt;MagicMirror Tram Arrival Plugin&lt;/h4&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

</description>
    </item>
    
    
    
    <item>
      <title>Mirror Mirror on the Wall</title>
      <link>https://gerard.au/mirror-mirror-on-the-wall/</link>
      <pubDate>Sun, 16 Mar 2025 00:00:00 +0000</pubDate>
      
      <guid>https://gerard.au/mirror-mirror-on-the-wall/</guid>
      <description>&lt;p&gt;With a young family, there is a lot of stuff going on at home.
School, Kinder, work, various appointments, social outings.
My wife and I run a shared calendar on Google, which is handy to have on our phones,
but difficult to share with others, especially the kids.
She really liked the look of the &lt;a href=&#34;https://au.skylightframe.com/products/the-skylight-calendar-15/&#34;&gt;SkyCalendar&lt;/a&gt;, but it&amp;rsquo;s a bit expensive
for a single use device (ie, a Calendar), and it also includes a monthly subscription fee for the majority of the features.
So, what could I cobble together for a bit less?&lt;/p&gt;
&lt;h1 id=&#34;the-hardware&#34;&gt;The Hardware&lt;/h1&gt;
&lt;p&gt;I started off looking for some kind of large Android tablet as a dashboard display. Unfortunately in that 11inch+ size, the prices really start to escalate. What you can get,
which are reasonably priced, are these small, generic, high resolution portable monitors from &lt;a href=&#34;https://www.alibaba.com/product-detail/Arzopa-15-6-Inch-Portable-Monitor-1600287592806.html&#34;&gt;Alibaba&lt;/a&gt;
or &lt;a href=&#34;https://www.amazon.com.au/dp/B0DD6KDPKQ?ref=ppx_yo2ov_dt_b_fed_asin_title&#34;&gt;Amazon&lt;/a&gt;.
It&amp;rsquo;s a 15.6 inch 1080P FHD Touch Monitor, with either USB-C or HDMI input.
The screen itself is surprisingly bright on the highest setting, and definitely sharp enough for our needs.
I&amp;rsquo;ve had an old Raspberry Pi 3 in the cupboard, which I decided would be perfect to drive it, but it only has HDMI out.
You are supposed to be able to get the touch functionality working with a separate USB-A cable (which came in the box),
but I couldn&amp;rsquo;t get it working with Raspbian. So, no touch for the first iteration.&lt;/p&gt;
&lt;p&gt;I swung by Ikea, and picked up one of these &lt;a href=&#34;https://www.ikea.com/au/en/p/roedalm-frame-birch-effect-80550055/&#34;&gt;Roedalm Birch frames&lt;/a&gt; to mount the screen in,
which was roughly the right size.&lt;/p&gt;
&lt;h1 id=&#34;mvp-picture&#34;&gt;MVP Picture&lt;/h1&gt;
&lt;p&gt;I really ruined the matt board within the frame, hastily cutting it out with a pair of scissors. Also, the length of the cable adaptors didn&amp;rsquo;t align correctly by having the frame centred.
I have picked up a separate matt board, which I&amp;rsquo;ll cut with a bit more precision at a later date. MVP baby.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;./images/frame.png&#34;&gt;&lt;figcaption&gt;
      &lt;h4&gt;MagicMirror in Ikea Frame&lt;/h4&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h1 id=&#34;the-software&#34;&gt;The Software&lt;/h1&gt;
&lt;p&gt;I&amp;rsquo;ve had my eye on &lt;a href=&#34;https://magicmirror.builders/&#34;&gt;MagicMirror&lt;/a&gt; for ages,
and although initially targeted at dashboards behind mirrors, it can be used for any kind of interactive display.
I deployed it in Docker to my home server (an upcycled old MacBook Pro with a smashed display).
MagicMirror does come with a bunch of included widgets, but the power is in the enormous ecosystem of &lt;a href=&#34;https://kristjanesperanto.github.io/MagicMirror-3rd-Party-Modules/&#34;&gt;plugins&lt;/a&gt;.
Coming from the &lt;a href=&#34;https://homebridge.io/&#34;&gt;HomeBridge&lt;/a&gt; ecosystem, I found MagicMirror a bit more effort to get going. Plugins are effectively git cloned into a mounted directory (i&amp;rsquo;m running it in Docker on another machine), and then most plugins will have
a NodeJS build step as well. Once the plugin is installed, you can then drop it in your &lt;code&gt;config.json&lt;/code&gt; file, for it to appear on your dashboard. I ended up installing the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/MMRIZE/MMM-CalendarExt3Journal&#34;&gt;MMM-CalendarExt3Journal&lt;/a&gt;: The hero piece of the dashboard. Provides a weekly calendar view of the main calendar.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/MMRIZE/MMM-CalendarExt3Agenda&#34;&gt;MMM-CalendarExt3Agenda&lt;/a&gt;: Provides a breakdown of events for the day. Handing to have a list of tasks in a consumable format.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/kolbyjack/MMM-Wallpaper&#34;&gt;MMM-Wallpaper&lt;/a&gt;: For cycling through a selection of images. This plugin has a variety of different image sourcing options, but I ended up just pointing it to a local directory.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/Jopyth/MMM-Remote-Control&#34;&gt;MMM-Remote-Control&lt;/a&gt;: A plugin for remotely refreshing the dashboard. Perfect for the remotely managed raspberry pi.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/jasonyork/MMM-auto-refresh&#34;&gt;MMM-auto-refresh&lt;/a&gt;: A plugin for refreshing the display.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once you&amp;rsquo;ve got your widgets all connected, you then need to style it. There doesn&amp;rsquo;t appear to be the concept of themes within the core framework.
You&amp;rsquo;re effectively forced to break open your browsers dev tools, to work out the various CSS selectors to start styling your dashboard.
It&amp;rsquo;s certainly super customisable, but it is limited to how familiar you are with CSS. This is not for the feint hearted.&lt;/p&gt;
&lt;p&gt;What it looks like in the end. Don&amp;rsquo;t just my colour choices.
&lt;figure&gt;&lt;img src=&#34;./images/magic_mirror_example.png&#34;&gt;&lt;figcaption&gt;
      &lt;h4&gt;MagicMirror&lt;/h4&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Sanitised example of my configuration file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;config&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;address&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0.0.0.0&amp;#34;&lt;/span&gt;,	&lt;span style=&#34;color:#75715e&#34;&gt;// Address to listen on, can be:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;port&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;8080&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;basePath&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;ipWhitelist&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [],	&lt;span style=&#34;color:#75715e&#34;&gt;// Set [] to allow all IP addresses
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;useHttps&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;,			&lt;span style=&#34;color:#75715e&#34;&gt;// Support HTTPS or not, default &amp;#34;false&amp;#34; will use HTTP
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;httpsPrivateKey&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,	&lt;span style=&#34;color:#75715e&#34;&gt;// HTTPS private key path, only require when useHttps is true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;httpsCertificate&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,	&lt;span style=&#34;color:#75715e&#34;&gt;// HTTPS Certificate path, only require when useHttps is true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;language&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;en&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;locale&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;en-US&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;logLevel&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;INFO&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;LOG&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;WARN&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ERROR&amp;#34;&lt;/span&gt;], &lt;span style=&#34;color:#75715e&#34;&gt;// Add &amp;#34;DEBUG&amp;#34; for even more logging
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;timeFormat&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;24&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;units&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;metric&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;modules&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;module&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;MMM-auto-refresh&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;config&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;refreshInterval&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1800000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;module&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;clock&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;position&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;top_left&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// This can be any of the regions.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;config&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;timeFormat&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;12&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;displaySeconds&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;module&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;calendar&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;config&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;calendars&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#a6e22e&#34;&gt;broadcastPastEvents&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#a6e22e&#34;&gt;color&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;green&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#a6e22e&#34;&gt;fetchInterval&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;60&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#a6e22e&#34;&gt;maximumEntries&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://calendar.google.com/calendar/ical/XXXX&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// Shared Google Calender
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#a6e22e&#34;&gt;broadcastPastEvents&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;uniforms&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#a6e22e&#34;&gt;color&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;blue&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#a6e22e&#34;&gt;fetchInterval&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;24&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;60&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;60&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#a6e22e&#34;&gt;maximumEntries&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://calendar.google.com/calendar/ical/XXXXX&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// School Uniform Calendar
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#a6e22e&#34;&gt;broadcastPastEvents&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;food&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#a6e22e&#34;&gt;color&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;yellow&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#a6e22e&#34;&gt;symbol&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utensils&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#a6e22e&#34;&gt;fetchInterval&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;60&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#a6e22e&#34;&gt;maximumEntries&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://192.168.1.193:13000/calendar&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// Meal Plan generated from Apple Note
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;module&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;MMM-CalendarExt3Agenda&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;position&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;top_right&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;config&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;endDayIndex&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;showMiniMonthCalendar&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;module&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;MMM-CalendarExt3Journal&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;position&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;bottom_bar&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;config&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;height&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;68vh&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// Half of the screen
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;hourLength&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;16&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// Display 12 hours
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;beginHour&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// From 7 o&amp;#39;clock
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;days&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// For 7 days,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;locale&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;en-AU&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;calendarSet&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;family&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;uniforms&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;food&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;module&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;MMM-Wallpaper&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;position&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fullscreen_below&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;config&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;source&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;local:/pictures&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;slideInterval&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;60&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1000&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// Change slides every minute
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;module&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;MMM-Remote-Control&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;config&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;customCommand&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {},  &lt;span style=&#34;color:#75715e&#34;&gt;// Optional, See &amp;#34;Using Custom Commands&amp;#34; below
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;showModuleApiMenu&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// Optional, Enable the Module Controls menu
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;secureEndpoints&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// Optional, See API/README.md
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;/*************** DO NOT EDIT THE LINE BELOW ***************/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;typeof&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;module&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;undefined&amp;#34;&lt;/span&gt;) { &lt;span style=&#34;color:#a6e22e&#34;&gt;module&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;exports&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;config&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;meal-planning-with-apple-notes&#34;&gt;Meal Planning with Apple Notes&lt;/h1&gt;
&lt;p&gt;My wife&amp;rsquo;s other main requirements, was being able to show what the week&amp;rsquo;s meal plan situation looks like. I do a lot of the supermarket shopping and cooking, but it&amp;rsquo;s important to have visibility of what we&amp;rsquo;re having across the week. A meal plan is something that doesn&amp;rsquo;t naturally fit very well within a calendar, and is something I usually put together in Apple Notes. So, how do I get my Apple Notes based data onto my MagicMirror? I was initially fretting about having to write some kind of iOS based application, to interact with Notes.&lt;/p&gt;
&lt;p&gt;After going down a rabbit hole, Notes doesn&amp;rsquo;t appear to be accessible via 3rd party apps. However, what you can integrate with is &lt;a href=&#34;https://support.apple.com/en-au/guide/shortcuts/welcome/ios&#34;&gt;iOS&amp;rsquo;s Shortcuts&lt;/a&gt;. You can use it to run a  small script after the application is minimised. What I&amp;rsquo;ve done, is effectively extract the contents of the &lt;code&gt;Dinner&lt;/code&gt; note every time the application is closed (and I&amp;rsquo;m on my home network), and make a HTTP Post call to a specific endpoint. I&amp;rsquo;m amazed that this something Apple supports out of the box.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img src=&#34;./images/dinner_selection.png&#34; width=&#34;200px&#34;&gt;&lt;figcaption&gt;
      &lt;h4&gt;Dinner Selection Note&lt;/h4&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;&lt;img src=&#34;./images/shortcuts.png&#34; width=&#34;200px&#34;&gt;&lt;figcaption&gt;
      &lt;h4&gt;Shortcuts Example&lt;/h4&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Then, mainly using Github Copilot, I wrote a small application which parses the Monday-Sunday header separated Notes file, and then serves the data within the ICS Calendar format, so the standard Calendar module can parse the data. The results?&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;./images/meal_planning_example.png&#34; width=&#34;200px&#34;&gt;&lt;figcaption&gt;
      &lt;h4&gt;Meal Planning&lt;/h4&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&#34;turning-the-display-onoff&#34;&gt;Turning the Display On/Off&lt;/h2&gt;
&lt;p&gt;The power of home based automation comes when you can tie all your bits and bobs together.
I&amp;rsquo;m running a &lt;a href=&#34;https://www.zigbee2mqtt.io/&#34;&gt;Zigbee2MQTT&lt;/a&gt; with HomeBridge setup, for control predominately via Apple Homekit, with
complex automations handled by &lt;a href=&#34;https://nodered.org/&#34;&gt;NodeRed&lt;/a&gt;. I&amp;rsquo;ve got a couple of IR motion Zigbee sensors within the room (think &lt;a href=&#34;https://www.philips-hue.com/en-au/p/hue-motion-sensor/8719514342149&#34;&gt;Philips Hue Motion Sensor&lt;/a&gt;)
which coupled with NodeRed, allow you to run automations based on motion. With the motion data available, I&amp;rsquo;m able to then
power off the display when nobody is present within the room; saving power when nothing is actually looking at it.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;./images/nod_red_display_off.png&#34;&gt;&lt;figcaption&gt;
      &lt;h4&gt;NodeRed Display Control&lt;/h4&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I&amp;rsquo;ve loaded a plugin into NodeRed to allow it to trigger &lt;code&gt;shell&lt;/code&gt; scripts on the RaspberryPi, which then turn the display
on/off when called.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$1&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;off&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  wlr-randr --output HDMI-A-1 --off
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;elif&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$1&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;on&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  wlr-randr --output HDMI-A-1 --on --mode 1920x1080
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Invalid argument. Use &amp;#39;off&amp;#39; or &amp;#39;on&amp;#39;.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  exit &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;fin&#34;&gt;Fin&lt;/h1&gt;
&lt;p&gt;It&amp;rsquo;s been incredibly fun building something IoT based which is actually solving a problem for my family. So many moving parts, but I think the result is worth it in the end. I can&amp;rsquo;t wait to explore some additional plugins, to make the display even more useful. Onwards and upwards.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Frolicking with AWS Bedrock with Knowledge Bases</title>
      <link>https://gerard.au/frolicking-with-aws-bedrock-with-knowledge-bases/</link>
      <pubDate>Fri, 14 Feb 2025 00:00:00 +0000</pubDate>
      
      <guid>https://gerard.au/frolicking-with-aws-bedrock-with-knowledge-bases/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;./images/shutterstock_1948822981.webp&#34; alt=&#34;A bunch of files&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;frolicking-with-aws-bedrock-with-knowledge-bases&#34;&gt;Frolicking with AWS Bedrock with Knowledge Bases&lt;/h1&gt;
&lt;p&gt;I&amp;rsquo;ve been meaning to play with AWS &lt;a href=&#34;https://aws.amazon.com/bedrock/knowledge-bases/)&#34;&gt;Bedrock&amp;rsquo;s Knowledge Bases&lt;/a&gt; feature for a while now. The ability to load relevant organizational data, and have it available through an AWS-powered service, is very appealing. Many enterprise customers are reluctant to run anything more homegrown, like &lt;a href=&#34;https://github.com/ollama/ollama&#34;&gt;Ollama&lt;/a&gt;, but being able to point at an AWS service and say that it does a thing, is a far easier sell than spinning up something yourself, that you then need to maintain.&lt;/p&gt;
&lt;p&gt;For those using Bedrock for the first time, you actually need to request individual model access through the &lt;a href=&#34;https://docs.aws.amazon.com/bedrock/latest/userguide/getting-started.html#getting-started-model-access)&#34;&gt;Console&lt;/a&gt;, which confuses me; I prefer it when everything in AWS land just works out of the box. I understand why you might need to request access for a third-party model, but the Amazon ones should just work out of the box. Anyone who has tried to use a new AWS region knows what I’m talking about;  I’m looking at you &lt;code&gt;ap-southeast-4&lt;/code&gt;. Anyway, I&amp;rsquo;ve added the latest &lt;code&gt;Anthropic&lt;/code&gt; and the &lt;code&gt;Titan Text Embeddings V2&lt;/code&gt; (you&amp;rsquo;ll see why shortly) models in the screenshot below. Get yourself a cup of coffee, and hopefully, they&amp;rsquo;ll be enabled when you return.&lt;/p&gt;
&lt;p&gt;![][/images/model_list.webp]&lt;/p&gt;
&lt;p&gt;Once you&amp;rsquo;ve been granted keys to the kingdom, it&amp;rsquo;s time to look at the &lt;code&gt;Knowledge Bases&lt;/code&gt; feature. &lt;code&gt;Knowledge Bases&lt;/code&gt; have effectively two main purposes. Firstly, to ETL data from a data source (S3, WebCrawler, Custom API) into a Vector Store. Secondly, to surface that Vector Store data is part of a Retrieval-Augmented Generation (RAG) process when interacting with your targeted model.&lt;/p&gt;
&lt;p&gt;The RAG process helps to improve accuracy, and relevance of text returned by the model, by providing data that is (hopefully) factually accurate so it can provide a tailored response. Think of it like this; the underlying model knows a bunch of general facts that have been wired into it as part of its training, but it knows nothing about the specifics of your organisation. For example, your organisation might use specific acronyms for project names. By providing those acronym references as part of a RAG, the model will be able to respond with those org-specific acronyms.&lt;/p&gt;
&lt;p&gt;![][/images/provide_knowledge.webp]&lt;/p&gt;
&lt;p&gt;The AWS Console gives a great overview of the parts you need to initialize a Knowledge Base, but I prefer to assemble it myself via &lt;a href=&#34;https://aws.amazon.com/cdk/&#34;&gt;AWS’ Cloud Development Kit (CDK)&lt;/a&gt;. It gives you a greater appreciation for what actually is required to get it running, and doesn&amp;rsquo;t abstract away what it&amp;rsquo;s doing in the background. It also makes it much easier to spin up and down resources, which is a great way of reducing costs during development. For example, there is no point leaving things running overnight, when nobody is using them.&lt;/p&gt;
&lt;p&gt;The CDK constructs at the time of writing (2024-12-03), were only Level 1, meaning you need to hand-wire in all the dependencies yourself. Not only do you need to wire in the dependencies yourself, but you also need to provide a mechanism to load up the index into the OpenSearch Vector database. This was all getting a little too hard.&lt;/p&gt;
&lt;p&gt;![][cdk_example.webp]&lt;/p&gt;
&lt;p&gt;Luckily, &lt;a href=&#34;https://www.linkedin.com/in/lee-james-gilmore&#34;&gt;Lee James Gilmore&lt;/a&gt; has an &lt;a href=&#34;https://blog.serverlessadvocate.com/amazon-bedrock-knowledge-bases-with-private-data-7685d04ef396&#34;&gt;excellent post&lt;/a&gt; describing the use of Level 3 Construct from &lt;a href=&#34;https://github.com/awslabs/generative-ai-cdk-constructs/blob/main/src/cdk-lib/bedrock/README.md?source=post_page-----7685d04ef396--------------------------------#knowledge-bases)&#34;&gt;AWS Labs&lt;/a&gt;. No more crying in CDK, and the ability to deploy and destroy a Knowledge Base stack, relatively easily. Although it does hide away some of what it’s doing with the token parsing, it’s all there on your machine for you to look at, instead of hiding throughout the Console.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;import \* as s3 from &amp;#34;aws-cdk-lib/aws-s3&amp;#34;;  
import { bedrock } from &amp;#34;@cdklabs/generative-ai-cdk-constructs&amp;#34;;

const kb \= new bedrock.KnowledgeBase(this, &amp;#34;KnowledgeBase&amp;#34;, {  
embeddingsModel: bedrock.BedrockFoundationModel.TITAN\_EMBED\_TEXT\_V2\_1024,  
instruction: &amp;#34;Use this knowledge base to answer questions about books. &amp;#34; \+ &amp;#34;It contains the full text of novels.&amp;#34;,  
});

const docBucket \= new s3.Bucket(this, &amp;#34;DocBucket&amp;#34;);

new bedrock.S3DataSource(this, &amp;#34;DataSource&amp;#34;, {  
bucket: docBucket,  
knowledgeBase: kb,  
dataSourceName: &amp;#34;books&amp;#34;,  
chunkingStrategy: bedrock.ChunkingStrategy.fixedSize({  
maxTokens: 500,  
overlapPercentage: 20,  
}),  
});

	const  crawlDataSource \= new bedrock.WebCrawlerDataSource(this, &amp;#34;IpponWebsite&amp;#34;, {  
  	knowledgeBase: kb,  
  	crawlingRate: 10,  
  	sourceUrls: \[&amp;#34;https://www.ipponaustralia.com.au/&amp;#34;\],  
  	chunkingStrategy: bedrock.ChunkingStrategy.fixedSize({  
    	maxTokens: 500,  
    	overlapPercentage: 20,  
  	}),  
	})  
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You&amp;rsquo;ll notice a couple of things with this CDK snippet:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I&amp;rsquo;ve added the &lt;code&gt;TITAN\_EMBED\_TEXT\_V2\_1024&lt;/code&gt; as the embedding model. This is used as part of the data parsing process, and must be enabled via the model selector.&lt;/li&gt;
&lt;li&gt;There are two data sources for our knowledge base. There is an S3 and amazingly an inbuilt Web-Crawler option.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once you&amp;rsquo;ve deployed your Knowledge Base, you can load some files into your target S3 bucket, for your Knowledge Base. I uploaded some PDFs of our fancy new practice decks to see how it would go with those. It supports a wide range of other &lt;a href=&#34;https://docs.aws.amazon.com/bedrock/latest/userguide/knowledge-base-ds.html#kb-ds-supported-doc-formats-limits&#34;&gt;document formats&lt;/a&gt;, but would suggest probably using something that isn’t a PDF in more of a production setting.&lt;/p&gt;
&lt;p&gt;Data is not automatically pulled into your &lt;code&gt;Knowledge Base&lt;/code&gt;, you need to &lt;code&gt;sync&lt;/code&gt; it. The Console or the CLI can do that for you.&lt;br&gt;
![][images/data_source.webp]&lt;/p&gt;
&lt;p&gt;Once the data has been loaded, you can test the model via the Console Test Knowledge Base feature, once you specify a model.&lt;/p&gt;
&lt;p&gt;![][images/chat_example.webp]&lt;/p&gt;
&lt;p&gt;Then, ask away via the Chatbot interface. You can see it pulling information from the documents we&amp;rsquo;ve loaded.&lt;/p&gt;
&lt;p&gt;![][images/ippon_chat_example.webp]&lt;br&gt;
The test interface is good for getting a feel for the kind of information it can extract. If you turn off the &lt;code&gt;Generate Responses&lt;/code&gt; toggle, you can see the kinds of information extracted from the Vector Search database. The Vector Search db contains effectively snippets of text, which is then passed on to the LLM. The formatting in my PDFs was a little off (I printed them from Google Slides), but I expect it  would be better with a text-first medium such as Confluence. It was easy to have it give long-winded answers, suggesting it needed help with the formatting. Simple questions, like &lt;code&gt;How many people work at Ippon?&lt;/code&gt;, could be easily extracted.&lt;/p&gt;
&lt;p&gt;![][images/generate_responses.webp]&lt;/p&gt;
&lt;h1 id=&#34;but-how-do-i-use-it&#34;&gt;But how do I use it?&lt;/h1&gt;
&lt;p&gt;Although logging onto the Console is super fun, chat-based interfaces need to be accessible to be useful. Slack is a great avenue for that integration, and there is no shortage of tutorials from AWS themselves on integrating &lt;a href=&#34;https://aws.amazon.com/blogs/machine-learning/deploy-a-slack-gateway-for-amazon-bedrock/%20and%20https://github.com/aws-samples/amazon-bedrock-knowledgebase-slackbot/tree/main&#34;&gt;Slack Chatbots&lt;/a&gt;. But what if we got &lt;code&gt;Claude 3.5 Sonnet&lt;/code&gt; to &amp;ldquo;write&amp;rdquo; a quick and dirty Lambda function for us?&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the final prompt I ended up with.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Write me a NodeJS AWS Lambda function to handle Slack messages. Have it access all Secrets via the AWS Secret Manager and verify that the inbound Slack request is valid. Once it receives a message, it sends it to AWS Bedrock, using the `Claude 3.5` model, and using a Knowledge base called: XXXX KnowledgeBaseId.  
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;![][images/generate_node_program.webp]&lt;/p&gt;
&lt;p&gt;Although the code it generated looked plausibly correct at first glance, it had some issues.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The first version didn’t validate the inbound Slack token at all (it just accepted it) and ignored the outbound token. Correcting it via prompts made it fix the issue.&lt;/li&gt;
&lt;li&gt;It mixed up the body parsing of incoming requests. Instead of parsing the JSON body, it tried to parse it as a query string.&lt;/li&gt;
&lt;li&gt;The expected body from Slack wasn’t exactly what it was expecting so that needed to be massaged.&lt;/li&gt;
&lt;li&gt;It defaulted to using an older NodeJS version and AWS SDK, which won’t do.&lt;/li&gt;
&lt;li&gt;Slack gets unhappy if you don’t respond within 3 seconds to their requests. Due to startup speeds, it’s sometimes unable to respond in time. I ended up using &lt;a href=&#34;https://docs.aws.amazon.com/lambda/latest/dg/config-rs-write-functions.html&#34;&gt;response streaming&lt;/a&gt; and swapping out the implementation suggested from the model.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I hammered it into something that looks like the following (⅔ written by AI, with a lot of debugging by me):&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;const { SecretsManagerClient, GetSecretValueCommand } \= require(&amp;#34;@aws-sdk/client-secrets-manager&amp;#34;);  
const { RetrieveAndGenerateCommand, BedrockAgentRuntimeClient } \= require(&amp;#39;@aws-sdk/client-bedrock-agent-runtime&amp;#39;)  
const crypto \= require(&amp;#39;crypto&amp;#39;);

const secretsManager \= new SecretsManagerClient({ region: &amp;#34;us-east-1&amp;#34; }); // Replace with your region  
const bedrockRuntime \= new BedrockAgentRuntimeClient({ region: &amp;#34;us-east-1&amp;#34; }); // Replace with your region

let SLACK\_SIGNING\_SECRET;  
let SLACK\_BOT\_TOKEN;

async function getSecrets() {  
if(SLACK\_SIGNING\_SECRET &amp;amp;&amp;amp; SLACK\_BOT\_TOKEN) {  
return;  
}  
try {  
const secretName \= &amp;#34;slack-bedrock-secrets&amp;#34;;  
const response \= await secretsManager.send(new GetSecretValueCommand({ SecretId: secretName }));  
const secrets \= JSON.parse(response.SecretString);  
SLACK\_SIGNING\_SECRET \= secrets.SLACK\_SIGNING\_SECRET;  
SLACK\_BOT\_TOKEN \= secrets.SLACK\_BOT\_TOKEN;  
} catch (error) {  
console.error(&amp;#34;Error retrieving secrets:&amp;#34;, error);  
throw error;  
}  
}

function verifySlackRequest(event) {  
const slackSignature \= event.headers\[&amp;#39;x-slack-signature&amp;#39;\];  
const timestamp \= event.headers\[&amp;#39;x-slack-request-timestamp&amp;#39;\];  
const body \= event.body;

const baseString \= `v0:${timestamp}:${body}`;  
const hmac \= crypto.createHmac(&amp;#39;sha256&amp;#39;, SLACK\_SIGNING\_SECRET);  
const computedSignature \= `v0=${hmac.update(baseString).digest(&amp;#39;hex&amp;#39;)}`;

return crypto.timingSafeEqual(Buffer.from(computedSignature), Buffer.from(slackSignature));  
}

async function invokeBedrockModel(prompt) {  
const retrieveAndGen \= new RetrieveAndGenerateCommand({  
input: { text: prompt },  
retrieveAndGenerateConfiguration: {  
type: &amp;#34;KNOWLEDGE\_BASE&amp;#34;,  
knowledgeBaseConfiguration: {  
knowledgeBaseId: process.env.KNOWLEDGE\_BASE\_ID,  
modelArn: process.env.MODEL\_ARN,  
}  
}  
})

try {  
const response \= await bedrockRuntime.send(retrieveAndGen);  
return response.output.text  
} catch (error) {  
console.error(&amp;#34;Error invoking Bedrock model:&amp;#34;, error);  
throw error;  
}  
}

async function respondToSlack(channel, text) {  
console.log(&amp;#34;Responding to slack: &amp;#34;, channel, &amp;#34;, text:&amp;#34;, text)  
const response \= await fetch(&amp;#39;https://slack.com/api/chat.postMessage&amp;#39;, {  
method: &amp;#39;POST&amp;#39;,  
headers: {  
&amp;#39;Authorization&amp;#39;: `Bearer ${SLACK\_BOT\_TOKEN}`,  
&amp;#39;Content-Type&amp;#39;: &amp;#39;application/json&amp;#39;  
},  
body: JSON.stringify({ channel, text })  
});

if (\!response.ok) {  
throw new Error(`Failed to send message to Slack: ${response.statusText}`);  
}  
}

function writeStreamResponse(responseStream, statusCode, body) {  
const httpResponseMetadata \= {  
statusCode: statusCode,  
headers: {  
},  
};  
responseStream \= awslambda.HttpResponseStream.from(  
responseStream,  
httpResponseMetadata  
);  
responseStream.write(body);  
responseStream.end();  
}

exports.handler \= awslambda.streamifyResponse(async (event, responseStream, \_context) \=\&amp;gt; {  
try {  
await getSecrets();

	if (\!verifySlackRequest(event)) {  
  	writeStreamResponse(responseStream, 401, &amp;#34;Unauthorized&amp;#34; )  
  	return {};  
	}

	const body \= JSON.parse(event.body);  
	console.log(&amp;#34;Request: &amp;#34;, body)  
	const {channel, text, bot\_profile} \= body.event;

	if (body.type \=== &amp;#39;url\_verification&amp;#39;) {  
  	writeStreamResponse(responseStream, 200, body.challenge )  
  	return {};  
	}  
	// This check is a big hamfisted. Ideally, it should check if the message is coming from the bot, and then ignore  
	// but this works for a PoC.  
	if (body.type \=== &amp;#39;event\_callback&amp;#39; &amp;amp;&amp;amp; body.event.type \=== &amp;#39;message&amp;#39; &amp;amp;&amp;amp; \!bot\_profile) {  
  	console.log(&amp;#34;\[Function\] Returning response to client&amp;#34;);  
  	writeStreamResponse(responseStream, 200, {})

  	const aiResponse \= await invokeBedrockModel(text);  
  	console.log(&amp;#34;Generated response: &amp;#34;, aiResponse)  
  	await respondToSlack(channel, aiResponse);  
	}  
	return {};  
} catch (error) {  
console.error(&amp;#34;Error processing request:&amp;#34;, error);  
console.dir(error);  
writeStreamResponse(responseStream, 500,  &amp;#34;Internal Server Error&amp;#34;)  
return {};  
}  
});  
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is an incredibly simple request/response demo and doesn’t deal with any of the standard conversational features you’d need for an actual working chatbot. It doesn’t maintain any context between the Slack conversation and the Claude calls, so it effectively “forgets” what you’ve asked. You’d definitely need to look into storing that data in something like DynamoDB to maintain the context between event calls from Slack. For a simple question/answer approach though, it works well. At one point, I got it stuck in a loop while it was responding to itself. Watch it burn those tokens (Don’t tell Duncan).&lt;/p&gt;
&lt;p&gt;![][bot_error.webp]&lt;/p&gt;
&lt;p&gt;Actual success with the code above! I generally will try to iterate on the code within the LLM chat for as long as possible, because it makes it much harder to partially debug portions of it if you’re constantly needing to manually merge any changes you make with the LLM output.&lt;br&gt;
![][images/sucess_message.webp]&lt;/p&gt;
&lt;h1 id=&#34;in-conclusion&#34;&gt;In Conclusion&lt;/h1&gt;
&lt;p&gt;Once you get your head around the concepts, and the various parts that need to be glued together, the AWS Bedrock offering, with Knowledge Base, is an incredibly interesting proposition, and a great way to start integrating these very powerful tools into your products. As with a lot of these AI offerings, it’s important not to slap it on everything just to see what sticks. Have fun, and stay safe out there.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>OpenTelemetry and Friends @ Programmable</title>
      <link>https://gerard.au/opentelemetry-at-programmable/</link>
      <pubDate>Fri, 03 Mar 2023 02:01:02 +0000</pubDate>
      
      <guid>https://gerard.au/opentelemetry-at-programmable/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;./images/programmable.jpeg&#34; alt=&#34;Presenter image&#34;&gt;&lt;/p&gt;
&lt;p&gt;I was lucky enough to present at &lt;a href=&#34;https://www.programmable.tech/&#34;&gt;Programmable&lt;/a&gt;, the Australian software engineering conference on &lt;a href=&#34;https://opentelemetry.io/&#34;&gt;OpenTelemetry&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/otel_intro_slide.png&#34; alt=&#34;Slide image&#34;&gt;
I&amp;rsquo;ve uploaded a copy of my presentation &lt;a href=&#34;./pdf/OpenTelemetry_and_Friends_Presentation.pdf&#34;&gt;here&lt;/a&gt;, as well as the &lt;a href=&#34;https://github.com/ggotti/open_telemetry_and_friends&#34;&gt;demo source code&lt;/a&gt; for the Microservices example. As you can see, my frontend development skills are top notch.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>OpenTelemetry and Friends</title>
      <link>https://gerard.au/opentelemetry-and-friends/</link>
      <pubDate>Tue, 15 Nov 2022 14:01:02 +0000</pubDate>
      
      <guid>https://gerard.au/opentelemetry-and-friends/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;./images/opentelemetry_octopus_crowd.png&#34; alt=&#34;octopus image&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;what-is-opentelemetry&#34;&gt;What is OpenTelemetry&lt;/h1&gt;
&lt;p&gt;OpenTelemetry (OTel) is an open-source initiative to provide a standardised approach for the capture and distribution of metrics, trace and log data from applications. It defines not just APIs and schemas, but also a set of standard vendor-neutral SDKs and monitoring agents to facilitate collection and exports.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://opentelemetry.io/docs/&#34;&gt;documentation&lt;/a&gt; itself is very detailed, if a little overwhelming in parts. The core to delivering applications that utilise OTel is the OpenTelemetry Collector. The OTel Collector is a small standalone executable that can be run as a sidecar in a container environment, or as a layer if used with something like AWS Lambda.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/opentelemetry_collector_diagram.png&#34; alt=&#34;x-ray-diagram&#34;&gt;&lt;/p&gt;
&lt;p&gt;The OTel Collector, at a high level, can be divided into &lt;a href=&#34;https://opentelemetry.io/docs/collector/configuration/&#34;&gt;three main parts&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Receivers: Can, via endpoints, receive data from applications, for example using &lt;code&gt;otlpreceiver&lt;/code&gt;, or they can be configured in a pull mode to scrape data from an application, for example using the &lt;code&gt;prometheusreceiver&lt;/code&gt; receiver. I&lt;/li&gt;
&lt;li&gt;Processors: Can bundle/modify the metric/trace/log data as it comes through. The common &lt;code&gt;batchprocessor&lt;/code&gt; groups and compresses data together to reduce load on exporters.&lt;/li&gt;
&lt;li&gt;Exporters: Exporters take the metric/trace/log data and export it to a downstream metric/trace/log solution. So for example, you might want to have trace data analysed with
AWS X-ray, but have log data go to Splunk. In the diagram above, we have metric data going to AWS CloudWatch, using the &lt;code&gt;awsemf&lt;/code&gt; exporter.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;arise-the-otel-distro&#34;&gt;Arise the OTel Distro&lt;/h1&gt;
&lt;p&gt;The OTel project released their own standard collector, which includes a bunch of sensible default receivers and exporters. However, what appears to be have happened is that a number of vendors have released expanded distributions of the collector, with additional bundled receivers and exporters to align with their target products. For example AWS has their &lt;a href=&#34;https://github.com/aws-observability/aws-otel-collector&#34;&gt;AWS Distro For Open Telemetry&lt;/a&gt; which includes receivers for various AWS container technologies, and exporters for X-ray and CloudWatch. SumoLogic &lt;a href=&#34;https://github.com/SumoLogic/sumologic-otel-collector&#34;&gt;has a distribution&lt;/a&gt; which supports exporting to SumoLogic (for both metrics and tracing). These specific distros are great for getting going quickly within a given ecosystem, but can cause issues if you want to route data to multiple providers.&lt;/p&gt;
&lt;p&gt;Other providers, such as &lt;a href=&#34;https://docs.newrelic.com/docs/more-integrations/open-source-telemetry-integrations/opentelemetry/opentelemetry-setup&#34;&gt;NewRelic&lt;/a&gt; and &lt;a href=&#34;https://docs.honeycomb.io/getting-data-in/opentelemetry-overview/#using-the-honeycomb-opentelemetry-endpoint&#34;&gt;Honeycomb.io&lt;/a&gt; provide HTTP/gRPC endpoints that can directly receive data from the standard &lt;code&gt;otlpexporter&lt;/code&gt; exporter, so you can just use the standard collector.&lt;/p&gt;
&lt;h1 id=&#34;agents-sdks-existing-libraries-oh-my&#34;&gt;Agents, SDKs, Existing Libraries Oh-My&lt;/h1&gt;
&lt;p&gt;Now that you understand how the collector works, the next step is getting your valuable application data into the collector. This is going to very much depend on the language your application is written in. In Java land, besides a manual SDK, there is support for an &lt;a href=&#34;https://opentelemetry.io/docs/instrumentation/java/automatic/&#34;&gt;agent-based automatic connector&lt;/a&gt;, which out of the box has a lot of support for popular Java libraries. If you would rather not use an agent-based approach, the OpenTelemetry project has an alternative mechanism for injecting trace behaviour &lt;a href=&#34;https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/f1774cabe2153b7b118d3be664f0fa757b5e43d1/instrumentation/spring/spring-boot-autoconfigure/README.md&#34;&gt;specifically for SpringBoot&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;SpringBoot doesn&amp;rsquo;t yet support OpenTelemetry natively out of the box, but does support a number of &lt;a href=&#34;https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.metrics&#34;&gt;metric export mechanisms&lt;/a&gt;, including Prometheus. In this case, the collector can be configured with a &lt;code&gt;prometheus&lt;/code&gt; receiver on the collector, which will scrape metrics from SpringBoot at a defined interval.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re looking into Quarkus, it supports &lt;a href=&#34;https://quarkus.io/guides/opentelemetry&#34;&gt;only tracing&lt;/a&gt; currently.&lt;/p&gt;
&lt;h1 id=&#34;how-do-i-use-it-with-aws&#34;&gt;How do I use it with AWS?&lt;/h1&gt;
&lt;p&gt;AWS has some &lt;a href=&#34;https://aws-otel.github.io/docs/introduction&#34;&gt;detailed documentation&lt;/a&gt; around integrating with their various services, with &lt;a href=&#34;https://aws-otel.github.io/docs/components/x-ray-receiver&#34;&gt;X-Ray&lt;/a&gt; being the target for tracing information. A common deployment pattern is using the collector as an ECS side-car, which will accept tracing data generated from a primary app, and then diseminate it to X-Ray for evaluation.&lt;/p&gt;
&lt;p&gt;The following is a sample CDK pattern, which deploys a Quarkus app (as described in their documentation), into ECS.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-typescript&#34; data-lang=&#34;typescript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cdk&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;aws-cdk-lib&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;Construct&lt;/span&gt; } &lt;span style=&#34;color:#66d9ef&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;constructs&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; {&lt;span style=&#34;color:#a6e22e&#34;&gt;aws_ec2&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;aws_ecr_assets&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;aws_ecs&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;aws_ecs_patterns&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;aws_iam&lt;/span&gt;} &lt;span style=&#34;color:#66d9ef&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;aws-cdk-lib&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; {&lt;span style=&#34;color:#a6e22e&#34;&gt;DockerImageAsset&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;NetworkMode&lt;/span&gt;} &lt;span style=&#34;color:#66d9ef&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;aws-cdk-lib/aws-ecr-assets&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;path&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; {&lt;span style=&#34;color:#a6e22e&#34;&gt;ContainerImage&lt;/span&gt;} &lt;span style=&#34;color:#66d9ef&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;aws-cdk-lib/aws-ecs&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;OtelDemoStack&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;extends&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cdk&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Stack&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;constructor&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;scope&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;Construct&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;props?&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;cdk.StackProps&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;super&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;scope&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;props&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;defaultVPC&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;aws_ec2&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Vpc&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;fromLookup&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;ImportVPC&amp;#39;&lt;/span&gt;,{&lt;span style=&#34;color:#a6e22e&#34;&gt;isDefault&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cluster&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;aws_ecs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Cluster&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;OtelCluster&amp;#34;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;vpc&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;defaultVPC&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;dockerImageServiceB&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;DockerImageAsset&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;OtelBuildServiceB&amp;#39;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;directory&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;path.join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;__dirname&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;../&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;../&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;service_b&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;networkMode&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;NetworkMode.HOST&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;file&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;src/main/docker/Dockerfile.jvm&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;platform&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;aws_ecr_assets.Platform.LINUX_AMD64&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;ignoreMode&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;undefined&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;otelPolicy&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;aws_iam&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;PolicyDocument&lt;/span&gt;({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;statements&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [&lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;aws_iam&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;PolicyStatement&lt;/span&gt;({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;actions&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;logs:PutLogEvents&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;logs:CreateLogGroup&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;logs:CreateLogStream&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;logs:DescribeLogStreams&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;logs:DescribeLogGroups&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;xray:PutTraceSegments&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;xray:PutTelemetryRecords&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;xray:GetSamplingRules&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;xray:GetSamplingTargets&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;xray:GetSamplingStatisticSummaries&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;resources&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;*&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            })],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;taskRole&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;aws_iam&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Role&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Role&amp;#39;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;assumedBy&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;aws_iam&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ServicePrincipal&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;ecs-tasks.amazonaws.com&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;inlinePolicies&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;otelPolicy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fargateTaskDefinition&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;aws_ecs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;FargateTaskDefinition&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;TaskDef&amp;#39;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;memoryLimitMiB&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;512&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;cpu&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;256&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;taskRole&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;taskRole&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;webcontainer&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fargateTaskDefinition&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;addContainer&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;WebContainer&amp;#34;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;image&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;ContainerImage.fromDockerImageAsset&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;dockerImageServiceB&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;logging&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;aws_ecs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;AwsLogDriver&lt;/span&gt;({ &lt;span style=&#34;color:#a6e22e&#34;&gt;streamPrefix&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;ServiceB&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;mode&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;aws_ecs.AwsLogDriverMode.NON_BLOCKING&lt;/span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;webcontainer&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;addPortMappings&lt;/span&gt;({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;containerPort&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;8080&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;fargateTaskDefinition&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;addContainer&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Otel&amp;#34;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;image&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;aws_ecs.ContainerImage.fromRegistry&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;amazon/aws-otel-collector:latest&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;logging&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;aws_ecs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;AwsLogDriver&lt;/span&gt;({ &lt;span style=&#34;color:#a6e22e&#34;&gt;streamPrefix&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;ServiceBOtel&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;mode&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;aws_ecs.AwsLogDriverMode.NON_BLOCKING&lt;/span&gt; }),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;command&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;--config=/etc/ecs/ecs-default-config.yaml&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;--set=service.telemetry.logs.level=DEBUG&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// // Create a load-balanced Fargate service and make it public
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;serviceB&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;aws_ecs_patterns&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ApplicationLoadBalancedFargateService&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;SampleOtelService&amp;#34;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;cluster&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;cluster&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;cpu&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;512&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;desiredCount&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;taskDefinition&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;fargateTaskDefinition&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;assignPublicIp&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// this is purely to not require a NAT option.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;memoryLimitMiB&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;1024&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;publicLoadBalancer&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;healthCheckGracePeriod&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;cdk.Duration.seconds&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;dockerImageServiceB&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;repository&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;grantPull&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;serviceB&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;taskDefinition&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;obtainExecutionRole&lt;/span&gt;());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It&amp;rsquo;s important to note, that X-ray currently requires a specific time based ID format, which Quarkus discusses in &lt;a href=&#34;https://quarkus.io/guides/opentelemetry#id-generator&#34;&gt;this section&lt;/a&gt;. You effectively need to use the X-Ray IDGenerator, or nothing will appear in the X-ray console.&lt;/p&gt;
&lt;p&gt;The X-ray console looks like the following. This example demonstrates hitting a sample &lt;code&gt;/hello&lt;/code&gt; endpoint, via the deployed load-balancer, hitting the Quarkus application, and then making a &lt;code&gt;HTTP GET&lt;/code&gt; call to a Star Wars test endpoint: &lt;code&gt;https://www.swapi.tech/api/starships/3&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/opentelemetry_xray.png&#34; alt=&#34;x-ray-console&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;whywhen-would-i-use-it&#34;&gt;Why/When Would I Use It?&lt;/h1&gt;
&lt;p&gt;It&amp;rsquo;s a great way of integrating metric and trace monitoring into new applications, or applications that don&amp;rsquo;t have existing solutions in this space. I&amp;rsquo;ve excluded logging for the moment as the specification is still in a &lt;a href=&#34;https://opentelemetry.io/docs/reference/specification/status/&#34;&gt;draft status&lt;/a&gt; at the time of writing, but it is certainly an exciting development. This is a pretty fast moving space, which will mature and solidify over time, but it will be the way of integrating vendor-agnostic tracing/metric/logging functionality into your application moving forward.&lt;/p&gt;
&lt;h1 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;If you&amp;rsquo;re starting a new project, or retrofitting metric and tracing support to an existing application, defintiely look into OpenTelemetry. It provides a solid, standards compliant mechanism to surface application telemetry, and works well even if you haven&amp;rsquo;t yet decided on your downstream vendors as yet. Stop waiting and start exporting!&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Lambda Docker Adventures with SQLite</title>
      <link>https://gerard.au/2021-05-12_lambda-docker-adventures-with-sqlite-36cd6d718a56/</link>
      <pubDate>Wed, 12 May 2021 00:00:08 -1000</pubDate>
      
      <guid>https://gerard.au/2021-05-12_lambda-docker-adventures-with-sqlite-36cd6d718a56/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;./images/1*FceEy6cF7n_nl0pBX83grA.jpeg&#34; alt=&#34;Photo originally by Matthew
Sleeper, beautifully enhanced
by me.&#34;&gt;&lt;/p&gt;
&lt;p&gt;Last year I spent some time playing around using
&lt;a href=&#34;https://tegola.io/&#34;&gt;Tegola&lt;/a&gt; (a mapping tile generator) and AWS Lambda
to generate map tiles using Geospatial data stored in SQLite and &lt;a href=&#34;https://gerard.gigliotti.com.au/lambda-tile-generation-with-tegola-aabadb1a0c2e&#34;&gt;hosted
on Amazon
EFS.&lt;/a&gt;
There was a serious performance penalty reading large data files over
EFS, so I needed to make sure my database was as small as possible. In
addition, you pay for all that data that you’re transferring, so
although not expensive for a low volume site, you could end up in a
pickle if things pick up.&lt;/p&gt;
&lt;p&gt;To further that thread of SQLite data in Lambda, in December last year
as an early Christmas present AWS released &lt;a href=&#34;https://aws.amazon.com/blogs/aws/new-for-aws-lambda-container-image-support/&#34;&gt;Container
Image&lt;/a&gt;
support, a sort of half-way between a traditional Lambda Zip function
and a ECS-Fargate container. This allows you to generate a Docker image
locally, push it into the Elastic Container Registry (ECR), and then use
that as a Lambda function. Cool, but what really caught my eye was the
fact that it supports up to a 10Gig container image. So, was using
Docker+Lambda a better solution for read-only SQLite databases than
Lambda+EFS? I wanted to build out something practical to validate the
technique, so I decided on an address auto-complete endpoint, where it
turns free text into decomposed address data. For example, if you supply
“101 Coll” as text, I want it to return the decomposed address
information for “101 Collins Street”.&lt;/p&gt;
&lt;h3 id=&#34;g-naf-fun-withsqlite&#34;&gt;G-NAF Fun with SQLite&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*DyneXSsdU0hXPE5N.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;The data I was using last time originated from the &lt;a href=&#34;https://data.gov.au/dataset/ds-dga-19432f89-dc3a-4ef3-b943-5326ef1dbecc&#34;&gt;Australian
Government’s Geoscape Geocoded National Address
File&lt;/a&gt;
(G-NAF), which contains a record of every single physical address in
Australia. The data itself is provided in a pipe-seperated set of
normalized files, which are easily loadable into Postgres/Postgis with
the included set of table schemas; once you get around the table
structure.&lt;/p&gt;
&lt;p&gt;Now I wanted to load this address data into SQLite, so I needed to make
a couple of less-than-ideal modifications to the standard load process:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Schema Refactor&lt;/strong&gt;: SQLite needs its foreign keys defined upfront, so
I needed to modify the included G-NAF schema for that.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Null Imports&lt;/strong&gt;: This took me far longer than I care to admit to
fix(and I only identified it as an issue later), but there is
currently no mechanism to have SQLite’s default CSV import mechanism
to treat blank values as nulls. So my database import was originally
littered with empty char fields. I ended up using a blunt hammer to
update all empty strings to NULL through a set of SQL UPDATE commands;
not pretty, but it works. Also very slowwww.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The end product is this enormous Docker Image
(&lt;a href=&#34;https://hub.docker.com/repository/docker/ggotti/g-naf-full-sqlite&#34;&gt;https://hub.docker.com/repository/docker/ggotti/g-naf-full-sqlite&lt;/a&gt;),
based off &lt;a href=&#34;https://github.com/ggotti/g-naf-full-sqlite&#34;&gt;this Github&lt;/a&gt;
repository. It’s a great way of being able to quickly see what G-NAF has
to offer, without needing to fumble around with the schema.&lt;/p&gt;
&lt;h3 id=&#34;distilling-itdown&#34;&gt;Distilling it Down&lt;/h3&gt;
&lt;p&gt;With my frankenstein SQLite image, it was time to convert that into
something I could use in my Lambda function. I created a new SQLite
database, and then I materialised the “ADDRESS_VIEW” from the G-NAF
schema into a separate table in my new database, which is effectively a
denormalized view of the address data.&lt;/p&gt;
&lt;p&gt;The next step was to create a text representation of the address data.
The excellent team at ISP &lt;a href=&#34;https://superloop.com/&#34;&gt;Superloop&lt;/a&gt; have come
up with a &lt;a href=&#34;https://github.com/superloop-ltd/gnaf/blob/master/address_search.sql&#34;&gt;MySQL
version&lt;/a&gt;
of converting the data into text. Unfortunately, it makes extensive use
of `CONCAT_WS`, which again is something not supported by SQLite. What
I ended up doing was hand converting their statement into an absolutely
unmaintainable mess of a thing, which sort of replicates the
`CONCAT_WS` behaviour using syntax that’s available. It’s not 100%,
but it’s close enough. Look at this horrific mess. Warning. May Burn
your Eyes Out.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/1*IlNeNI1VLBPof609P-1uZA.png&#34; alt=&#34;Screenshot of code from
tablePopulator.sql&#34;&gt;&lt;/p&gt;
&lt;p&gt;AWS SAM supports the build and development of Docker Images as part of
the
&lt;a href=&#34;https://aws.amazon.com/blogs/compute/using-container-image-support-for-aws-lambda-with-aws-sam/&#34;&gt;CLI&lt;/a&gt;.
I ended up using that to build the project scaffold, and then introduced
a multi-step Docker Build process to pull in the SQLite image, generate
the distilled address database, and then copying the generated database
into the appropriate NodeJS Lambda docker image:
public.ecr.aws/lambda/nodejs:14. I was originally building this on my
2015 Macbook Pro, and it was seriously struggling with my grotesquely
unoptimised build process. I ended up switching it to an older Linux
desktop, but it didn’t struggle as much with the heavy disk IO.&lt;/p&gt;
&lt;p&gt;SAM itself is really getting better and better with every release. It’s
so quick to just spin up a new application. The only thing I had to do
outside of SAM was define the Elastic Container Repository (ECR), and
just feed those details into SAM. &lt;code&gt;sam build &amp;amp;&amp;amp; sam deploy&lt;/code&gt; and you’re
good to go.&lt;/p&gt;
&lt;h3 id=&#34;demo&#34;&gt;Demo&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;./images/1*qkMoaoGJrA8maZJTKUtdgw.gif&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;The Lambda application itself is pretty simple, with a small JavaScript
application connecting to SQLite and doing a LIKE search against one of
the tables. This could probably be improved with the FTS5 Full Text
Search extension, but LIKE works well enough for this demo. To demo it’s
usage, I put a simple web-page using
&lt;a href=&#34;https://tarekraafat.github.io/autoComplete.js/#/&#34;&gt;autoComplete.js&lt;/a&gt; to
hit the Lambda function as the user types. Give it a &lt;a href=&#34;https://ggotti.github.io/address-auto-complete/&#34;&gt;try
here&lt;/a&gt;, or watch the
above video.&lt;/p&gt;
&lt;p&gt;Source code can be found on &lt;a href=&#34;https://github.com/ggotti/address-auto-complete&#34;&gt;Github
here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;revisiting-tegola&#34;&gt;Revisiting Tegola&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;./images/1*HkmTBpWJ68HTpPT2v0NpzA.png&#34; alt=&#34;Tegola Generated
file example&#34;&gt;&lt;/p&gt;
&lt;p&gt;So the real question is, how does the Docker-Image approach work in
comparison to reading large SQLite databases off EFS? The results may
surprise you. I left the RAM the same (3008mb), and then ran a
comparison between the two, against the set of same Tagola coordinates;
a rather complex area of the Melbourne CBD. I manually adjusted the RAM
between requests, to guarantee a fresh instance of the function. The
Lambda first function was consistently taking &lt;strong&gt;25–26 seconds&lt;/strong&gt; to
process the request. Whereas the Docker image was taking between &lt;strong&gt;22–24
seconds&lt;/strong&gt; to process, when it worked. A large number of requests were
hitting the API Gateway’s &lt;strong&gt;30 second limiter&lt;/strong&gt;, and therefore timing
out. There seemed to be a slight delay when the Image was being pulled
for the first time, which may factor into the delays. I suspect behind
the scenes it does some caching between ECR and Lambda of the image
itself, independent of the actual Lambda execution “warmth” behaviour.
Increasing the amount of available RAM to 4GB seemed to improve
performance consistently.&lt;/p&gt;
&lt;p&gt;So there seems to be a slight increase in the RAM requirements for the
Docker image. However, the usability of the Docker format for pushing
data in, and the fact that the ECR data transfers within the same region
are free, make the Docker option pretty appealing.&lt;/p&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;So, can you use SQLite with Docker in Lambda? You can. Can you import
G-NAF data into a relatively compact SQLite database? You can. So is it
worth it in the end? The experience with SAM is pretty seamless, and
it’s a great way of pushing large read only data images around; less
than &lt;a href=&#34;https://aws.amazon.com/blogs/aws/new-for-aws-lambda-container-image-support/&#34;&gt;10GB
currently&lt;/a&gt;
supported. It certainly removes some of the complexity of managing the
data via EFS. Just be prepared to pay a little more on your execution
costs running it via Docker.&lt;/p&gt;
&lt;p&gt;P&lt;strong&gt;S&lt;/strong&gt;: If you were fully crazy you could probably build some kind of
updatable website where you have a separate Lambda function updating an
ECR image based off a singular write data fed in via an SQS queue.
Maybe.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Lambda Tile Generation with Tegola</title>
      <link>https://gerard.au/2020-10-27_lambda-tile-generation-with-tegola-aabadb1a0c2e/</link>
      <pubDate>Tue, 27 Oct 2020 00:00:08 -1000</pubDate>
      
      <guid>https://gerard.au/2020-10-27_lambda-tile-generation-with-tegola-aabadb1a0c2e/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;./images/1*72sKc5O3XygjRPbDZ9WpLQ.png&#34; alt=&#34;Multi Technology Mix (MTM) at its finest. OSM
Layer ©MapTile&#34;&gt;&lt;/p&gt;
&lt;p&gt;Inspired by the excellent &lt;a href=&#34;http://nbnmtm.australiaeast.cloudapp.azure.com/&#34;&gt;NBN MTM
Alpha&lt;/a&gt; I’ve been
recently playing around with the &lt;a href=&#34;https://data.gov.au/dataset/ds-dga-c79b2219-7e1f-46a9-961f-e87668122f02/details&#34;&gt;NBN connection
type&lt;/a&gt;
data released on &lt;a href=&#34;http://data.gov.au&#34;&gt;data.gov.au&lt;/a&gt; in July 2020.
Combining it with &lt;a href=&#34;https://data.gov.au/dataset/ds-dga-19432f89-dc3a-4ef3-b943-5326ef1dbecc/details&#34;&gt;Geocoded National Address
File&lt;/a&gt; ,
you can produce maps that surface address level connection details. For
NBN enthusiasts (such as myself), it’s pretty exciting, as the actual
NBN map is purposely evasive.&lt;/p&gt;
&lt;p&gt;So once you’ve loaded all that juicy data into
&lt;a href=&#34;https://postgis.net/&#34;&gt;PostGIS&lt;/a&gt; how do you get that information out? The
open source mapping space has changed significantly since I played
around with it in the early 2010s. Tile generation no longer revolves
around muttering incantations to summon life out of
&lt;a href=&#34;https://mapnik.org/&#34;&gt;Mapnik&lt;/a&gt; for raster tile generation. Now, there is
the &lt;a href=&#34;https://docs.mapbox.com/vector-tiles/specification/&#34;&gt;MapBox Vector
Specification&lt;/a&gt;
which allows you to serve piping hot geo data into the complementary
&lt;a href=&#34;https://docs.mapbox.com/mapbox-gl-js/api/&#34;&gt;Mapbox GL JS&lt;/a&gt; mapping
library. To serve vector tiles using the MapBox Vector Specification,
&lt;a href=&#34;https://tegola.io/&#34;&gt;Tegola&lt;/a&gt; caught my eye as a lightweight, well
supported application. Most excitingly, it supports Lambda based
generation.&lt;/p&gt;
&lt;h3 id=&#34;the-idea&#34;&gt;The Idea&lt;/h3&gt;
&lt;p&gt;The majority of articles (such as &lt;a href=&#34;https://medium.com/@alexrolek/running-a-serverless-vector-tile-backend-with-aws-lambda-and-go-8e6ad46a7c38&#34;&gt;Running a Serverless Vector Tile
Backend with AWS Lambda and
Go&lt;/a&gt;)
talk about running Tegola with a Postgres backend, and the associated
connection pool issues with that. If I was doing this at work, I’d be
looking at potentially an Amazon RDS Proxy with a smallish RDS instance.
Even on a db.t3.small RDS Postgres instance, you’d be looking at
&lt;a href=&#34;https://aws.amazon.com/rds/proxy/pricing/&#34;&gt;$25.92&lt;/a&gt; for the proxy, and
&lt;a href=&#34;https://aws.amazon.com/rds/postgresql/pricing/&#34;&gt;$40.32&lt;/a&gt; for the
database. That’s $66.24 USD a month, which is $93.15 Australian
Dollarydoos. That’s too much money. I could run a LightSail/Digital
Ocean instance, but I’d want to put a Load Balancer in front ($18 USD
for a LightSail LB). So how can we run it on Lambda, but not need a
database?&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*6wpHlj_EOT0O37y5&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Enter Lambda’s support for &lt;a href=&#34;https://aws.amazon.com/blogs/aws/new-a-shared-file-system-for-your-lambda-functions/&#34;&gt;Amazon Elastic File System
(EFS)&lt;/a&gt;
which was announced in June. No longer do you need to use nested layers,
or some S3 tomfoolery to load data into the 512-MB temporary storage
available to Lambda. So with an expandable filesystem, it really opens
up the possibilities for making larger files available to Lambda.&lt;/p&gt;
&lt;p&gt;Tegola primarily supported PostGIS, but it also supports
&lt;a href=&#34;https://www.geopackage.org/&#34;&gt;GeoPackage&lt;/a&gt;, an SQLite based standard for
storing geospatial data. Now, SQLite and NFS don’t &lt;a href=&#34;https://www.sqlite.org/faq.html&#34;&gt;really
mix&lt;/a&gt;. But this will be read-only, so
we’ll just give it a go. So Tegola for tile generation, with a
GeoPackage on NFS for the data storage. &lt;strong&gt;What could go wrong&lt;/strong&gt;?&lt;/p&gt;
&lt;h3 id=&#34;the-reality&#34;&gt;The Reality&lt;/h3&gt;
&lt;p&gt;My initial plan was to run the NBN dataset, as well as an OSM extract of
Australia. I loaded them Postgis using &lt;a href=&#34;https://github.com/go-spatial/tegola-osm&#34;&gt;Tegola
OSM&lt;/a&gt;, and once I had it
working, I fumbled through using
&lt;a href=&#34;https://gdal.org/programs/ogr2ogr.html&#34;&gt;ogr2ogr&lt;/a&gt; to export the data
from PostGis into GeoPackage. Make sure you use the correct spatial
reference system (SRS) otherwise bad things will happen; Tegola prefers
&lt;em&gt;4326&lt;/em&gt; for its GeoPackage configurations. Some of the data types also
caused issues with the conversion, so I commented out a number of the
layers in the Tegola OSM config. All up, my OSM data was about 4gig, and
my NBN data was about 3.5gig. So once I had it working on my local
computer, it was time to load it into Lambda.&lt;/p&gt;
&lt;p&gt;Tegola has a pre-built Lambda version, so that was simple, as was wiring
in the EFS volume. Of note, I did need to use an EC2 instance to load my
7.5gig of data into EFS, which was unfortunate, as I was hoping I could
use an S3-style console for uploading. The only struggle I had was
getting the API Gateway setup, along with CORS, and that’s mainly
because I didn’t read the documentation correctly about setting the
`’application/octet-stream` media-type; &lt;a href=&#34;https://github.com/go-spatial/tegola/issues/607&#34;&gt;this
Github&lt;/a&gt; Issue also
helped. So with that corrected, i wired it up to my web-app and I was
good to go…except…&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/1*L68Wt1nIsc6ZXXy5aDbJxg.png&#34; alt=&#34;Melbourne CBD with NBN
Mapping Data&#34;&gt;&lt;/p&gt;
&lt;p&gt;It didn’t work. I bumped the memory allocation. Still didn’t work.
Looking into the logs, it appeared it was timing out during the
execution; hitting the 30 second API Gateway timeout. Using EFS requires
connectivity via your VPC, but recent improvements have drastically
reduced those cold-start times. The biggest issue seemed to be the size
of the GeoPackage files. Removing the dream of serving the OSM data
myself, I left the NBN GeoPackage file, and it worked significantly
better. It still choked on denser areas of the map, for example the
Melbourne CBD, and for that I’ve reverted to using an S3 based cache
mechanism. Tegola has an &lt;a href=&#34;https://tegola.io/documentation/cache-seeding-and-purging/&#34;&gt;excellent cache
seeding&lt;/a&gt;
function, so data can be served instead from a cache. A word of warning
on caching, it can be very expensive, both from a time + storage
perspective, to cache tiles at lower zoom levels. Cache seed only where
something is expensive to generate on the fly.&lt;/p&gt;
&lt;h4 id=&#34;component-overview&#34;&gt;Component Overview&lt;/h4&gt;
&lt;p&gt;&lt;img src=&#34;./images/1*ieT9fLEOnNPxO18W8D53_g.png&#34; alt=&#34;Wiring it
all together&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;So, can you serve MapBox Vector Specification tiles on the cheap using
Lambda + Tegola + EFS? Yes you can. But caveat emptor. You’ll need to
make sure your GeoPackage aren’t too big, potentially splitting them
into specific layers; instead of the giant single layer I used. You’ll
also want to make liberal use of the cache-seeding function, where
possible, but that does come with a rather large file size overhead.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Colliding with Quarkus</title>
      <link>https://gerard.au/colliding-with-quarkus/</link>
      <pubDate>Tue, 10 Sep 2019 17:11:00 +0000</pubDate>
      
      <guid>https://gerard.au/colliding-with-quarkus/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;./images/quarkus.png&#34; alt=&#34;Quarkus hero banner&#34;&gt;&lt;/p&gt;
&lt;p&gt;Recently at &lt;a href=&#34;https://australia.voxxeddays.com/&#34;&gt;Voxxed Days Melbourne&lt;/a&gt; there was a Red Hat booth, and they were handing out &lt;a href=&#34;https://quarkus.io/&#34;&gt;Quarkus&lt;/a&gt; stickers. Obviously if it&amp;rsquo;s good enough to put on a sticker, it must be important. So, what is Quarkus? According to their website, Quarkus is a Cloud Native, (Linux) Container First framework for writing Java applications. It&amp;rsquo;s really a mix of two tightly integrated components: a Java Framework and GraalVM.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/quarkusLaptop.png&#34; alt=&#34;Laptop with Stickers&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;java-framework&#34;&gt;Java Framework&lt;/h2&gt;
&lt;p&gt;On first impressions, the framework seems very similar to SpringBoot, which isn&amp;rsquo;t a bad thing at all. At its core, it supports all the dependency injection features you could want, based on &lt;a href=&#34;http://docs.jboss.org/cdi/spec/2.0/cdi-spec.html&#34;&gt;this&lt;/a&gt;. From there, Quarkus combines a list of curated extensions to allow you to easily bolt on new functionality as required. If you need to include a REST server, you can include the &lt;code&gt;quarkus-resteasy&lt;/code&gt; dependency. If you need access to AWS&amp;rsquo; DynamoDB, there is &lt;code&gt; quarkus-amazon-dynamodb&lt;/code&gt;. You don&amp;rsquo;t need to go hunting for dependencies or versions to use; all the important stuff is available for you to pull in via the &lt;code&gt;listExtensions&lt;/code&gt; command.&lt;/p&gt;
&lt;h2 id=&#34;graalvm&#34;&gt;GraalVM&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://www.graalvm.org/&#34;&gt;GraalVM&lt;/a&gt; is a relatively new universal virtual machine for running JVM-based languages, as well as JavaScript, Python, Ruby and R. The feature that Quarkus really embraces is the &lt;a href=&#34;https://github.com/oracle/graal/tree/master/substratevm&#34;&gt;SubstrateVM&lt;/a&gt;, which allows Java applications to be compiled into native executable images. Instead of running your Java code against a JVM, you can compile it down to a portable, native executable. Quarkus abstracts all this complexity from you, and allows you to just run &lt;code&gt;nativeImage&lt;/code&gt; to build a executable binary.&lt;/p&gt;
&lt;h1 id=&#34;taking-it-for-a-spin&#34;&gt;Taking it for a spin&lt;/h1&gt;
&lt;p&gt;So I have put together a &lt;a href=&#34;https://github.com/ggotti/kanye-quarkus&#34;&gt;small HTTP server example&lt;/a&gt; which basically just proxies requests to the &lt;code&gt;https://api.kanye.rest&lt;/code&gt; service, which returns a random Kanye West quote. The application has three small classes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;VoteKanyeResource&lt;/li&gt;
&lt;li&gt;KanyeService&lt;/li&gt;
&lt;li&gt;KanyeQuote&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;votekanyeresource&#34;&gt;VoteKanyeResource&lt;/h2&gt;
&lt;p&gt;The class uses dependency injection to inject the &lt;code&gt;KanyeService&lt;/code&gt;, which is then invoked in the &lt;code&gt;quote&lt;/code&gt; method, which is defined as HTTP accessible using the &lt;code&gt;@GET&lt;/code&gt; annotation.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; au.com.gigliotti.quarkus;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; org.eclipse.microprofile.rest.client.inject.RestClient;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; javax.enterprise.context.ApplicationScoped;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; javax.inject.Inject;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; javax.ws.rs.GET;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; javax.ws.rs.Path;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; javax.ws.rs.Produces;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; javax.ws.rs.core.MediaType;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; java.util.concurrent.CompletableFuture;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; java.util.concurrent.CompletionStage;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@Path&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/quote&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@ApplicationScoped&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;VoteKanyeResource&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;@Inject&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;@RestClient&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    KanyeService quoteService;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;@GET&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;@Produces&lt;/span&gt;(MediaType.&lt;span style=&#34;color:#a6e22e&#34;&gt;TEXT_PLAIN&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; CompletionStage&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;String&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;quote&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; CompletableFuture.&lt;span style=&#34;color:#a6e22e&#34;&gt;supplyAsync&lt;/span&gt;(() &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; quoteService.&lt;span style=&#34;color:#a6e22e&#34;&gt;getRandomQuote&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;getQuote&lt;/span&gt;());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;kanyeservice&#34;&gt;KanyeService&lt;/h3&gt;
&lt;p&gt;This service uses the &lt;a href=&#34;https://github.com/eclipse/microprofile-rest-client&#34;&gt;MicroProfile&lt;/a&gt; REST client. This allows the application to map the REST call onto an interface structure, similar to Spring Data. You&amp;rsquo;ll notice that there is no reference to the HTTP target; that&amp;rsquo;s stored in an &lt;code&gt;application.properties&lt;/code&gt; file; you don&amp;rsquo;t want to be embedding those kinds of values.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; au.com.gigliotti.quarkus;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; org.eclipse.microprofile.rest.client.annotation.ClientHeaderParam;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; javax.ws.rs.Consumes;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; javax.ws.rs.GET;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; javax.ws.rs.Path;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@Path&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@RegisterRestClient&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@Consumes&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;application/json&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;interface&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;KanyeService&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;@GET&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;@ClientHeaderParam&lt;/span&gt;(name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;User-Agent&amp;#34;&lt;/span&gt;, value&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;curl/7.54.0&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    KanyeQuote &lt;span style=&#34;color:#a6e22e&#34;&gt;getRandomQuote&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;container-magic&#34;&gt;Container Magic&lt;/h3&gt;
&lt;p&gt;The real super power for Quarkus is the &lt;a href=&#34;https://quarkus.io/guides/building-native-image-guide&#34;&gt;building native images&lt;/a&gt; feature. All you need to do with this example is to run &lt;code&gt;gradle buildNative&lt;/code&gt; to compile into a native executable. On MacOSX, Quarkus actually spins up a Docker container in the background to do the compilation, so you don&amp;rsquo;t need to configure GraalVM locally. The compilation on my 2015 MacBook Pro isn&amp;rsquo;t super fast, at approximately 2.5 minutes to compile, but this process is something that you&amp;rsquo;d only do when you&amp;rsquo;re ready to deploy into your container.&lt;/p&gt;
&lt;p&gt;As a native executable, this application starts in 0.010 seconds, which is blisteringly quick.&lt;/p&gt;
&lt;h1 id=&#34;still-in-beta&#34;&gt;Still In Beta&lt;/h1&gt;
&lt;p&gt;At the time of writing, Quarkus was at version 0.21.1, so it&amp;rsquo;s still very much in beta. I struggled to get the &lt;code&gt;Gradle&lt;/code&gt; plugin working with &lt;code&gt;Kotlin&lt;/code&gt;, so I needed to revert to using standard Java. Speaking of build tooling, the integration works nicely if you&amp;rsquo;re familiar with the Java ecosystem,  but there is still a steep learning curve for newcomers. Saying that, the &lt;a href=&#34;https://quarkus.io/get-started/&#34;&gt;documentation&lt;/a&gt; is really top notch, as are the &lt;a href=&#34;https://github.com/quarkusio/quarkus-quickstarts&#34;&gt;samples&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;You probably want to wait to version 1 before running your production workloads using Quarkus. Saying that, it&amp;rsquo;s refreshing to see a viable alternative to Spring, particularly one with such a focus on developer productivity and cloud deployment options.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Testing with Gauge</title>
      <link>https://gerard.au/testing-with-gauge/</link>
      <pubDate>Sat, 16 Mar 2019 17:11:00 +0000</pubDate>
      
      <guid>https://gerard.au/testing-with-gauge/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;./images/gauge_header.jpg&#34; alt=&#34;Gauge Header&#34;&gt;
End-to-end (e2e) testing is one of the most overlooked, underappreciated aspects of software delivery. A well written e2e suite gives you the confidence to develop and deploy quickly. A badly written e2e suite results in deliverables of unknown quality, leads to frustration, and inevitably project delays. As &lt;a href=&#34;https://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052&#34;&gt;Michael Feathres&lt;/a&gt; wrote in his book, Working Effectively with Legacy Code:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;To me, legacy code is simply code without tests. I’ve gotten some grief for this definition… I have no problem defining legacy code as code without tests. It is a good working definition, and it points to a solution.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The problem I have found with e2e testing suites, is that they are generally very difficult to setup and maintain. Particularly in the JavaScript space there are a bunch of existing frameworks and libraries available, but I feel that every time I try and do something I spend the majority of my time fumbling with different versions and configuration incompatibilities. It&amp;rsquo;s like trying to tape a bunch of cats together.&lt;/p&gt;
&lt;p&gt;Enter &lt;a href=&#34;https://gauge.org/&#34;&gt;Gauge&lt;/a&gt;, a new lightweight Open Source Testing Framework. It provides an opinionated CLI tool to allow you to rapidly develop tests, without having to worry about the underlying plumbing. Install gauge, select a language, start writing tests. A promise too good to be true?&lt;/p&gt;
&lt;h1 id=&#34;gauge-overview&#34;&gt;Gauge Overview&lt;/h1&gt;
&lt;p&gt;There are a number of parts that makeup Gauge, and it is important to understand how each of them interact.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/gauge_overview.png&#34; alt=&#34;Gauge Overview Diagram&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;specification-files&#34;&gt;Specification Files&lt;/h2&gt;
&lt;p&gt;Gauge uses a Markdown based format for its &lt;a href=&#34;https://docs.gauge.org/latest/writing-specifications.html&#34;&gt;specification files&lt;/a&gt;. It is similar to the Gherkin syntax used by Cucumber, but offers the flexibility of being able to have steps littered throughout more complex explanations. There is a fairly standard Scenario &amp;gt; Step structure, similar to Gherkin, and it supports familiar tagging and table syntaxes. Anything that you expect in Gherkin should be available in Gauge.&lt;/p&gt;
&lt;p&gt;There are however, a couple of very powerful other features.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.gauge.org/latest/writing-specifications.html#concept-steps&#34;&gt;Concepts Steps&lt;/a&gt;: Allow you to combine re-usable logical groups of steps into a single unit. Think of having a &amp;ldquo;Login with user &amp;ldquo;bob&amp;rdquo; function, where you define the login as a Concept which you can reuse across different scenarios.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.gauge.org/latest/writing-specifications.html#special-parameters&#34;&gt;Special Parameters&lt;/a&gt;: It natively allows you to externalize large text In Files, or pass in large tables via CSV. No more enormous specification files.&lt;/li&gt;
&lt;li&gt;VS Code Integration: The integration is second to none, with step auto-complete and the ability to handle execution from within VS Code.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;gauge-runner&#34;&gt;Gauge Runner&lt;/h2&gt;
&lt;p&gt;Gauge itself is written in Go, but allows you to define your step definitions in a variety of other languages. There is official Gauge support for Java, C#, Ruby, JavaScript, Go and Python. The Java and JavaScript runners seem to be the most feature rich, and we will be looking at the JS runner because it supports Taiko.&lt;/p&gt;
&lt;h2 id=&#34;step-definitions&#34;&gt;Step Definitions&lt;/h2&gt;
&lt;p&gt;The step definitions provide the glue for the execution of your tests, similar to how they work for Cucumber. However, there are a couple of additional features that I would like to call out:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.gauge.org/latest/writing-specifications.html#tags&#34;&gt;Filtering Hooks&lt;/a&gt;: Allow you to trigger filters based on tags associated with a scenario. So for example, you might setup a test differently if it is a production test instead of a local test.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.gauge.org/latest/writing-specifications.html#data-store&#34;&gt;Data Stores&lt;/a&gt;: Gauge provides a mechanism to store and retrieve data between step definitions. This is important, because Gauge allows your tests to be run in parallel.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;gauge-cli&#34;&gt;Gauge CLI&lt;/h2&gt;
&lt;p&gt;The CLI is the heart of Gauge. It allows you to execute your specs as required, initialize your project, and interact with your test suite. The power is in its simplicity; you have one utility that you need to remember and that is it.&lt;/p&gt;
&lt;h2 id=&#34;taiko&#34;&gt;Taiko&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://taiko.gauge.org/&#34;&gt;Taiko&lt;/a&gt; is a Node library for interacting with Chrome, which on the surface sounds very similar to Selenium. Despite the similarities, its main difference is its &lt;a href=&#34;https://taiko.gauge.org/#smart-selectors&#34;&gt;smart selector system&lt;/a&gt;. The aim with this is that you treat the webpage itself as a black-box, and you interact with it like a user would.&lt;/p&gt;
&lt;p&gt;Let us take a login scenario as an example, where a user needs to enter their username and password before proceeding. Using a traditional Selenium approach, you would use some kind of selector, generally a CSS selector, to identify the input boxes, and then write in the values as required. With Taiko, you state that you want to write a value into a field, and you select it by its onscreen label; this might even be a label above the actual element. Taiko then works out which element you require based on the action you are performing, and the data you have provided. You effectively are able to use the presentation of the page to interact with it, instead of having to worry about the underlying structure. If a developer inadvertently renames an element, or restructures some of the HTML, as long as the presentation is the same/similar, your tests will still function. I am sure there are scenarios where this won&amp;rsquo;t work effectively, so there are fallback options.&lt;/p&gt;
&lt;p&gt;Another amazing feature of Taiko is the &lt;a href=&#34;https://taiko.gauge.org/#interactive-recorder&#34;&gt;Interactive Recorder&lt;/a&gt;, with is a &lt;a href=&#34;https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop&#34;&gt;RPL&lt;/a&gt; interactive shell which allows you to interact with your browser dynamically. This allows you to verify the Taiko commands you would like to use, and will also generate the code for you when you are done.&lt;/p&gt;
&lt;h1 id=&#34;example&#34;&gt;Example&lt;/h1&gt;
&lt;p&gt;You will first need to install &lt;a href=&#34;https://docs.gauge.org/latest/installation.html&#34;&gt;Gauge&lt;/a&gt;. There are plenty of package options available; for example on MacOS you can use &lt;code&gt;brew&lt;/code&gt;. Once installed, all you need to do is run the following to initialise your Taiko based project.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;gauge init js
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And then running it is as simple as:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;gauge run ./specs
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img src=&#34;./images/gauge_init.png&#34; alt=&#34;Gauge Initialisation&#34;&gt;&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;re already cooking with gas. Now, say we want to have a test that goes to the &lt;a href=&#34;https://www.jhipster.tech&#34;&gt;Jhipster&lt;/a&gt; page and clicks the &lt;code&gt;Sponsors&lt;/code&gt; link. You can use the Taiko RPL to generate the step definition for you. See below for an example of interacting with Taiko.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/gauge_rpl.png&#34; alt=&#34;Taiko RPL&#34;&gt;&lt;/p&gt;
&lt;p&gt;The code generated by the &lt;code&gt;.code&lt;/code&gt; command can then be inserted into a step definition, or multiple step definitions. It is generally better to have generic step definitions, and then use detailed definitions for things that really require them. You can also always use Concepts to bundle up complex interactions into single steps, another useful feature of Gauge.&lt;/p&gt;
&lt;p&gt;If we massage the above generated code into the following step definitions, we can make it generic.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;openBrowser&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;write&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;closeBrowser&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;goto&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;press&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;text&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;contains&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;taiko&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;assert&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;assert&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;headless&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;process&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;env&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;headless_chrome&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;toLowerCase&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;true&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;beforeSuite&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;openBrowser&lt;/span&gt;({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;headless&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;headless&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;afterSuite&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;closeBrowser&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;step&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Goto &amp;lt;url&amp;gt;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;goto&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;step&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Page contains &amp;lt;textToCheck&amp;gt;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;textToCheck&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;text&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;textToCheck&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;step&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Click link &amp;lt;linkText&amp;gt;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;linkText&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;click&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;link&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;linkText&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then, the specification file can look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-markdown&#34; data-lang=&#34;markdown&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Fun with Gauge
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;## Go to JHipster
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;*&lt;/span&gt; Goto &amp;#34;https://jhipster.tech&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;*&lt;/span&gt; Page contains &amp;#34;Jhipster news&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;*&lt;/span&gt; Click link &amp;#34;SPONSORS&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Running it again via &lt;code&gt;gauge run ./specs&lt;/code&gt; will have the test navigate to the website and interact with the defined elements. It also generates a pretty report for you so you can see what is up.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/gauge_report.png&#34; alt=&#34;Gauge Report&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;final-thoughts&#34;&gt;Final Thoughts&lt;/h1&gt;
&lt;p&gt;Gauge for me fills a gap in the market when it comes to fast, maintainable testing. It takes the lessons learned around Cucumber and Selenium, and removes the obvious pain points around them. It seems obvious that e2e testing would move in this direction, and I am excited to see where it is going to develop in the future. Go get Gauge, and start testing.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>CKAD Exam Tips</title>
      <link>https://gerard.au/2019-03-03_ckad-exam-tips-e9d5a047e252/</link>
      <pubDate>Sun, 03 Mar 2019 00:00:08 -1000</pubDate>
      
      <guid>https://gerard.au/2019-03-03_ckad-exam-tips-e9d5a047e252/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;./images/1*xzz90LbpAl2ZAY3k7qTFvg.png&#34; alt=&#34;Kubernetes
Everywhere&#34;&gt;&lt;/p&gt;
&lt;p&gt;I recently received my Certified Kubernetes Application Developer (CKAD)
certification, and boy was it fun. I thought I’d put together some of my
tips around my exam prep, as I’d only really dabbled in Kubernetes
previously.&lt;/p&gt;
&lt;h3 id=&#34;learning-kubernetes-itself&#34;&gt;Learning Kubernetes Itself&lt;/h3&gt;
&lt;h3 id=&#34;kubernetes-inaction&#34;&gt;Kubernetes in Action&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;./images/1*22nc2ikFGk8auwUWl5Efnw.jpeg&#34; alt=&#34;Cover for Kubernetes in
Action by
Marko Lukša&#34;&gt;&lt;/p&gt;
&lt;p&gt;To start with I decided to read through &lt;a href=&#34;https://www.manning.com/books/kubernetes-in-action&#34;&gt;Kubernetes in
Action&lt;/a&gt; by &lt;a href=&#34;https://github.com/luksa&#34;&gt;Marko
Lukša&lt;/a&gt;. A tome at 624 pages, the book covers
the basics needed for the exam, as well as additional sections regarding
extending Kubernetes and multi-clusters. You’ll definitely want to run
through the book at your computer, so you can punch the examples in and
see how they react. It’s particularly important for topics such as the
deployment handling. There are numerous Kubernetes release cycles a
year, so some of the stuff in the book wasn’t entirely up to date.
Saying that, Manning released a small update in February.&lt;/p&gt;
&lt;p&gt;The book approach isn’t for everyone, and there are plenty of other
introductory tutorials on the web, including a &lt;a href=&#34;https://www.cncf.io/certification/training/&#34;&gt;free
introductory&lt;/a&gt; course from
CNCF. Some of my colleagues who were studying at the same time preferred
this.&lt;/p&gt;
&lt;h4 id=&#34;pick-a-distribution-touse&#34;&gt;Pick a Distribution to Use&lt;/h4&gt;
&lt;p&gt;&lt;img src=&#34;./images/1*Ao4j4-480-Gt8gDKk766_Q.jpeg&#34; alt=&#34;kops
logo&#34;&gt;&lt;/p&gt;
&lt;p&gt;Although Docker for Mac now includes
&lt;a href=&#34;https://docs.docker.com/docker-for-mac/kubernetes/&#34;&gt;Kubernetes&lt;/a&gt;, I
think it’s better to use a more realistic distribution. I ended up using
&lt;a href=&#34;https://github.com/kubernetes/kops&#34;&gt;kops&lt;/a&gt; to deploy a working
Kubernetes cluster on AWS. One of the neat features of kops is that it
allows you to generate valid &lt;a href=&#34;https://github.com/kubernetes/kops/blob/master/docs/terraform.md&#34;&gt;Terraform configuration
files&lt;/a&gt;,
which I then used to create and destroy the cluster as required. Kops
can get expensive if you leave it running.&lt;/p&gt;
&lt;p&gt;I focused on kops because I wanted to learn about how Kubernetes
interacted with AWS resources, like EFS and IAM Roles.&lt;/p&gt;
&lt;h3 id=&#34;exam-specifictips&#34;&gt;Exam Specific Tips&lt;/h3&gt;
&lt;h4 id=&#34;read-the-curriculum-overview&#34;&gt;Read the Curriculum Overview&lt;/h4&gt;
&lt;p&gt;Make sure you read the &lt;a href=&#34;https://github.com/cncf/curriculum&#34;&gt;Curriculum
Overview&lt;/a&gt;, as it defines the topics
that will be covered in the exam, and the percentage of each topic on
the exam. Make sure you understand each of the dot points, and how they
are created, modified and examined on a Kubernetes cluster. All of them
are relevant.&lt;/p&gt;
&lt;h4 id=&#34;handle-yourkubectl&#34;&gt;Handle your kubectl&lt;/h4&gt;
&lt;p&gt;&lt;img src=&#34;./images/1*UJ-7EQrcWiHbRrrMbQYJfg.png&#34; alt=&#34;Get ready
for kubectl&#34;&gt;&lt;/p&gt;
&lt;p&gt;You need to be proficient in using kubectl regarding the topics covered
in the Curriculum Overview. In particular you need to know how to create
things with a minimal amount of typing. Some resources need to be
created via a YAML syntax, but for the majority of resources CLI
arguments should be sufficient. In terms of CLI tips:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the `kubectl run` command is particularly powerful. Use it with the
&lt;code&gt;-&lt;/code&gt;-dry-run -o yaml` arguments to output YAML files.&lt;/li&gt;
&lt;li&gt;I recommend setting up a `k` alias for `kubectl` so you don’t need
to keep on typing it.&lt;/li&gt;
&lt;li&gt;The exam makes use of 3 clusters and numerous namespaces. Get
comfortable in switching between clusters and namespaces with
`kubectl config` otherwise you’ll waste a lot of time prefixing the
namespace to your commands, and you will introduce the possibility for
additional errors.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I spent a lot of time running through the exercises defined
&lt;a href=&#34;https://github.com/dgkanatsios/CKAD-exercises%20&#34;&gt;here&lt;/a&gt;, as I found it
a great way to learn the commands. It doesn’t necessarily represent the
exam question format, but I found it invaluable. You should be able to
answer every question before taking the exam without hesitation.&lt;/p&gt;
&lt;h4 id=&#34;practice-your-linuxfoo&#34;&gt;Practice your Linux Foo&lt;/h4&gt;
&lt;p&gt;You need to be proficient in your Linux terminal skills. You’ll need to
be able to pipe data around, ssh into different terminals, and be
comfortable with sudo. Having a workable knowledge of vim or nano is
important. You don’t want to be copying large amounts of text back and
forward between the terminal and the exam notepad, so you need to be
able to edit in place. There isn’t a huge amount of typing required, as
you’ll be copying from kubernetes.io for references, or generating YAML
from cli commands, but you certainly need to be able to quickly edit a
file.&lt;/p&gt;
&lt;h4 id=&#34;bookmarks-inchrome&#34;&gt;Bookmarks in Chrome&lt;/h4&gt;
&lt;p&gt;&lt;img src=&#34;./images/1*tTtEDJSeKfo0IUWERgiS_Q.png&#34; alt=&#34;Bookmark
toolbar&#34;&gt;&lt;/p&gt;
&lt;p&gt;Have a good set of Bookmarks defined in Chrome, and have them available
in the often unloved bookmark bar. You should ensure you have links for
all the topics covered by the Curriculum Overview.&lt;/p&gt;
&lt;h4 id=&#34;the-examitself&#34;&gt;The Exam Itself&lt;/h4&gt;
&lt;p&gt;The exam itself is pretty time constrained. Take note of the percentage
worth of each question, because the length of the question does not
necessarily correlate to its scoring. I’ve spoken to some of my
colleagues who suggested working from the last question backwards, but I
found it just as effective starting from question 1, and then skipping
over questions that I couldn’t quickly solve. You’ll want to get as many
runs on the board so you can clear the required pass mark.&lt;/p&gt;
&lt;p&gt;In addition, I believe the exam is automatically marked, so you need to
be spot on with your spelling. Either copy names directly out of the
question, or be very careful when you type them.&lt;/p&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;I highly recommend sitting the CKAD Exam, if only just for the
experience. Hopefully some of these tips will help you in that quest.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Journey to ippon.acrossthe.world</title>
      <link>https://gerard.au/2018-10-30_journey-to-ippon-acrossthe-world-bd74952d7caa/</link>
      <pubDate>Tue, 30 Oct 2018 00:00:08 -1000</pubDate>
      
      <guid>https://gerard.au/2018-10-30_journey-to-ippon-acrossthe-world-bd74952d7caa/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;./images/1*Wl_WPND_pAqwKoW2oWeePw.png&#34; alt=&#34;8-bit Flinders
Street Station&#34;&gt;&lt;/p&gt;
&lt;p&gt;The Ippon Australia team have been working hard over the last couple of
weeks on a recruitment challenge, called &lt;a href=&#34;https://ippon.acrossthe.world/&#34;&gt;Ippon Across The
World&lt;/a&gt;. It’s a short game to test
technical skills, without being too over the top, and hopefully it’s a
little fun along the way. Give it a try if you haven’t seen it yet.&lt;/p&gt;
&lt;h3 id=&#34;err-why&#34;&gt;Err Why?&lt;/h3&gt;
&lt;p&gt;During the downtime between projects, it’s important to maintain team
cohesion, as well as learn new skills. Self-paced learning is good to a
point, but you learn so much more by having a tangible deliverable, and
a target to work towards. We also have on-boarded some great new team
members recently, and it was a great opportunity to get to know them a
little better.&lt;/p&gt;
&lt;h3 id=&#34;design&#34;&gt;Design&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*UNUekXqQs3yEEnNS&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;We went with an 8-bit video game motif, because, well who doesn’t like
8-bit? We wanted to capture that original Carmen Sandiego vibe but with
a Melbourne twist.&lt;/p&gt;
&lt;h3 id=&#34;architect&#34;&gt;Architect&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*tlo4-6Z237liRSJN&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;AWS is our default cloud provider, so we used a modern AWS design for
our application. We wanted to leverage as many AWS services as possible,
to give us a holistic view of how some of these newer components hang
together.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CloudFront&lt;/strong&gt; is incredibly important to hosting a modern application
on AWS; saying that, it is very often ignored or misunderstood. It
provides not just caching services, but also edge-content delivery and
TLS support. You can even now run Lambda Edge functions as part of
your content delivery strategy; we’re using it here for CORS header
support.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Route53&lt;/strong&gt; is the DNS provider for AWS, both internally and
externally. You can also purchase and manage domains through it. I
particularly enjoy finding novelty domain names.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AWS Connect&lt;/strong&gt; is a rather unusual AWS offering, and we wanted to try
it out. It’s effectively a call-center in a box, allowing you to
establish elaborate inbound and outbound call flows, including
ML-based text-to-speech, as well as human operator steps.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;S3&lt;/strong&gt; static file storage for the website and Java libraries.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;API Gateway&lt;/strong&gt; for rate limiting and routing requests to Lambda.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lambda&lt;/strong&gt; for serverless API processing. The Lambda functions are
primarily used to respond to answers and provide the next URL to
follow, as well as for processing analytics events.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DynamoDB is&lt;/strong&gt; used for storing temporary data, which we’re cleaning
up with the automated TTL function. For those that are unfamiliar with
DynamoDB, it’s a scaled NoSQL offering from AWS.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pinpoint (and Cognito)&lt;/strong&gt; used for analytics; tracking primarily
website interactions.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;user-interface&#34;&gt;User Interface&lt;/h3&gt;
&lt;p&gt;We decided to use raw JavaScript with &lt;a href=&#34;https://greensock.com/gsap&#34;&gt;GSAP&lt;/a&gt;
for the animation components, with a focus on modern desktop browsers.
No legacy Internet Explorer support here. Apart from the animation
aspects, we also used &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API&#34;&gt;Web Crypto
API&lt;/a&gt;
for one of the challenges, which was pretty exciting to see in action.&lt;/p&gt;
&lt;h3 id=&#34;cicd-pipeline&#34;&gt;CI/CD Pipeline&lt;/h3&gt;
&lt;p&gt;No project is too small for a CI/CD pipeline. It makes life easier if
you’re deploying something more than once, and it’s important when you
have a number of people wanting to make a lot of little changes quickly.&lt;/p&gt;
&lt;p&gt;We used &lt;a href=&#34;https://www.terraform.io/&#34;&gt;Terraform&lt;/a&gt; for setting up the base
infrastructure, and then a combination of shell scripts for updating
deployed assets. &lt;a href=&#34;https://docs.gitlab.com/ee/ci/pipelines.html&#34;&gt;Gitlab&lt;/a&gt;,
which is our source-control tool, has an amazing pipeline mechanism for
defining build processes, and we use that to push changes into the
various environments when a single commit occurs.&lt;/p&gt;
&lt;h3 id=&#34;whats-next&#34;&gt;What’s Next&lt;/h3&gt;
&lt;p&gt;Shipping an application is not the end of the journey, it’s the
beginning. We’re collecting usage metrics via Pinpoint, which allows us
to see how far users (anonymously) get through the game. We will be
offering slight variations to the challenges depending on skillsets, and
potentially offering a harder difficult level for the clever cookies.&lt;/p&gt;
&lt;p&gt;We certainly enjoyed putting t&lt;a href=&#34;https://ippon.acrossthe.world&#34;&gt;his
together&lt;/a&gt; and we hope that you enjoy
playing it.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Originally published
&lt;a href=&#34;https://www.linkedin.com/pulse/journey-ipponacrosstheworld-gerard-gigliotti/&#34;&gt;https://www.linkedin.com/pulse/journey-ipponacrosstheworld-gerard-gigliotti/&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Open Banking, Open Opportunities</title>
      <link>https://gerard.au/open-banking-open-opportunities/</link>
      <pubDate>Mon, 23 Jul 2018 14:01:02 +0000</pubDate>
      
      <guid>https://gerard.au/open-banking-open-opportunities/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;./images/open-banking-1036857.jpeg&#34; alt=&#34;Open Banking&#34;&gt;&lt;/p&gt;
&lt;p&gt;Open Banking represents a real paradigm shift in the ownership and use of customer data. It not only provides significant opportunities for consumers, fintechs and the banks but also represents a precursor to broader data ownership changes beyond just the realm of financial services.&lt;/p&gt;
&lt;p&gt;Scott Farrell defines open banking as giving &amp;ldquo;customers a right to direct that the information they already share with their bank be safely shared with others they trust.&amp;rdquo; Traditionally viewed as an asset of the organisation, empowering consumers to be able to easily, and safely, share their data sounds like a really great idea. However what are the likely consumer implications?&lt;/p&gt;
&lt;p&gt;If significant, democratising changes of the past are anything to go by then consumer uptake will be slow. Indeed, just take a look at the &amp;ldquo;choice of fund&amp;rdquo; legislation in superannuation which is almost 15 years old and still sees relatively few workers chose anything but their employers default fund. Another recent example would be the latest advancement in the Australian banking landscape, the NPP, allowing almost real time payments is almost unheard of by consumers. Some of Australia’s largest banks haven’t been able to integrate it into their offerings as yet.&lt;/p&gt;
&lt;p&gt;As such, short term adoption of Open Banking is likely to be limited but it does put in place a foundation with the potential to deliver significant consumer value well into the future. To appreciate the value of the proposition it’s worth considering the examples of outcomes it could enable for customers.&lt;/p&gt;
&lt;h1 id=&#34;customer-value-add-services&#34;&gt;Customer Value Add Services&lt;/h1&gt;
&lt;p&gt;One of the more interesting points in the &lt;a href=&#34;https://treasury.gov.au/consultation/c2018-t247313/&#34;&gt;Open Banking report&lt;/a&gt; was &lt;em&gt;Recommendation 3.3&lt;/em&gt;, which states that &amp;ldquo;&amp;hellip; data that results from material enhancement by the application of insights, analysis or transformation by the data holder should not be included in the scope of Open Banking&amp;rdquo;. What that means is any value add or proprietary processing you add as a bank to a customer’s transaction data does not need to be shared. The opportunity present, therefore, is being able to provide additional services on top of the underlying data in unique and compelling ways.&lt;/p&gt;
&lt;p&gt;For example, imagine as a bank being able to provide proper categorisation of transactions to help drive financial outcomes for a customer. You could tell a customer they spent $24 on coffee this week, which was $4 over their budget. Or you could provide a kind of financial health check service to ensure they’re using the most optimal product for their situation. Providing value add services increases customer loyalty, and discourages Fintechs from plugging obvious gaps in your offering.&lt;/p&gt;
&lt;h1 id=&#34;data-driven-acquisitions&#34;&gt;Data-Driven Acquisitions&lt;/h1&gt;
&lt;p&gt;A less obvious benefit of a free-flowing data approach, is the ability to use data for acquisition purposes. Imagine that you have a credit card with Bank A, and you’re applying for a new credit card with Bank B. By leveraging the Open Banking platform, Bank B will be able to incorporate the financial transaction data from Bank A as part of its signup process, and leverage that data to provide concrete examples of how their product is superior. For example, Bank B could show that &amp;ldquo;transaction X&amp;rdquo; would have resulted in &amp;ldquo;Y&amp;rdquo; number of reward points. The transaction data could even be used to target the kind of product being applied for, say a &amp;ldquo;Platinum Card&amp;rdquo; instead of a &amp;ldquo;Gold&amp;rdquo; card. A more advanced version of this approach could leverage the data supplied to customise the product specifically for a customer.&lt;/p&gt;
&lt;p&gt;The key with this approach is to demonstrate the value that you can deliver for your potential customer immediately. A flashy brochure-wear website, showing picture after picture of near identical credit cards is fine, but that doesn’t differentiate your brand; that’s a transactional cost of doing business. Being able to effectively categorise data rapidly, using either fixed or machine-generated rules, could be used to significantly speed up the signup process, reducing the number of dropouts and increasing conversions. Looking at one of the big four’s credit card signup forms, they have 6 sections relating to finances, including a 100 word description of what an expense is; this could be massively simplified with Open Banking data.&lt;/p&gt;
&lt;h1 id=&#34;fintech--developer-friendly&#34;&gt;Fintech &amp;amp; Developer Friendly&lt;/h1&gt;
&lt;p&gt;There is serious opportunity in being the bank that embraces the Fintech and Developer community in a meaningful and ongoing way, through both brand exposure, as well as from new customer streams. What this means is investing in developer advocates, relevant API documentation, sample code, and support channels for developers. If developers find it easier to build products using your services and data, and you make the experience compelling for them, they’ll be more likely to continue to use your products, and potentially branch out into using your products in other areas.&lt;/p&gt;
&lt;p&gt;Open Banking will dictate a minimum set of standards that will need to be adhered to, but that shouldn&amp;rsquo;t be the target for any bank. Providing additional data points, or even write based access for some services would be a huge carrot for Fintechs. Imagine for example, a Fintech company which helps people manage and track their savings goals. If your bank allowed the Fintech company to transfer money between a customer’s various accounts, they could leverage and recommend your bank’s services to its customers. A symbiotic relationship in this case would benefit all parties.&lt;/p&gt;
&lt;h1 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;The premise of Open Banking is consumer empowerment, industry invigoration and driving competition in the banking sector. As such, it is somewhat ironic that, at least in the short term, the biggest beneficiaries of this approach are likely to be Australia’s big four banks.&lt;/p&gt;
&lt;p&gt;There is no doubt that they will be burdened with the cost of implementing approaches that facilitate the operation of Open Banking but, in the process, will also empower themselves to take advantage of this in ways that were not historically leveraged. This is a massive opportunity to drive significant engagement with their existing client base and thus reduce churn.&lt;/p&gt;
&lt;p&gt;Further, should some innovative fintech startups create value in ways unforeseen by the Australian big four, their sheer market power and financial strength means that they could most probably play the fast follower by replicating the proposition or just acquire the company. Again, let’s consider the example of peer to peer lending, another approach to &amp;ldquo;opening up&amp;rdquo; the banking sector, where those platforms that were successful got bought, or invested in, by the big banks.&lt;/p&gt;
&lt;p&gt;As an initiative Open Banking should absolutely be applauded. It creates a foundation that should drive positive changes in banking over the short, medium and long term. The ultimate success of the initiative should be measured by consumer adoption and the flow on benefits that ensue in opening up consumer data in markets beyond just banking.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>AWS AppSync is coming for your API</title>
      <link>https://gerard.au/2018-06-17_aws-appsync-is-coming-for-your-api-979e4c21ae7c/</link>
      <pubDate>Sun, 17 Jun 2018 00:00:08 -1000</pubDate>
      
      <guid>https://gerard.au/2018-06-17_aws-appsync-is-coming-for-your-api-979e4c21ae7c/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;./images/1*_iSFCcn5RxWFzTgdtx3a0A.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;According to the &lt;a href=&#34;https://aws.amazon.com/blogs/aws/introducing-amazon-appsync/&#34;&gt;introductory post&lt;/a&gt;,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;AWS AppSync is a “serverless GraphQL service for real-time data queries, synchronization, communications and offline programming features.”.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I know that statement doesn’t feature every 2018 buzz word, but it is getting pretty close. But what does it actually do?&lt;/p&gt;
&lt;p&gt;If you’d like to read more about AWS AppSync, head over to the &lt;a href=&#34;https://blog.ippon.tech/aws-appsync-is-coming-for-your-api/&#34;&gt;Ippon
Blog&lt;/a&gt;. If
reading isn’t for you, the associated pony-heavy sample is on &lt;a href=&#34;https://github.com/ggotti/ponyvote&#34;&gt;Github
here&lt;/a&gt;:&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>It&#39;s about the little (internet of) things</title>
      <link>https://gerard.au/2018-01-14_it-s-about-the-little--internet-of--things-fba724e2124a/</link>
      <pubDate>Sun, 14 Jan 2018 00:00:08 -1000</pubDate>
      
      <guid>https://gerard.au/2018-01-14_it-s-about-the-little--internet-of--things-fba724e2124a/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;./images/1*opmauOvaJc93b8wlywy3Jw.jpeg&#34; alt=&#34;The
Lamp&#34;&gt;&lt;/p&gt;
&lt;p&gt;After moving into my new place I decided that I wanted to start down the
treacherous path of home automation. I’d dabbled with a WeMo switch
previously (see
&lt;a href=&#34;https://gerard.gigliotti.com.au/fitbit-powered-home-automation-17166f5824cb&#34;&gt;here&lt;/a&gt;),
but that was more of a toe in the water. Now I wanted to jump in. My
hesitation with home automtion/IoT in the past has been that it’s
difficult to find a use case where it would actually enhanced my life.
In addition, I don’t want any IoT automation to interfere with the
day-to-day operation of how a reasonable person expects the device to
operate. For example, if you’ve installed Philips Hue lights throughout
your house, and your automations don’t work because somebody has turned
off your globe at the switch, that’s a problem with the automation, not
with the switch.&lt;/p&gt;
&lt;p&gt;I luckily live near a tram line, and I catch the tram to get to work.
Melbourne’s tram network is run by Yarra Trams, and they produce a
pretty good app which provides arrival information for tram stops. Just
an aside, the arrival information isn’t calculated by GPS, but rather a
series of radio-based timing gates relative to your stop. The problem I
have is that even during peak frequency, the trams are spaced 10–12
minutes apart. You don’t want to have to wait at the stop for 10–12
minutes. As a result, when I get ready in the morning, I usually have
the iOS app up checking the tram arrival time. Constantly sliding down
to refresh is giving me RSI, and although there is a notification
system, it requires my input initially. This is my use case!&lt;/p&gt;
&lt;p&gt;Now I can remember Werner Vogels at last year’s AWS Architect &amp;amp;
Developer Day in Melbourne talking about natural interfaces, and this is
a perfect example of where one can apply. My idea was to use a connected
light globe, in this case one from LIFX (more on that later), and
connect it to the &lt;a href=&#34;http://ws.tramtracker.com.au/pidsservice/pids.asmx&#34;&gt;Yarra Trams
API&lt;/a&gt;. As the tram
got closer, I wanted the light to get brighter, and then when it was
absolutely time to leave, I wanted it to flash to warn me.&lt;/p&gt;
&lt;h3 id=&#34;parts&#34;&gt;Parts&lt;/h3&gt;
&lt;h4 id=&#34;yarra-tramsapi&#34;&gt;Yarra Trams API&lt;/h4&gt;
&lt;p&gt;Yarra Trams had a public API before it was cool, so although it’s
SOAP-based, it provides data for all your tram network needs. Bang in
the stop number and you’re good to get your predicted arrival
information:
&lt;a href=&#34;http://ws.tramtracker.com.au/pidsservice/pids.asmx?op=GetNextPredictedRoutesCollection&#34;&gt;GetNextPredictedRoutesCollection &lt;/a&gt;.
Before you call that, you need to get a ClientGuid by calling
&lt;a href=&#34;http://ws.tramtracker.com.au/pidsservice/pids.asmx?op=GetNewClientGuid&#34;&gt;GetNewClientGuid&lt;/a&gt;,
and then you’re good to go. Simple.&lt;/p&gt;
&lt;h4 id=&#34;lifx&#34;&gt;LIFX&lt;/h4&gt;
&lt;p&gt;&lt;img src=&#34;./images/1*kV7u23SgO8dmV3wfowS7Jw.jpeg&#34; alt=&#34;LIFX
Glove&#34;&gt;&lt;/p&gt;
&lt;p&gt;One of my fundamental home automation rules, is that the devices must be
controllable locally. I don’t want to have to communicate over the
internet to talk to a device which is running on my local network; it’s
unnecessary. LIFX have a pretty robust looking Remote API, but luckily
they also have a local LAN protocol for communicating with globes. LIFX
Globes are standalone, so the protocol has a discovery mechanism using
UDP for finding globes. NPM has (almost) everything, including a &lt;a href=&#34;https://github.com/MariusRumpf/node-lifx/commit/45c7ab8f88799486d921e6b69e618fb479bc7f0a&#34;&gt;NodeJS
implementation&lt;/a&gt;
of the protocol.&lt;/p&gt;
&lt;h4 id=&#34;docker&#34;&gt;Docker&lt;/h4&gt;
&lt;p&gt;Although Docker is supported on &lt;a href=&#34;https://github.com/moby/moby/pull/24815&#34;&gt;Raspberry
Pesis&lt;/a&gt; , Docker Machine doesn’t
just work out the box. I followed this
&lt;a href=&#34;https://www.thepolyglotdeveloper.com/2017/05/create-raspberry-pi-cluster-docker-swarm-raspbian-linux/&#34;&gt;guide&lt;/a&gt;
to trick it into working. Probably not production grade, but I haven’t
had too many issues with it.&lt;/p&gt;
&lt;h3 id=&#34;lazily-gluing-ittogether&#34;&gt;Lazily Gluing It Together&lt;/h3&gt;
&lt;p&gt;My implementation is fairly rudimentary; it’s on
&lt;a href=&#34;https://github.com/ggotti/tramLight&#34;&gt;Github&lt;/a&gt;. The application runs on a
timer, and effectively runs for 30 minutes over my departure window.
When a tram is within 7.5 minutes, the light is turned on, and then as
the tram gets closer, the brightness increases. When it’s really close
the light pulses to signify it’s now time to leave. If the tram is
arriving in less than 4 minutes, the light is then turned off, as I
won’t be able to make it in time.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/1*Cq-itC3kXL-MjtLnbpv7vA.gif&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;The real benefit of this “thing” is that it allows me to continue my
morning routine without needing to constantly refresh my phone for tram
arrival information. It’s non-invasive, yet still informative. This is
how we should think of IoT devices, they need to enhance, not inhibit
our lives.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Putting the FUN into Functioning CI/CD</title>
      <link>https://gerard.au/2017-06-25_putting-the-fun-into-functioning-ci-cd-ae7adb19ea17/</link>
      <pubDate>Sun, 25 Jun 2017 00:00:08 -1000</pubDate>
      
      <guid>https://gerard.au/2017-06-25_putting-the-fun-into-functioning-ci-cd-ae7adb19ea17/</guid>
      <description>&lt;iframe src=&#34;//www.slideshare.net/slideshow/embed_code/key/EpqbX0Zi8hZaKw&#34; width=&#34;595&#34; height=&#34;485&#34; frameborder=&#34;0&#34; marginwidth=&#34;0&#34; marginheight=&#34;0&#34; scrolling=&#34;no&#34; style=&#34;border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;&#34; allowfullscreen&gt; &lt;/iframe&gt;
&lt;p&gt;This is a presentation I gave at the inaugural &lt;a href=&#34;https://www.meetup.com/jHipster-Melbourne/&#34;&gt;Melbourne JHipster
meetup&lt;/a&gt;. A big thanks to all
you came along to hear me ramble for 15 minutes or so.&lt;/p&gt;
&lt;p&gt;The itsy bitsy source code is located
&lt;a href=&#34;https://github.com/ggotti/jhipster-ci-cd-talk&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/1*l0hbykPdxUcJHQp065qCAw.jpeg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Intro to JHipster</title>
      <link>https://gerard.au/2017-05-03_intro-to-jhipster-f80e8283ef06/</link>
      <pubDate>Wed, 03 May 2017 00:00:08 -1000</pubDate>
      
      <guid>https://gerard.au/2017-05-03_intro-to-jhipster-f80e8283ef06/</guid>
      <description>&lt;h3 id=&#34;my-first-look-atjhipster&#34;&gt;My first look at JHipster&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;./images/1*jznFZXW3tXjNJRjYpknPpg.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;So I’ve just recently switched jobs, and one of the most interesting
aspects of my new employer, &lt;a href=&#34;https://www.ippon.tech/&#34;&gt;Ippon&lt;/a&gt;, is that
they develop &lt;a href=&#34;https://jhipster.github.io/&#34;&gt;JHipster&lt;/a&gt;. One of my first
tasks was to familiarise myself with the framework, what it is, how to
use it, and how to deploy it, and boy was I in for an adventure. On
first glance it looks like a standard Yeoman based scaffolder for Java
applications. You answer some questions in the CLI, you hit enter, and
boom, out comes a fully formed Spring-Boot Java application. However the
more I looked into it, the more I started to enjoy all the included
goodies.&lt;/p&gt;
&lt;h3 id=&#34;features&#34;&gt;Features&lt;/h3&gt;
&lt;h4 id=&#34;microservices&#34;&gt;Microservices&lt;/h4&gt;
&lt;p&gt;Instead of just generating Java Applications, JHipster has an entire
microservice approach for deploying applications. People talk about
microservices as a nebulous cure to all technological woes, but the
reality can be quite complex and difficult if not properly managed.
JHipster presents a vision of how to build and deploy a microservices
solution, based on Spring Cloud and Netflix OSS. It might not align to
everybody’s vision, but it’s a solid, production-ready foundation. In
its most basic configuration, there are three main components to the
JHipster microservice stack:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/1*ovusz6rmoRIMjPU_hnixRA.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Gateway: The naming of this component confuddled me initially, but
it’s effectively an API Gateway Server and a static file-server for
your SPA.&lt;/li&gt;
&lt;li&gt;Microservices: The Java based microservice heart. Can be deployed in a
variety of configurations, including databases and the like.&lt;/li&gt;
&lt;li&gt;Service Discovery: Method for services to discover one another, and
share configuration details. There was initially a JHipster developed
JHipster Registry, but Consul can now be used in place of that.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Note: There is also the option for an authorisation server, but that’s
a discussion for another time.&lt;/em&gt;&lt;/p&gt;
&lt;h4 id=&#34;entity-creation&#34;&gt;Entity Creation&lt;/h4&gt;
&lt;p&gt;JHipster has an inbuilt UML like language which allows you to model
relationships between entities. I’ve struggled with these kinds of tools
in the past (I’m looking at you Rational Software Architect), but this
one is pretty intuitive. It generates helpful boiler code not just at
the entity level, but also full CRUD level endpoints. It also generates
sensible Java code which won’t make you vomit. There is an online tool
called &lt;a href=&#34;https://jhipster.github.io/jdl-studio/&#34;&gt;JDL Studio&lt;/a&gt;, which
allows you to model your entities. You then then feed the generated file
into the entity sub generator, and out comes all the entities beans,
Liquibase scripts, endpoints and tests you could ever dream of.&lt;/p&gt;
&lt;h4 id=&#34;docker&#34;&gt;Docker&lt;/h4&gt;
&lt;p&gt;Docker is used for spinning up dependencies, and can be used to package
your application. Docker make everything better &lt;em&gt;*exclusions apply&lt;/em&gt;.&lt;/p&gt;
&lt;h4 id=&#34;testing&#34;&gt;Testing&lt;/h4&gt;
&lt;p&gt;Out of the box it contains options for a bunch of testing frameworks, on
both the Java and JavaScript side of the fence. Tools like Protractor
are available out of the box with PhantomJS. I’d probably swap that out
for a Chrome based Docker image with a Remote Web Driver until headless
Chrome is available.&lt;/p&gt;
&lt;h4 id=&#34;ci-generation&#34;&gt;CI Generation&lt;/h4&gt;
&lt;p&gt;Need to add a pipeline to Gitlab? Done.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/1*WbtOwgK08xoSqfZkjsFcFA.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;deployment&#34;&gt;Deployment&lt;/h4&gt;
&lt;p&gt;Out of the box it’s trivial to run a JHipster application through
docker-compose. Everything is there with a nice little bow on it. If you
want to run something in production, there are a bunch of deployment
options in that space. Due to my pathological love of containers, I gave
the
&lt;a href=&#34;https://jhipster.github.io/rancher/https://jhipster.github.io/rancher/&#34;&gt;Rancher&lt;/a&gt;
option a go. Due to its need to have access to a Docker Registry, it’s
not quite as straightforward as some of the other interactions, but it
does work as it says on the box. I ended up using Amazon’s &lt;a href=&#34;https://aws.amazon.com/ecr/&#34;&gt;Container
Registry&lt;/a&gt;, which worked after following
&lt;a href=&#34;http://rancher.com/using-amazon-container-registry-service/&#34;&gt;this&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;where-to-use-jhipster&#34;&gt;Where to use JHipster?&lt;/h3&gt;
&lt;p&gt;The obvious starting place is the microservice generator. It’s a quick
and relatively painless way of generating Java microservices. I’ve
worked for a number of organisations which have developed similar, but
not nearly as feature rich tools internally, and it’s a great way of
reducing all that Sprint 0 project setup malarchy. I mean who doesn’t
like wiring up Spring Contexts?&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/1*AQzsGz4DahaRMcSBKmBw4A.png&#34; alt=&#34;Rancher UI with
JHipster Deployed&#34;&gt;&lt;/p&gt;
&lt;p&gt;The gateway component is an interesting concept, but certainly for the
customers I’ve worked with, a harder sell. They’ve spent a number of
years investing in monolithic API gateways, and there would certainly be
a reluctance to move away from that towards a more feature-centric API
and application approach. Saying that, the ridginessness of the
monolithic API gateway and the constraints it can put on innovating
front end applications may foster a willingness to push gateway
functionality back into application teams. This would be where the
JHipster Gateway would really shine, particularly for JVM centric
organisations.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>AWS Lambda, How to Tape a Bunch of Cats Together</title>
      <link>https://gerard.au/2016-10-06_aws-lambda--how-to-tape-a-bunch-of-cats-together-a92ba544fdf7/</link>
      <pubDate>Thu, 06 Oct 2016 00:00:08 -1000</pubDate>
      
      <guid>https://gerard.au/2016-10-06_aws-lambda--how-to-tape-a-bunch-of-cats-together-a92ba544fdf7/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;./images/1*KU_sEwSKy7rJh4w4rM6I5w.jpeg&#34; alt=&#34;AWS Architecture &amp;amp; Developer Day, Melbourne with
Werner Vogels&#34;&gt;&lt;/p&gt;
&lt;p&gt;I was lucky enough to attend an &lt;a href=&#34;https://aws.amazon.com/events/aws-architect-dev-day/&#34;&gt;AWS Architect and Developer
Day&lt;/a&gt; in Melbourne
recently, and as you’d expect, Lambda was an underlying theme. I didn’t
attend a single presentation in the developer track that wasn’t plugging
Lambda in one form or another. Lambda for micro-services, Lambda for
Alexa, Lambda as a database event trigger, Lambda for transactions,
Lambda for Lambda. Lambda. Lambda. λ.&lt;/p&gt;
&lt;p&gt;I ❤️ Lambda. I think it’s an excellent tool in the ever increasing bag
of tricks that is AWS, but I’m a bit hesitant when Amazon talks about it
as the golden hammer which is going to fix all your problems. For
starters, you need some rigor around how you develop and deploy your
functions, particularly in large development teams. For example, the
multi-stage functionality offered by the API Gateway are great, but no
enterprise customer is going to allow you to run a “DEV, TEST and PROD”
environments in a single AWS account. Never going to happen. Ever. And
nor should it. Separating environments (or stages) across multiple AWS
accounts, allows for separation of responsibilities that large
organisations love, and are generally required to have. So how should we
look at using Lambda in a large company?&lt;/p&gt;
&lt;h3 id=&#34;developers-get-their-own-aws-credentials-in-a-linkedaccount&#34;&gt;Developers get their own AWS credentials, in a &lt;a href=&#34;http://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/consolidated-billing.html&#34;&gt;Linked Account&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To use Lambda, Developers need AWS credentials to deploy functions, hook
in event triggers and explore the various managed-services that AWS has
to offer. If your company is using AWS, and each one of your backend
developers does not have access to an AWS account, you are doing it
wrong. That’s not to say that it’s just a developer free-for all, where
every man and his dog can spin up new P2 instances on a whim, but there
needs to be sensible controls, and monitoring in-place to strike a
balance. I’d recommend a set of IAM permissions to limit accounts
initially to a subset of managed services, with an appropriate tagging
system in place to identify resource ownership, and a fairly aggressive
monitoring strategy to ensure costs don’t blow out, and developers
aren’t spinning up bot-nets.&lt;/p&gt;
&lt;h3 id=&#34;python--nodejs-only-for-api-gateway-events&#34;&gt;Python &amp;amp; NodeJS Only for API-Gateway Events&lt;/h3&gt;
&lt;p&gt;I’ve mentioned this in a &lt;a href=&#34;https://gerard.gigliotti.com.au/aws-lambda-bringing-java-back-e3c40b111a24&#34;&gt;previous
post&lt;/a&gt;,
but the performance of Java in Lambda is a non-starter if you’re wanting
to use it behind the API-Gateway. Until AWS improves the performance of
the cold-start mechanism for Java based Lambda functions (&lt;a href=&#34;https://forums.aws.amazon.com/thread.jspa?threadID=199685&amp;amp;start=25&amp;amp;tstart=0&#34;&gt;see
here&lt;/a&gt;),
it’s unworkably slow. You can happily use Java for non-time sensitive
operations, such a Dynamo DB triggers, but just don’t use it for API
Gateway functions. If you’re thinking about writing another function to
keep the container warm by triggering it every 5 minutes, back away from
the keyboard, go get yourself a coffee, come back, and then spend that
time you were going to use writing the trigger function to rewrite your
code in NodeJS or Python. You’ll be much happier, everybody will be much
happier.&lt;/p&gt;
&lt;h3 id=&#34;lambda-function-migration&#34;&gt;Lambda Function Migration&lt;/h3&gt;
&lt;p&gt;The following diagram demonstrates an approach for migrating Lambda
functions between environments. There could be multiple environments in
a single AWS Account, such as with the Test AWS Account, or there could
be a single environment, just like production. Why the multiple account
approach? It separates concerns, and ensures that breaches or problems
in lower accounts have no chance of affecting production. You don’t want
to be in the scenario where somebody accidentally deploys a development
version of a Lambda function into your production API Gateway stage,
something that can easily occur if you’re using a single AWS Account
with poorly configured IAM permissions. In this model, there are
multiple stage-gates to ensure only verified changes make it to
production.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/1*bmWvyuqaa3meqsRYRoBhMg.png&#34; alt=&#34;Continues Integration/ Continuous Delivery (CI/CD) for Lambda
Functions&#34;&gt;&lt;/p&gt;
&lt;p&gt;CI/CD can sometimes be bandied about as a single term, but it’s two
distinct practices, which you may wish to use two different tools to
achieve. For the CD component A lot of enterprise customers will just
use a bunch of Jenkins servers together, which can work, but generally
adds a lot of maintenance overhead. Other tools, like
&lt;a href=&#34;http://www.spinnaker.io/&#34;&gt;Spinnaker&lt;/a&gt; or &lt;a href=&#34;http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-codepipeline-pipeline.html&#34;&gt;AWS
CodePipeline&lt;/a&gt;
are generally a better fit. The above diagram has the pipeline tool
being powered off an artefact deploy to S3, which is then deployed out
to the various accounts and environments.&lt;/p&gt;
&lt;h3 id=&#34;lambda-formvp&#34;&gt;Lambda for MVP&lt;/h3&gt;
&lt;p&gt;The real reason to use Lambda, is that it’s fast to develop &amp;amp; deploy.
You can quickly write a small set of functions to prove out a
hypothesis , and then deploy them to run in a production-ready
configuration. You’re not worrying about infrastructure, you’re not
worrying about scaling, you’re focusing on you business problem at hand.
You can code, deploy and analyse faster than you’ve ever been able to
before, which makes it perfect for Minimum Viable Products. Every
company should is interested in the flexibility.&lt;/p&gt;
&lt;p&gt;If you’re Lambda function is being executed too many times a month, and
it’s no longer economical to run in the Lambda, then that’s a great
problem to have, as your product is getting used. You can then start to
look at moving off Lambda, onto more flexible infrastructure. I’ve
worked on projects which sit on thousands of dollars worth of
infrastructure a month, but are only called a few thousand times a day,
which is just not economical. You won’t be running your internet banking
infrastructure on Lambda, but you might want to run your fraud
complaints system.&lt;/p&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;As I said, I ❤️ Lambda. I jump at any opportunity I get to use it. But
large companies that are still skeptical of its magical powers are
missing out on the opportunities to innovate just as quickly as that
startup around the corner. It doesn’t need to be as footloose and
fancy-free as Amazon makes it out to be, and you can still use it in a
controlled and sensible way. If you’re an IT Architect, and you’re
reading this, come to the dark side. The water is lovely.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>The Enterprise JavaScript Conundrum</title>
      <link>https://gerard.au/2016-04-26_the-enterprise-javascript-conundrum-67fe7e2d2f13/</link>
      <pubDate>Tue, 26 Apr 2016 00:00:08 -1000</pubDate>
      
      <guid>https://gerard.au/2016-04-26_the-enterprise-javascript-conundrum-67fe7e2d2f13/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;./images/1*AfZk_o4iwmZ_IlJfmT6RRQ.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;I’m a full stack developer in the way that McDonalds is a breakfast
restaurant; yes, they serve breakfast, but only really from 5am to
10:30am. I only get into the nitty gritty of the frontend when I
absolutely have to, and it’s certainly not a place that I generally
spend a lot of time. Saying that, it’s important to keep up with what’s
out there, at least from an awareness perspective. The frontend darlings
at the moment are clearly Angular (particularly in the Melbourne
market), and React. I’ve worked with Angular (1.3) previously, but
thought it was time to see what React was about, as there’s a lot of
talk about it around the office water cooler. It became a rather
interesting adventure down the rabbit hole…&lt;/p&gt;
&lt;p&gt;First up, the tooling around JavaScript is changing at a neck-snapping
speed. If we look at Babel’s &lt;a href=&#34;https://babeljs.io/docs/setup/#installation&#34;&gt;installation
instructions&lt;/a&gt; (more on that
later), there are 30 configuration options. Kudos to the Babel team for
supporting such an impressive plethora of tools, but it makes selecting
an “optimal” configuration challenging. Then there are the tools
themselves. Tools such as Grunt which were du jour a couple of years ago
have been relegated to almost a pariah status amongst some of the new
libraries. If you’re not starting with Webpack (maybe with a hint of
Gulp), then you’re not starting on the right foot. Webpack itself has a
fairly steep learning curve, but once you’ve got it setup correctly you
wonder why you ever lived without it. The automatic refresh capabilities
with the &lt;a href=&#34;http://webpack.github.io/docs/webpack-dev-server.html&#34;&gt;inbuilt
dev-server&lt;/a&gt; are
super helpful. Saying that, it doesn’t “just work” out of the box, and
you still need to tinker around with it before it comes to life.&lt;/p&gt;
&lt;p&gt;To use the latest version of
&lt;a href=&#34;http://www.ecma-international.org/ecma-262/6.0/#sec-arrow-function-definitions&#34;&gt;JavaScript&lt;/a&gt;,
ES2015(ES6), you really need to use a transpiler like Babel to convert
the code down into something more universally implemented across the
various browsers + node. If for example you want to use the new &lt;a href=&#34;https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions%29&#34;&gt;arrow
function&lt;/a&gt;
it doesn’t work on Mobile Safari, which is not going to get you many
friends on mobile.&lt;/p&gt;
&lt;p&gt;It’s a pretty exciting time to be a JavaScript developer, but I’m
concerned that the pace of innovation isn’t compatible with the approach
taken by large enterprise customers when it comes to maintaining their
websites/applications. Companies in Australia might be shifting their
development practices to being more agile-oriented, but their approach
to projects and code maintenance is still very much stuck in the
waterfall. You might work for &lt;em&gt;n&lt;/em&gt; sprints to deliver a new application,
where you’ve used something like Webpack, and React, but once it’s
delivered it’s then handed off to a Business As Usual (BAU) team which
doesn’t necessarily have the funding to continually update and maintain
the codebase. I’ve worked on applications during the last year that will
be in one form or another still being used by their respective clients
for the next five years. What happens if they need to add some
functionality in five years time? Will they still be able to download
and use the Webpack tooling with the required dependencies?&lt;/p&gt;
&lt;p&gt;The solution is understandably to change the way codebases are
developed, maintained and funded within companies, but it’s hard to
encourage these practices without some kind of “crises” to facilitate
the discussion. I think it’s about curtailing over excited developers to
a certain set of approved tools, and then making sure new versions or
approaches are rolled out across every living application. I was working
with a client last year where they were trying to run multiple, older
versions of Angular applications on the same page. These problems are a
development nightmare and ultimately end up in sub-standard user
experiences. A trickle upgrade approach needs to be mandated for all
enterprise projects, especially when dealing with consumer facing
JavaScript projects. It might not be as exciting now, but you’ll be
thankful when you’re getting called up in five years to update your
business critical Angular 1.5 app.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>AWS Lambda — Bringing Java Back</title>
      <link>https://gerard.au/2015-08-24_aws-lambda---bringing-java-back-e3c40b111a24/</link>
      <pubDate>Mon, 24 Aug 2015 00:00:08 -1000</pubDate>
      
      <guid>https://gerard.au/2015-08-24_aws-lambda---bringing-java-back-e3c40b111a24/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;./images/0*KOaCb5W2Qq4x4hnB.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;So I spend the day toiling away at the big-bank enterprise Java coal
face, so I’m generally not very motivated to play with it when I get
home. I’ll sheepishly admit that I hadn’t even installed Java 8 onto my
laptop until recently. It’s not that I wasn’t excited about Java’s
lambda support, the streams processing, or Nashhorn, it’s just that
it’ll be years before I’ll get to actually use them for a corporate
customer.&lt;/p&gt;
&lt;p&gt;I was pretty excited by the &lt;a href=&#34;https://aws.amazon.com/blogs/aws/aws-lambda-update-run-java-code-in-response-to-events/&#34;&gt;AWS Lambda
announcement&lt;/a&gt;
back in June announcing Java support. So I thought I’d give it a try.
After playing around with it, for a bit, I’ve written up a list of hints
and lessons from my first week playing with it:&lt;/p&gt;
&lt;h4 id=&#34;make-your-zip-filesmall&#34;&gt;Make your zip file small….&lt;/h4&gt;
&lt;p&gt;Every time you make an update to your Lambda function, you need to
update the entire associated JAR; no in-window text editing for you. If
you’re on a terrible ADSL connection, like I am, then failing to do this
will result in a lot of heartache. If you’re relying on other AWS
services, ensure you pull in only the AWS dependencies for the services
you require. Do not use the standard catch all &lt;a href=&#34;http://mvnrepository.com/artifact/com.amazonaws/aws-java-sdk&#34;&gt;AWS SDK For
Java&lt;/a&gt;. It
comes in at over 45 megabytes, and it’ll make you cry. Amazon is kind
enough to split each service into its own little package. Also use S3 to
store your binaries; the sync appears to be much faster than the general
Lambda upload.&lt;/p&gt;
&lt;h4 id=&#34;warm-up&#34;&gt;Warm up&lt;/h4&gt;
&lt;p&gt;Lambda functions take a while to warm up. If you haven’t used it for a
while, or you’ve just deployed it, be prepared to see a long response
time on your first request. This is fine for when you’re just playing
around with it from the AWS-console, but it can be a little annoying if
you’re invoking it from the API Gateway, which has a fixed 10 second
timeout. 504s abound. For production, I imagine you could use some kind
of heartbeat for the Lambda function, at least to have a minimum number
of instances running at all times.&lt;/p&gt;
&lt;h4 id=&#34;context-of-therequest&#34;&gt;Context of the Request&lt;/h4&gt;
&lt;p&gt;One of AWS’ strengths is the robust identity and access management
framework. When your Lambda function executes, it automagically has the
access granted in the associated roles and policies. So for example, if
your function is trying to insert data into a DynamoDB database, a
policy like this will enable you to do so.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Version&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2012-10-17&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Statement&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Sid&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Stmt1428341300017&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Action&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;dynamodb:DeleteItem&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;dynamodb:GetItem&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;dynamodb:PutItem&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;dynamodb:Query&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;dynamodb:Scan&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;dynamodb:UpdateItem&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;dynamodb:ListTables&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Effect&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Allow&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Resource&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Sid&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Resource&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Action&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;logs:CreateLogGroup&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;logs:CreateLogStream&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;logs:PutLogEvents&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Effect&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Allow&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The other side of the coin is making sure that you make use of those
automatic credentials. I wasted hours, hours, trying to make my Lambda
function connect to DynamoDB. My mistake? Not using the default
constructor to access the credentials. I’m sure the seasoned AWS
developers will be able to tell you exactly which
&lt;a href=&#34;http://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/auth/AWSCredentials.html&#34;&gt;AWSCredentials&lt;/a&gt;
implementation to use, but for young players the default constructor is
your best friend.&lt;/p&gt;
&lt;h4 id=&#34;cloud-watchconsole-debugging&#34;&gt;Cloud Watch — Console Debugging&lt;/h4&gt;
&lt;p&gt;Lambda can send all your logging statements to Cloud Watch. There are
really two ways to log. You can use System.out, which will make seasoned
Java developers turn over in their respective graves, or you could use
the
&lt;a href=&#34;http://docs.aws.amazon.com/lambda/latest/dg/java-context-object.html&#34;&gt;“context.getLogger()”&lt;/a&gt;
method available in the context. Of course, the issue of having to pass
your logging mechanism around the stack isn’t ideal, but if that’s the
pattern, that’s the pattern.&lt;/p&gt;
&lt;h4 id=&#34;forget-the-128-mboption&#34;&gt;Forget the 128 mb option&lt;/h4&gt;
&lt;p&gt;The base 128mb option is great if you want to write a hello world
example, but if you’re doing anything else other than that, you’ll want
the 256mb memory allocation as a minimum.&lt;/p&gt;
&lt;p&gt;AWS Lambda really turns the Java application deployment model on its
head. It abstracts away the complexities of deploying production
scalable apps, and wraps it up with a nice little bow. It might not be
replacing your legacy stack anytime soon, but it’s certainly a great
glimpse into the future.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Enterprise IT, be careful where you tread</title>
      <link>https://gerard.au/2015-01-29_enterprise-it--be-careful-where-you-tread-83c3477d1295/</link>
      <pubDate>Thu, 29 Jan 2015 00:00:08 -1000</pubDate>
      
      <guid>https://gerard.au/2015-01-29_enterprise-it--be-careful-where-you-tread-83c3477d1295/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;./images/0*yyGXw2C8WoAhPE3f.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;The client I’m working with at the moment is going through a
re-organisation of its delivery teams, and as part of the process
they’re also re-evaluating the tooling used for projects. The management
has given the group free reign in dictating the frameworks and tools we
want to use moving forward, which is liberating, but also potentially
fraught with danger. As technologists it’s so easy to jump straight into
the “shiny shiny” (thanks Paul), and ignore the reality of working
within a risk adverse enterprise IT space. Things to consider:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Maintainability &amp;amp; Supportability&lt;/strong&gt;: Software in large organisations
is used for a very, very long time. The project I’m working on at the
moment is replacing a web application, which is over 14 years old.
It’s not necessarily the prettiest thing, but it’s very stable, and
more importantly it’s maintainable. People with the skillset are easy
to find, and the platform is still supported, which leads to the next
point.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Only proven technologies need apply&lt;/strong&gt;: I love playing with the
latest and greatest at home, but if it hasn’t been proven to work
somewhere else, I’m not interested. For example, we’re building a new
web-app, and I’d love to write it in Golang, but if I can’t get a
Microsoft supported database driver, it’s a hard sell.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Team Velocity&lt;/strong&gt;: Pick technologies that your team knows well. If
you’ve got a bunch of Ruby developers, build your solution in Ruby.
Java developers, a Java solution. This isn’t necessarily exciting, but
it ensures you’re getting results sooner.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It’s fun to be able to branch out and learn new tools, but the above
should always be considered when selecting new tooling for a project. Is
it still going to be fun to work with in a decade’s time?&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Adobe Experience Manager &amp; Docker — Part 2</title>
      <link>https://gerard.au/2014-12-01_adobe-experience-manager---docker---part-2-a97d2b72ac83/</link>
      <pubDate>Mon, 01 Dec 2014 00:00:08 -1000</pubDate>
      
      <guid>https://gerard.au/2014-12-01_adobe-experience-manager---docker---part-2-a97d2b72ac83/</guid>
      <description>&lt;p&gt;So last time I left you, I’d put together a post on getting an Adobe
Experience Manager 6 Author instance running in a Docker container. It
was a great way for me to get my head around Docker, while also playing
with the technology stack I’m currently using at work. Getting it
running was fun, but by itself a single Author instance is a little
pointless; you can achieve the same thing by clicking on the AEM JAR.&lt;/p&gt;
&lt;p&gt;What isn’t pointless, is an Author+Publisher+Dispatcher stack, all
running in individual Docker containers. To orchestrate all this
madness, I’ve put together a &lt;a href=&#34;http://www.fig.sh/&#34;&gt;Fig YAML file&lt;/a&gt; within
a &lt;a href=&#34;https://github.com/ggotti/aem-dev-env&#34;&gt;GitHub repo&lt;/a&gt; to co-ordinate
the various containers. Fig basically is an orchestration utility for
Docker to help describe relationships between containers.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*nKomlIZP3S24m1S-.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Why Docker? It allows you to have a portable full AEM stack that you can
distribute amongst your development team. The power of containerising
the various AEM components allows you to ensure a consistent and
repeatable configuration between team members. I definitely wouldn’t be
using this in a production setting, but it’s perfectly fine for
development.&lt;/p&gt;
&lt;h4 id=&#34;getting-started&#34;&gt;Getting Started&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;Make sure you’ve got Docker, Fig and boot2docker available;
boot2docker is only required on MacOS X or Windows.&lt;/li&gt;
&lt;li&gt;Clone/Download the &lt;a href=&#34;https://github.com/ggotti/aem-dev-env&#34;&gt;Github
repo&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Copy your the installation media and license files into the author
and publisher directories. The install media must be given specific
names depending on location. cq-author-4502.jar for author, and
cq-publish-4503.jar for the publisher.&lt;/li&gt;
&lt;li&gt;Navigate to the basedirectory, and run: fig up.&lt;/li&gt;
&lt;li&gt;Go get a coffee.&lt;/li&gt;
&lt;li&gt;If all has gone to plan, the stack should be running. If you’re
using boot2docker, you’ll need to reference it via its IP ( you can
find it using boot2docker ip ). The ports are as follows:&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;Dispatcher: Accessible over port 80 or 443. For example:
&lt;a href=&#34;http://192.168.59.103/content/geometrixx-outdoors/en.html&#34;&gt;http://192.168.59.103/content/geometrixx-outdoors/en.html&lt;/a&gt; .&lt;/li&gt;
&lt;li&gt;Publisher: Accessible over port 4503.&lt;/li&gt;
&lt;li&gt;Author: Accessible over port 4502.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*xlLwV0trW6D1-yhH.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Hopefully you’ll be able to enjoy your full stack AEM development.&lt;/p&gt;
&lt;h4 id=&#34;a-couple-of-notescaveats&#34;&gt;A couple of notes/caveats&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Link relationships in Docker are currently only one way. There are a
couple of ways around this, but for the moment, I’ve set the build to
only allow one way communication between containers. Ie,
author-&amp;gt;publisher, dispatcher-&amp;gt;publisher. The Fig DNS stuff looks
interesting.&lt;/li&gt;
&lt;li&gt;The standard boot2docker image does not have enough disk or RAM to get
this working correctly. Look at re-initialising it with at least 40gb
of diskspace, and at least 4GB of memory: boot2docker init -m 4092 -s
40000 .&lt;/li&gt;
&lt;li&gt;I downgraded the base image from Centos7 to Centos6. I was having
trouble installing Apache in Centos7, but it seemed to work fine under
6.&lt;/li&gt;
&lt;li&gt;The author-&amp;gt;publisher replication configuration seems to be lost
after setup. If it can’t connect, you’ll want to check that
“publisher:4503” is set in the replication settings.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*Ynq44Eq3DLkcUX4M.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Adobe Experience Manager &amp; Docker</title>
      <link>https://gerard.au/2014-09-08_adobe-experience-manager---docker-b9a3a40e9e81/</link>
      <pubDate>Mon, 08 Sep 2014 00:00:08 -1000</pubDate>
      
      <guid>https://gerard.au/2014-09-08_adobe-experience-manager---docker-b9a3a40e9e81/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;./images/0*far8enfV1jiLIYAf.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Adobe Experience Manager
(&lt;a href=&#34;http://docs.adobe.com/docs/en/aem/6-0.html&#34;&gt;AEM&lt;/a&gt;) is the current
darling of content management systems for large enterprise clients. The
software’s relatively slick interface, and Adobe’s relentless sales team
mean that you can’t swing a cat in Melbourne without hitting a company
looking at deploying AEM.&lt;/p&gt;
&lt;p&gt;I’ve been working on an AEM project the last view months, but to learn
more about AEM deployment process, I decided that I’d try and get it
running within a Docker container. Getting a basic author instance
running on your workstation is as easy as double clicking the
installation file, but more complex deployments with publishers and
dispatchers are more challenging. However, to start with I’ve put
together a basic Docker AEM author image as a proof of concept.&lt;/p&gt;
&lt;p&gt;Automating the AEM installation was challenging enough, as I ended up
needing to use a Python script to monitor when the installation was
actually finished; something that Adobe seems to hide in a command line
argument and a HTTP callback. What happened to the days when an
installer could quit all by itself?&lt;/p&gt;
&lt;p&gt;My complete inexperience with Docker also introduced some infuriating
hindrances in the process. I got all excited by the plethora of recipes
on the &lt;a href=&#34;https://registry.hub.docker.com/&#34; title=&#34;Docker Registry&#34;&gt;Docker
registry&lt;/a&gt; but it
took me a while to discover that not all images are created equal, and
that not all images are compatible. I was quit pleased when I saw I
could get a Java image (“dockerfile / java”), but I wasn’t too pleased
two hours later when I finally noticed that it was based on Ubuntu, and
therefore wasn’t accessible in my Centos image. A valuable lesson
indeed.&lt;/p&gt;
&lt;p&gt;I’ve uploaded my
&lt;a href=&#34;https://registry.hub.docker.com/u/ggotti/aem-author/&#34; title=&#34;Adobe Experience Manager Dockerfile&#34;&gt;Dockerfile&lt;/a&gt;
to the Docker registry, for your enjoyment. Hopefully it’ll give you a
hand with your AEM projects.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Fitbit Powered Home Automation</title>
      <link>https://gerard.au/2014-02-18_fitbit-powered-home-automation-17166f5824cb/</link>
      <pubDate>Tue, 18 Feb 2014 00:00:08 -1000</pubDate>
      
      <guid>https://gerard.au/2014-02-18_fitbit-powered-home-automation-17166f5824cb/</guid>
      <description>&lt;p&gt;My new employer, &lt;a href=&#34;http://www.odecee.com.au&#34;&gt;Odecee&lt;/a&gt;, &lt;a href=&#34;http://www.odecee.com.au/blog/sprout-recap-january-2014/&#34;&gt;runs innovation
events&lt;/a&gt; every
quarter where employees are given a hectic 48 hours to work on an idea.
This time around I worked on a project leveraging Apache Storm, which
was great fun, but the thing that really caught my attention was the
white-paper research being done by the iBeacon team. In particular, the
fact that the Bluetooth LTE software they were using on their iPhones
was able to detect my Fitbit Flex, through the office floor. A little
creepy that my location could be tracked by anyone with an iPhone in the
vicinity, but it got me thinking, could I use this data leakage for
good?&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*ey17YfzFScbbn2tC.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Home automation was the first thing that came to mind. Electricity
prices in Australia are &lt;a href=&#34;http://www.abc.net.au/news/2012-03-21/australians-pay-highest-power-prices-says-study/3904024&#34;&gt;outrageously
expensive&lt;/a&gt;,
and I was looking at a way of controlling my appliance usage using a
&lt;a href=&#34;http://www.belkin.com/au/Products/home-automation/c/wemo-home-automation/&#34;&gt;WeMO
Switch&lt;/a&gt;.
Traditionally you interact with these switches using either the WeMO
Switch + Motion product to detect movement, by manually controlling the
devices with your phone, or more recently using
&lt;a href=&#34;http://ifttt.com&#34;&gt;IFTTT’s&lt;/a&gt; location services. All these approaches are
cumbersome. I want my house to know when I’m home, and this is where the
Fitbit comes in. I wear my Fitbit almost all the time, so it’s a perfect
way of detecting my presence.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*06WFHyA9M2-VYDIb.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;So the plan was to use a Raspberry PI with a Bluetooth 4.0 USB Dongle to
scan for my Fitbit. I would then connect the Raspberry Pi to IFTTT
(somehow), which has a handy WeMO channel for controlling the plug.&lt;/p&gt;
&lt;p&gt;I wrote a tiny &lt;a href=&#34;https://github.com/ggotti/homeScan&#34;&gt;Python script&lt;/a&gt; which
scans for the presence of my Fitbit using the
&lt;a href=&#34;http://www.bluez.org/&#34;&gt;Bluez&lt;/a&gt; hcitool utility. When my Fitbit’s MAC
address is detected, it records a timestamp against the reading. Then,
every 10 seconds it checks the device and timestamp map. If a device
hasn’t been detected for the last 30 minutes, it sends an email to IFTTT
telling it to turn the WeMO switch off. If a device has been detected,
and the script previously recorded an off state, then it sends an email
to IFTTT to turn the WeMO on.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*PPpijc34BXsvbMO5.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Why email? Well although IFTTT allows you to connect all manner of
web-sites together, there is no straightforward way of adding your own
data in, particularly via some kind of API. To get around this, email
seemed to me like the easiest option. So my two IFTTT plans wait for
emails, and when an email is received with a #deviceOn/#deviceOff tag,
the WeMo will be toggled on and off.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*LeGUVw9GYTNCAGBa.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;There you have it, the basis for a Fitbit powered home-automation
system. It seems to be working relatively well at the moment, and I
haven’t seen any noticeable drop in my Fitbit battery. Hopefully there’s
a drop in my power bill.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Meow you doing</title>
      <link>https://gerard.au/2013-02-25_meow-you-doing-e6c60b357633/</link>
      <pubDate>Mon, 25 Feb 2013 00:00:08 -1000</pubDate>
      
      <guid>https://gerard.au/2013-02-25_meow-you-doing-e6c60b357633/</guid>
      <description>&lt;p&gt;So for my birthday last year, I was pleasantly surprised to receive a
Lucky Cat, traditionally refered to as a Maneki-nek in Japanese. You
typically see them in businesses, beckoning you to come inside. The cat,
powered by two AA batteries, slowly waves its hand at you. That is,
until it runs out of batteries.&lt;/p&gt;
&lt;p&gt;Annoyed at having to change the batteries, even with rechargeables, I
ventured a little outside my comfort zone, and splashed out on an
&lt;a href=&#34;http://en.wikipedia.org/wiki/Arduino&#34;&gt;Arduino Uno&lt;/a&gt; which can be wired
up to operate various electronics. It’s a bridge between the hardware
and the software worlds. I like a good bridge.&lt;/p&gt;
&lt;h4 id=&#34;arduino-sketch&#34;&gt;Arduino &amp;amp; Sketch&lt;/h4&gt;
&lt;p&gt;Knowing almost nothing about circuitry, I hacked in a design that came
within the &lt;a href=&#34;http://littlebirdelectronics.com/products/sparkfun-inventors-kit-for-arduino&#34;&gt;Sparkfun Inventors
Kit&lt;/a&gt;
documentation. After spending ages tinkering with the wiring, and a bit
of Macgyver style blue tacking, I was able to get the Arduino to power
the beckoning hand.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*TI9-vVWfHFn5P8ZP.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;The Arduino takes has its own special language, which you use to write
“sketches”, which are then uploaded to the device. My sketch, which is
rather primitive, is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;/*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;  Cat.sketch
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;Sketch is used to read in an integer value from a serial connection,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;  and then enable PIN 9 for the supplied amount of time.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Port 9 is being used to control the cat
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; CAT_PIN_OUT &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;9&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;/*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;  Sketch initialisation.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;setup&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;pinMode&lt;/span&gt;(CAT_PIN_OUT, OUTPUT);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;//Sets up serial connection
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Serial.&lt;span style=&#34;color:#a6e22e&#34;&gt;begin&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;9600&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;/*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;loop&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;//The duration, in milliseconds to elave the cat arm running for
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; timeout &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;/*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;    Puts the code into a never ending loop
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;  */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt;(true)  &lt;span style=&#34;color:#75715e&#34;&gt;// &amp;#34;true&amp;#34; is always true, so this will loop forever.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#75715e&#34;&gt;// First we check to see if incoming data is available:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; (Serial.&lt;span style=&#34;color:#a6e22e&#34;&gt;available&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        timeout &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//Read in the timeout value from the serial input.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        timeout &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;  Serial.&lt;span style=&#34;color:#a6e22e&#34;&gt;parseInt&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Serial.&lt;span style=&#34;color:#a6e22e&#34;&gt;print&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Timeout &amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Serial.&lt;span style=&#34;color:#a6e22e&#34;&gt;print&lt;/span&gt;(timeout);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Serial.&lt;span style=&#34;color:#a6e22e&#34;&gt;println&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;//Turn the power on, wait the &amp;#34;timeout&amp;#34; of time, and then turn it off
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt;(timeout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;analogWrite&lt;/span&gt;(CAT_PIN_OUT, &lt;span style=&#34;color:#ae81ff&#34;&gt;255&lt;/span&gt;);  &lt;span style=&#34;color:#75715e&#34;&gt;// turns arm On. 255 means full power
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;delay&lt;/span&gt;(timeout);   &lt;span style=&#34;color:#75715e&#34;&gt;//leaves cat arm running for as long as the timeout  millisecconds          analogWrite(CAT_PIN_OUT, 0);  // turns arm off
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//Short delay before trying to re-read the serial connection
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;delay&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;50&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So this code basically just reads in an integer value from the serial connection, and then turns the cat’s arm on for the supplied amount of time. The next step is to invoke the code from the computer.&lt;/p&gt;
&lt;h4 id=&#34;python-twitter&#34;&gt;Python &amp;amp; Twitter&lt;/h4&gt;
&lt;p&gt;Communication from your computer (in this case, my mac), to the Arduino is relatively simple. All you need is a library called pySerial (&lt;a href=&#34;http://pyserial.sourceforge.net/&#34;&gt;http://pyserial.sourceforge.net/&lt;/a&gt;) and you’re ready to go.  So I grabbed the library, and I enjoyed stopping and starting my cat, but I wanted a little more from it. And that ‘more’ manifested itself in the twitter streaming API(&lt;a href=&#34;https://dev.twitter.com/docs/streaming-apis)&#34;&gt;https://dev.twitter.com/docs/streaming-apis)&lt;/a&gt;.  The streaming API offers a tiny, miniscule sample of twitter;’s global stream of tweets, which you can filter on. So after a bit of searching I stumbled across tweepy (&lt;a href=&#34;https://github.com/tweepy/tweepy)&#34;&gt;https://github.com/tweepy/tweepy)&lt;/a&gt;, a python API that has access to the streaming API. Without further ado:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;import serial
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;import time
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;import sys
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;import inspect
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;from Queue import Queue
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;from threading import Thread
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;import time
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;import tweepy
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;from tweepy.streaming import StreamListener
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;from tweepy import OAuthHandler
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;from tweepy import Stream
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Twitter consumer and access keys
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;consumer_key&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;lt;INSERT_HERE&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;consumer_secret&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;lt;INSERT_HERE&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;access_key &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;lt;INSERT_HERE&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;access_secret &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;lt;INSERT_HERE&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Wave duration
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;WAVE_DURATION &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;3000&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;#&lt;/span&gt; in milliseconds
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SLEEP_TIME &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;#&lt;/span&gt; in seconds
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#List of twitter terms
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;STREAM_FILTER&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&amp;#39;#&lt;/span&gt;cat,&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;#&lt;/span&gt;cats&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Establish authentication information
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;auth &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; tweepy.&lt;span style=&#34;color:#a6e22e&#34;&gt;OAuthHandler&lt;/span&gt;(consumer_key, consumer_secret)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;auth.&lt;span style=&#34;color:#a6e22e&#34;&gt;set_access_token&lt;/span&gt;(access_key, access_secret)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;api &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; tweepy.&lt;span style=&#34;color:#a6e22e&#34;&gt;API&lt;/span&gt;(auth)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# serial connection to the arduino
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;s &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; serial.&lt;span style=&#34;color:#a6e22e&#34;&gt;Serial&lt;/span&gt;(port&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;dev&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;tty.usbmodem1d21&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&amp;#39;&lt;/span&gt;, baudrate&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;9600&lt;/span&gt;,timeout&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Queue for work thread. We only want the thread handling one &amp;#34;wave&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# item at a time.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;q &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Queue&lt;/span&gt;(maxsize&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Thread which handles the cat&amp;#39;s arm waving
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;def &lt;span style=&#34;color:#a6e22e&#34;&gt;worker&lt;/span&gt;()&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Loop forever. Retrieve item from queue, and then sleep
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; True:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     item &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; q.&lt;span style=&#34;color:#a6e22e&#34;&gt;get&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      time.&lt;span style=&#34;color:#a6e22e&#34;&gt;sleep&lt;/span&gt;(SLEEP_TIME)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      s.&lt;span style=&#34;color:#a6e22e&#34;&gt;write&lt;/span&gt;(WAVE_DURATION)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      s.&lt;span style=&#34;color:#a6e22e&#34;&gt;write&lt;/span&gt;(&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#starting cat thread
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;t &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Thread&lt;/span&gt;(target&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;worker)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;t.daemon &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; True
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;t.&lt;span style=&#34;color:#a6e22e&#34;&gt;start&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Tweepy sample based on Based on this:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# http:&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//answers.oreilly.com/topic/2605-how-to-capture-tweets-in-real-time-with-twitters-streaming-api/
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;class &lt;span style=&#34;color:#a6e22e&#34;&gt;StreamListener&lt;/span&gt;(tweepy.StreamListener)&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    def &lt;span style=&#34;color:#a6e22e&#34;&gt;on_status&lt;/span&gt;(self, status)&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print status.text
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#75715e&#34;&gt;# Adds the wave request to the queue.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#75715e&#34;&gt;# Will throw an error if there is already a request
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#75715e&#34;&gt;# in the queue
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        try:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              q.&lt;span style=&#34;color:#a6e22e&#34;&gt;put_nowait&lt;/span&gt;(&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&amp;#39;&lt;/span&gt;cat&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         except:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             print &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;def &lt;span style=&#34;color:#a6e22e&#34;&gt;on_error&lt;/span&gt;(self, status_code)&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&amp;#39;&lt;/span&gt;Encountered error with status code:&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&amp;#39;&lt;/span&gt;, status_code
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; True &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;#&lt;/span&gt; Don&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&amp;#39;&lt;/span&gt;t kill the stream
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;def &lt;span style=&#34;color:#a6e22e&#34;&gt;on_timeout&lt;/span&gt;(self)&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&amp;#39;&lt;/span&gt;Timeout...&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; True &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;#&lt;/span&gt; Don&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&amp;#39;&lt;/span&gt;t kill the stream
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Start the stream
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sapi &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; tweepy.streaming.&lt;span style=&#34;color:#a6e22e&#34;&gt;Stream&lt;/span&gt;(auth, &lt;span style=&#34;color:#a6e22e&#34;&gt;StreamListener&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sapi.&lt;span style=&#34;color:#a6e22e&#34;&gt;filter&lt;/span&gt;(track&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;[STREAM_FILTER] )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I ran into some threading issues combining the two APIs. By setting up a processing thread, and queue with a maximum depth of 1, I was able to ensure the Arduino was only handling one request at a time.&lt;/p&gt;
&lt;p&gt;So here we go, the cat in action:
&lt;video class=&#34;video-shortcode&#34; width=&#34;100%&#34; preload=&#34;auto&#34; controls&gt;
    &lt;source src=&#34;./videos/tumblr_miob6o32vr1rnitgb.mp4&#34; type=&#34;video/mp4&#34;&gt;
    There should have been a video here but your browser does not seem
    to support it.
&lt;/video&gt;
&lt;/p&gt;
&lt;p&gt;The next step is to get myself an Ethernet shield for the Arduino, and maybe a web-cam.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Raspberry Pi Vs TDI</title>
      <link>https://gerard.au/2012-11-13_raspberry-pi-vs-tdi-bd361980e002/</link>
      <pubDate>Tue, 13 Nov 2012 00:00:08 -1000</pubDate>
      
      <guid>https://gerard.au/2012-11-13_raspberry-pi-vs-tdi-bd361980e002/</guid>
      <description>&lt;p&gt;After waiting almost 14 weeks, my &lt;a href=&#34;http://www.raspberrypi.org&#34;&gt;Raspberry
Pi&lt;/a&gt; Model B finally arrived in the mail. I
rushed down to the local Office Works, picked up a cheap SD card and a
micro-USB charger, and then proceeded to download and install Raspbian;
the Debian based distribution optimized for the Raspberry Pi. After some
fiddling with the clear-case, and a lot of finger crossing, the machine
booted up. So, now what?&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*jm-M6WLb7RRaPZw-.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;When the Raspberry PI hit the shelves, one of things that I got really
excited about was seeing it running WebSphere Application Server V8.5.
&lt;a href=&#34;http://devangelist.blogspot.com.au/2012/04/liberty-pi-running-websphere-on.html&#34;&gt;Simone
Maple&lt;/a&gt;
put up a post showing a Raspberry PI running the new Liberty Profile. So
it got me thinking; if it can run Websphere, can it run &lt;a href=&#34;http://www-01.ibm.com/software/tivoli/products/directory-integrator/&#34;&gt;Tivoli
Directory
Integrator&lt;/a&gt;?
TDI is a Java based integration product for quickly writing programs to
move data from one point to another; think of it as a swiss-army-knife
application.&lt;/p&gt;
&lt;p&gt;The biggest hurdle is that the RaspberryPI runs an ARM chip, whereas the
official TDI distribution is bundled with an Intel x86 compiled IBM JVM
only. Unfortunately, IBM has not yet released an ARM compatible JVM, so
I’d need to see if I could get the application running with an ARM
compatible JVM. In this case, I selected OpenJDK, based on information
in &lt;a href=&#34;http://www.jonrogers.co.uk/2012/05/crashplan-on-the-raspberry-pi/&#34;&gt;this
post&lt;/a&gt;.
Unfortunately, it doesn’t yet include JIT, so the performance will be
less than ideal. The JVM can be installed using the standard debian
package manager, using the command:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-graf&#34; data-lang=&#34;graf&#34;&gt;sudo apt-get install openjdk-6-jre libjna-java
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So after installing Java, and checking that I could run it, I then
attempted to install TDI, using the command line installation options:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-graf&#34; data-lang=&#34;graf&#34;&gt;sudo ./install_tdiv711_linux_x86.bin LAX_VM &amp;#34;/user/lib/jbm/java-1.6.0-openjdk-armhf/jre/bin/java&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img src=&#34;./images/0*T6qun68wIOzXWIAP.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;I was hopeful that because they’d included an option to use a system JVM
instead of the bundled JVM, that it might just work. One the installer
roared to life, I selected only the TDI server component, and after
waiting over an hour, the installation eventually completed.&lt;/p&gt;
&lt;p&gt;So, would the server start? After fiddling with the JVM settings in the
script file (tdiSetJavaHome.sh), I attempted to start the App Server
using the following command:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-graf&#34; data-lang=&#34;graf&#34;&gt;./ibmdisrv -s ~/solutionDir -c ~/HelloWorld.xml -r Hello
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After pondering the meaning of life, the application failed with a
standard “Class Not Found” exception around the SSL handling, suggesting
that the application was dependent on some JARs within the IBM JVM
implementation. I fished the required JAR files out of a standard RedHat
install of TDI (note: this is never recommended), and then cheekily
copied them into the &amp;lt;INSERT_HERE_DIRECTORY&amp;gt;. I also went into the
solution.properties file and disabled the SSL, system queue and system
store settings to free up RAM. After starting it up again, and crossing
all my fingers and toes, the server started.&lt;/p&gt;
&lt;p&gt;My test application contained a basic TDI Assembly Line which consisted
of HTTP Server component, and a small JavaScript component which
calculates PI by iterating over a loop (based on &lt;a href=&#34;http://www.instructables.com/community/Calculating-Pi-using-simple-JavaScript/&#34;&gt;this
post&lt;/a&gt;).
The script in my test example looped over 1 million times. I know its
comparing apples and raspberry’s, but I wanted to see how the Raspberry
PI compared to my standard work laptop (a Lenovo T410 with a i5 2.53GH
CPU). My laptop could perform the loop in 242 milliseconds, whilst the
Raspberry PI pumped out the same result in 51132 milliseconds. It
doesn’t really prove a lot, and I’m assuming that the 512mb version
would perform a little better. Once OpenJDK is optimised for the
Raspberry PI, I’m sure the performance will improve.&lt;/p&gt;
&lt;p&gt;Laptop Result&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*L5c5y9RHNqrFFP_3.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Raspberry PI Result&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*0NfO6Lcs8WJq83V_.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;So, it’s not breaking any speed records, but it does prove the old Java
adage: write once, run slowly everywhere.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Down the PhoneGap Rabbit Hole</title>
      <link>https://gerard.au/2012-10-04_down-the-phonegap-rabbit-hole-63f84e57b8f/</link>
      <pubDate>Thu, 04 Oct 2012 00:00:08 -1000</pubDate>
      
      <guid>https://gerard.au/2012-10-04_down-the-phonegap-rabbit-hole-63f84e57b8f/</guid>
      <description>&lt;p&gt;I can still remember the day I picked up my first iPhone, a piano black
3G, the first iPhone they’d released in Australia. The little tike could
do everything; surf the net, play 3D games, sync my contacts, make phone
calls. I got all excited about writing my own apps for it, and then I
just did nothing. The unwieldy Objective C syntax and the rather hectic
day job whittled away all enthusiasm I had for building anything on the
platform. Fast forward 4 years and the iOS development environment is a
lot less prickly thanks to the introduction of development frameworks,
and in particular, PhoneGap. PhoneGap allows you to write apps, for
multiple mobile platforms, using good ol’fashioned HTML &amp;amp; Javascript
instead of having to learn the underlying languages for each. Obviously
there are tradeoffs, and they’re well documented (type “phonegap is ”
into google), but the rapid development and multiplatform support should
hopefully overcome any of the other foibles.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*_P9rfQXyKiXvPlcf.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Apart from writing these tirades, I also co-run a bourgeoning &lt;a href=&#34;http://twomunch.com&#34;&gt;Melbourne
food blog&lt;/a&gt;. We’ve got a Wordpress site, with a
custom theme I developed for the desktop, and a lighter one for mobile.
It’s great, and I love running around taking photos of my food (much to
the disgust of my dining companions) and I thought to myself that it
would be great to expand in this area. I didn’t want a direct port of
the website; that’s just useless; nobody uses those apps. So the goal is
to make something people may actually use.&lt;/p&gt;
&lt;h4 id=&#34;v01proof-ofconcept&#34;&gt;V0.1 — Proof of Concept&lt;/h4&gt;
&lt;p&gt;After spending a couple of nights sketching out the look and feel, I
thought it would be time to see if I could translate it into HTML.
Better to get a semi-functional proof of concept going than spend hours
refining a design which ultimately doesn’t actually work on a phone. So
using Eclipse as my editor, Bitnami
&lt;a href=&#34;http://bitnami.org/stack/mappstack&#34;&gt;MAPP&lt;/a&gt; as my app server stack, and
the trusty &lt;a href=&#34;https://chrome.google.com/webstore/detail/kkelicaakdanhinjdeammmilcgefonfh&#34;&gt;Chrome Window
Resizer&lt;/a&gt;,
I translated my rather poor sketches into something that you could click
through. Although both Chrome &amp;amp; Mobile Safari are Webkit at heart, there
are some obvious differences that come into play, especially around
speed. I’d made the innocent mistake of setting the initial-scale to 0.5
value, as a way of ensuring the page would look good on Retina devices.
JQuery mobile didn’t like this, and so an animation that played
perfectly well on my desktop, turned into a spluttering mess on the
phone. Not really the kind of application experience I was looking for.
So at the moment, I’m trying to iron out the JavaScript gremlins in the
layout, using the Chrome extension
&lt;a href=&#34;https://chrome.google.com/webstore/detail/geelfhphabnejjhdalkjhgipohgpdnoc&#34;&gt;Ripple&lt;/a&gt;.
Ripple implements the PhoneGap APIs in Chrome, allowing easy debugging
of web-apps written in PhoneGap (this remove the need to recompile the
application after each change). Well, easier debugging, not easy
debugging :)&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Five Tivoli Identity Manager 5.1 Workflow Tips</title>
      <link>https://gerard.au/2012-08-24_five-tivoli-identity-manager-5-1-workflow-tips-d6b9900cb0e0/</link>
      <pubDate>Fri, 24 Aug 2012 00:00:08 -1000</pubDate>
      
      <guid>https://gerard.au/2012-08-24_five-tivoli-identity-manager-5-1-workflow-tips-d6b9900cb0e0/</guid>
      <description>&lt;p&gt;The Tivoli Identity Manager 5.1 workflow engine can be punishing for
beginners, so I’ve put together a list of handy hints to get you
started. I spent hours banging my head into the desk working some of
these out, but at least now you don’t have to!&lt;/p&gt;
&lt;h4 id=&#34;1-external-script-files-for-development&#34;&gt;1. External Script Files for Development&lt;/h4&gt;
&lt;p&gt;Based on the glorious suggestion from this &lt;a href=&#34;http://blog.stephen-swann.co.uk/2011/03/how-to-externalise-javascript-in-itim.html&#34;&gt;Stephen Swann’s
blog&lt;/a&gt;,
it’s so much easier to externalise your Javascript nodes during
development. It means you don’t need to interact with the workflow
editor as much, and you can quickly test your changes. I’ve attached
below a sample Javascript function which reads in an external Javascript
file from the filesystem, and then executes it.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;externalWorkflow&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;fileName&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#75715e&#34;&gt;// Systems new line component
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;newLineChar&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;java&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;lang&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;System&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;getProperty&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;line.separator&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// Open the external JavaScript file
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;scriptFile&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;java&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;io&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;BufferedReader&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;java&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;io&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;FileReader&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;fileName&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lineOfCode&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;// Read the file and add each line of code we find to a variable.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;// Needs to append the new line character which the readLine function is removing.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; ((&lt;span style=&#34;color:#a6e22e&#34;&gt;lineOfCode&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;scriptFile&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;readLine&lt;/span&gt;()) &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    { &lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lineOfCode&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;newLineChar&lt;/span&gt;; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#75715e&#34;&gt;// Close the file scriptFile.close();
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#75715e&#34;&gt;// Execute the code that we have gathered together
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  eval(&lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;externalWorkflow&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;D:/Workflows_KE/A20010_parseRequest.js&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;2-pojo-is-the-newblack&#34;&gt;2. POJO is the new Black&lt;/h4&gt;
&lt;p&gt;The IBM JSEngine allows you to access plain old Java objects (POJOs)
from within your workflow. This allows you to reuse your existing Java
classes with your TIM workflows, and it allows you to access some of the
more advanced Java features, such Java XML bindings. It’s important,
however, not to use Java as a crutch, particularly if you come from a
Java background. It’s sometimes very tempting to jump into banging out
Java classes, when the same outcome can be achieved a little differently
with a lot less Javascript. With great power comes great responsibility.&lt;/p&gt;
&lt;h4 id=&#34;3-js-objects-javaobjects&#34;&gt;3. JS Objects != Java Objects&lt;/h4&gt;
&lt;p&gt;I learnt this lesson the hard way. Although TIM 5.1 has the ability to
interact with Java objects (by referencing them in the
scriptframework.properties file), the implementation of the IBM JSEngine
appears to have its own “String” object. The following script example,
throws a “Script interpreter error” because the IBM JSEngine doesn’t
implement the getClass() method:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-graf&#34; data-lang=&#34;graf&#34;&gt;var example1 = new java.lang.Integer(0);
var example2 = new java.lang.String(&amp;#34;Hello&amp;#34;);
activity.auditEvent(&amp;#34;Integer: &amp;#34; + example1.getClass());
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img src=&#34;./images/0*aL_rUtMuyxs9YVTG.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-graf&#34; data-lang=&#34;graf&#34;&gt;activity.auditEvent(&amp;#34;String: &amp;#34; + example2.getClass());
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So be careful with your String manipulation, you never know what’s
missing.&lt;/p&gt;
&lt;h4 id=&#34;4-consistent-logging&#34;&gt;4. Consistent Logging&lt;/h4&gt;
&lt;p&gt;One thing that really bugs me (pun intended) when reviewing workflow
code, is the inconsistent use of TIMs logging functions. There are two
approaches to logging, and both have their place.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;http://pic.dhe.ibm.com/infocenter/tivihelp/v2r1/index.jsp?topic=%2Fcom.ibm.itim.doc%2Fref%2Fref_ic_javext_enrole_log.html&#34;&gt;Enrole.log() &lt;/a&gt;— An
error message logged to the msg.log file. Great for detailed technical
errors.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://pic.dhe.ibm.com/infocenter/tivihelp/v2r1/index.jsp?topic=%2Fcom.ibm.itim.doc%2Fref%2Fref_ic_javext_activauditevent.html&#34;&gt;Activity.auditEvent()&lt;/a&gt; — As
the name suggests, a great way of logging audit events which are
visible in the GUI. Log events like state changes, and email
destinations, not stack traces.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;5-automated-unittesting&#34;&gt;5. Automated Unit Testing&lt;/h4&gt;
&lt;p&gt;As I mentioned in &lt;a href=&#34;http://gerard.gigliotti.com.au/post/27885913627/unit-testing-tivoli-identity-manager-workflows&#34;&gt;my previous
post&lt;/a&gt;,
it always pays to automate your workflow unit testing as much as
possible. It can be very tedious manually entering a lot of data into
TIM, so if you can automate it then you’re a step closer to making your
life a lot easier. A lot of TIM developers gloss over this, and it shows
when it comes to QA&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Unit Testing Tivoli Identity Manager Workflows</title>
      <link>https://gerard.au/2012-07-24_unit-testing-tivoli-identity-manager-workflows-d95c35897d7a/</link>
      <pubDate>Tue, 24 Jul 2012 00:00:08 -1000</pubDate>
      
      <guid>https://gerard.au/2012-07-24_unit-testing-tivoli-identity-manager-workflows-d95c35897d7a/</guid>
      <description>&lt;p&gt;At work, I’ve recently dabbled in the mysterious art of Tivoli Identity
Manager (TIM) workflow development. TIM is an extremely flexible
identity lifecycle management product, and part of this flexibility is
due to the ability to write custom workflows with Javascript. However,
the ability to write Javascript workflows is a double-edged sword; the
product makes it very difficult to automate any unit testing of the
workflows. Luckily, automated unit testing can be easily accomplished by
leveraging a couple of different software packages.&lt;/p&gt;
&lt;p&gt;We’re going to use a combination of the &lt;a href=&#34;https://www-304.ibm.com/software/brandcatalog/ismlibrary/details?catalog.label=1TW10IM12&#34;&gt;IBM Tivoli Identity Manager API
Web Services
Wrappers&lt;/a&gt;,
an unsupported web-services interface which exposes the TIM Java API via
SOAP, and we’re going to invoke it using
&lt;a href=&#34;http://www.soapui.org/&#34;&gt;SOAP-UI&lt;/a&gt;, an open-source testing tool which can
invoke web-services. A match made in heaven.&lt;/p&gt;
&lt;p&gt;The download and installation of the TIM Wrapper is straightforward, and
it just sits within the same application server instance as TIM. Once
you’ve installed the application, you can test that the wrapper is
available by retrieving its WSDL through a web-browser
&lt;a href=&#34;http://%3Chost&#34;&gt;http://&amp;lt;host&lt;/a&gt;
name&amp;gt;:9080/ITIMWebServices/services/WSItimService?WSDL&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*TjQajxYj-T2ApXJ3.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Once you’ve confirmed that the WSDL is accessible, it’s time to fire up
SOAP-UI. Right click on “Projects”, and then select “New soapUI
Project”. Add a new project name in, and enter the WSDL tested above. A
new project should be created with all the operations. The WSItimService
WSDL contains most of the TIM API, but not all of it; you might need to
use one of the other services offered by the wrapper to access all of
the API operations. Now we’re ready to setup our unit test extravaganza.&lt;/p&gt;
&lt;p&gt;Right click on the project and click “New Test Suite”, and then enter a
suitable name. The test suite can contain one or more test cases. Your
new test suite will appear, and then right click within the white space
of the dialogue and click “New TestCase”, and then enter “TestCase1” as
the name. The following test case will appear:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*E0beTRCrANWwwviC.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;The test case dialogue allows you to specify a set of steps to validate
your test case. For our sample test case, we’re going to create a new
person object in TIM, and then validate that it’s been successfully
created.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*KV_KeoRRZ1fSAqP3.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;The first step is to login to TIM. Almost all requests into TIM require
a valid session, which can be established using the
“WSItimServiceSoapBinding -&amp;gt; login” request. Click the “SOAP” labeled
button within the testcase, and enter “Establish Session” in the name. A
list of valid requests will then appear; scroll down and select the
“WSItimServiceSoapBinding -&amp;gt; login” request. Click “Ok”. An “Establish
Session” dialogue will now appear, containing a template SOAP message.
To enable login, you’ll need to enter in a valid TIM user ID in the
“principal” element, and the user’s password in the “credential”
element. For example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;soapenv:Envelope&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;xmlns:soapenv=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://schemas.xmlsoap.org/soap/envelope/&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;xmlns:ser=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://services.ws.itim.ibm.com&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;soapenv:Header/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;soapenv:Body&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;ser:login&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;ser:principal&amp;gt;&lt;/span&gt;itim manager&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/ser:principal&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;ser:credential&amp;gt;&lt;/span&gt;superpassword&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/ser:credential&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/ser:login&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/soapenv:Body&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/soapenv:Envelope&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To test that you’ve done it correctly, click the green submit request
button. If all has gone to plan, you should see a rather lengthy
response, containing a “clientSession” and a “sessionID”. These two
values are used in subsequent requests to maintain the session.&lt;/p&gt;
&lt;p&gt;The next step is to create a person in TIM. Click the “SOAP” button
again, but this time select the “WSItimServiceSoapBinding -&amp;gt;
createPerson” request. The XML for this request is a bit more
complicated, so I’ve mocked up a sample below. Copy the following piece
of XML into the request field:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;soapenv:Envelope&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;xmlns:soapenv=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://schemas.xmlsoap.org/soap/envelope/&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;xmlns:xsd=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://www.w3.org/2001/XMLSchema&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;xmlns:xsi=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;soapenv:Body&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;!-- This is a createPerson request --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;createPerson&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;xmlns=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://services.ws.itim.ibm.com&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;!-- The information within this session element is retrieved from the login response. We&amp;#39;ll talk about setting these values later on--&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;session&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;ns1:clientSession&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;xmlns:ns1=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://model.ws.itim.ibm.com&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;?????&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/ns1:clientSession&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;content:enforceChallengeResponse&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;xmlns:content=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://model.ws.itim.ibm.com&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;false&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/content:enforceChallengeResponse&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;excerpt:locale&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;xmlns:excerpt=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://model.ws.itim.ibm.com&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;excerpt:country&amp;gt;&lt;/span&gt;AU&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/excerpt:country&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;excerpt:language&amp;gt;&lt;/span&gt;en&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/excerpt:language&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;excerpt:variant/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/excerpt:locale&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;ns4:sessionID&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;xmlns:ns4=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://model.ws.itim.ibm.com&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;?????&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/ns4:sessionID&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/session&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;!-- This is the container where the person will be created. This is a very simple TIM install, so we&amp;#39;re just creating the person at the base ou. --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;wsContainer&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;ns6:itimDN&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;xmlns:ns6=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://model.ws.itim.ibm.com&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;erglobalid=00000000000000000000,ou=dev, DC=COM&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/ns6:itimDN&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;ns8:select&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;xmlns:ns8=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://model.ws.itim.ibm.com&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;false&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/ns8:select&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;ns11:profileName&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;xmlns:ns11=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://model.ws.itim.ibm.com&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;OrganizationalUnit&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/ns11:profileName&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/wsContainer&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;!-- This secion sets the attributes for the person being created. Again, this is a very simple person example. If you&amp;#39;re creating a more complex example,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;       it&amp;#39;s usually easier to create a sample user through the GUI, and then lookup that person using an LDAP browser to determine the various attribute value. --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;wsPerson&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;ns13:attributes&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;xmlns:ns13=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://model.ws.itim.ibm.com&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;item&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                 &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;!-- This sets the preferred UID for the user. The ${ } brackets contain a piece of Groovy code which will be executed before the request is sent to the server.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;                     This piece of code appends a randomyl generated number to the UID.--&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;ns13:name&amp;gt;&lt;/span&gt;uid&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/ns13:name&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;ns13:values&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                     &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;item&amp;gt;&lt;/span&gt;UIDOne${=(int)(Math.random()*9999999)}&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/item&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/ns13:values&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/item&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;item&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;ns13:name&amp;gt;&lt;/span&gt;sn&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/ns13:name&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;ns13:values&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                     &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;item&amp;gt;&lt;/span&gt;Lee${=(int)(Math.random()*9999999)}&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/item&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/ns13:values&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/item&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;item&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;ns13:name&amp;gt;&lt;/span&gt;givenName&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/ns13:name&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;ns13:values&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                     &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;item&amp;gt;&lt;/span&gt;Peggy&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/item&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/ns13:values&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/item&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;item&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;ns13:name&amp;gt;&lt;/span&gt;cn&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/ns13:name&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;ns13:values&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                     &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;item&amp;gt;&lt;/span&gt;Test CN&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/item&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/ns13:values&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/item&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/ns13:attributes&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;ns15:name&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;xmlns:ns15=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://model.ws.itim.ibm.com&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;UIDOne${=(int)(Math.random()*9999999)}&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/ns15:name&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;ns16:select&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;xmlns:ns16=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://model.ws.itim.ibm.com&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;false&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/ns16:select&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;!-- The type of object being created, in this case, a person. --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;ns17:profileName&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;xmlns:ns17=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://model.ws.itim.ibm.com&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;Person&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/ns17:profileName&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/wsPerson&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;date&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;xsi:type=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;xsd:dateTime&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;2012-02-29T00:38:35.111Z&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/date&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/createPerson&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/soapenv:Body&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/soapenv:Envelope&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This piece of XML code however, is incomplete. We need to copy the
session values from the “WSItimServiceSoapBinding -&amp;gt; login” response,
into this new request. Luckily, SOAPUI has a “Property Transfer”
function to copy the value into the second request. Go back to the Test
Case view, and click the “Property Transfer” button and then specify a
name (ie Session Transfer).&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*9L5bjVlUZcuFn3b2.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;A session transfer dialogue will appear. Click the “Plus” button in the
corner, and enter the name “ClientSession”. A dialogue will appear with
“source” and “target” panels. In the “Source” panel, ensure “Establish
Session” is selected, and the property is set to “Response”. Then set
the following XPATH within the textbox:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-graf&#34; data-lang=&#34;graf&#34;&gt;declare namespace  soapenv=&amp;#34;http://schemas.xmlsoap.org/soap/envelope/&amp;#34;;
declare namespace ser=&amp;#34;http://services.ws.itim.ibm.com&amp;#34;;
//soapenv:Envelope/soapenv:Body/ser:loginResponse/ser:loginReturn/ser:clientSession
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In the “Target” panel, ensure “Create Test Person” is selected, and the
property is set to “Request”. Then set the following XPATH within the
textbox:&lt;/p&gt;
&lt;p&gt;declare namespace soapenv=“http://schemas.xmlsoap.org/soap/envelope/”;
declare namespace ser=“http://services.ws.itim.ibm.com”;&lt;/p&gt;
&lt;p&gt;declare namespace mod=“http://model.ws.itim.ibm.com”;
//soapenv:Envelope/soapenv:Body/ser:createPerson/ser:session/mod:clientSession&lt;/p&gt;
&lt;p&gt;Click “Run” to execute the XPATH. The client session value will now be
copied from the “Login” response to the “createPerson” request. This
also needs to be performed for the Session ID. Use the following XPATHS.&lt;/p&gt;
&lt;p&gt;Source&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;declare namespace soapenv=&amp;#34;http://schemas.xmlsoap.org/soap/envelope/&amp;#34;; declare namespace ser=&amp;#34;http://services.ws.itim.ibm.com&amp;#34;; //soapenv:Envelope/soapenv:Body/ser:loginResponse/ser:loginReturn/ser:sessionID
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Target&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;declare namespace soapenv=&amp;#34;http://schemas.xmlsoap.org/soap/envelope/&amp;#34;; declare namespace ser=&amp;#34;http://services.ws.itim.ibm.com&amp;#34;; declare namespace mod=&amp;#34;http://model.ws.itim.ibm.com&amp;#34;; //soapenv:Envelope/soapenv:Body/ser:createPerson/ser:session/mod:sessionID
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now that we have the session information, we should be able to execute
our first run of the test case. Go back to the test case dialogue, and
click “Run”. If all goes well, you should see a green FINISHED graphic.
You should be able to see the Person in the TIM Pending requests view.&lt;/p&gt;
&lt;p&gt;You may have noticed that although we can now successfully create a
person in TIM, we’re not actually verifying that the person is being
successfully created. TIM is asynchronous, so the response to the
“createPerson” request is a “requestID” and a status that states that
the request is in a “not started” state.&lt;/p&gt;
&lt;p&gt;What we really want to know is if the person has been successfully
created without checking through the GUI. To do this, we’re going to use
a Loop within the TestCase to periodically poll TIM to check the
response status. Once the request has been processed, we’ll then
programmatically evaluate whether it was successful. Manual verification
be gone! Create a new SOAP message called “Get Request Wait” for the
““WSItimServiceSoapBinding -&amp;gt; getRequest” request. Set the body as
follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;soapenv:Envelope&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;xmlns:soapenv=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://schemas.xmlsoap.org/soap/envelope/&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;xmlns:ser=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://services.ws.itim.ibm.com&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;xmlns:mod=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://model.ws.itim.ibm.com&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;soapenv:Header/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;soapenv:Body&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;ser:getRequest&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;ser:session&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;mod:clientSession&amp;gt;&lt;/span&gt;?&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/mod:clientSession&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;mod:enforceChallengeResponse&amp;gt;&lt;/span&gt;false&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/mod:enforceChallengeResponse&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;mod:locale&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;mod:country&amp;gt;&lt;/span&gt;AU&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/mod:country&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;mod:language&amp;gt;&lt;/span&gt;en&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/mod:language&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/mod:locale&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;mod:sessionID&amp;gt;&lt;/span&gt;?&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/mod:sessionID&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/ser:session&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;!-- This is where the request ID will be set by the property transfer --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;ser:requestId&amp;gt;&lt;/span&gt;?&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/ser:requestId&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/ser:getRequest&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/soapenv:Body&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/soapenv:Envelope&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We need to create an additional SOAP message, using the same syntax as
above, but it needs to be called “Get Request Final”. This is the
message we’ll eventually use to verify the completion status. Before we
do that, create a new “Property transfer” called “Get Request Transfer”
above the first “Get Request” message. Create the following transfers
within this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Transfer: ClientSession
Source: Establish Session
Source Property: Response
Source XPATH:
declare namespace soapenv=&amp;#34;http://schemas.xmlsoap.org/soap/envelope/&amp;#34;;
declare namespace ser=&amp;#34;http://services.ws.itim.ibm.com&amp;#34;;
//soapenv:Envelope/soapenv:Body/ser:loginResponse/ser:loginReturn/ser:clientSession
Target: Get Request Wait
Target Property: Request
Target XPATH:
declare namespace soapenv=&amp;#34;http://schemas.xmlsoap.org/soap/envelope/&amp;#34;;
declare namespace ser=&amp;#34;http://services.ws.itim.ibm.com&amp;#34;;
declare namespace mod=&amp;#34;http://model.ws.itim.ibm.com&amp;#34;;
//soapenv:Envelope/soapenv:Body/ser:getRequest/ser:session/mod:clientSession
Transfer: SessionID
Source: Establish Session
Source Property: Response
Source XPATH:
declare namespace soapenv=&amp;#34;http://schemas.xmlsoap.org/soap/envelope/&amp;#34;;
declare namespace ser=&amp;#34;http://services.ws.itim.ibm.com&amp;#34;;
//soapenv:Envelope/soapenv:Body/ser:loginResponse/ser:loginReturn/ser:sessionID
Target: Get Request Wait
Target Property: Request
Target XPATH:
declare namespace soapenv=&amp;#34;http://schemas.xmlsoap.org/soap/envelope/&amp;#34;;
declare namespace ser=&amp;#34;http://services.ws.itim.ibm.com&amp;#34;;
declare namespace mod=&amp;#34;http://model.ws.itim.ibm.com&amp;#34;;
//soapenv:Envelope/soapenv:Body/ser:getRequest/ser:session/mod:clientSession
Transfer: RequestID
Source: Create Test Person
Source Property: Response
Source XPATH:
declare namespace soapenv=&amp;#34;http://schemas.xmlsoap.org/soap/envelope/&amp;#34;;
declare namespace ser=&amp;#34;http://services.ws.itim.ibm.com&amp;#34;;
declare namespace mod=&amp;#34;http://model.ws.itim.ibm.com&amp;#34;;
//soapenv:Envelope/soapenv:Body/ser:createPersonResponse/ser:createPersonReturn/ser:requestId
Target: Get Request Wait
Target Property: Request
Target XPATH:
declare namespace soapenv=&amp;#34;http://schemas.xmlsoap.org/soap/envelope/&amp;#34;;
declare namespace ser=&amp;#34;http://services.ws.itim.ibm.com&amp;#34;;
declare namespace mod=&amp;#34;http://model.ws.itim.ibm.com&amp;#34;;
//soapenv:Envelope/soapenv:Body/ser:getRequest/ser:requestId
Transfer: ClientSession_final
Source: Establish Session
Source Property: Response
Source XPATH:
declare namespace soapenv=&amp;#34;http://schemas.xmlsoap.org/soap/envelope/&amp;#34;;
declare namespace ser=&amp;#34;http://services.ws.itim.ibm.com&amp;#34;;
//soapenv:Envelope/soapenv:Body/ser:loginResponse/ser:loginReturn/ser:clientSession
Target: Get Request Final
Target Property: Request
Target XPATH:
declare namespace soapenv=&amp;#34;http://schemas.xmlsoap.org/soap/envelope/&amp;#34;;
declare namespace ser=&amp;#34;http://services.ws.itim.ibm.com&amp;#34;;
declare namespace mod=&amp;#34;http://model.ws.itim.ibm.com&amp;#34;;
//soapenv:Envelope/soapenv:Body/ser:getRequest/ser:session/mod:clientSession
Transfer: SessionID_final
Source: Establish Session
Source Property: Response
Source XPATH:
declare namespace soapenv=&amp;#34;http://schemas.xmlsoap.org/soap/envelope/&amp;#34;;
declare namespace ser=&amp;#34;http://services.ws.itim.ibm.com&amp;#34;;
//soapenv:Envelope/soapenv:Body/ser:loginResponse/ser:loginReturn/ser:sessionID
Target: Get Request Final
Target Property: Request
Target XPATH:
declare namespace soapenv=&amp;#34;http://schemas.xmlsoap.org/soap/envelope/&amp;#34;;
declare namespace ser=&amp;#34;http://services.ws.itim.ibm.com&amp;#34;;
declare namespace mod=&amp;#34;http://model.ws.itim.ibm.com&amp;#34;;
//soapenv:Envelope/soapenv:Body/ser:getRequest/ser:session/mod:clientSession
Transfer: RequestID_final
Source: Create Test Person
Source Property: Response
Source XPATH:
declare namespace soapenv=&amp;#34;http://schemas.xmlsoap.org/soap/envelope/&amp;#34;;
declare namespace ser=&amp;#34;http://services.ws.itim.ibm.com&amp;#34;;
declare namespace mod=&amp;#34;http://model.ws.itim.ibm.com&amp;#34;;
//soapenv:Envelope/soapenv:Body/ser:createPersonResponse/ser:createPersonReturn/ser:requestId
Target: Get Request Final
Target Property: Request
Target XPATH:
declare namespace soapenv=&amp;#34;http://schemas.xmlsoap.org/soap/envelope/&amp;#34;;
declare namespace ser=&amp;#34;http://services.ws.itim.ibm.com&amp;#34;;
declare namespace mod=&amp;#34;http://model.ws.itim.ibm.com&amp;#34;;
//soapenv:Envelope/soapenv:Body/ser:getRequest/ser:requestId
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img src=&#34;./images/0*sOGhHr8TcMiXaGDo.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Now we need to establish some loop logic. Create a new Property Transfer called “Loop Status” between “Get Request Wait” and “Get Request Final”. Now, click on the test case name, “TestCase1” in my case, and then click the “Test Properties” tab in the lower left hand corner. Add a new property called “getRequestStatus”. Back in the “Loop Status” property transfer, create the following transfer:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Transfer: Job Status
Source: Get Request Wait
Source Property: Response
Source XPATH:
declare namespace soapenv=&amp;#34;http://schemas.xmlsoap.org/soap/envelope/&amp;#34;;
declare namespace ser=&amp;#34;http://services.ws.itim.ibm.com&amp;#34;;
declare namespace mod=&amp;#34;http://model.ws.itim.ibm.com&amp;#34;;
//soapenv:Envelope/soapenv:Body/ser:getRequestResponse/ser:getRequestReturn/ser:processStateString
Target: TestCase1
Target Property: getRequestStatus
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This transfer uses XPATH to copy the request status into the “getRequestStatus” property/variable. We now need a piece of Groovy code to evaluate the status. Within the TestCase, add a new “Groovy Script” below “Loop Status”, called “While Loop”. Within the script, copy the following Groovy script, which will act as our loop logic.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-groovy&#34; data-lang=&#34;groovy&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//This retrieves the status from the testCase property
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;status &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; testRunner&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;testCase&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;getPropertyValue&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;getRequestStatus&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//Prints out the job status
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;log&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;info&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Job Status: &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; status&lt;span style=&#34;color:#f92672&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;/*If the record hasn&amp;#39;t processed yet, loop back around.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;These are the various TIM5.1 processing states. If it is one of
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;these states, go back up to the Get Request Wait Step. Repeat the loop
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;until it has finished processing.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;status &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;In Process&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; status &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Running&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; status &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Not Started&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;//Sleep for 10 seconds.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; Thread&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;sleep&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;10000&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;//Loop back to the request
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  testRunner&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;gotoStepByName&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Get Request Wait&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The above loop logic will exit out when the request state, retrieved by the “Get Request Wait” request, isn’t in the state “In Process”, “Running” or “Not Started”. The key now is to verify whether the request state, once it has finished processing, is successful. We can do this using an assertion on the “Get Request Final” request. Assertions in SOAPUI allow you to apply rules against responses to validate that the response received is correct. Open the “Get Request Final” request, and then click the “Assertions” button in the bottom left corner of the dialogue.&lt;/p&gt;
&lt;p&gt;Add a new insertion, called &amp;ldquo;XPATH Match”, and enter the following values into the dialogue:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;declare namespace soapenv=&amp;#34;http://schemas.xmlsoap.org/soap/envelope/&amp;#34;;
declare namespace ser=&amp;#34;http://services.ws.itim.ibm.com&amp;#34;;
declare namespace mod=&amp;#34;http://model.ws.itim.ibm.com&amp;#34;;
//soapenv:Envelope/soapenv:Body/ser:getRequestResponse/ser:getRequestReturn/ser:statusString

Expected Result:
Succeeded
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This assertion will check that the response received from TIM is successful. Now that the assertion is in place, we can go back to the test case and run it. This code will now establish a session with TIM, create a person, and then verify that the person has been successfully created.  Although this example is fairly basic, this kind of automated unit testing can significantly decrease the amount of time spent developing and verifying TIM Workflow code, as we can create new objects in a matter of seconds, instead of having to tediously do it through the web-interface. This same logic can be applied to not just “Person” objects, but to anything that can be created through the API. The workflow sky is really the limit..&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*Um7bT8qKw1eO4Uc6.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;To get you started, a standalone SOAP-UI project containing this example can be downloaded from &lt;a href=&#34;https://href.li/?https://github.com/ggotti/Tivoli-Identity-Manager-Unit-Testing&#34;&gt;Github&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Hadoop &amp; HBase — Hello World</title>
      <link>https://gerard.au/2012-07-08_hadoop---hbase---hello-world-cb687112914e/</link>
      <pubDate>Sun, 08 Jul 2012 00:00:08 -1000</pubDate>
      
      <guid>https://gerard.au/2012-07-08_hadoop---hbase---hello-world-cb687112914e/</guid>
      <description>&lt;h1 id=&#34;hadoop--hbase--hello-world&#34;&gt;Hadoop &amp;amp; HBase — Hello World&lt;/h1&gt;
&lt;hr&gt;
&lt;h3 id=&#34;hadoop--hbasehelloworld&#34;&gt;Hadoop &amp;amp; HBase — Hello World&lt;/h3&gt;
&lt;p&gt;Ever since seeing the picture of the cute little stuffed elephant on the
page, I was excited about Hadoop. I was also excited about the potential
to quickly build large-scale, distributed applications, but I was mainly
excited about the elephant.&lt;/p&gt;
&lt;p&gt;Harnessing that enthusiasm, this post will document how to build a
venerable “hello world” example using Hadoop &amp;amp; Hbase.
&lt;a href=&#34;http://en.wikipedia.org/wiki/HBase&#34;&gt;Hbase&lt;/a&gt;is a non-relational,
distributed database modeled after Google’s BigTable. Hbase runs on top
of Hadoop, which, in this example provides a distributed file system
where HBase’s data sits.&lt;/p&gt;
&lt;h4 id=&#34;step-1-install-the-operating-system-in-avm&#34;&gt;Step 1: Install the Operating System in a VM&lt;/h4&gt;
&lt;p&gt;First off, I’ve downloaded Ubuntu 12.04 Server, 64 Bit edition, and
installed it in a Virtual Machine. I’ve used VMware Workstation 8.0.3,
but VirtualBox will work just as well. You’ll also want to install a
text editor, such as VIM
(&lt;a href=&#34;http://www.washington.edu/computing/unix/vi.html&#34;&gt;here&lt;/a&gt;’s a tutorial
for those who haven’t used it before):&lt;/p&gt;
&lt;p&gt;sudo apt-get install vim&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*TEY4IF6NWFLbpQSr.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;step-2-modify-hostsfile&#34;&gt;Step 2: Modify Hosts File&lt;/h4&gt;
&lt;p&gt;I ran into a number of unusual issues initially when trying to get HBase
to start, and they were caused by an incorrectly configured /etc/hosts
file. I would have saved myself hours if I’d actually read the Hbase
documentation. Open the /etc/hosts file using:&lt;/p&gt;
&lt;p&gt;sudo vim /etc/hosts&lt;/p&gt;
&lt;p&gt;Change the following host mapping, from:&lt;/p&gt;
&lt;p&gt;127.0.0.1 localhost&lt;/p&gt;
&lt;p&gt;127.0.1.1 ubuntu&lt;/p&gt;
&lt;p&gt;to this:&lt;/p&gt;
&lt;p&gt;127.0.0.1 localhost&lt;/p&gt;
&lt;p&gt;&amp;lt;machineIPAddress&amp;gt; Ubuntu&lt;/p&gt;
&lt;p&gt;where &amp;lt;machineIPAddress&amp;gt; is the IP of the Virtual Machine. The Virtual
Machine in this case is called “Ubuntu”. You can use iconfig on the VM
to get your VMs IP Address.&lt;/p&gt;
&lt;p&gt;In addition, you’ll need to modify the host file entry on your local
machine so it can resolve the hostname of the Virtual Machine.&lt;/p&gt;
&lt;h4 id=&#34;step-3-install-oracle-java-implementation&#34;&gt;Step 3: Install Oracle Java Implementation&lt;/h4&gt;
&lt;p&gt;Hadoop is built and tested with the Oracle JVM, which is surprisingly a
little difficult to install on Ubuntu. The easiest option is to use a
prepackaged version; I ended up using this &lt;a href=&#34;http://www.duinsoft.nl/packages.php?t=en&#34;&gt;Duinsoft
script&lt;/a&gt; to install it. Open
the file /etc/apt/sources.list using:&lt;/p&gt;
&lt;p&gt;sudo vim /etc/apt/sources.list&lt;/p&gt;
&lt;p&gt;And then add the following item to the end of the file:&lt;/p&gt;
&lt;p&gt;deb &lt;a href=&#34;http://www.duinsoft.nl/pkg&#34;&gt;http://www.duinsoft.nl/pkg&lt;/a&gt; debs all&lt;/p&gt;
&lt;p&gt;The next step is to import the key for the repository:&lt;/p&gt;
&lt;p&gt;sudo apt-key adv — keyserver keys.gnupg.net — recv-keys 5CB26B26&lt;/p&gt;
&lt;p&gt;Finally, install Java using the following two commands:&lt;/p&gt;
&lt;p&gt;sudo apt-get update&lt;/p&gt;
&lt;p&gt;sudo apt-get install update-sun-jre&lt;/p&gt;
&lt;h4 id=&#34;step-4-installssh&#34;&gt;Step 4: Install SSH&lt;/h4&gt;
&lt;p&gt;The next step is to install an SSH server; this is required to run
Hadoop. I also find it a little easier to interact with the Virtual
Machine through an SSH session, rather than through the VM interface. To
do this, run:&lt;/p&gt;
&lt;p&gt;sudo apt-get install openssh-server&lt;/p&gt;
&lt;h4 id=&#34;step-5-hadoopuser&#34;&gt;Step 5: Hadoop User&lt;/h4&gt;
&lt;p&gt;Once the SSH server has been installed, a new user needs to be created
to run Hadoop. To do this, run:&lt;/p&gt;
&lt;p&gt;sudo addgroup hadoop&lt;/p&gt;
&lt;p&gt;sudo adduser — ingroup hadoop huser&lt;/p&gt;
&lt;p&gt;The huser needs to have a public key generated to allow it to connect to
the server without a password. The first step is to login as the user:&lt;/p&gt;
&lt;p&gt;sudo -i -u huser&lt;/p&gt;
&lt;p&gt;Then generate the key, and add it to the allowed list of keys which can
be used to connect:&lt;/p&gt;
&lt;p&gt;ssh-keygen -t dsa -P ‘’ -f ~/.ssh/id_dsa&lt;/p&gt;
&lt;p&gt;cat ~/.ssh/id_dsa.pub &amp;gt;&amp;gt; ~/.ssh/authorized_keys&lt;/p&gt;
&lt;h4 id=&#34;step-6-downloadhadoop&#34;&gt;Step 6: Download Hadoop&lt;/h4&gt;
&lt;p&gt;It’s now time to download the Hadoop Common package. Enter the following
to download the 1.0.3 release to the husers home directory&lt;/p&gt;
&lt;p&gt;cd ~&lt;/p&gt;
&lt;p&gt;wget
&lt;a href=&#34;http://mirror.overthewire.com.au/pub/apache/hadoop/common/hadoop-1.0.3/hadoop-1.0.3.tar.gz&#34;&gt;http://mirror.overthewire.com.au/pub/apache/hadoop/common/hadoop-1.0.3/hadoop-1.0.3.tar.gz&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Once the file has downloaded, untar it with the following command:&lt;/p&gt;
&lt;p&gt;tar -zxvf hadoop-1.0.3.tar.gz&lt;/p&gt;
&lt;p&gt;A folder called hadoop-1.0.3 will now be in the home directory.
Unfortunately the intended destination is protected, so you’ll need to
use a user with sudo access to move the download. To do this, enter the
following:&lt;/p&gt;
&lt;p&gt;exit&lt;/p&gt;
&lt;p&gt;sudo mv /home/huser/hadoop-1.0.3 /usr/local/hadoop&lt;/p&gt;
&lt;p&gt;sudo -i -u huser&lt;/p&gt;
&lt;p&gt;It’s now time to configure!&lt;/p&gt;
&lt;h4 id=&#34;step-7-configure-hadoop&#34;&gt;Step 7: Configure Hadoop&lt;/h4&gt;
&lt;p&gt;The newly copied Hadoop installation needs to have its JAVA_HOME
variables set. To do this, navigate to the directory:&lt;/p&gt;
&lt;p&gt;cd /usr/local/hadoop/conf&lt;/p&gt;
&lt;p&gt;And then open the file hadoop-env.sh using the following command. This
file contains some environment variable settings used by Hadoop:&lt;/p&gt;
&lt;p&gt;vim hadoop-env.sh&lt;/p&gt;
&lt;p&gt;Once the file is open, append the following line to it (you will need to
update this depending on the java version installed):&lt;/p&gt;
&lt;p&gt;export JAVA_HOME=/opt/java/32/jre1.6.0_31&lt;/p&gt;
&lt;p&gt;Save the file.&lt;/p&gt;
&lt;p&gt;The next important configuration step is to setup Hadoop. We’re going to
be running Hadoop in a single-node setup. This is a very basic
configuration, but it’s good for development. The first step is to
create a folder on the filesystem for Hadoop to store its files:&lt;/p&gt;
&lt;p&gt;mkdir /home/huser/hadoop_tmp&lt;/p&gt;
&lt;p&gt;Once done, navigate to /usr/local/hbase/conf, and open up the file
core-site.xml, and ensure the contents of it look like this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-graf&#34; data-lang=&#34;graf&#34;&gt;&amp;lt;?xml version=”1.0”?&amp;gt;
 &amp;lt;?xml-stylesheet type=”text/xsl” href=”configuration.xsl”?&amp;gt;
 &amp;lt;configuration&amp;gt;
 &amp;lt;property&amp;gt;
 &amp;lt;name&amp;gt;hadoop.tmp.dir&amp;lt;/name&amp;gt;
 &amp;lt;value&amp;gt;/home/huser/hadoop_tmp&amp;lt;/value&amp;gt;
 &amp;lt;/property&amp;gt;
 &amp;lt;property&amp;gt;
 &amp;lt;name&amp;gt;fs.default.name&amp;lt;/name&amp;gt;
 &amp;lt;value&amp;gt;hdfs://ubuntu:8020&amp;lt;/value&amp;gt;
 &amp;lt;/property&amp;gt;
 &amp;lt;/configuration&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The value for “fs.default.name” is the name of the default file system,
and the value for “hadoop.tmp.dir” is the base for temporary files on
the local file system. Once you’re done, save the file.&lt;/p&gt;
&lt;p&gt;The Hadoop filesystem needs to be formatted with the new settings. To do
this, execute:&lt;/p&gt;
&lt;p&gt;/usr/local/hadoop/bin/hadoop namenode –format&lt;/p&gt;
&lt;p&gt;Once done, the instance can be started with the following command:&lt;/p&gt;
&lt;p&gt;/usr/local/hadoop/bin/start-all.sh&lt;/p&gt;
&lt;p&gt;The Hadoop instance should now be running. To confirm this, go back to
your host machine, and navigate to the following page in a web-browser&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://ubuntu:50070/dfshealth.jsp&#34;&gt;http://ubuntu:50070/dfshealth.jsp&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*ZkoX8CwjldUAdSjS.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;step-8-adding-a-hbase-directory-inhadoop&#34;&gt;Step 8: Adding a Hbase Directory in Hadoop&lt;/h4&gt;
&lt;p&gt;A directory needs to be made within the Hadoop filesystem to store the
Hbase data. To do this, execute the following:&lt;/p&gt;
&lt;p&gt;/usr/local/hadoop/bin/hadoop fs -mkdir hbase_dir&lt;/p&gt;
&lt;p&gt;A directory now exists on the Hadoop file system, called
/user/huser/hbase_dir, you should be able to see it through the web
interface.&lt;/p&gt;
&lt;h4 id=&#34;step-9-downloadhbase&#34;&gt;Step 9: Download HBase&lt;/h4&gt;
&lt;p&gt;The next step is to download HBase. Enter the following to download the
1.0.3 release to the husers home directory&lt;/p&gt;
&lt;p&gt;cd ~&lt;/p&gt;
&lt;p&gt;wget
&lt;a href=&#34;http://mirror.mel.bkb.net.au/pub/apache/hbase/hbase-0.94.0/hbase-0.94.0.tar.gz&#34;&gt;http://mirror.mel.bkb.net.au/pub/apache/hbase/hbase-0.94.0/hbase-0.94.0.tar.gz&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Once the file has downloaded, untar it with the following command:&lt;/p&gt;
&lt;p&gt;tar -zxvf hbase-0.94.0.tar.gz&lt;/p&gt;
&lt;p&gt;A folder called hbase-0.94.0 will now be in the home directory. Again
the intended destination is protected, so you’ll need to use a user with
sudo access to move the download. To do this, enter the following:&lt;/p&gt;
&lt;p&gt;exit&lt;/p&gt;
&lt;p&gt;sudo mv /home/huser/hbase-0.94.0 /usr/local/hbase&lt;/p&gt;
&lt;p&gt;sudo -i -u huser&lt;/p&gt;
&lt;h4 id=&#34;step-10-configure-hbase&#34;&gt;Step 10: Configure Hbase&lt;/h4&gt;
&lt;p&gt;The newly copied Hbase installation needs to have its JAVA_HOME
variables set. To do this, navigate to the directory:&lt;/p&gt;
&lt;p&gt;cd /usr/local/hbase/conf&lt;/p&gt;
&lt;p&gt;And then open the file hbase-env.sh using the following command. This
file contains some environment variable settings used by Hadoop:&lt;/p&gt;
&lt;p&gt;vim hadoop-env.sh&lt;/p&gt;
&lt;p&gt;One the file is open, append the following line to it (you will need to
update this depending on the java version)&lt;/p&gt;
&lt;p&gt;export JAVA_HOME=/opt/java/32/jre1.6.0_31&lt;/p&gt;
&lt;p&gt;Save the file.&lt;/p&gt;
&lt;p&gt;The next step is to define the hbase setup. To do this, open the file
using:&lt;/p&gt;
&lt;p&gt;vim hbase-site.xml&lt;/p&gt;
&lt;p&gt;And then ensure the contents of the file appear as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;property&amp;gt;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;name&amp;gt;&lt;/span&gt;hbase.rootdir&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/name&amp;gt;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;value&amp;gt;&lt;/span&gt;hdfs://ubuntu:8020/user/huser/hbase_dir&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/value&amp;gt;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;description&amp;gt;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/description&amp;gt;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/property&amp;gt;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;property&amp;gt;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;name&amp;gt;&lt;/span&gt;hbase.master&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/name&amp;gt;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;value&amp;gt;&lt;/span&gt;ubuntu:60000&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/value&amp;gt;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;description&amp;gt;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/description&amp;gt;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/property&amp;gt;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now it’s all configured, it’s time to start Hbase up. To do this,
execute:&lt;/p&gt;
&lt;p&gt;/usr/local/hbase/bin/start-hbase.sh&lt;/p&gt;
&lt;p&gt;The application should now be running. To confirm this, go back to your
host machine, and navigate to the following page in a web-browser:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://ubuntu:60010/master-status&#34;&gt;http://ubuntu:60010/master-status&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*SN1CAQBLkr5nHkOc.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;step-11-creating-your-firsttable&#34;&gt;Step 11: Creating Your First Table&lt;/h4&gt;
&lt;p&gt;HBase contains its own shell, where you can interact with it. To start
the shell, execute:&lt;/p&gt;
&lt;p&gt;/usr/local/hbase/bin/hbase shell&lt;/p&gt;
&lt;p&gt;Within the shell, enter the following command to create a new table
called “words”, with a column family called “definitions”:&lt;/p&gt;
&lt;p&gt;create ‘words’,’definitions’&lt;/p&gt;
&lt;p&gt;The next step will be to load up some data. Obviously, you’d generally
do this with your application, but for this tutorial we’ll used fixed
data. Execute the following, one line at a time.&lt;/p&gt;
&lt;p&gt;put ‘words’,’hello’,’definitions:1&amp;rsquo;,’A greeting (salutation) said when
meeting someone or acknowledging someone’s arrival or presence.’&lt;/p&gt;
&lt;p&gt;put ‘words’,’hello’,’definitions:2&amp;rsquo;,’A greeting used when answering the
telephone.’&lt;/p&gt;
&lt;p&gt;put ‘words’,’world’,’definitions:1&amp;rsquo;,’to consider or cause to be
considered from a global perspective’&lt;/p&gt;
&lt;p&gt;These commands insert two rows into the database, two for “hello”, and
one for “world”. The “hello” row has two different columns within the
“definitions” column family.&lt;/p&gt;
&lt;h4 id=&#34;step-12-finally-programtime&#34;&gt;Step 12: Finally, Program Time&lt;/h4&gt;
&lt;p&gt;So all the prep work, is done, it’s time to run a sample program. I’ve
attached below a simple Java class which queries the rows within this
table and prints them out to the screen.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; au.com.gigliotti.sandpit.hbase;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; java.util.NavigableMap;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; java.util.NavigableSet;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; org.apache.hadoop.conf.Configuration;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; org.apache.hadoop.hbase.HBaseConfiguration;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; org.apache.hadoop.hbase.client.HBaseAdmin;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; org.apache.hadoop.hbase.client.HTableFactory;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; org.apache.hadoop.hbase.client.HTableInterface;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; org.apache.hadoop.hbase.client.Result;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; org.apache.hadoop.hbase.client.ResultScanner;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; org.apache.hadoop.hbase.client.Scan;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; org.apache.hadoop.hbase.util.Bytes;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; * Sample HBase application which retrieves all rows from a table and then
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; * prints them to the console. The table definition is as follows:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; * table: words column family: definitions
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; * @author Gerard Gigliotti (gerard.gigliotti.com.au)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;HBaseSample&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// The name of the table being queried&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;final&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;byte&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; WORDS_TABLE &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Bytes.&lt;span style=&#34;color:#a6e22e&#34;&gt;toBytes&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;words&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;// The column family&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;final&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;byte&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; DEFINITIONS_FAMILY &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Bytes
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           .&lt;span style=&#34;color:#a6e22e&#34;&gt;toBytes&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;definitions&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;    * Construct creates a new instance of HBaseSample, and invokes the start
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;    * method.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;   */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;(String&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; args) &lt;span style=&#34;color:#66d9ef&#34;&gt;throws&lt;/span&gt; Exception {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       (&lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; HBaseSample()).&lt;span style=&#34;color:#a6e22e&#34;&gt;start&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;     * Method prints out all rows within the words table.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;    * */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;start&lt;/span&gt;() &lt;span style=&#34;color:#66d9ef&#34;&gt;throws&lt;/span&gt; Exception {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Load&amp;#39;s the hbase-site.xml config&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      Configuration config &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; HBaseConfiguration.&lt;span style=&#34;color:#a6e22e&#34;&gt;create&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     HTableFactory factory &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; HTableFactory();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        HBaseAdmin.&lt;span style=&#34;color:#a6e22e&#34;&gt;checkHBaseAvailable&lt;/span&gt;(config);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Link to table&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     HTableInterface table &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; factory.&lt;span style=&#34;color:#a6e22e&#34;&gt;createHTableInterface&lt;/span&gt;(config,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               WORDS_TABLE);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Used to retrieve rows from the table&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Scan scan &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Scan();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Scan through each row in the table&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ResultScanner rs &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; table.&lt;span style=&#34;color:#a6e22e&#34;&gt;getScanner&lt;/span&gt;(scan);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#75715e&#34;&gt;// Loop through each retrieved row&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (Result r &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; rs.&lt;span style=&#34;color:#a6e22e&#34;&gt;next&lt;/span&gt;(); r &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;; r &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; rs.&lt;span style=&#34;color:#a6e22e&#34;&gt;next&lt;/span&gt;()) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              System.&lt;span style=&#34;color:#a6e22e&#34;&gt;out&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;println&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Key: &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; String(r.&lt;span style=&#34;color:#a6e22e&#34;&gt;getRow&lt;/span&gt;()));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#75715e&#34;&gt;/*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;               * Cycles through each qualifier within the &amp;#34;definitions&amp;#34; family.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;              * The column family, with the qualifier, make up the column
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;                 * name; it is usually represented in the syntax
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;                 * family:qualifier. In our example, each qualifier is a number.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;                 */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             NavigableMap familyMap &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; r
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                      .&lt;span style=&#34;color:#a6e22e&#34;&gt;getFamilyMap&lt;/span&gt;(DEFINITIONS_FAMILY);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#75715e&#34;&gt;// This is a list of the qualifier keys&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             NavigableSet keySet &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; familyMap.&lt;span style=&#34;color:#a6e22e&#34;&gt;navigableKeySet&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Print out each value within each qualifier&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;byte&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; key : keySet) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                 System.&lt;span style=&#34;color:#a6e22e&#34;&gt;out&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;println&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;t Definition: &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; String(key))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                           &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;, Value:&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                          &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; String(r.&lt;span style=&#34;color:#a6e22e&#34;&gt;getValue&lt;/span&gt;(DEFINITIONS_FAMILY, key)));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       } &lt;span style=&#34;color:#66d9ef&#34;&gt;catch&lt;/span&gt; (Exception e) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#66d9ef&#34;&gt;throw&lt;/span&gt; e;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        } &lt;span style=&#34;color:#66d9ef&#34;&gt;finally&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         rs.&lt;span style=&#34;color:#a6e22e&#34;&gt;close&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you’ll have noticed, the connection settings are not defined within the class. A separate file, called hbase-site.xml, which needs to be added to your classpath, contains the settings for connecting to Hbase.&lt;/p&gt;
&lt;p&gt;The code can be downloaded from &lt;a href=&#34;https://href.li/?https://github.com/ggotti/hbase_sample&#34;&gt;GitHub here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;That’s it; you’ll now have your very own singing and dancing Hadoop &amp;amp; Hbase instance.&lt;/p&gt;
&lt;h4 id=&#34;references&#34;&gt;References&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;The HBase documentation is very useful:
&lt;a href=&#34;http://hbase.apache.org/book.html&#34;&gt;http://hbase.apache.org/book.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A great post about establishing a Hadoop instance:
&lt;a href=&#34;http://www.michael-noll.com/tutorials/running-hadoop-on-ubuntu-linux-single-node-cluster/&#34;&gt;http://www.michael-noll.com/tutorials/running-hadoop-on-ubuntu-linux-single-node-cluster/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Another great post, but focusing more on Hbase:
&lt;a href=&#34;http://ria101.wordpress.com/2010/01/28/setup-hbase-in-pseudo-distributed-mode-and-connect-java-client/&#34;&gt;http://ria101.wordpress.com/2010/01/28/setup-hbase-in-pseudo-distributed-mode-and-connect-java-client/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    
    
    <item>
      <title>CSC Ghana #8 Final Post</title>
      <link>https://gerard.au/2012-05-28_csc-ghana--8-final-post-a572a0994610/</link>
      <pubDate>Mon, 28 May 2012 00:00:08 -1000</pubDate>
      
      <guid>https://gerard.au/2012-05-28_csc-ghana--8-final-post-a572a0994610/</guid>
      <description>&lt;p&gt;I thought a good way of finishing up my Corporate Service Corps (CSC)
Ghana #8 assignment would be to answer the following set of interview
questions; I was asked these during our final interview.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*yLUWY5HP3uuFcNj5.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What did CSC participation mean to you?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;To me it was an honour to be selected to represent IBM on such a
critical assignment for the people of Ghana. It was a privilege to work
with such an exemplary group of IBMers from around the world; we were
able to leverage each other’s unique abilities to deliver a quality set
of deliverables in a very short time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tell us a little about your assignment.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;On our assignment we focused on developing a supply chain IT roadmap for
the Ministry of Health (MoH). We analysed the existing supply chains
system, and then made a number of recommendations for improving their
approach by leveraging new IT systems.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What surprised you about your experiences in Ghana?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The thing that surprised me the most was how willing everyone was to
help. People are sometimes weary of consultants, but there was a genuine
desire within the MoH to fix their supply chain issues and deliver
better health outcomes for the people of Ghana.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What do you think you learned/gained from the experience?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I really gained a greater appreciation for the challenges that you can
face when working within a multicultural team. My assignment team had
people from the US, China, India and Australia, and it was interesting
to see the differences in how each team member approached their work.
Even something as simple as spelling can be an issue. For example, a
number of words in Australian English are spelt with an “s” (ie,
organise), but in US English they are spelt with a “z” (organize).&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*TfEvja2utOdEdOLM.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What will you take back to your day job?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I’ll bring back to my day job a global network of IBM contacts which
I’ll be able to leverage to help deliver better outcomes for our
clients.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Another team from IBM will be coming in the fall. Any words of
advice?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Just a little list of things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Make sure you bring over a Visa card for ATM withdrawals. MasterCard
isn’t widely accepted.&lt;/li&gt;
&lt;li&gt;Try Grasscutter, a local delicacy. Country Kitchen, in Accra,
occasionally has it as a special. I suggest not buying it from the
roadside.&lt;/li&gt;
&lt;li&gt;If you can only do one weekend activity, make sure it’s the Dodi
Princess Cruise.&lt;/li&gt;
&lt;li&gt;Bring some food from your country of origin to share with the team.
Some of the best moments on the trip centred on these exchanges.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*EhgZQ-DuydOepx7Z.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;That’s all for Ghana #8. It’s been emotional.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Press Release</title>
      <link>https://gerard.au/2012-05-24_press-release-e462dfaaff34/</link>
      <pubDate>Thu, 24 May 2012 00:00:08 -1000</pubDate>
      
      <guid>https://gerard.au/2012-05-24_press-release-e462dfaaff34/</guid>
      <description>&lt;p&gt;As the project finished up, a press release was issued documenting our
work. A couple of different news agencies chose to cover the story:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;http://www.ghanaweb.com/GhanaHomePage/health/artikel.php?ID=239011&#34;&gt;Ghana News Agency — IBM experts present recommendations to Ministry
of
Health&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://spyghana.com/health-news/health-news-health-news/ibm-helping-ministry-of-health-improve-access-to-healthcare/&#34;&gt;SpyGhana — IBM Helping Ministry of Health Improve Access to
Healthcare&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://vibeghana.com/2012/05/15/ibm-experts-present-recommendations-to-ministry-of-health/&#34;&gt;Vibe Ghana — IBM experts present recommendations to Ministry of
Health&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://www.finchannel.com/Main_News/Tech/109245_IBM_Experts_Deliver_Recommendations_to_Ghana_Ministry_of_Health_for_Increasing_Access/&#34;&gt;The Financial — IBM Experts Deliver Recommendations to Ghana Ministry
of Health for Increasing
Access&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://www.sacbee.com/2012/05/14/4487812/ibm-experts-deliver-recommendations.html&#34;&gt;Sacramento Bee — IBM Experts Deliver Recommendations to Ghana
Ministry of Health for Increasing Access to More Affordable Health
Care&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*6ZzhY5Ddysxk5hcD.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Dancing troupe at the Mountain Gorillas View Hotel</title>
      <link>https://gerard.au/2012-05-21_https---www-tumblr-com-video-file-23487819702-tumblr-m4dvhjm4lw1rnitgb-411620ceeb1f/</link>
      <pubDate>Tue, 22 May 2012 00:00:08 -1000</pubDate>
      
      <guid>https://gerard.au/2012-05-21_https---www-tumblr-com-video-file-23487819702-tumblr-m4dvhjm4lw1rnitgb-411620ceeb1f/</guid>
      <description>&lt;video class=&#34;video-shortcode&#34; width=&#34;100%&#34; preload=&#34;auto&#34; controls&gt;
    &lt;source src=&#34;./videos/tumblr_m4dvhjm4Lw1rnitgb_r1.mp4&#34; type=&#34;video/mp4&#34;&gt;
    There should have been a video here but your browser does not seem
    to support it.
&lt;/video&gt;

&lt;p&gt;Dancing troupe at the Mountain Gorillas View Hotel. I was the only
audience member for 25 minutes.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Barrel of Monkeys</title>
      <link>https://gerard.au/2012-05-21_barrel-of-monkeys-6a8e9c89170f/</link>
      <pubDate>Mon, 21 May 2012 00:00:08 -1000</pubDate>
      
      <guid>https://gerard.au/2012-05-21_barrel-of-monkeys-6a8e9c89170f/</guid>
      <description>&lt;p&gt;I was too smug about the trekking yesterday, and I was made to pay for
it today. Due to the expense of the gorilla permits ($500 a day, rising
to $750 in July), I decided to visit the Golden Monkeys on my second
day; another rare attraction limited to the Parc National des Volcans
and its neighbouring parks. This trek was supposed to be easier than the
gorillas, and I was looking forward to a leisurely walk around the
peripheries of the park. Oh I was so wrong.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*btXYXV2KP9lQU6MS.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;As we headed out from our drop off point, it started to rain. Not that
slow-motion-romantic-rain which occurs when you’re rushing towards a
loved one, but the now-you’re-in-the-tropics-boy kind of rain. It
bucketed down in a manner similar to the end sequence of the movie 2012.
Stupidly, I had left my raincoat in Australia but I had luckily picked
one up at Johannesburg airport. For future reference, yes, the $40
pocket raincoat is not as effective as a $400 gor-tex one. Surprisingly
there is a reason that outdoor adventure gear costs so much. Well, maybe
not that much. My camera bag, even under my top, was soaked, but the
camera was only a little damp and still functioning. The rain turned all
the ground to mud, making dodging thistles, vines and ankle twisting
rocks that much harder. We eventually located the tracker group, and
then finally a group of &lt;a href=&#34;http://en.wikipedia.org/wiki/Golden_monkey&#34;&gt;Golden
Monkeys&lt;/a&gt;. Unlike the
Gorillas, which happily sit there and let you photograph them, the
monkeys crazily jump all over the place. Amazingly, the rain stopped for
a good half an hour, allowing me to take some truly appalling photos.
I’m blaming the monkeys.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*VUf1JZAU7fmOZ81z.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*sGqReJcmTSBfBG-k.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*h02EJdFPlh1fw4aE.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*f2ve6BcRHZmxCrop.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;After 45 minutes, the heavens opened up again, and we decided to head
back. The forest by now resembled a muddy river, and it was so deep in
parts that we had to wade through it. Nobody was safe from the downpour,
and I was so soaked that water was pouring out of my sleeves like a
spout. Bear Grylls eat your heart out.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Gorillas in the Mist</title>
      <link>https://gerard.au/2012-05-20_gorillas-in-the-mist-149072a43a9/</link>
      <pubDate>Sun, 20 May 2012 00:00:08 -1000</pubDate>
      
      <guid>https://gerard.au/2012-05-20_gorillas-in-the-mist-149072a43a9/</guid>
      <description>&lt;p&gt;The journey to Rwanda was excruciatingly tiring. Starting in Kumasi, we
had to drive 6 hours back to Accra, where I had to catch a 5 hour flight
to Johannesburg, which left at 11pm and arrived at 6am (it’s in a
different time zone). I then had a 5 hour stopover at the airport, and
then another 4 hour flight to Kigali, via
&lt;a href=&#34;http://en.wikipedia.org/wiki/Bujumbura&#34;&gt;Bujumbura&lt;/a&gt;. The entire
experience was surreal; the second flight wasn’t announced at the
airport, wasn’t on the departure board, and there were only 20 people on
a jet with seating for 150.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*wZ7SPcm6EX1sU0Zu.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;On arrival, immigration lovingly gave me a 50% cheaper visa than I was
expecting, and my bag miraculously appeared on the carousel. Outside, my
guide John was waiting for me with a little sign with my name on it.
Tick that off my list of things to do. John then told me that I was the
only person on the tour; 4 days of John and me.&lt;/p&gt;
&lt;p&gt;First stop was a rather deserved rest at the Lemigo Hotel in Kigali. I
ended up getting a club sandwich from room service, and it only cost me
5000 Rwandan Francs ($8 AUD). The next day, John was scheduled to pick
me up at 2:00pm, so I decided to walk around the neighbourhood near the
hotel. I ended up at Great Wall Chinese Restaurant, which served up this
rather interesting interpretation of sweat and sour pork. Avert your
eyes if you’re easily offended.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*cvy0kJtx8o82WVba.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;At 2:00pm, we made our way to the Mountain Gorilla View Lodge, though
the magnificent Rwandan countryside. I can’t explain how pretty Rwanda
is as a country; everywhere you look there is a view more beautiful than
the last. The lodge itself was fantastic, with very attentive staff. The
fire place in my room was kept burning all afternoon and well into the
evening, and someone else delivered a hot water bottle before I went to
sleep.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*dM_xrq9OgPM1vw6B.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*e_jA0YthaJIRHhCu.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;I did not enjoy the 5:30am wake up call for the Gorilla trekking, but
the excitement of getting to see a gorilla in the wild soon overtook the
sleepiness. John and I had a quick trip to the Parc National des Volcans
headquarters, where I was assigned to see the Agashya Group, which is
the second largest group, and one of the easier to locate. We drove out
to the starting point, and then after some 30 minutes of trekking
through some rather muddy forest, we located the group. They were almost
all sitting down eating.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*zGpQIrgUZJg3SAzp.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*0uuc2BVyfsi0FBQG.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*UKqd9dwEWKTf7czV.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*PUFyzgWpaUUwNFQX.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;After about 20 minutes, the giant silverback decided he wanted something
else to eat, so he headed off. This is where things got interesting, as
we followed the group through the forest with the guides cutting a path
through the thick vegetation.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*pTCQGp2lHh0xxVsW.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*53QZaJIhSTgOVIzN.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*zuwqMO-qiqGhRVNq.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;All too soon the magical hour was over. It was a privilege being able to
see such magnificent creatures in the wild.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>I’m now on my own private tour of Rwanda; it’s just the guide and me.</title>
      <link>https://gerard.au/2012-05-18_i-m-now-on-my-own-private-tour-of-rwanda--it-s-just-the-guide-and-me--d9ab6088caf7/</link>
      <pubDate>Fri, 18 May 2012 00:00:08 -1000</pubDate>
      
      <guid>https://gerard.au/2012-05-18_i-m-now-on-my-own-private-tour-of-rwanda--it-s-just-the-guide-and-me--d9ab6088caf7/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;./images/primate_safari.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m now on my own private tour of Rwanda; it’s just the guide and me.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Wechiau Community Hippo Sanctuary</title>
      <link>https://gerard.au/2012-05-17_wechiau-community-hippo-sanctuary-45e1573c1eec/</link>
      <pubDate>Thu, 17 May 2012 00:00:08 -1000</pubDate>
      
      <guid>https://gerard.au/2012-05-17_wechiau-community-hippo-sanctuary-45e1573c1eec/</guid>
      <description>&lt;p&gt;The next stop on our itinerary was Wechiau Community Hippo Sanctuary, an
ecotourism project in the upper western region of Ghana. The Lonely
Planet guide describes it as the most underrated tourist attraction in
Ghana. The problem with Wechiau is that it’s difficult to visit. By car
it’s almost three hours from Mole, and public transport is limited to
tro-tros only, so only the stubbornly determined make the trip. Which is
a shame, because Wechiau is awesome.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*yBpLA_mvPguIcaSy.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;When we arrived at the sanctuary, we were blown away by the hospitality
of the staff. On arrival, they found us a nice comfortable seat and then
they presented us with a menu of activities that we could do at the
sanctuary. Obviously they had hippopotamus safaris, but they also had a
plethora of community visits. We ended up selecting the river water
safari and a village walk.&lt;/p&gt;
&lt;p&gt;We had a short 10 minute drive out to our accommodation. The lodging is
basic, but that’s part of the charm; no electricity, mosquito nets
abound, drop toilets and bucket showers. Normally I’d squirm at this
kind of thing, but I was so excited to be visiting the sanctuary that I
didn’t really mind. They set your expectations well in advance.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*Qam55vuMrd1u-rM8.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Once we dropped our bags off, we headed straight out to our hippo
safari. The three of us, a couple from Holland, and two guides piled
into a rather rickety looking canoe and headed off looking for hippos.
We didn’t have to go far; a group of 13 had formed less than 200 metres
from the landing, and so we paddled straight over and started taking
photos. Unfortunately there isn’t much of a hippo you can see during the
day, but we stayed out there for 40 minutes. It was excruciatingly hot
on the water, but I’m glad we got to see such a large group. Back at the
camp, I spoke to someone that spent three hours on the safari and could
only find one.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*ogT68Q5Gq_2gHI-q.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*O4r1xA5hASvgYEHe.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*aYWH4Ph0x4ZoawoB.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;After the safari, we visited a local village. The money from the tours
goes directly back into the villages; the sanctuary just recently
installed two water pumps at each of the 17 villages within the
sanctuary.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*W1uPieG8x29hClpm.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*IIjkV7hr2kyB4Cka.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Back at the lodging, we were supplied with a boiling pot of water to
cook our two minute noodles, and after a couple of glasses of wine we
retired for the night. The girls decided that they wanted to sleep on
the roof, whereas conservative me decided it was better to stay inside.
Inside was the better choice, as at 1:00pm it started to rain heavily,
and everyone had to come back in.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*cyE1_hq5xtcmcp0u.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;I could have spent days at the sanctuary, there was that much to see,
and I’d highly recommend it to anyone who finds themselves in Ghana.
Unfortunately, due to the limited amount of available time, we had to
leave early the next morning to head back to Kumasi.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Mole National Park</title>
      <link>https://gerard.au/2012-05-16_mole-national-park-4287bbf8ef29/</link>
      <pubDate>Wed, 16 May 2012 00:00:08 -1000</pubDate>
      
      <guid>https://gerard.au/2012-05-16_mole-national-park-4287bbf8ef29/</guid>
      <description>&lt;p&gt;Mole National Park is the largest national park in Ghana, located in the
North West corner of the country. It’s a bone crushing 670 kilometers
from Accra, and the trip generally takes 11 hours. We ended up stopping
in Techiman overnight, because the roads are just so treacherous; one
minute you’re on a dual carriage way, and the next there are potholes
the size of a VW Beetle.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*PE3kL-RveTpKU3hG.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;After having some issues with booking the accommodation, we finally were
able to book a room within the motel in the middle of the park. It’s a
run-of-the-mill motel, apart from the fact it’s in the middle of a
national park and the animals are allowed to walk through it. When we
were checking in, there was a baboon running along the roof. Once the
room was organized, we headed out for our first walking safari.&lt;/p&gt;
&lt;p&gt;The Lonely Planet guide describes Mole as being the cheapest safari in
Africa. The price for the guide is 3 cedi ($1.6 AUD) an hour, for a
minimum of two hours. So we decide to start with a walking safari. We
were assigned a guide, Christopher, and with a gun slung across his
back, we head out into the park.&lt;/p&gt;
&lt;p&gt;I’ve effectively done no exercise for 4 weeks, and walking through the
searing Ghanian sun is difficult enough as it is, let alone having to
walk through dense savannah scrub. The majority of the first walk was
spent seeing glimpses of the backsides of bushbuck through thick
vegetation. So we headed back to the motel, where we ordered dinner (I
had two beef kebabs and yam chips) and had a few drinks by the pool
before it rained us out.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*PxsIPRN6fVYkNjPY.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Walk number two was at 7:00am the next morning. Thankfully the terrain
was a little more forgiving, consisting primarily of grassland. We did
get to see some cute warthog families, and monkeys. It was also
refreshing to see the front of a bushbuck, and their cousin, the
waterbuck.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*mBUJcJdiNczUgFQW.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*TAZi36DlWLkVq44p.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*S6L_2bm_6YPF36sx.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;I had resigned myself to the fact that we’d never get to see an
elephant. We were told that they frequently can be seen drinking from
the watering hole near the motel, or indeed the pool, but the “lower”
temperature and rain over the two days had kept them away. We spent four
hours optimistically watching the waterhole, and then due to a
misunderstanding with our driver, we had to rent a vehicle for our
driving safari, which we luckily shared with two American girls from
Minnesota. It cost 45 cedi ($27 AUD), an hour, for the car, which is
outrageously expensive by Ghanian standards.&lt;/p&gt;
&lt;p&gt;So sitting on the roof of the car, we slowly made our way through the
park. When we saw an animal, we would bang on the roof and the guide
would stop. We opted for three hours of driving, and after almost two
and a half hours we hadn’t seen a lot. The other safari car that we
passed hadn’t seen any elephants, so it was not looking good. As we
slowly headed back to the motel, we caught a glimpse of one in the
distance, and so we quickly stopped the car and proceeded on foot. It
was so exhilarating following the elephants through the scrub, and we
eventually got within 20 meters of a male. All happy, we headed back to
the motel.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*oaGcVASEqcK-LLMX.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*FD5ll4ZgvKUfau9i.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*vhUOIPVu126xgE83.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*Rq5V3Ti6kcHGEQPN.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*9OOnXsMNQrbvA9Nj.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Mole National Park was an amazing experience, and I’m glad we made the
trek up here. Next stop, Wechiau Hippo Sanctuary.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>View from Mole Motel</title>
      <link>https://gerard.au/2012-05-14_view-from-mole-motel-e56d03f97c85/</link>
      <pubDate>Wed, 16 May 2012 00:00:08 -1000</pubDate>
      
      <guid>https://gerard.au/2012-05-14_view-from-mole-motel-e56d03f97c85/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;./images/mole_motel.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;View from Mole Motel&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Final presentation</title>
      <link>https://gerard.au/2012-05-13_final-presentation-6e083496c55f/</link>
      <pubDate>Sat, 12 May 2012 00:00:08 -1000</pubDate>
      
      <guid>https://gerard.au/2012-05-13_final-presentation-6e083496c55f/</guid>
      <description>&lt;p&gt;Our four weeks of work ended on Friday with presentations to the various
stakeholders. Adaeze delivered a stirring 30 minute presentation to the
crowd, and then we spent the next 15 minutes answering questions as a
group. The other two groups then delivered their presentations, and then
we spent the rest of the afternoon being filmed individually and in
groups. I don’t think the video is going to go viral, but it’ll
hopefully provide an insight into the program and the work we’ve been
doing.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*UKUGi2T4Ry2stXBZ.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*oQLRyxGVtEdXD7mQ.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;All of us made it out for dinner as a group, and then it was back to the
hotel for some dessert and photo sharing. I currently have over 60
gigabytes of photos, not including my own.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*6fGqJib3qG6HdR8A.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;The departure times on Saturday varied, as people flew back to their
home countries. Our little trio, of Petra, Piper and I were actually the
first to depart, with a grueling 7 hour driver ahead of us to Techiman.
It was a sad leaving so many new friends behind, but there were numerous
promises of future visits.&lt;/p&gt;
&lt;p&gt;Off to Mole!&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Final presentation</title>
      <link>https://gerard.au/2012-05-12_final-presentation-2f1e7e35a042/</link>
      <pubDate>Sat, 12 May 2012 00:00:07 -1000</pubDate>
      
      <guid>https://gerard.au/2012-05-12_final-presentation-2f1e7e35a042/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;./images/final_presentation.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Mövenpick</title>
      <link>https://gerard.au/2012-05-10_m-venpick-d22642bf8e2a/</link>
      <pubDate>Thu, 10 May 2012 00:00:06 -1000</pubDate>
      
      <guid>https://gerard.au/2012-05-10_m-venpick-d22642bf8e2a/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;http://www.twomunch.com/2012/05/05/the-art-of-swiss-ice-cream/&#34;&gt;Mövenpick&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Review of Mövenpick, Accra&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Clicking Send</title>
      <link>https://gerard.au/2012-05-10_clicking-send-afb794d7686f/</link>
      <pubDate>Thu, 10 May 2012 00:00:05 -1000</pubDate>
      
      <guid>https://gerard.au/2012-05-10_clicking-send-afb794d7686f/</guid>
      <description>&lt;p&gt;The last week of work has been hectic. We’ve been attending last minute
meetings, sending out a constant flow of drafts, and running around
Accra picking up gifts for loved ones back home. I’m now the proud owner
of a small box of delicious Ghanian dark chocolate, and probably one too
many bracelets. Unfortunately the school poster sized map of Accra that
I bought for 2 cedi ($1 AUD) from the roadside has been lost in the
turmoil.&lt;/p&gt;
&lt;p&gt;We had an opportunity this week to check out the IBM office in Accra,
and I managed to snap a photo of this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*X29TPXS50isVgfjo.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;We just clicked send on our final report, and all that is left now is
the presentation tomorrow morning. Good times.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>My standard lunch: chicken &amp; rice (Jolloff)</title>
      <link>https://gerard.au/2012-05-09_my-standard-lunch--chicken---rice--jolloff--54dc5891369a/</link>
      <pubDate>Wed, 09 May 2012 00:00:05 -1000</pubDate>
      
      <guid>https://gerard.au/2012-05-09_my-standard-lunch--chicken---rice--jolloff--54dc5891369a/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;./images/jolloff.png&#34; alt=&#34;&#34;&gt;
My standard lunch: chicken &amp;amp; rice (Jolloff)&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Jamestown &amp; Bojo Beach, Weekend #3</title>
      <link>https://gerard.au/2012-05-09_jamestown---bojo-beach--weekend--3-40d582c4ad6c/</link>
      <pubDate>Wed, 09 May 2012 00:00:03 -1000</pubDate>
      
      <guid>https://gerard.au/2012-05-09_jamestown---bojo-beach--weekend--3-40d582c4ad6c/</guid>
      <description>&lt;p&gt;Not content with being run off our feet on Saturday, we made Sunday
equally as busy. The first stop was Jamestown, the historic quarter in
Accra, around James Fort. The area isn’t as developed as some of the
other parts of Accra, and we were luckily able to find a guide to show
us around (/he just jumped in the bus). We went down to the fish market
area, where the locals weren’t too keen on being photographed by a bunch
of tourists, but a couple of words and I suspect some cedi later from
the guide, won them over.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*tFhJ1UMRCVRt3sRZ.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*bCiwTAkoED4Xe4Jt.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;The next stop was the lighthouse, which had incredible views of the old
Accra harbour and the city. I loved the fact that we were able to walk
up it. No occupational health and safety when you’re going around the
twist.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*qBNnt6_1gt4Y0F3B.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*tCTtgfUPJwk-DloT.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;After we’d seen enough of Jamestown, we headed out to Independence
Square. We drive past it every day to the office, but we hadn’t had the
opportunity to check it out. We also got to snap some photos of the
creatively named Independence Arch.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*5_lmZCagZk4D6LYx.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Next to the Independence Arch is the Accra Sports stadium. After a
rather confusing conversation with the guards at the entrance, they gave
us a quick guided tour of the stadium.&lt;/p&gt;
&lt;p&gt;The crown jewel of the weekend was without a doubt the trip to Bojo
beach. About 40 minutes out of town, Bojo beach is an oasis of calm in
comparison to Labadi Pleasure Beach. The beach is actually located on a
sand bar, and you have to get a canoe across to it. Once you’re there,
you can enjoy the clean water, deck chairs, and the obligatory bar. We
ordered some lunch at the restaurant, and then went across to the beach
for a leisurely couple of hours of swimming.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*_EWVznP2V8PR6j5x.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*yjKMQTG3UKTUB8T1.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*bhJEyAjP5QU7OV1x.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;It’s interesting to note that Ghanians don’t really swim. A lot of the
people just mill around in the shallows, shrieking at the sight of waves
higher than their waists. I’m not a particularly capable swimmer, but I
was complimented a couple of times on my freestyle prowess. Maybe I
should move to Ghana and join the swim team.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title> One of the local restaurants near the office, Country Kitchen, had a special on grass cutter, also…</title>
      <link>https://gerard.au/2012-05-08_one-of-the-local-restaurants-near-the-office--country-kitchen--had-a-special-on-grass-cutter--also--ef01247465e7/</link>
      <pubDate>Tue, 08 May 2012 00:00:03 -1000</pubDate>
      
      <guid>https://gerard.au/2012-05-08_one-of-the-local-restaurants-near-the-office--country-kitchen--had-a-special-on-grass-cutter--also--ef01247465e7/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;./images/grass_cutter.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;One of the local restaurants near the office, Country Kitchen, had a
special on grass cutter, also known as the greater cane rat. It came in
a peanut soup, with a conglomerated ball of fufu.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title> Dodi Princess Burnt to Ashes. We were on the last cruise</title>
      <link>https://gerard.au/2012-05-07_dodi-princess-burnt-to-ashes--we-were-on-the-last-cruise--c53a14adc07c/</link>
      <pubDate>Mon, 07 May 2012 00:00:03 -1000</pubDate>
      
      <guid>https://gerard.au/2012-05-07_dodi-princess-burnt-to-ashes--we-were-on-the-last-cruise--c53a14adc07c/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;http://www.dailyguideghana.com/?p=47121&#34;&gt;Dodi Princess Burnt to Ashes. We were on the last
cruise.&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Makola Market &amp; Aburi Botanical Gardens, Weekend #3</title>
      <link>https://gerard.au/2012-05-06_makola-market---aburi-botanical-gardens--weekend--3-a4c2dafddf8e/</link>
      <pubDate>Sun, 06 May 2012 00:00:03 -1000</pubDate>
      
      <guid>https://gerard.au/2012-05-06_makola-market---aburi-botanical-gardens--weekend--3-a4c2dafddf8e/</guid>
      <description>&lt;p&gt;Our last weekend in Ghana as a group was going to be hectic. We’d
forgone the tour to see the monkeys, instead choosing to spend the time
in and around Accra. We hadn’t really had the opportunity to check off
all the touristy things to see in Accra.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*9KPpr5z7t0Gg77BP.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Starting at 9:00, we headed straight to &lt;a href=&#34;http://en.wikipedia.org/wiki/Makola_Market&#34;&gt;Makola
Market&lt;/a&gt;. Makola is the
largest market in Ghana, and to say that it’s not enormous would be an
understatement. It’s a sprawling labyrinth of street level stalls mixed
with three or four story buildings containing even more stalls. We had
been allocated an hour to walk around, but some of the group found it a
little overwhelming. I loved it. Unlike some of the other destinations
in Accra, we weren’t being harassed to buy, so we were free to walk
around. The fresh food was particularly interesting, with pigs feet and
snails the size of a man’s fist.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*eNZGhGEx0wG6hO8x.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*u7gVxLumpjuxo72j.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*k1tksotOP4-WHciy.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Walking around at street level was hectic, with women carrying baskets
on their heads, vying for the same roadway as large delivery trucks and
other pedestrians. It was busy, crazy, safe fun. Definitely worth a
look.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*r7vhUSta9jPAfxQf.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;We’ll contrast this to the Arts Centre, which is where they sell
souvenirs to tourists. Vendors already start heckling you before the bus
has even pulled to a stop. There is some great stuff to buy inside, and
some people in the group love the bargaining process, but I just feel
rushed and annoyed that I can’t just browse around in peace. Brother,
would you like to see my paintings?&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*iYoA90a33bm_P2tt.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Due to the time constraints, we’ve been skipping either lunch or dinner
everyday. Today, lunch was being skipped, so we headed off to the &lt;a href=&#34;http://en.wikipedia.org/wiki/Aburi_Botanical_Gardens&#34;&gt;Aburi
Botanical Gardens&lt;/a&gt;
within the Eastern Region of Ghana. I’m not particularly fond of gardens
at the best of times, but this one I actually enjoyed. Why’s that?
Because after exploring some of the different areas, Dwight, Amy and
myself trundled down one of the paths carved out by the locals, and we
ended up half lost through this thick forest part of the garden.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*6K8bPyUg5BVwmIJx.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Before it started to rain, we bundled into the bus and then headed back
into Accra for dinner. We ended up at a place called Buka, which has a
range of Ghanaian and Nigerian dishes. I had mixed feelings about my
goat kontonmire stew. More on that later.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>View from the mountains in the Eastern Region, Ghana</title>
      <link>https://gerard.au/2012-05-05_view-from-the-mountains-in-the-eastern-region--ghana-c70328450a68/</link>
      <pubDate>Sat, 05 May 2012 00:00:03 -1000</pubDate>
      
      <guid>https://gerard.au/2012-05-05_view-from-the-mountains-in-the-eastern-region--ghana-c70328450a68/</guid>
      <description>&lt;hr&gt;
&lt;p&gt;&lt;img src=&#34;./images/tumblr_m3jzuawKQp1rnitgbo1_1280.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Eating Indian In</title>
      <link>https://gerard.au/2012-05-04_eating-indian-in-1d0cba1a6f2b/</link>
      <pubDate>Fri, 04 May 2012 00:00:03 -1000</pubDate>
      
      <guid>https://gerard.au/2012-05-04_eating-indian-in-1d0cba1a6f2b/</guid>
      <description>&lt;hr&gt;
&lt;p&gt;After three straight weeks of eating out, restaurant fatigue is setting
in. Although the food we’ve been eating has been generally on the good
side (I’m looking at you, Kakum National Park Lasagna), it’s just been
too much. It’s been particularly hard on the vegetarian members of the
group, who are often only left with rice and cabbage. Even the
description of the meal, rice and cabbage, sounds particularly
unappetizing.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*DT9uxsVwB-YelFPU.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Luckily, one of the members of our group, Andal, is particularly gifted
in the kitchen. She somehow managed to get the hotel staff to let us use
the kitchen. So on Monday night, she came home from work early, and
cooked up a venerable feast of Indian vegetarian dishes. Not a dressed
chicken in sight.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*cmv45Hpt0Wi9-Bm9.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*waLXdbqkgqEE69UP.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;The feast consisted of roti, dhal, raita, cauliflower curry, potato
curry, and a sago kheer. We haven’t eaten this well since we arrived,
and I loved all of it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*A-7-eZLFonNInuKG.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*9LfMT4Y57vaDooaG.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;If the first dessert wasn’t enough, we then got treated to some Irish
coffee in the lounge.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*Drk1yMxL_9kxG2md.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>&#34;Water is Medicine&#34; campaign at the Ministry of Health</title>
      <link>https://gerard.au/2012-05-03_-water-is-medicine--campaign-at-the-ministry-of-health-658287c79e62/</link>
      <pubDate>Thu, 03 May 2012 00:00:03 -1000</pubDate>
      
      <guid>https://gerard.au/2012-05-03_-water-is-medicine--campaign-at-the-ministry-of-health-658287c79e62/</guid>
      <description>&lt;hr&gt;
&lt;p&gt;&lt;img src=&#34;./images/1*Gy8NUwROScMVqPLdPUlnJg.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;“Water is Medicine” campaign at the Ministry of Health&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Banana bread and a “puff puff”. The perfect Ghanaian office morning tea.</title>
      <link>https://gerard.au/2012-05-02_banana-bread-and-a--puff-puff---the-perfect-ghanaian-office-morning-tea--b6cc39d8a872/</link>
      <pubDate>Wed, 02 May 2012 00:00:03 -1000</pubDate>
      
      <guid>https://gerard.au/2012-05-02_banana-bread-and-a--puff-puff---the-perfect-ghanaian-office-morning-tea--b6cc39d8a872/</guid>
      <description>&lt;hr&gt;
&lt;p&gt;&lt;img src=&#34;./images/1*Ycwim9VgeA0pIt2jK_r84Q.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Banana bread and a “puff puff”. The perfect Ghanaian office morning tea.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title> What all the cool kids are currently listening to in Ghana.</title>
      <link>https://gerard.au/2012-05-01_what-all-the-cool-kids-are-currently-listening-to-in-ghana--e3bf861d86a6/</link>
      <pubDate>Tue, 01 May 2012 00:00:03 -1000</pubDate>
      
      <guid>https://gerard.au/2012-05-01_what-all-the-cool-kids-are-currently-listening-to-in-ghana--e3bf861d86a6/</guid>
      <description>&lt;hr&gt;
&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;
      &lt;iframe allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen&#34; loading=&#34;eager&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; src=&#34;https://www.youtube.com/embed/17vC8qZILJE?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; title=&#34;YouTube video&#34;&gt;&lt;/iframe&gt;
    &lt;/div&gt;

&lt;p&gt;What all the cool kids are currently listening to in Ghana.&lt;/p&gt;
&lt;p&gt;- P Square Ft Akon &amp;amp; May D — Chop My Money&lt;/p&gt;
&lt;p&gt;(Source: &lt;a href=&#34;https://www.youtube.com/&#34;&gt;https://www.youtube.com/&lt;/a&gt;)&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>SOS Children’s Village, Weekend #2</title>
      <link>https://gerard.au/2012-05-01_sos-children-s-village--weekend--2-b60ba82b798a/</link>
      <pubDate>Tue, 01 May 2012 00:00:02 -1000</pubDate>
      
      <guid>https://gerard.au/2012-05-01_sos-children-s-village--weekend--2-b60ba82b798a/</guid>
      <description>&lt;hr&gt;
&lt;p&gt;After a rather long work week, a nice relaxing weekend would have been
lovely. However, not wanting to waste any of our precious time in Ghana,
we made this weekend as hectic as the last.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*bIfbIhKIumURgwf5.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;On Saturday, we organized a last minute visit to the SOS Children’s
Village in Accra. SOS is an organization that runs orphanages around the
world. Instead of “Oliver twist” style dormitories, SOS Villages are
arranged into small houses, with each house containing between 8 and 12
kids. Each house then has a house mother, who is responsible for running
their particular household and caring for the kids. The idea behind this
is that it mimics the family environment as closely as possible. Kids
stay in the village until they’re 16 or so, when they’re moved to
accommodation nearer to high school.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*Sb_d3917N9mtgXa8.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;All twelve of us headed off to the village, and after a quick tour of
the primary school, it was time to visit the houses. We entered the
first house and greeted the mother. The kids, who were watching TV,
looked as equally shocked as we were; the flash photography from some of
the others didn’t really help. We introduced ourselves individually,
which added to the shyness, and then after some awkward attempts at
conversations, we proceeded to the next house.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*oaVRB8Q75u_gWAvM.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*LS_TtMtd1XNVbh9Y.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;And then kids started to come up to us. They were incredibly fascinated
by the array of cameras we were wielding. From tiny point and shoots, to
big SLRs, they loved them all. We watchfully handed them over, and 260
shots later, they filled up my CF Card. The little ones just wanted to
push buttons, but the older ones were more interested in how the camera
worked.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*2wqlKq6_Ik2kS1iI.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*NiWW5JXxScH5te0E.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Once, the cameras had exhausted their appeal, some bright spark thought
it would be a good idea for us to have a soccer match in the 32 degree,
90% humidity weather. I’m unfit at the best of times , but running
around with a bunch of people on a soccer field was particularly
challenging.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*KExV9KhyBfy1I4u2.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Unfortunately, it was soon time to leave, but I could have spent all day
there. We dropped off our rather large box of presents, and then headed
to the bus. The little 4 year old girl who insisted on holding my hand
all the way to the door wasn’t too happy to see us go.&lt;/p&gt;
&lt;p&gt;PS: The kids took almost all of these photos.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Lake Volta Cruise, Weekend #2</title>
      <link>https://gerard.au/2012-05-01_lake-volta-cruise--weekend--2-c81746c7ff80/</link>
      <pubDate>Tue, 01 May 2012 00:00:01 -1000</pubDate>
      
      <guid>https://gerard.au/2012-05-01_lake-volta-cruise--weekend--2-c81746c7ff80/</guid>
      <description>&lt;hr&gt;
&lt;p&gt;Sunday we headed out to Lake Volta, a large man-made dam used to
generate all of Ghana’s power supply. Apart from it being a picturesque
location for a dam, the main attraction is a boat cruise along the lake
to Dodi Island. The cruise takes three hours to reach the island, where
you then spend 30 minutes, and then three hours back to the starting
location. The boat cruise is the experience, not the island.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*ejt4IWVV0S8fUQgY.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;So we arrived at 8:00 to ensure that we got tickets for the cruise,
which can be very popular. We were told on arrival the boat was leaving
today at 10:30, instead of the scheduled 9:30. No problem, we just
amused ourselves with the limited selection of souvenir sellers. There’s
always a chance to haggle for a bargain.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*RCHSpkR52XSxZo9a.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;We finally got to board the Dodi Princess at 10:00, where we found a
nice set of tables to enjoy the view from. So we sat back, sipped on our
one complimentary drink, and just chatted. Soon enough our buffet lunch
was served; we got fried rice, jollof rice, coleslaw, and a choice of
chicken or fish. It was a little cold, but I wasn’t expecting much for a
boat.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*lrJEt6vdP9mUmtAi.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;The boat eventually arrived at the island and boy were we in for a
shock. The tour company had organized some traditional drummers and
dancers on the pier to greet the boat, but after the pier there was a
swarm of people begging for money. We spent our 30 minutes walking
around the island, getting harassed, and then we went back to the boat
for the cruise home.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*REXQz8Hnle-gLbBm.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Funnily enough, I actually enjoyed the boating part of the experience;
it was relaxing and an enjoyable way to see a different part of the
countryside. If they just improved the Dodi Island experience a bit, it
would definitely be a lot better.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*54vrwGBdcKT4lBp4.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;We ended up getting dinner at a restaurant a little downstream, but we
didn’t manage to get back to the hotel until 9:00pm.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*_N3IEme5BUUkqC4d.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Hour five of our Dodi Island Cruise, Lake Volta.</title>
      <link>https://gerard.au/2012-04-29_hour-five-of-our-dodi-island-cruise--lake-volta--e710c852d185/</link>
      <pubDate>Sun, 29 Apr 2012 00:00:01 -1000</pubDate>
      
      <guid>https://gerard.au/2012-04-29_hour-five-of-our-dodi-island-cruise--lake-volta--e710c852d185/</guid>
      <description>&lt;hr&gt;
&lt;p&gt;&lt;img src=&#34;./images/1*z-d-dnvJ304vrxcaPgJdVA.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Hour five of our Dodi Island Cruise, Lake Volta.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>What&#39;s this all about again?</title>
      <link>https://gerard.au/2012-04-27_what-s-this-all-about-again--d1e3ced6d913/</link>
      <pubDate>Fri, 27 Apr 2012 00:00:02 -1000</pubDate>
      
      <guid>https://gerard.au/2012-04-27_what-s-this-all-about-again--d1e3ced6d913/</guid>
      <description>&lt;hr&gt;
&lt;p&gt;You’d think that after two weeks in, I would have already explained what
I was doing in Ghana. Up until now, I’ve been particularly vague about
what I’m doing here, just to be on the safe side. However, after a brief
discussion with the in country coordinator today, I can apparently say a
little more than just complete silence.&lt;/p&gt;
&lt;p&gt;IBM has partnered with &lt;a href=&#34;http://www.cdcdevelopmentsolutions.org/&#34;&gt;CDC Development
Solutions&lt;/a&gt;, a non-profit,
non-government organization which leverages volunteer resources to drive
economic growth in emerging markets, and &lt;a href=&#34;http://www.usaid.gov/&#34;&gt;USAID&lt;/a&gt;,
the US Government’s overseas AID program, to develop the &lt;a href=&#34;http://icvonline.org/&#34;&gt;Centre of
Excellence for International Corporate
Volunteerism&lt;/a&gt; (CEICV) . The first project of this
new, three way partnership, is the project I’m on in Ghana. My project
team is working on developing a roadmap for an IT system, for the newly
proposed Supply Chain Management Unit (SCMU), which sits under the
Ministry of Health. So there you have it, that’s what I’m doing.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*Ke1-1Xo1AAEiUXPH.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Week #2 has been even more hectic than the first. We had a visit out to
the Eastern Region of the country to see some of their various health
facilities. The 90km trip took almost 2 hours, down roads ranging from
four lane highways to dirt tracks. But once we got out there, the
scenery was spectacular. Unfortunately, we were there to work, so the
only photo I managed to grab was between meetings&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*TOumjlwWW45ARWTU.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Talk to you soon :)&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Someone has reviewed one of my favourite restaurants in Accra ;)</title>
      <link>https://gerard.au/2012-04-27_someone-has-reviewed-one-of-my-favourite-restaurants-in-accra----a3e0236c0c40/</link>
      <pubDate>Fri, 27 Apr 2012 00:00:01 -1000</pubDate>
      
      <guid>https://gerard.au/2012-04-27_someone-has-reviewed-one-of-my-favourite-restaurants-in-accra----a3e0236c0c40/</guid>
      <description>&lt;hr&gt;
&lt;p&gt;&lt;a href=&#34;http://www.twomunch.com/2012/04/27/the-edge-of-the-peri-peri/&#34;&gt;Someone&lt;/a&gt; has reviewed one of my favourite restaurants in Accra ;)&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>ANZAC Day Memorial Service at Christiansborg War Cemetery, Osu, Ghana</title>
      <link>https://gerard.au/2012-04-25_anzac-day-memorial-service-at-christiansborg-war-cemetery--osu--ghana-4764e87650da/</link>
      <pubDate>Wed, 25 Apr 2012 00:00:01 -1000</pubDate>
      
      <guid>https://gerard.au/2012-04-25_anzac-day-memorial-service-at-christiansborg-war-cemetery--osu--ghana-4764e87650da/</guid>
      <description>&lt;hr&gt;
&lt;p&gt;&lt;img src=&#34;./images/1*9_ojwhBTU3y4HrKsttpFbw.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;ANZAC Day Memorial Service at Christiansborg War Cemetery, Osu, Ghana&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Cape Coast, Weekend #1</title>
      <link>https://gerard.au/2012-04-24_cape-coast--weekend--1-55f2ae80357e/</link>
      <pubDate>Tue, 24 Apr 2012 00:00:01 -1000</pubDate>
      
      <guid>https://gerard.au/2012-04-24_cape-coast--weekend--1-55f2ae80357e/</guid>
      <description>&lt;hr&gt;
&lt;p&gt;Week one was a bit of a shock to the system. We’ve been running around
all over the place interviewing different stakeholders. Sometimes the
instructions provided are like “Go to Building X (which is usually in a
different suburb), and ask for Person Y”. Somehow, with the help of our
ever patient driver, we haven’t gotten terribly lost as yet. On a more
important issue, there aren’t any cafes near the office, so we routinely
have to drive to Osu, a suburb that’s only 3 kilometers away but takes
20 minutes to get there in traffic.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*qmKdWqs9SuJtRrwR.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;For the weekend, the group decided to go visit the Cape Coast, a region,
you guessed it, along the coast. It’s famous for its beautiful beaches
and its plentiful supply of castles. I love a good castle. The Cape
Coast is located a numbing three hours from Accra, but we ended up
renting a large mini-bus which had plenty of space. Its off-roading
abilities were dubiously tested with the last couple of kilometers to
the hotel being on a dirt ‘road’.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*1RRTM6G4hAi8stV3.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://en.wikipedia.org/wiki/Elmina_Castle&#34;&gt;Elmina Castle&lt;/a&gt; was our
first stop. Initially constructed by the Portuguese to trade for Gold,
it was eventually turned into a hub for slave trading. The lovely, well
preserved building, lost some of its gloss when the tour guide described
all the atrocities that had occurred within its walls.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*d7waD_5brF180nEz.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Secondly, we visited the &lt;a href=&#34;http://en.wikipedia.org/wiki/Cape_Coast_Castle&#34;&gt;Cape Coast
Castle&lt;/a&gt;, which is the
better know of the two. Unlike the first castle, we weren’t bombarded
with people trying to sell us personalised sea shells, which was a
welcome change. This castle was specifically built for trading slaves,
and featured some rudimentary drainage within the rooms.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*3khQjhs6Ubo5nTYH.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;After a sweltering hot visit to the castles, we headed back to the hotel
for a well-earned swim at the beach.&lt;/p&gt;
&lt;p&gt;The following day, we headed out to the &lt;a href=&#34;http://en.wikipedia.org/wiki/Kakum_National_Park&#34;&gt;Kakum National
Park&lt;/a&gt;. It’s main
attraction is the Tree Walk, which consists of a number of bridges
strung between trees within the forest canopy. At its highest point,
it’s over 40 meters above the ground.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*_KeM3H5NgGFAG7SQ.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*RtC2DhshwSTPNPhF.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;(Adaeze &amp;amp; Francis)&lt;/p&gt;
&lt;p&gt;The weekend was an amazing experience. I was however, well tired out by
the end of it.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Day #1 — Workshop</title>
      <link>https://gerard.au/2012-04-22_day--1---workshop-dc574eabf3c3/</link>
      <pubDate>Sun, 22 Apr 2012 00:00:01 -1000</pubDate>
      
      <guid>https://gerard.au/2012-04-22_day--1---workshop-dc574eabf3c3/</guid>
      <description>&lt;hr&gt;
&lt;p&gt;Although photos of lions, markets and food are fun, this is
unfortunately not a holiday. Monday morning we had a full day workshop
with a number of the key stakeholders. Presentations, meet &amp;amp; greets, and
brainstorming sessions. It was a lot of work.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*4EWVvxdJXcJgnx_m.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;(Phone Courtesy of
&lt;a href=&#34;http://cscghanadeiva.wordpress.com/&#34; title=&#34;Deiva&#39;s Blog&#34;&gt;Deiva&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;We have three project teams, made up of four people, each working on
semi-intertwined health related project. We’re working on specific
deliverables as part of a larger body of work, so it’ll take us a while
to get up to speed with the current status. We have an entire week of
meetings ahead of us, as we try and balance out the various client
requirements. It’s hard to separate the forest from the trees, when you
can’t identify a forest or a tree.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>IBM Ranked Second on &#34;100 Best Corporate Citizens&#34; List</title>
      <link>https://gerard.au/2012-04-19_ibm-ranked-second-on--100-best-corporate-citizens--list-e07faab587fc/</link>
      <pubDate>Thu, 19 Apr 2012 00:00:01 -1000</pubDate>
      
      <guid>https://gerard.au/2012-04-19_ibm-ranked-second-on--100-best-corporate-citizens--list-e07faab587fc/</guid>
      <description>&lt;hr&gt;
&lt;p&gt;&lt;a href=&#34;http://citizenibm.com/2012/04/ibm-ranked-second-on-%E2%80%9C100-best-corporate-citizens%E2%80%9D-list.html&#34;&gt;IBM Ranked Second on “100 Best Corporate Citizens”
List&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Welcome to Accra, Boys &amp; Girls</title>
      <link>https://gerard.au/2012-04-18_welcome-to-accra--boys---girls-36e336902582/</link>
      <pubDate>Wed, 18 Apr 2012 00:00:01 -1000</pubDate>
      
      <guid>https://gerard.au/2012-04-18_welcome-to-accra--boys---girls-36e336902582/</guid>
      <description>&lt;hr&gt;
&lt;p&gt;A wall of heat hit me when I got off the plane in Accra at 9:00pm. An
angry, humid, treacherous heat, which was hell bent on showing me who
was boss. Our driver met us just out of customs, and we were quickly
whisked off to the hotel. A good thing too, as I almost fell asleep on
the bus; who knew sitting in a plane seat for 6 hours watching ‘How I
Met Your Mother’ could be so tiring?&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*M-uKmBDFNSMg0fwo.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Our first full day in Accra involved a moderate amount of walking and an
excessive amount of sweating. I did get to try tiny boned Guineafowl for
lunch.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*1aEjmILCyKusjQEx.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;The following day was a little less of a temperature shock, with a visit
to the Textiles Market. The market had a good mix of textiles,
paintings, carvings and handmade drums. Just like all markets in the
developing world, the stallholders vigorously peddled their wears, and I
was luckily saved a couple of times by our lovely guide, Francis. When
you get the price from the vendor, you divide it by four, and then you
start negotiating.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*Rr3zlUb1QixoKoD_.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Accra is a beautifully chaotic city on the coastline. The spaghetti
roads, the old stone buildings, the MTN booths on every corner. New
buildings are going up everywhere; there’s a lot of development, but you
can still walk down the street and see somebody carrying a chicken in a
plastic bag.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*zEJkpVbQRWKCs2_K.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*L80UnW6RuEhQ5SY8.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Jozi Layover</title>
      <link>https://gerard.au/2012-04-17_jozi-layover-8bfebdd2d18c/</link>
      <pubDate>Tue, 17 Apr 2012 00:00:01 -1000</pubDate>
      
      <guid>https://gerard.au/2012-04-17_jozi-layover-8bfebdd2d18c/</guid>
      <description>&lt;hr&gt;
&lt;p&gt;Jozi, Jo’burg, Johannesburg, regardless of its name, is Australia’s
gateway to Africa. With a relaxing 22 hour layover in my pocket, I had
plenty of time to enjoy some of the wonders of the city, in a safe, fun
filled way. Luckily, one of the other CSC participants was taking the
same afternoon flight to Accra, so we decided to go out and visit a Lion
Sanctuary just out of town.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*k1QC1mpJDa1LFuqb.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;After precariously explaining ourselves at the front desk, we were
introduced to a driver, who had been recommended, and we headed out to
the sanctuary. The first part of the visit involved a car based safari,
where we saw a number of lions, including an apparently rare white lion.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*SllBgUtc-oaFaSbC.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*dKVi4jH9IFb_cSXw.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;However, the real highlight occurred after the driving safari. The
centre had an area where you could play with lion cubs.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*ZOcPo7x0jSwp-Erv.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;If that wasn’t exciting enough, you could also hand feed a pair of
giraffes.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*Tqm63udphA9mG_T2.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Once we’d finished with the lion safari, we headed to a crocodile farm,
which also had a disturbing large section of snakes. Not being a snake
lover, I refrained from having one coiled around my neck.&lt;/p&gt;
&lt;p&gt;I also had the opportunity to hold a baby crocodile, which unfortunately
peed down my arm. If you have the opportunity to hold one, don’t squeeze
too hard.&lt;/p&gt;
&lt;p&gt;After our little bit of wildlife, we headed back to OR Tumbo
International Airport for our leisurely six hour flight to Accra.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Pack your bags</title>
      <link>https://gerard.au/2012-04-11_pack-your-bags-6e0183c895ca/</link>
      <pubDate>Wed, 11 Apr 2012 00:00:01 -1000</pubDate>
      
      <guid>https://gerard.au/2012-04-11_pack-your-bags-6e0183c895ca/</guid>
      <description>&lt;hr&gt;
&lt;p&gt;Cooped up in the office every day, it’s sometimes hard to feel like
you’re part of something bigger. You work with the same people, deal
with same problems, send the same emoticons and go to the same $2
coffee joint. When you read interesting stories like
&lt;a href=&#34;http://www.engadget.com/2012/03/06/cha-ching-ibms-watson-heads-to-citigroup-to-meddle-in-human-fi/&#34;&gt;this&lt;/a&gt;,
there is quite a bit of disconnect between that and the java web-service
you’ve spent all day working on.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*rFdSyBmCgB-LPlf9.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;One of the things that I’m most excited about with the Corporate Service
Corps program is the ability to meet, and work with IBMers from around
the globe. It was one of the primary reasons for me apply for the
program. The Ghana assignment team is comprised of Client Executives,
Chip Designers, Application Developers, Financial Analysts and Sales
People from every business unit and every continent. Oh, and me. There
are very few companies in the world that have the ability to offer these
kinds of opportunities to employees.&lt;/p&gt;
&lt;p&gt;So it’s my last day before heading off to Johannesburg tomorrow morning
at an eyeball bleeding 6:30am. I’ll see you on the other side of the
ditch.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Off and racing. Next stop, Johannesburg.</title>
      <link>https://gerard.au/2012-04-11_off-and-racing--next-stop--johannesburg--6f79de98fcca/</link>
      <pubDate>Wed, 11 Apr 2012 00:00:00 -1000</pubDate>
      
      <guid>https://gerard.au/2012-04-11_off-and-racing--next-stop--johannesburg--6f79de98fcca/</guid>
      <description>&lt;hr&gt;
&lt;p&gt;&lt;img src=&#34;./images/1*FR7MSaHzRiQb_3j8KHDOYg.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Off and racing. Next stop, Johannesburg.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>MEL to ACC to KGL to MEL</title>
      <link>https://gerard.au/2012-03-02_mel-to-acc-to-kgl-to-mel-1dececd9c941/</link>
      <pubDate>Fri, 02 Mar 2012 00:00:00 -1000</pubDate>
      
      <guid>https://gerard.au/2012-03-02_mel-to-acc-to-kgl-to-mel-1dececd9c941/</guid>
      <description>&lt;hr&gt;
&lt;p&gt;Booking flights around, to, or even near Africa, from Australia, is
hard. It’s not that you can’t book the flights online, it’s that the
familiar online tools such as &lt;a href=&#34;http://expedia.com&#34;&gt;Expedia&lt;/a&gt; and
&lt;a href=&#34;http://kayak.com&#34;&gt;Kayak&lt;/a&gt; just don’t have the ability to plan
complicated route itineraries that don’t involve flying through a hub
city like New York.&lt;/p&gt;
&lt;p&gt;To show you the joy, I’ve selected three travel dates that roughly
coincide with what I’ve booked. To keep it simple, I’ve selected
mult-city bookings at all sites, flying Melbourne to Accra (the capital
of Ghana), Accra to Kigali (Capital of Rwanda) and finally Kigali to
Melbourne.&lt;/p&gt;
&lt;p&gt;First up, Expedia.com.au: It suggests flying KLM the entire way,
connecting via Kuala Lumpur, and Amsterdam. Even fly from Accra to
Kigali, it suggests connecting via Amsterdam. And the return flight is a
leisurely 51 hours. Not great.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*6mUjz0T2vMDm0YRJ.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Secondly, I tried expedia.com. The US based site usually has better
deals, and a greater selection of flights. It suggested flying to Accra
via Bangkok and Addis Ababa on Thai and Ethiopian Air. This is actually
the route the travel agent recommended. However, coming back, it’s
gotten a little bit too creative for its own good recommending Addis
Ababa, Bangkok, Guangzhou to Melbourne.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*UvjqZ0s6jlEjv11V.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Finally a tried Kayak, which decided it was all too complicated and went
home.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*rI44WzAaUfi-yXeA.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;So what have I ended up doing? I booked QANTAS and South African Air via
South Africa, but I had to book the tickets on two different sites, and
I have an overnight stay in Johannesburg.&lt;/p&gt;
&lt;p&gt;Only 6 weeks to go.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Teaming Over Time Zones</title>
      <link>https://gerard.au/2012-02-05_teaming-over-time-zones-e11d0940034e/</link>
      <pubDate>Sun, 05 Feb 2012 00:00:00 -1000</pubDate>
      
      <guid>https://gerard.au/2012-02-05_teaming-over-time-zones-e11d0940034e/</guid>
      <description>&lt;hr&gt;
&lt;p&gt;Part of the CSC program involves a weekly dialup meeting with everyone
in the team. When the first meeting appeared in my calendar, the start
time for the meeting was 12:00am to 1:00am. Surely that’s not the best
time for a team meeting? However, when the team is made up of people
from Brazil, Canada, China, Germany, India, Ireland, Singapore and the
USA, that’s unfortunately the most appropriate time for the majority of
people. Thanks to
&lt;a href=&#34;http://www.timeanddate.com/worldclock/meeting.htm&#34;&gt;timeanddate.com&lt;/a&gt; for
curing my time-zone ineptitude.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*jKHjh6QWyIfHPzUK.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Photo From Flickr, By &lt;a href=&#34;http://www.flickr.com/photos/mikesgeography/&#34;&gt;Michele
Mazzone&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;So how does a conference call go with 12 people scattered across the
planet? Surprisingly well. There are the standard extended semi-awkward
pauses when people try not to politely talk over each other, and the
occasional call dropout, but apart from that the two calls thus far have
gone well — apart from 12:00am being past my bedtime. We’ve been using
the &lt;a href=&#34;https://www.lotuslive.com&#34;&gt;Lotus Live&lt;/a&gt; meeting service which makes
it a little easier to share presentations and screens between
participants.&lt;/p&gt;
&lt;p&gt;As part of the preparation, we’ve been given a set of tasks to complete
every week, with a number of them centring on team work. We’ve developed
introductory participant profiles to help introduce ourselves to each
other, and collaboratively developed a code of conduct for the team.
Interestingly, regardless of our backgrounds, business units or areas of
expertise, we all came up with a similar set of suggestions for the code
of conduct. Good teams aren’t restricted by time zones.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>IBM Corporate Service Corps</title>
      <link>https://gerard.au/2012-01-29_ibm-corporate-service-corps-2af4a32e956f/</link>
      <pubDate>Sun, 29 Jan 2012 00:00:00 -1000</pubDate>
      
      <guid>https://gerard.au/2012-01-29_ibm-corporate-service-corps-2af4a32e956f/</guid>
      <description>&lt;hr&gt;
&lt;p&gt;The IBM Corporate Service Corps is a selective leadership program
available to IBMers with three or more years of service. You’re assigned
to a team of approximately 10 colleagues from around the world and given
a project to work on in an emerging market. The project involves two
months of after hours pre-work and then a month of work within your
designated country. Click
&lt;a href=&#34;http://www.ibm.com/ibm/responsibility/corporateservicecorps/&#34; title=&#34;IBM CSC Homepage&#34;&gt;here&lt;/a&gt;
if you’d like to know more about the program.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./images/0*b8K599It_VCpV9Tc.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;So I’ve been assigned to a team that will be working in Accra, Ghana.
What do I know about Ghana? Well apart from the fact it’s in Africa, not
a lot; I’m hardly a global citizen. So over the next two months I’ll be
learning a lot more about Ghana and the CSC program, and updating this
tumblr.&lt;/p&gt;
</description>
    </item>
    
    
    
    
  </channel>
</rss>
