Russell Haering 2012-01-11T07:06:39+00:00 http://russellhaering.com/ Russell Haering russellhaering@gmail.com Cast Preview Release 2011-04-25T00:00:00+00:00 http://russellhaering.com/2011/04/25/cast-preview-release <p>For the last few months I've been working on and off for Cloudkick (now Rackspace) on a project that we are calling <a href="http://cast-project.org">Cast</a>. I'm happy to announce that this afternoon we're releasing Cast version 0.1. The source has been on <a href="https://github.com/cloudkick/cast">Github</a> all along, but with this release we feel that the project has finally progressed to a point where:</p> <ol> <li>We've implemented the functionality planned for the first iteration.</li> <li>The afforementioned functionality actually works against the current version of Node.js.</li> <li>We have a <a href="http://cast-project.org">website</a> and documented most of the imporant parts.</li> </ol> <h2>Thats Great, So What Is It?</h2> <p>In short, Cast is an open-source deployment and service management system.</p> <p>At Cloudkick we tend to see users deploying their code in one of three ways:</p> <ol> <li>Services are deployed via a configuration management system such as Puppet or Chef.</li> <li>Services are deployed by some sort SSH wrapper such as Fabric or Capistrano.</li> <li>Services are deployed to a "Platform as a Service" such as Heroku.</li> </ol> <p>But none of these are perfect. Respectively:</p> <ol> <li>The high overhead in interacting with configuration management systems is fine when they are managing 'infrastructure' (that is, the systems on which you run your services), but tend to impede a smooth "devops" style workflow with fast iterations and easy deployment and upgrades.</li> <li>SSH wrappers typically work well enough on small scales, but but they feel like a hack, and don't trivially integrate with in-house systems.</li> <li>Of all the options, people seem to like these the best. The price speaks for itself - Platforms as a Service (PaaS) are hugely valuable to their users. The problem is that these platforms are closed systems, inflexible and not very "sysadmin friendly". When they go down, you're trapped. When the pricing or terms change, you're trapped. If they don't or can't do what you want, you're trapped.</li> </ol> <p>With this situation in mind, what could we write for our users? An Open Platform (optionally, as a Service).</p> <h2>What Can it Do?</h2> <p>Using Cast you can:</p> <ol> <li>Upload your application to a server.</li> <li>Create 'instances' of your application. Think 'staging' and 'production'.</li> <li>Manage (start, stop, restart, etc) services provided by your application.</li> <li>Deploy new versions of your application.</li> <li>Do all of this from the command line <em>or</em> from a <a href="http://cast-project.org/docs/rest-api/">REST API</a>.</li> </ol> <p>We have a lot more interesting features planned. Hint: think "Cast cluster". But if this sounds like something you're interested in, stay tuned, <a href="https://groups.google.com/group/cast-dev/">share your thoughts</a> or consider looking into a job at the new <a href="http://jobs.rackspace.com/search?q=%22san+francisco%22">San Francisco Rackspace Office</a></p> Directory-Backed Resources in Node.js 2011-02-12T00:00:00+00:00 http://russellhaering.com/2011/02/12/nodejs-directory-backed-resources <p>After writing my <a href="http://russellhaering.com/2011/02/07/nodejs-flow-control/">last post</a> about the flow control techniques we are using in <a href="https://github.com/cloudkick/cast">cast</a> I realized that the process of writing the post was probably as valuable to me as the post will be to anyone else (especially since it was on such a well covered topic). It forced me to explicitly state and justify my reasoning, and caused me to rethink a few things in the process. Since then, whenever I've found myself contemplating a design issue I've tried to think of how I would write it up. This post is the logical extension of that plan, that is, an actual writeup of just such an issue.</p> <h2>The Problem</h2> <p>In cast, we maintain very little state in memory between requests. Most state is stored to the filesystem in the form of the directory structures that we use to manage applications, services, etc. As such we have a lot of code dedicated to managing these resources, including the 'Instance' class I mentioned last time (pedants: I know, <a href="http://howtonode.org/prototypical-inheritance">JavaScript technically has no classes</a>, but thats the pattern we're emulating so thats what I'm going to call them). For a lot of reasons, most of which are outside the scope of this discussion, we've found an Object-Oriented model to be the best way to represent these resources.</p> <p>There are three main ways that instances of such a class tend to be created:</p> <ol> <li>We create a new resource on the filesystem and return an instace of the class for further operations.</li> <li>We create a single instance of the class to represent a resource that already exists.</li> <li>We create a list of class instances to represent the set of all existing resources of this sort on the filesystem.</li> </ol> <p>The problem is that creating a new resource on the filesystem (obviously) requires filesystem operations and when retrieving one or more instances to represent pre-existing resources we almost invariably want to make sure that the resource in question actually exists, again requiring filesystem operations. This means that each of these operations must be done in an asynchronous manner. In short</p> <div class="highlight"><pre><code class="javascript"><span class="kd">var</span> <span class="nx">foo</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Instance</span><span class="p">(</span><span class="s1">&#39;foo&#39;</span><span class="p">);</span> </code></pre> </div> <p>is not sufficient to either create a new instance or retrieve one with any assurance that it exists.</p> <h2>The Solution</h2> <p>Once again, the solution itself isn't all that revolutionary: we need factories. While many seem to associate the term with unpleasant memories of Java, factories are pretty standard practice in Node, and as a general matter are a very good thing.</p> <p>There are two general patterns with factories in Node. The first, and the one that is most common in Node's public APIs, is to return objects that implement the EventEmitter (a sort of general purpose promise - but don't tell anyone I said that) interface, then let the user register callbacks for various events on that object. For example, to create a TCP connection:</p> <div class="highlight"><pre><code class="javascript"><span class="kd">var</span> <span class="nx">net</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;net&#39;</span><span class="p">);</span> <span class="c1">// Create a connection to google.com on port 80</span> <span class="kd">var</span> <span class="nx">goog</span> <span class="o">=</span> <span class="nx">net</span><span class="p">.</span><span class="nx">createConnection</span><span class="p">(</span><span class="mi">80</span><span class="p">,</span> <span class="s1">&#39;google.com&#39;</span><span class="p">);</span> <span class="c1">// Wait for the connection to actually open before using it</span> <span class="nx">goog</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;connect&#39;</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="nx">goog</span><span class="p">.</span><span class="nx">setEncoding</span><span class="p">(</span><span class="s1">&#39;utf8&#39;</span><span class="p">);</span> <span class="nx">goog</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;data&#39;</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">chunk</span><span class="p">)</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">chunk</span><span class="p">);</span> <span class="p">});</span> <span class="nx">goog</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="s1">&#39;GET /\r\nhost: google.com\r\n\r\n&#39;</span><span class="p">);</span> <span class="p">});</span> </code></pre> </div> <p>The other, which is less common in Node itself, is to pass a callback to the factory function, which is called with the instance once it is ready. One case where this pattern still exists is in the Node API is in fs.stat():</p> <div class="highlight"><pre><code class="javascript"><span class="kd">var</span> <span class="nx">fs</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;fs&#39;</span><span class="p">);</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">stat</span><span class="p">(</span><span class="s1">&#39;/tmp&#39;</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">stats</span><span class="p">)</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">stats</span><span class="p">.</span><span class="nx">isDirectory</span><span class="p">());</span> <span class="p">});</span> </code></pre> </div> <p>Note: the pattern of passing a callback directly to a function is quite common in Node, but fs.stat() is the only case I know of where the second argument (ie, the one that isn't 'err') is something other than a primitive or a list of primitives. On reflection that's probably a bad way to distinguish a 'factory', so maybe the above is a bad, example but you get the idea.</p> <p>This second pattern is more appropriate for our purposes, as the instances will tend to be short lived and would be unlikely to ever emit any other event.</p> <p>Sticking to this pattern, what we've ended up with is something like our instance API in cast:</p> <div class="highlight"><pre><code class="javascript"><span class="c1">// Create instance &#39;foo&#39; using fooapp version 1.0</span> <span class="nx">deployment</span><span class="p">.</span><span class="nx">create_instances</span><span class="p">(</span><span class="s1">&#39;foo&#39;</span><span class="p">,</span> <span class="s1">&#39;fooapp&#39;</span><span class="p">,</span> <span class="s1">&#39;1.0&#39;</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">instance</span><span class="p">)</span> <span class="p">{</span> <span class="p">...</span> <span class="p">});</span> <span class="c1">// Retrieve instance &#39;foo&#39;</span> <span class="nx">deployment</span><span class="p">.</span><span class="nx">get_instance</span><span class="p">(</span><span class="s1">&#39;foo&#39;</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">instance</span><span class="p">)</span> <span class="p">{</span> <span class="p">...</span> <span class="p">});</span> <span class="c1">// Retrieve a list of all instances</span> <span class="nx">deployment</span><span class="p">.</span><span class="nx">list_instances</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">instances</span><span class="p">)</span> <span class="p">{</span> <span class="p">...</span> <span class="p">});</span> </code></pre> </div> <h2>Factory Factories</h2> <p>I've spent the last week or so reworking our test framework in cast and updating everything to work with Node 0.4.0, but when I get the chance one thing I may try is factoring out all of the common code into a single module. This would include a single class that all such resources could inherit from, which would likely include an 'exists' method to test for existence and (yeah, go ahead and hate me, I'm going to say it anyway) "factory factories" to generate factory methods for retrieving and listing them.</p> <p>While "factory factories" are commonly cited as a great reason to hate Java, in my opinion JavaScript's 'functions as first-class objects' model makes quite pretty. In this case they could make generation of factory methods for directory-backed resource FooResource look something like this:</p> <div class="highlight"><pre><code class="javascript"><span class="p">...</span> <span class="nx">exports</span><span class="p">.</span><span class="nx">get_fooresource</span> <span class="o">=</span> <span class="nx">dir_resources</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="nx">FooResource</span><span class="p">);</span> <span class="nx">exprots</span><span class="p">.</span><span class="nx">get_fooresource</span> <span class="o">=</span> <span class="nx">dir_resources</span><span class="p">.</span><span class="nx">get_list</span><span class="p">(</span><span class="nx">FooResource</span><span class="p">);</span> </code></pre> </div> Flow Control in Node.js 2011-02-07T00:00:00+00:00 http://russellhaering.com/2011/02/07/nodejs-flow-control <p>Late last summer I began working on Cloudkick's new deployment tool, <a href="https://github.com/cloudkick/cast">cast</a> which is written in <a href="http://nodejs.org/">Node.js</a>. At the time I had only minimal experience with JavaScript, having occasionally used it for frontend work on personal projects. After a crash course from <a href="http://bjorn.tipling.com">Bjorn</a>, Cloudkick's in-house JavaScript wizard, I was able to write mostly working code.</p> <h2>The Problem</h2> <p>As <a href="http://www.google.com/search?q=nodejs+flow+control">others have found</a> however, writing a Node app that is maintainable is a lot different than just writing one that works.</p> <p>In cast, we frequently perform long sequences of filesystem operations. For example, for each application deployed in cast we maintain a symlink to the 'current' version. Because symlinks can be atomically replaced with a call to <a href="http://www.kernel.org/doc/man-pages/online/pages/man2/rename.2.html">rename</a> the 'current' link will <em>always</em> point to the single current version of the application.</p> <p>The sequence of operations we need to perform to update the 'current' symlink is:</p> <ol> <li>Determine the path to the new version. We have a method that can do this for us, but it hits the filesystem so calls to it are asynchronous.</li> <li>Make sure the new version exists. Again, we're hitting the filesystem so this happens asynchronously as well.</li> <li>Create a new symlink pointing to the specified version. Another filesystem operation.</li> <li>Swap the new symlink into place using 'rename'.</li> </ol> <p>The naive solution comes out looking something like this:</p> <div class="highlight"><pre><code class="javascript"><span class="nx">Instance</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">activate_version</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">version</span><span class="p">,</span> <span class="nx">callback</span><span class="p">)</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">self</span> <span class="o">=</span> <span class="k">this</span><span class="p">;</span> <span class="kd">var</span> <span class="nx">new_version_link</span> <span class="o">=</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">self</span><span class="p">.</span><span class="nx">root</span><span class="p">,</span> <span class="s1">&#39;new&#39;</span><span class="p">);</span> <span class="kd">var</span> <span class="nx">cur_version_link</span> <span class="o">=</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">self</span><span class="p">.</span><span class="nx">root</span><span class="p">,</span> <span class="s1">&#39;current&#39;</span><span class="p">);</span> <span class="nx">self</span><span class="p">.</span><span class="nx">get_version_path</span><span class="p">(</span><span class="nx">version</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">vp</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">callback</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span> <span class="p">}</span> <span class="nx">path</span><span class="p">.</span><span class="nx">exists</span><span class="p">(</span><span class="nx">vp</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">exists</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">exists</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">callback</span><span class="p">(</span><span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s1">&#39;Cannot activate nonexistent version \&#39;&#39;</span> <span class="o">+</span> <span class="nx">version</span> <span class="o">+</span> <span class="s1">&#39;\&#39;&#39;</span><span class="p">));</span> <span class="p">}</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">symlink</span><span class="p">(</span><span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">vp</span><span class="p">),</span> <span class="nx">new_version_link</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">callback</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span> <span class="p">}</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">rename</span><span class="p">(</span><span class="nx">new_version_link</span><span class="p">,</span> <span class="nx">current_version_link</span><span class="p">,</span> <span class="nx">callback</span><span class="p">);</span> <span class="p">});</span> <span class="p">});</span> <span class="p">});</span> <span class="p">};</span> </code></pre> </div> <p>This isn't too bad, but its already getting unwieldy after only four operations (other methods perform ten or more such operations). There are a few problems here:</p> <ol> <li>We quickly end up with very deep nesting which pushes all our code increasingly further to the right and limits the space left on each line for code.</li> <li>Inserting a new operation early in the chain forces us add another level of indentation to every subsequent operation. This causes commits to contain massive diffs which can hide other accidental (or, theoretically, malicious) changes and obfuscates the actual changes.</li> <li>Its just plain difficult to read.</li> </ol> <h2>The Solution</h2> <p>This isn't exactly revolutionary, but the first step is to use one of the <a href="https://github.com/ry/node/wiki/modules#async-flow">many</a> flow control modules available for Node. For cast we've been using <a href="https://github.com/caolan/async">async</a> and I've grown quite attached to it, but there are a lot of other nice ones available as well. Using async, our 'activate_version' method now looks like this:</p> <div class="highlight"><pre><code class="javascript"><span class="nx">Instance</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">activate_version</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">version</span><span class="p">,</span> <span class="nx">callback</span><span class="p">)</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">self</span> <span class="o">=</span> <span class="k">this</span><span class="p">;</span> <span class="kd">var</span> <span class="nx">new_version_path</span><span class="p">;</span> <span class="kd">var</span> <span class="nx">new_version_link</span> <span class="o">=</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">self</span><span class="p">.</span><span class="nx">root</span><span class="p">,</span> <span class="s1">&#39;new&#39;</span><span class="p">);</span> <span class="kd">var</span> <span class="nx">current_version_link</span> <span class="o">=</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">self</span><span class="p">.</span><span class="nx">root</span><span class="p">,</span> <span class="s1">&#39;current&#39;</span><span class="p">);</span> <span class="nx">async</span><span class="p">.</span><span class="nx">series</span><span class="p">([</span> <span class="c1">// Get the path to the specified version</span> <span class="kd">function</span><span class="p">(</span><span class="nx">callback</span><span class="p">)</span> <span class="p">{</span> <span class="nx">self</span><span class="p">.</span><span class="nx">get_version_path</span><span class="p">(</span><span class="nx">version</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">vp</span><span class="p">)</span> <span class="p">{</span> <span class="nx">new_version_path</span> <span class="o">=</span> <span class="nx">vp</span><span class="p">;</span> <span class="k">return</span> <span class="nx">callback</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span> <span class="p">});</span> <span class="p">},</span> <span class="c1">// Make sure the version exists</span> <span class="kd">function</span><span class="p">(</span><span class="nx">callback</span><span class="p">)</span> <span class="p">{</span> <span class="nx">path</span><span class="p">.</span><span class="nx">exists</span><span class="p">(</span><span class="nx">new_version_path</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">exists</span><span class="p">)</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">err</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">exists</span><span class="p">)</span> <span class="p">{</span> <span class="nx">err</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s1">&#39;Cannot activate nonexistent version \&#39;&#39;</span> <span class="o">+</span> <span class="nx">version</span> <span class="o">+</span> <span class="s1">&#39;\&#39;&#39;</span><span class="p">);</span> <span class="p">}</span> <span class="k">return</span> <span class="nx">callback</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span> <span class="p">});</span> <span class="p">},</span> <span class="c1">// Create the new link</span> <span class="kd">function</span><span class="p">(</span><span class="nx">callback</span><span class="p">)</span> <span class="p">{</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">symlink</span><span class="p">(</span><span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">new_version_path</span><span class="p">),</span> <span class="nx">new_version_link</span><span class="p">,</span> <span class="nx">callback</span><span class="p">);</span> <span class="p">},</span> <span class="c1">// Atomically move it into place</span> <span class="nx">async</span><span class="p">.</span><span class="nx">apply</span><span class="p">(</span><span class="nx">fs</span><span class="p">.</span><span class="nx">rename</span><span class="p">,</span> <span class="nx">new_version_link</span><span class="p">,</span> <span class="nx">current_version_link</span><span class="p">)</span> <span class="p">],</span> <span class="nx">callback</span><span class="p">);</span> <span class="p">};</span> </code></pre> </div> <p>I don't want to turn this into an async tutorial, so check out the documentation (or code) if you're curious about the specifics, but in short we get a number of benefits here:</p> <ol> <li>We describe our list of actions by providing a list of functions: its very intuitive.</li> <li>We avoid both of the problems I mentioned that result from nesting</li> <li>We provide a single final callback that is always fired, allowing centralized error handling, logging, etc. Alternatively, if the signatures match, we can pass the method's callback argument to async.</li> <li>We can use async's 'parallel', 'waterfall' or several other methods to change the behavior without significantly altering the pattern.</li> </ol> <p>Once you're using some sort of flow control library, the some things to keep in mind that keep things manageable:</p> <ol> <li>Don't chain multiple asynchronous calls within one function passed to async. If two calls are part of the same logical operation then chances are you should make that operation its own asynchronous function.</li> <li>If you have a group of logically related calls that could be parallelized, split them into their own function and use async.parallel() (or some equivalent).</li> <li>If you need to be able to undo your operations in reverse order, you can build a stack of reverse operations as you go, then execute it with async in the final callback.</li> </ol> Projects, Projects, Projects 2011-01-16T00:00:00+00:00 http://russellhaering.com/2011/01/16/projects <p>I'm nearly half way through my senior year here at Oregon State University (if all goes well I should graduate in June), but for the next few months it appears that my life will be dominated by a few major projects and a whole lot of minor ones. Since I haven't been very successful in my efforts to think of a good topic to write about recently, hopefully a brief synopsis of my various projects will work instead.</p> <h1>Major Projects</h1> <h2>Fuzzing Linux's USB Subsystem</h2> <p>Among my various projects, this is the one I've spent the least time on so far, but one I'm definitely pretty excited about. One of the major requirements of students in the <a href="http://oregonstate.edu/dept/honors/">Honors College</a> here is that each student complete a "Thesis" project. In short, pick a topic, spend a bunch of time on it (doing research, making something, etc) then write a paper about what you did and defend it in front of a panel of experts.</p> <p>While struggling to decide on a topic (having good ideas is the easy part, you also have to find a faculty member willing to mentor you on the project) I found myself in a guest lecture by <a href="http://ifup.org">Brandon Philips</a> who was speaking about Linux Kernel development. Since I've always thought Kernel hacking would be sort of fun, I sent him an email asking for Kernel-related project ideas. One of his suggestions that caught my interest was creating a hardware device to fuzz Linux's USB implementation and drivers. I'm hoping I can get away with doing this in software against a virtual machine, but I have yet to really pursue it too much.</p> <h2>Mobile Accessibility Project</h2> <p>The other major project I have going for school at the moment is my "Senior Project". I'm working with fellow student Kiel Friedt and our mentor Carlos Jensen to create an Android application, designed for the deaf, that runs in the background and causes then phone to vibrate and display a visual notification when it detects sounds such as sirens or alarms.</p> <p>Obviously this is pretty ambitious given the limitations of the hardware, but as of last night we have basic real-time signal processing working and can generate notifications upon detection of pre-programmed tones. Obviously we're still a long way from being able to detect and identify alarms or sirens, but it does make for a neat demo of what we're working on.</p> <h2>Cast</h2> <p>Of my major projects this is definitely my favorite, and the only one not directly related to school. Since the latter portion of the summer I've been working with a few other developers at <a href="https://cloudkick.com/">Cloudkick</a> on an open source deployment tool we're calling <a href="https://github.com/cloudkick/cast/">cast</a>.</p> <p>Basically, we're creating a service in <a href="http://nodejs.org">Node.js</a> that provides a RESTful API for deploying, monitoring and managing applications, regardless of their language or purpose. In future versions we intend to add support for clustering, with peer-to-peer distribution across the cluster and a variety of other neat features to help with large deployments in the cloud or the datacenter.</p> <h1>Minor Projects / Courses</h1> <h2>Compiler</h2> <p>This term I'm taking a "translators" course in which, over the course of the term, we are constructing a compiler for a contrived language that <a href="http://web.engr.oregonstate.edu/~budd/">Dr. Budd</a> came up with. The compiler itself is written in Java, with various stages due over the course of the term (the lexer was due last week, the parser is due this week, and so on). Combined with the written assignments, it seems like this will probably account for somewhat over half of my time spent on non-major projects this term.</p> <h2>Scientific Visualizations</h2> <p>I'm also taking a scientific visualization course in which it seems that we will be creating a number of visualizations over the course of the term. We use OpenGL, which I have some experience with both from the computer graphics course I took last term and from some playing around I did with it back in highschool (I was going to make the next great first person shooter, or something like that).</p> Building Android Client Applications 2010-08-20T00:00:00+00:00 http://russellhaering.com/2010/08/20/building-android-client-applications <p>I've had the privilege of spending this summer down in San Francisco working as an intern at <a href="https://cloudkick.com">Cloudkick</a> (side note: if you like the cloud and you're interested in working with an incredible team on some sweet projects, check out our <a href="http://cloudkick.jobscore.com/list">jobs page</a>) working on all sorts of cool projects.</p> <p>In any case, one of the projects I've been working on this summer is an Android client for Cloudkick. Earlier today I pushed version 0.2.1 (the first public release of the 0.2.x series, after I got cold feet on 0.2.0) of the application to the Android Market, and to celebrate all the lessons I've learned, I wanted to share a few of them. In this case, I'm going to give an overview of how to create an Android client for an existing web service. This won't be a full tutorial (indeed, it will assume that you already know how to create a functional Android application of some sort), but if you're interested the source to Cloudkick for Android is <a href="http://github.com/cloudkick/cloudkick-android">available on GitHub</a>.</p> <h3>Disclaimer</h3> <ol> <li>I'm not an experienced Java developer, and I'm certainly not an Android expert, so if it looks like I'm doing something wrong, I probably am. I'd love to know about it though, so if you're feeling nice, drop me a comment.</li> <li>A lot of this code is taken out of context, so if you want it to actually work, just get the <a href="http://github.com/cloudkick/cloudkick-android">original, functional source code</a>.</li> </ol> <h2>Step 1: API Client Library</h2> <p>The first thing you'll need is an API client library of some sort. I started out with something like this.</p> <div class="highlight"><pre><code class="java"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">CloudkickAPI</span> <span class="o">{</span> <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="n">String</span> <span class="n">TAG</span> <span class="o">=</span> <span class="s">&quot;CloudkickAPI&quot;</span><span class="o">;</span> <span class="kd">private</span> <span class="kd">static</span> <span class="n">String</span> <span class="n">API_HOST</span> <span class="o">=</span> <span class="s">&quot;api.cloudkick.com&quot;</span><span class="o">;</span> <span class="kd">private</span> <span class="kd">static</span> <span class="n">String</span> <span class="n">API_VERSION</span> <span class="o">=</span> <span class="s">&quot;1.0&quot;</span><span class="o">;</span> <span class="kd">private</span> <span class="kd">final</span> <span class="n">String</span> <span class="n">key</span><span class="o">;</span> <span class="kd">private</span> <span class="kd">final</span> <span class="n">String</span> <span class="n">secret</span><span class="o">;</span> <span class="kd">private</span> <span class="kd">final</span> <span class="n">HttpClient</span> <span class="n">client</span><span class="o">;</span> <span class="kd">private</span> <span class="n">SharedPreferences</span> <span class="n">prefs</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span> <span class="kd">public</span> <span class="nf">CloudkickAPI</span><span class="o">(</span><span class="n">Context</span> <span class="n">context</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">EmptyCredentialsException</span> <span class="o">{</span> <span class="n">prefs</span> <span class="o">=</span> <span class="n">PreferenceManager</span><span class="o">.</span><span class="na">getDefaultSharedPreferences</span><span class="o">(</span><span class="n">context</span><span class="o">);</span> <span class="n">key</span> <span class="o">=</span> <span class="n">prefs</span><span class="o">.</span><span class="na">getString</span><span class="o">(</span><span class="s">&quot;editKey&quot;</span><span class="o">,</span> <span class="s">&quot;&quot;</span><span class="o">);</span> <span class="n">secret</span> <span class="o">=</span> <span class="n">prefs</span><span class="o">.</span><span class="na">getString</span><span class="o">(</span><span class="s">&quot;editSecret&quot;</span><span class="o">,</span> <span class="s">&quot;&quot;</span><span class="o">);</span> <span class="k">if</span> <span class="o">(</span><span class="n">key</span> <span class="o">==</span> <span class="s">&quot;&quot;</span> <span class="o">||</span> <span class="n">secret</span> <span class="o">==</span> <span class="s">&quot;&quot;</span><span class="o">)</span> <span class="o">{</span> <span class="k">throw</span> <span class="k">new</span> <span class="nf">EmptyCredentialsException</span><span class="o">();</span> <span class="o">}</span> <span class="n">HttpParams</span> <span class="n">params</span> <span class="o">=</span> <span class="k">new</span> <span class="n">BasicHttpParams</span><span class="o">();</span> <span class="n">HttpProtocolParams</span><span class="o">.</span><span class="na">setVersion</span><span class="o">(</span><span class="n">params</span><span class="o">,</span> <span class="n">HttpVersion</span><span class="o">.</span><span class="na">HTTP_1_1</span><span class="o">);</span> <span class="n">HttpProtocolParams</span><span class="o">.</span><span class="na">setContentCharset</span><span class="o">(</span><span class="n">params</span><span class="o">,</span> <span class="n">HTTP</span><span class="o">.</span><span class="na">DEFAULT_CONTENT_CHARSET</span><span class="o">);</span> <span class="n">HttpProtocolParams</span><span class="o">.</span><span class="na">setUseExpectContinue</span><span class="o">(</span><span class="n">params</span><span class="o">,</span> <span class="kc">true</span><span class="o">);</span> <span class="n">SchemeRegistry</span> <span class="n">registry</span> <span class="o">=</span> <span class="k">new</span> <span class="n">SchemeRegistry</span><span class="o">();</span> <span class="n">registry</span><span class="o">.</span><span class="na">register</span><span class="o">(</span><span class="k">new</span> <span class="n">Scheme</span><span class="o">(</span><span class="s">&quot;http&quot;</span><span class="o">,</span> <span class="n">PlainSocketFactory</span><span class="o">.</span><span class="na">getSocketFactory</span><span class="o">(),</span> <span class="mi">80</span><span class="o">));</span> <span class="n">registry</span><span class="o">.</span><span class="na">register</span><span class="o">(</span><span class="k">new</span> <span class="n">Scheme</span><span class="o">(</span><span class="s">&quot;https&quot;</span><span class="o">,</span> <span class="n">SSLSocketFactory</span><span class="o">.</span><span class="na">getSocketFactory</span><span class="o">(),</span> <span class="mi">443</span><span class="o">));</span> <span class="n">ClientConnectionManager</span> <span class="n">connman</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ThreadSafeClientConnManager</span><span class="o">(</span><span class="n">params</span><span class="o">,</span> <span class="n">registry</span><span class="o">);</span> <span class="n">client</span> <span class="o">=</span> <span class="k">new</span> <span class="n">DefaultHttpClient</span><span class="o">(</span><span class="n">connman</span><span class="o">,</span> <span class="n">params</span><span class="o">);</span> <span class="o">}</span> <span class="o">}</span> </code></pre> </div> <p>There are a few things going on here:</p> <ol> <li>The TAG string is useful for logging (I saw this suggested on StackOverflow, I'm not sure if its standard practice or what)</li> <li>The Cloudkick API requires OAuth authentication, so I retrieve an OAuth key and secret from the preferences storage, which we haven't set up yet. So unless you know how to do that yourself, this isn't going to actually work just yet.</li> <li>If the credentials are empty, throw a custom exception. This is handy for redirecting the user to the login screen if they haven't attempted a login.</li> <li>The HTTP Client setup is somewhat more complicated than I would like, but I needed it to support multi-threading. I'm still not positive I got all of the necessary settings, but it seems to work for me.</li> </ol> <p>Right, so this obviously doesn't actually <em>do</em> anything so far, so lets go ahead and make that happen:</p> <div class="highlight"><pre><code class="java"><span class="kd">private</span> <span class="n">String</span> <span class="nf">doRequest</span><span class="o">(</span><span class="n">String</span> <span class="n">path</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">BadCredentialsException</span><span class="o">,</span> <span class="n">OAuthException</span><span class="o">,</span> <span class="n">IOException</span> <span class="o">{</span> <span class="n">HttpResponse</span> <span class="n">response</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span> <span class="n">StringBuilder</span> <span class="n">body</span> <span class="o">=</span> <span class="k">new</span> <span class="n">StringBuilder</span><span class="o">();</span> <span class="k">try</span> <span class="o">{</span> <span class="n">HttpGet</span> <span class="n">request</span> <span class="o">=</span> <span class="k">new</span> <span class="n">HttpGet</span><span class="o">(</span><span class="s">&quot;https://&quot;</span> <span class="o">+</span> <span class="n">API_HOST</span> <span class="o">+</span> <span class="s">&quot;/&quot;</span> <span class="o">+</span> <span class="n">API_VERSION</span> <span class="o">+</span> <span class="n">path</span><span class="o">);</span> <span class="n">OAuthConsumer</span> <span class="n">consumer</span> <span class="o">=</span> <span class="k">new</span> <span class="n">CommonsHttpOAuthConsumer</span><span class="o">(</span><span class="n">key</span><span class="o">,</span> <span class="n">secret</span><span class="o">);</span> <span class="n">consumer</span><span class="o">.</span><span class="na">sign</span><span class="o">(</span><span class="n">request</span><span class="o">);</span> <span class="n">response</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="na">execute</span><span class="o">(</span><span class="n">request</span><span class="o">);</span> <span class="k">if</span> <span class="o">(</span><span class="n">response</span><span class="o">.</span><span class="na">getStatusLine</span><span class="o">().</span><span class="na">getStatusCode</span><span class="o">()</span> <span class="o">==</span> <span class="mi">401</span><span class="o">)</span> <span class="o">{</span> <span class="n">response</span><span class="o">.</span><span class="na">getEntity</span><span class="o">().</span><span class="na">consumeContent</span><span class="o">();</span> <span class="k">throw</span> <span class="k">new</span> <span class="nf">BadCredentialsException</span><span class="o">();</span> <span class="o">}</span> <span class="n">InputStream</span> <span class="n">is</span> <span class="o">=</span> <span class="n">response</span><span class="o">.</span><span class="na">getEntity</span><span class="o">().</span><span class="na">getContent</span><span class="o">();</span> <span class="n">BufferedReader</span> <span class="n">rd</span> <span class="o">=</span> <span class="k">new</span> <span class="n">BufferedReader</span><span class="o">(</span><span class="k">new</span> <span class="n">InputStreamReader</span><span class="o">(</span><span class="n">is</span><span class="o">));</span> <span class="n">String</span> <span class="n">line</span><span class="o">;</span> <span class="k">while</span> <span class="o">((</span><span class="n">line</span> <span class="o">=</span> <span class="n">rd</span><span class="o">.</span><span class="na">readLine</span><span class="o">())</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> <span class="n">body</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">line</span><span class="o">);</span> <span class="o">}</span> <span class="o">}</span> <span class="k">finally</span> <span class="o">{</span> <span class="k">if</span> <span class="o">(</span><span class="n">response</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="n">response</span><span class="o">.</span><span class="na">getEntity</span><span class="o">().</span><span class="na">isStreaming</span><span class="o">())</span> <span class="o">{</span> <span class="n">response</span><span class="o">.</span><span class="na">getEntity</span><span class="o">().</span><span class="na">consumeContent</span><span class="o">();</span> <span class="o">}</span> <span class="o">}</span> <span class="k">return</span> <span class="n">body</span><span class="o">.</span><span class="na">toString</span><span class="o">();</span> <span class="o">}</span> <span class="kd">public</span> <span class="n">ArrayList</span><span class="o">&lt;</span><span class="n">Node</span><span class="o">&gt;</span> <span class="n">getNodes</span><span class="o">()</span> <span class="kd">throws</span> <span class="n">BadCredentialsException</span><span class="o">,</span> <span class="n">OAuthException</span><span class="o">,</span> <span class="n">IOException</span><span class="o">,</span> <span class="n">JSONException</span> <span class="o">{</span> <span class="n">String</span> <span class="n">body</span> <span class="o">=</span> <span class="n">doRequest</span><span class="o">(</span><span class="s">&quot;/query/nodes&quot;</span><span class="o">);</span> <span class="n">ArrayList</span><span class="o">&lt;</span><span class="n">Node</span><span class="o">&gt;</span> <span class="n">nodes</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ArrayList</span><span class="o">&lt;</span><span class="n">Node</span><span class="o">&gt;();</span> <span class="n">JSONArray</span> <span class="n">rawNodes</span> <span class="o">=</span> <span class="k">new</span> <span class="n">JSONArray</span><span class="o">(</span><span class="n">body</span><span class="o">);</span> <span class="kt">int</span> <span class="n">rawCount</span> <span class="o">=</span> <span class="n">rawNodes</span><span class="o">.</span><span class="na">length</span><span class="o">();</span> <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">rawCount</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span> <span class="n">nodes</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="k">new</span> <span class="n">Node</span><span class="o">(</span><span class="n">rawNodes</span><span class="o">.</span><span class="na">getJSONObject</span><span class="o">(</span><span class="n">i</span><span class="o">)));</span> <span class="o">}</span> <span class="n">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">&quot;Retrieved &quot;</span> <span class="o">+</span> <span class="n">nodes</span><span class="o">.</span><span class="na">size</span><span class="o">()</span> <span class="o">+</span> <span class="s">&quot; Nodes&quot;</span><span class="o">);</span> <span class="k">return</span> <span class="n">nodes</span><span class="o">;</span> <span class="o">}</span> </code></pre> </div> <p>So, notable things here:</p> <ol> <li>It is important to consume all content off of the HTTP response, even if an error occurs, otherwise the connection won't be freed up and you will eventually run out of free connections in your pool.</li> <li>We have another custom exception which gets thrown if a 401 status code is returned, indicating that the user's credentials were invalid (as opposed to empty).</li> <li>We throw <em>everything</em>. You don't really need to list these out, but it comes in handy later when you want to know what exceptions to look for.</li> <li>I'm constructing custom Node objects from the returned JSON list. The details aren't really important, but a Node is basically a constructor that fills in a bunch of 'final' fields.</li> </ol> <p>Obviously you'll want more API methods than this, but you get the idea. Each method just calls doRequest() then constructs an object or list of objects to be returned.</p> <h2>Step 2: An Activity</h2> <p>To actually display the data, you'll need an activity. A lot of web API clients spend a lot of time displaying and drilling down lists, so I'm going to use one of those as an example.</p> <div class="highlight"><pre><code class="java"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">DashboardActivity</span> <span class="kd">extends</span> <span class="n">Activity</span> <span class="kd">implements</span> <span class="n">OnItemClickListener</span> <span class="o">{</span> <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="n">String</span> <span class="n">TAG</span> <span class="o">=</span> <span class="s">&quot;DashboardActivity&quot;</span><span class="o">;</span> <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="kt">int</span> <span class="n">SETTINGS_ACTIVITY_ID</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="kt">int</span> <span class="n">LOGIN_ACTIVITY_ID</span> <span class="o">=</span> <span class="mi">1</span><span class="o">;</span> <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="kt">int</span> <span class="n">refreshRate</span> <span class="o">=</span> <span class="mi">30</span><span class="o">;</span> <span class="kd">private</span> <span class="n">CloudkickAPI</span> <span class="n">api</span><span class="o">;</span> <span class="kd">private</span> <span class="n">ProgressDialog</span> <span class="n">progress</span><span class="o">;</span> <span class="kd">private</span> <span class="n">ListView</span> <span class="n">dashboard</span><span class="o">;</span> <span class="kd">private</span> <span class="n">NodesAdapter</span> <span class="n">adapter</span><span class="o">;</span> <span class="kd">private</span> <span class="kt">boolean</span> <span class="n">haveNodes</span> <span class="o">=</span> <span class="kc">false</span><span class="o">;</span> <span class="kd">private</span> <span class="kt">boolean</span> <span class="n">isRunning</span> <span class="o">=</span> <span class="kc">false</span><span class="o">;</span> <span class="kd">private</span> <span class="kd">final</span> <span class="n">ArrayList</span><span class="o">&lt;</span><span class="n">Node</span><span class="o">&gt;</span> <span class="n">nodes</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ArrayList</span><span class="o">&lt;</span><span class="n">Node</span><span class="o">&gt;();</span> <span class="kd">private</span> <span class="kd">final</span> <span class="n">Handler</span> <span class="n">reloadHandler</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Handler</span><span class="o">();</span> <span class="nd">@Override</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">onCreate</span><span class="o">(</span><span class="n">Bundle</span> <span class="n">savedInstanceState</span><span class="o">)</span> <span class="o">{</span> <span class="kd">super</span><span class="o">.</span><span class="na">onCreate</span><span class="o">(</span><span class="n">savedInstanceState</span><span class="o">);</span> <span class="n">PreferenceManager</span><span class="o">.</span><span class="na">setDefaultValues</span><span class="o">(</span><span class="k">this</span><span class="o">,</span> <span class="n">R</span><span class="o">.</span><span class="na">xml</span><span class="o">.</span><span class="na">preferences</span><span class="o">,</span> <span class="kc">false</span><span class="o">);</span> <span class="n">dashboard</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ListView</span><span class="o">(</span><span class="k">this</span><span class="o">);</span> <span class="n">adapter</span> <span class="o">=</span> <span class="k">new</span> <span class="n">NodesAdapter</span><span class="o">(</span><span class="k">this</span><span class="o">,</span> <span class="n">R</span><span class="o">.</span><span class="na">layout</span><span class="o">.</span><span class="na">node_item</span><span class="o">,</span> <span class="n">nodes</span><span class="o">);</span> <span class="n">dashboard</span><span class="o">.</span><span class="na">setAdapter</span><span class="o">(</span><span class="n">adapter</span><span class="o">);</span> <span class="n">dashboard</span><span class="o">.</span><span class="na">setOnItemClickListener</span><span class="o">(</span><span class="k">this</span><span class="o">);</span> <span class="n">dashboard</span><span class="o">.</span><span class="na">setBackgroundColor</span><span class="o">(</span><span class="n">Color</span><span class="o">.</span><span class="na">WHITE</span><span class="o">);</span> <span class="n">setContentView</span><span class="o">(</span><span class="n">dashboard</span><span class="o">);</span> <span class="n">reloadAPI</span><span class="o">();</span> <span class="o">}</span> <span class="kd">private</span> <span class="kt">void</span> <span class="nf">reloadAPI</span><span class="o">()</span> <span class="o">{</span> <span class="k">try</span> <span class="o">{</span> <span class="n">api</span> <span class="o">=</span> <span class="k">new</span> <span class="n">CloudkickAPI</span><span class="o">(</span><span class="k">this</span><span class="o">);</span> <span class="n">haveNodes</span> <span class="o">=</span> <span class="kc">false</span><span class="o">;</span> <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">EmptyCredentialsException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span> <span class="n">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">&quot;Empty Credentials, forcing login&quot;</span><span class="o">);</span> <span class="n">Intent</span> <span class="n">loginActivity</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Intent</span><span class="o">(</span><span class="n">getBaseContext</span><span class="o">(),</span> <span class="n">LoginActivity</span><span class="o">.</span><span class="na">class</span><span class="o">);</span> <span class="n">startActivityForResult</span><span class="o">(</span><span class="n">loginActivity</span><span class="o">,</span> <span class="n">LOGIN_ACTIVITY_ID</span><span class="o">);</span> <span class="o">}</span> <span class="o">}</span> <span class="nd">@Override</span> <span class="kd">protected</span> <span class="kt">void</span> <span class="nf">onActivityResult</span><span class="o">(</span><span class="kt">int</span> <span class="n">requestCode</span><span class="o">,</span> <span class="kt">int</span> <span class="n">resultCode</span><span class="o">,</span> <span class="n">Intent</span> <span class="n">data</span><span class="o">)</span> <span class="o">{</span> <span class="k">if</span> <span class="o">(</span><span class="n">requestCode</span> <span class="o">==</span> <span class="n">SETTINGS_ACTIVITY_ID</span><span class="o">)</span> <span class="o">{</span> <span class="n">reloadAPI</span><span class="o">();</span> <span class="o">}</span> <span class="k">if</span> <span class="o">(</span><span class="n">requestCode</span> <span class="o">==</span> <span class="n">LOGIN_ACTIVITY_ID</span><span class="o">)</span> <span class="o">{</span> <span class="k">try</span> <span class="o">{</span> <span class="k">if</span> <span class="o">(</span><span class="n">data</span><span class="o">.</span><span class="na">getBooleanExtra</span><span class="o">(</span><span class="s">&quot;login&quot;</span><span class="o">,</span> <span class="kc">false</span><span class="o">))</span> <span class="o">{</span> <span class="n">reloadAPI</span><span class="o">();</span> <span class="o">}</span> <span class="k">else</span> <span class="o">{</span> <span class="n">finish</span><span class="o">();</span> <span class="o">}</span> <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">NullPointerException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span> <span class="n">finish</span><span class="o">();</span> <span class="o">}</span> <span class="o">}</span> <span class="o">}</span> <span class="o">}</span> </code></pre> </div> <p>This is pretty straightforward, when the Activity is created it sets up the list view which uses a subclassed ArrayAdapter called NodesAdapter (more on that momentarily). Then, it calls reloadAPI() which attempts to instantiate a CloudkickAPI. However if an EmptyCredentialsException is thrown it starts up a LoginActivity. To be honest, the way the LoginActivity in Cloudkick for Android works is probably very dissimilar from anything most people will need (the API it uses was designed to be easily parsed from a C client, the login process requires multiple requests with possible user input between them, etc), but the concept with this is fairly simple: get and validate some sort of credentials from the user, and store the necessary tokens for use by the API client. Then return from the LoginActivity, setting a boolean to indicate that the user actually logged in (as opposed to using the back button to exit the application).</p> <p>This last bit might seem a little odd, but the idea is that when the user opens the application for the first time and sees the login screen, you (obviously) want them to be able to exit if they don't feel like logging in just then. If you were to simply call reloadAPI() every time the LoginActivity returned, if the user just pressed the back button they would be continuously bounced back to the LoginActivity until they filled in some sort of credentials. So we detect that case by the absence of the "login" boolean in the returned Intent and simply exit if they didn't attempt to login. This is another one of those things that I'm sure there is a better way to handle, but I haven't put much thought into figuring out what it is.</p> <p>Now ListViews aren't much fun unless you populate them, so lets look at how to do that. Inside of the DashboardActivity we add some methods:</p> <div class="highlight"><pre><code class="java"><span class="nd">@Override</span> <span class="kd">protected</span> <span class="kt">void</span> <span class="nf">onResume</span><span class="o">()</span> <span class="o">{</span> <span class="kd">super</span><span class="o">.</span><span class="na">onResume</span><span class="o">();</span> <span class="n">isRunning</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span> <span class="n">reloadService</span><span class="o">.</span><span class="na">run</span><span class="o">();</span> <span class="n">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">&quot;Reloading service started&quot;</span><span class="o">);</span> <span class="o">}</span> <span class="nd">@Override</span> <span class="kd">protected</span> <span class="kt">void</span> <span class="nf">onPause</span><span class="o">()</span> <span class="o">{</span> <span class="kd">super</span><span class="o">.</span><span class="na">onPause</span><span class="o">();</span> <span class="n">isRunning</span> <span class="o">=</span> <span class="kc">false</span><span class="o">;</span> <span class="k">if</span> <span class="o">(</span><span class="n">progress</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> <span class="n">progress</span><span class="o">.</span><span class="na">dismiss</span><span class="o">();</span> <span class="n">progress</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span> <span class="o">}</span> <span class="n">reloadHandler</span><span class="o">.</span><span class="na">removeCallbacks</span><span class="o">(</span><span class="n">reloadService</span><span class="o">);</span> <span class="n">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">&quot;Reloading callbacks canceled&quot;</span><span class="o">);</span> <span class="o">}</span> <span class="kd">private</span> <span class="kt">void</span> <span class="nf">refreshNodes</span><span class="o">()</span> <span class="o">{</span> <span class="k">if</span> <span class="o">(</span><span class="n">api</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> <span class="k">if</span> <span class="o">(!</span><span class="n">haveNodes</span><span class="o">)</span> <span class="o">{</span> <span class="n">progress</span> <span class="o">=</span> <span class="n">ProgressDialog</span><span class="o">.</span><span class="na">show</span><span class="o">(</span><span class="k">this</span><span class="o">,</span> <span class="s">&quot;&quot;</span><span class="o">,</span> <span class="s">&quot;Loading Nodes...&quot;</span><span class="o">,</span> <span class="kc">true</span><span class="o">);</span> <span class="o">}</span> <span class="k">new</span> <span class="nf">NodeUpdater</span><span class="o">().</span><span class="na">execute</span><span class="o">();</span> <span class="o">}</span> <span class="o">}</span> <span class="kd">private</span> <span class="kd">class</span> <span class="nc">NodeUpdater</span> <span class="kd">extends</span> <span class="n">AsyncTask</span><span class="o">&lt;</span><span class="n">Void</span><span class="o">,</span> <span class="n">Void</span><span class="o">,</span> <span class="n">ArrayList</span><span class="o">&lt;</span><span class="n">Node</span><span class="o">&gt;&gt;</span> <span class="o">{</span> <span class="kd">private</span> <span class="n">Exception</span> <span class="n">e</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span> <span class="nd">@Override</span> <span class="kd">protected</span> <span class="n">ArrayList</span><span class="o">&lt;</span><span class="n">Node</span><span class="o">&gt;</span> <span class="n">doInBackground</span><span class="o">(</span><span class="n">Void</span><span class="o">...</span><span class="na">voids</span><span class="o">)</span> <span class="o">{</span> <span class="k">try</span> <span class="o">{</span> <span class="k">return</span> <span class="n">api</span><span class="o">.</span><span class="na">getNodes</span><span class="o">();</span> <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">Exception</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span> <span class="k">this</span><span class="o">.</span><span class="na">e</span> <span class="o">=</span> <span class="n">e</span><span class="o">;</span> <span class="k">return</span> <span class="kc">null</span><span class="o">;</span> <span class="o">}</span> <span class="o">}</span> <span class="nd">@Override</span> <span class="kd">protected</span> <span class="kt">void</span> <span class="nf">onPostExecute</span><span class="o">(</span><span class="n">ArrayList</span><span class="o">&lt;</span><span class="n">Node</span><span class="o">&gt;</span> <span class="n">retrieved_nodes</span><span class="o">)</span> <span class="o">{</span> <span class="c1">// Get rid of the progress dialog either way</span> <span class="k">if</span> <span class="o">(</span><span class="n">progress</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> <span class="n">progress</span><span class="o">.</span><span class="na">dismiss</span><span class="o">();</span> <span class="n">progress</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span> <span class="o">}</span> <span class="c1">// Handle errors</span> <span class="k">if</span> <span class="o">(</span><span class="n">e</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> <span class="k">if</span> <span class="o">(</span><span class="n">e</span> <span class="k">instanceof</span> <span class="n">InvalidCredentialsException</span><span class="o">)</span> <span class="o">{</span> <span class="n">Toast</span><span class="o">.</span><span class="na">makeText</span><span class="o">(</span><span class="n">DashboardActivity</span><span class="o">.</span><span class="na">this</span><span class="o">.</span><span class="na">getApplicationContext</span><span class="o">(),</span> <span class="s">&quot;Invalid Credentials&quot;</span><span class="o">,</span> <span class="n">Toast</span><span class="o">.</span><span class="na">LENGTH_SHORT</span><span class="o">).</span><span class="na">show</span><span class="o">();</span> <span class="n">Intent</span> <span class="n">settingsActivity</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Intent</span><span class="o">(</span><span class="n">getBaseContext</span><span class="o">(),</span> <span class="n">Preferences</span><span class="o">.</span><span class="na">class</span><span class="o">);</span> <span class="n">startActivityForResult</span><span class="o">(</span><span class="n">settingsActivity</span><span class="o">,</span> <span class="n">SETTINGS_ACTIVITY_ID</span><span class="o">);</span> <span class="o">}</span> <span class="k">else</span> <span class="nf">if</span> <span class="o">(</span><span class="n">e</span> <span class="k">instanceof</span> <span class="n">IOException</span><span class="o">)</span> <span class="o">{</span> <span class="n">Toast</span><span class="o">.</span><span class="na">makeText</span><span class="o">(</span><span class="n">DashboardActivity</span><span class="o">.</span><span class="na">this</span><span class="o">.</span><span class="na">getApplicationContext</span><span class="o">(),</span> <span class="s">&quot;A Network Error Occurred&quot;</span><span class="o">,</span> <span class="n">Toast</span><span class="o">.</span><span class="na">LENGTH_SHORT</span><span class="o">).</span><span class="na">show</span><span class="o">();</span> <span class="o">}</span> <span class="k">else</span> <span class="o">{</span> <span class="n">Toast</span><span class="o">.</span><span class="na">makeText</span><span class="o">(</span><span class="n">DashboardActivity</span><span class="o">.</span><span class="na">this</span><span class="o">.</span><span class="na">getApplicationContext</span><span class="o">(),</span> <span class="s">&quot;Unknown Refresh Error&quot;</span><span class="o">,</span> <span class="n">Toast</span><span class="o">.</span><span class="na">LENGTH_SHORT</span><span class="o">).</span><span class="na">show</span><span class="o">();</span> <span class="n">Log</span><span class="o">.</span><span class="na">e</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">&quot;Unknown Refresh Error&quot;</span><span class="o">,</span> <span class="n">e</span><span class="o">);</span> <span class="o">}</span> <span class="o">}</span> <span class="c1">// Handle success</span> <span class="k">else</span> <span class="nf">if</span> <span class="o">(</span><span class="n">isRunning</span><span class="o">)</span> <span class="o">{</span> <span class="n">nodes</span><span class="o">.</span><span class="na">clear</span><span class="o">();</span> <span class="n">nodes</span><span class="o">.</span><span class="na">addAll</span><span class="o">(</span><span class="n">retrieved_nodes</span><span class="o">);</span> <span class="n">haveNodes</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span> <span class="n">adapter</span><span class="o">.</span><span class="na">notifyDataSetChanged</span><span class="o">();</span> <span class="c1">// Schedule the next run</span> <span class="n">reloadHandler</span><span class="o">.</span><span class="na">postDelayed</span><span class="o">(</span><span class="n">reloadService</span><span class="o">,</span> <span class="n">refreshRate</span> <span class="o">*</span> <span class="mi">1000</span><span class="o">);</span> <span class="n">Log</span><span class="o">.</span><span class="na">i</span><span class="o">(</span><span class="n">TAG</span><span class="o">,</span> <span class="s">&quot;Next reload in &quot;</span> <span class="o">+</span> <span class="n">refreshRate</span> <span class="o">+</span> <span class="s">&quot; seconds&quot;</span><span class="o">);</span> <span class="o">}</span> <span class="o">}</span> <span class="o">}</span> <span class="kd">private</span> <span class="kd">final</span> <span class="n">Runnable</span> <span class="n">reloadService</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Runnable</span><span class="o">()</span> <span class="o">{</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">run</span><span class="o">()</span> <span class="o">{</span> <span class="c1">// This happens asynchronously and schedules the next run</span> <span class="n">refreshNodes</span><span class="o">();</span> <span class="o">}</span> <span class="o">};</span> </code></pre> </div> <p>So this is where things get a bit weird. Obviously you don't want to lock up the UI when you retrieve data from the API, but since we're still (as far as I can tell) mostly stuck doing things synchronously on Android, we need to push API requests into a background thread. So I'm just going to run through what happens here, in the order that it happens:</p> <ol> <li>Whenever the Activity is resumed we set a variable to indicate that its running, then kick off this Runnable called reloadService.</li> <li>reloadService just calls refreshNodes()</li> <li>refreshNodes() makes sure the API is non-null (I don't remember why I did that or if I still need that check...). Then if we don't have any nodes yet it pops up a fatty modal dialog box (some day I'm going to replace this with a spinning "Loading" animation in the ListView itself like I just did for the NodeViewActivity). Finally it instantiates and executes a NodeUpdater object.</li> <li>The NodeUpdater extends AsyncTask, which is a neat little API that Android has for doing tasks in the background. Basically the method doInBackground() is (believe it or not) done in a background thread.</li> <li>This is all great, but it really messes with error handling. Remember that giant list of Exceptions that the API's getNodes() method can throw? Well we can't modify the UI from this background thread, so normal exception handling patterns sort of go out the window. Instead, if any sort of Exception is thrown, we store it in a field called 'e' for later access.</li> <li>Once the API call has returned the onPostExecute() method is called, this time in the UI thread. If one of those obnoxious modal loading dialogs exists we kill it off right away. Then, we check whether an exception is stored in that 'e' field and if it is we put it through a big if/elseif where we check its type against various Exception subclasses that we might want the user to know about, and show the user some sort of Toast based on the type of exception.</li> <li>If no exception occurred and 'isRunning' is still true (ie, the user didn't get bored and leave in the meantime), we replace the contents of the existing (currently empty) node array with the returned list of nodes. Then we set 'haveNodes' so the user won't see that silly modal dialog again, and use a handler to schedule another run of the reloadService in 30 seconds.</li> <li>When the activity is paused any scheduled API calls are canceled and 'isRunning' is set to false so that any ongoing API calls don't try to update the UI when they return.</li> </ol> <h2>Step 3: The List Adapter</h2> <p>In order to make the ListView not suck, we also need to use a custom adapter.</p> <div class="highlight"><pre><code class="java"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">NodesAdapter</span> <span class="kd">extends</span> <span class="n">ArrayAdapter</span><span class="o">&lt;</span><span class="n">Node</span><span class="o">&gt;</span> <span class="o">{</span> <span class="kd">private</span> <span class="kd">final</span> <span class="kt">int</span> <span class="n">resource</span><span class="o">;</span> <span class="kd">public</span> <span class="nf">NodesAdapter</span><span class="o">(</span><span class="n">Context</span> <span class="n">context</span><span class="o">,</span> <span class="kt">int</span> <span class="n">resource</span><span class="o">,</span> <span class="n">ArrayList</span><span class="o">&lt;</span><span class="n">Node</span><span class="o">&gt;</span> <span class="n">nodes</span><span class="o">)</span> <span class="o">{</span> <span class="kd">super</span><span class="o">(</span><span class="n">context</span><span class="o">,</span> <span class="n">resource</span><span class="o">,</span> <span class="n">nodes</span><span class="o">);</span> <span class="k">this</span><span class="o">.</span><span class="na">resource</span> <span class="o">=</span> <span class="n">resource</span><span class="o">;</span> <span class="o">}</span> <span class="nd">@Override</span> <span class="kd">public</span> <span class="n">View</span> <span class="nf">getView</span><span class="o">(</span><span class="kt">int</span> <span class="n">position</span><span class="o">,</span> <span class="n">View</span> <span class="n">convertView</span><span class="o">,</span> <span class="n">ViewGroup</span> <span class="n">parent</span><span class="o">)</span> <span class="o">{</span> <span class="n">RelativeLayout</span> <span class="n">nodeView</span><span class="o">;</span> <span class="n">Node</span> <span class="n">node</span> <span class="o">=</span> <span class="n">getItem</span><span class="o">(</span><span class="n">position</span><span class="o">);</span> <span class="n">String</span> <span class="n">inflater</span> <span class="o">=</span> <span class="n">Context</span><span class="o">.</span><span class="na">LAYOUT_INFLATER_SERVICE</span><span class="o">;</span> <span class="n">LayoutInflater</span> <span class="n">li</span> <span class="o">=</span> <span class="o">(</span><span class="n">LayoutInflater</span><span class="o">)</span><span class="n">getContext</span><span class="o">().</span><span class="na">getSystemService</span><span class="o">(</span><span class="n">inflater</span><span class="o">);</span> <span class="k">if</span><span class="o">(</span><span class="n">convertView</span><span class="o">==</span><span class="kc">null</span><span class="o">)</span> <span class="o">{</span> <span class="n">nodeView</span> <span class="o">=</span> <span class="k">new</span> <span class="n">RelativeLayout</span><span class="o">(</span><span class="n">getContext</span><span class="o">());</span> <span class="n">li</span><span class="o">.</span><span class="na">inflate</span><span class="o">(</span><span class="n">resource</span><span class="o">,</span> <span class="n">nodeView</span><span class="o">,</span> <span class="kc">true</span><span class="o">);</span> <span class="o">}</span> <span class="k">else</span> <span class="o">{</span> <span class="n">nodeView</span> <span class="o">=</span> <span class="o">(</span><span class="n">RelativeLayout</span><span class="o">)</span> <span class="n">convertView</span><span class="o">;</span> <span class="o">}</span> <span class="c1">// Set the a color representing the state</span> <span class="n">TextView</span> <span class="n">statusView</span> <span class="o">=</span> <span class="o">(</span><span class="n">TextView</span><span class="o">)</span><span class="n">nodeView</span><span class="o">.</span><span class="na">findViewById</span><span class="o">(</span><span class="n">R</span><span class="o">.</span><span class="na">id</span><span class="o">.</span><span class="na">node_item_status</span><span class="o">);</span> <span class="n">statusView</span><span class="o">.</span><span class="na">setBackgroundDrawable</span><span class="o">(</span><span class="k">new</span> <span class="n">ColorDrawable</span><span class="o">(</span><span class="n">node</span><span class="o">.</span><span class="na">getStateColor</span><span class="o">()));</span> <span class="c1">// Set the background</span> <span class="n">ColorDrawable</span> <span class="n">transparent</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ColorDrawable</span><span class="o">(</span><span class="n">Color</span><span class="o">.</span><span class="na">TRANSPARENT</span><span class="o">);</span> <span class="n">ColorDrawable</span> <span class="n">opaque</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ColorDrawable</span><span class="o">(</span><span class="n">node</span><span class="o">.</span><span class="na">color</span><span class="o">);</span> <span class="n">StateListDrawable</span> <span class="n">bg</span> <span class="o">=</span> <span class="k">new</span> <span class="n">StateListDrawable</span><span class="o">();</span> <span class="n">bg</span><span class="o">.</span><span class="na">addState</span><span class="o">(</span><span class="k">new</span> <span class="kt">int</span><span class="o">[]</span> <span class="o">{</span><span class="n">android</span><span class="o">.</span><span class="na">R</span><span class="o">.</span><span class="na">attr</span><span class="o">.</span><span class="na">state_selected</span><span class="o">},</span> <span class="n">transparent</span><span class="o">);</span> <span class="n">bg</span><span class="o">.</span><span class="na">addState</span><span class="o">(</span><span class="k">new</span> <span class="kt">int</span><span class="o">[]</span> <span class="o">{</span><span class="n">android</span><span class="o">.</span><span class="na">R</span><span class="o">.</span><span class="na">attr</span><span class="o">.</span><span class="na">state_pressed</span><span class="o">},</span> <span class="n">transparent</span><span class="o">);</span> <span class="n">bg</span><span class="o">.</span><span class="na">addState</span><span class="o">(</span><span class="k">new</span> <span class="kt">int</span><span class="o">[]</span> <span class="o">{},</span> <span class="n">opaque</span><span class="o">);</span> <span class="n">nodeView</span><span class="o">.</span><span class="na">setBackgroundDrawable</span><span class="o">(</span><span class="n">bg</span><span class="o">);</span> <span class="c1">// Set the name and tags</span> <span class="n">TextView</span> <span class="n">nameText</span> <span class="o">=</span> <span class="o">(</span><span class="n">TextView</span><span class="o">)</span><span class="n">nodeView</span><span class="o">.</span><span class="na">findViewById</span><span class="o">(</span><span class="n">R</span><span class="o">.</span><span class="na">id</span><span class="o">.</span><span class="na">name</span><span class="o">);</span> <span class="n">TextView</span> <span class="n">tagsText</span> <span class="o">=</span> <span class="o">(</span><span class="n">TextView</span><span class="o">)</span><span class="n">nodeView</span><span class="o">.</span><span class="na">findViewById</span><span class="o">(</span><span class="n">R</span><span class="o">.</span><span class="na">id</span><span class="o">.</span><span class="na">tags</span><span class="o">);</span> <span class="n">nameText</span><span class="o">.</span><span class="na">setText</span><span class="o">(</span><span class="n">node</span><span class="o">.</span><span class="na">name</span><span class="o">);</span> <span class="n">tagsText</span><span class="o">.</span><span class="na">setText</span><span class="o">(</span><span class="n">node</span><span class="o">.</span><span class="na">getTagString</span><span class="o">());</span> <span class="k">return</span> <span class="n">nodeView</span><span class="o">;</span> <span class="o">}</span> <span class="o">}</span> </code></pre> </div> <p>Basically whenever the ListView wants to display a node in the list, it will call getView on the adapter. In this case most of that code is dedicated to setting a crazy background on the list items. If there are a relatively small number of background colors you will need for list items, just do this in XML and save yourself the trouble. But one cool feature of Cloudkick is that you can assign one of a bunch of colors to each of your nodes (just for organizational purposes). In order to make the selector (the orange - with the default theme - thing that highlights list items when you scroll) show through, I had to use a StateListDrawable to make the background transparent when the item was selected or pressed and opaque otherwise. But like I say, most people won't need this so your adapter will probably be much simpler than this.</p> <h2>Step 4: Drill Down</h2> <p>If all your application does is display a ListView, it had better be a really really nice ListView. Even then, you probably want to be able to drill down to get details on those items. This is easy, we just need to add one function to the DashboardActivity:</p> <div class="highlight"><pre><code class="java"><span class="kd">public</span> <span class="kt">void</span> <span class="nf">onItemClick</span><span class="o">(</span><span class="n">AdapterView</span><span class="o">&lt;?&gt;</span> <span class="n">parent</span><span class="o">,</span> <span class="n">View</span> <span class="n">view</span><span class="o">,</span> <span class="kt">int</span> <span class="n">position</span><span class="o">,</span> <span class="kt">long</span> <span class="n">id</span><span class="o">)</span> <span class="o">{</span> <span class="n">Bundle</span> <span class="n">data</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Bundle</span><span class="o">();</span> <span class="n">data</span><span class="o">.</span><span class="na">putSerializable</span><span class="o">(</span><span class="s">&quot;node&quot;</span><span class="o">,</span> <span class="n">nodes</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">position</span><span class="o">));</span> <span class="n">Intent</span> <span class="n">intent</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Intent</span><span class="o">(</span><span class="n">DashboardActivity</span><span class="o">.</span><span class="na">this</span><span class="o">,</span> <span class="n">NodeViewActivity</span><span class="o">.</span><span class="na">class</span><span class="o">);</span> <span class="n">intent</span><span class="o">.</span><span class="na">putExtras</span><span class="o">(</span><span class="n">data</span><span class="o">);</span> <span class="n">startActivity</span><span class="o">(</span><span class="n">intent</span><span class="o">);</span> <span class="o">}</span> </code></pre> </div> <p>And don't forget that line (already shown above) in onCreate():</p> <div class="highlight"><pre><code class="java"><span class="n">dashboard</span><span class="o">.</span><span class="na">setOnItemClickListener</span><span class="o">(</span><span class="k">this</span><span class="o">);</span> </code></pre> </div> <p>When the user clicks a node, the node is serialized into a Bundle which is passed to a NodeViewActivity.</p> <p>The NodeViewActivity is very similar to the DashboardActivity, with a few modifications:</p> <ol> <li>It extracts the serialized node from the passed Bundle and uses that to instantly populate (part of) the view.</li> <li>It polls for changes on both the node itself, and its check data. This works pretty much the same way as it does here, there are simply two</li> <li>It uses a nicer loading animation in the ListView itself, sort of like Gmail, <a href="http://seesmic.com/seesmic_mobile/android/">Seesmic</a> (which I really like) and half of the other apps on the market.</li> </ol> <p>And thats basically it. There is a lot of code duplication between Activities, so one thing on my TODO list is attempting to abstract most of that out into a single base class, but I'm not certain when I'll be able to make that happen.</p> <p>Again, check out the full source over <a href="http://github.com/cloudkick/cloudkick-android">on GitHub</a>, and drop me a comment if you found this helpful, or if it looks like I'm doing something wrong.</p> Blogging with Mingus 2010-05-17T00:00:00+00:00 http://russellhaering.com/2010/05/17/blogging-mingus <p>This will be the 4th post on this blog announcing that I'm switching to new blogging software - of 6 total posts. Maybe I just like deploying things more than maintaining them. But in this particular case, Wordpress had to go. Every few days it would just die randomly. The php-cgi processes would just lock up until I came in and killed things manually. Wordpress is actually pretty decent from a user perspective, and I suspect the lockups had something to do with the way I was running it or one of the plugins I was using.</p> <p>I settled on <a href="http://github.com/montylounge/django-mingus">Mingus</a>. Mingus is basically a collection of reusable Django "apps" put together to form a blog. At first I was skeptical, but I have to say I'm impressed. It has an impressive array of built in apps and features. For example the <a href="http://kylefuller.co.uk/projects/django-request/">django-request</a> app gives a nice overview of visitors to the site without the need for AWStats or Google Analytics. Of course it has built in Google Analytics support as well. Mingus comes with so many built in features that I have yet to try most of them, and have ended up disabling some.</p> <p>Mingus is not something a non-technical person could probably set up, but if you know a little about python webapps you could probably get Mingus going in a few minutes. Importing posts, tweaking the layout, etc will take somewhat longer, but Mingus is very hackable and you'll hardly be alone: github currently shows 45 forks of Mingus, including <a href="http://github.com/russellhaering/django-mingus">mine</a>.</p> Emulating an Event-Based Virtualization API 2010-02-22T00:00:00+00:00 http://russellhaering.com/2010/02/22/emulating-an-event-based-virtualization-api <p>For the last few months at the OSL we've been experimenting with a program called <a href="http://code.google.com/p/ganeti/">Ganeti</a>. Ganeti is a system for managing a virtualization cluster. There are a lot of benefits to using Ganeti, which I'll save for another time, but one of my favorites is that it has an HTTP API. For example (with hostnames redacted)</p> <div class="highlight"><pre><code class="bash">russell_h@skyship ~ <span class="nv">$ </span>curl -k https://localhost:8889/2/nodes <span class="o">[</span> <span class="o">{</span> <span class="s2">&quot;id&quot;</span>: <span class="s2">&quot;node1.example.com&quot;</span>, <span class="s2">&quot;uri&quot;</span>: <span class="s2">&quot;/2/nodes/node1.example.com&quot;</span> <span class="o">}</span>, <span class="o">{</span> <span class="s2">&quot;id&quot;</span>: <span class="s2">&quot;node2.example.com&quot;</span>, <span class="s2">&quot;uri&quot;</span>: <span class="s2">&quot;/2/nodes/node2.example.com&quot;</span> <span class="o">}</span>, <span class="o">{</span> <span class="s2">&quot;id&quot;</span>: <span class="s2">&quot;node3.example.com&quot;</span>, <span class="s2">&quot;uri&quot;</span>: <span class="s2">&quot;/2/nodes/node3.example.com&quot;</span> <span class="o">}</span>, <span class="o">{</span> <span class="s2">&quot;id&quot;</span>: <span class="s2">&quot;nod4.example.com&quot;</span>, <span class="s2">&quot;uri&quot;</span>: <span class="s2">&quot;/2/nodes/node4.example.com&quot;</span> <span class="o">}</span> <span class="o">]</span> </code></pre> </div> <p>Now I don't know about anyone else, but to me that just screams "web interface".</p> <p>One thing that has always bothered me about web interfaces to backend systems like this is that the state of the system is constantly changing. In general, when you load up the interface it takes a "snapshot" of the system, and thats what you see until you (or some javascript) poll for an updated version. Compare that to a desktop application like <a href="http://virt-manager.et.redhat.com/">virt-manager</a> which is constantly updating the view, allowing you to make decisions based on the state the system <em>is</em> in, rather than the state it <em>was</em> in. For more on the topic, albeit on a somewhat different scale, I highly recommend George Reese's recent article, <em><a href="http://broadcast.oreilly.com/2010/02/towards-event-driven-cloud-apis.html">Towards Event-Driven Cloud APIs</a>.</em></p> <p>One solution is to just poll really often. You can even cache the poll response for a few seconds to avoid hitting the backend API every time someone reloads the page. But thats still relatively inefficient, particularly in situations where there is a high likelihood that nothing has changed.</p> <p>Short of coding an event based API into Ganeti (an idea I haven't entirely discarded), the best option I could think of was to implement a sort of event-based "middleware". The idea is that a client (web browser) can subscribe to a given backend URI. If there are no existing subscribers to that URI, the contents will be fetched, returned to the client, and cached for some given period, say 5 seconds. Once the cache has expired the backend will be polled again. The result will be compared to the previous, cached result, and if the contents have changed the updated version will be sent to every subscriber before being cached. If someone subscribes to an already "active" URI they will be served the cached contents, as well as any future changes.</p> <p>The whole system being something of an experiment, I decided to implement it using Friendfeed's <a href="http://www.tornadoweb.org/">Tornado</a> and HTML5 <a href="http://dev.w3.org/html5/websockets/">Web Sockets</a>.</p> <p>A backend URI is represented by its cached contents and the set of callbacks provided by subscribers</p> <div class="highlight"><pre><code class="python"><span class="k">class</span> <span class="nc">URI</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">content</span><span class="o">=</span><span class="p">{},</span> <span class="n">callbacks</span><span class="o">=</span><span class="p">[]):</span> <span class="bp">self</span><span class="o">.</span><span class="n">content</span> <span class="o">=</span> <span class="n">content</span> <span class="bp">self</span><span class="o">.</span><span class="n">callbacks</span> <span class="o">=</span> <span class="n">callbacks</span> </code></pre> </div> <p>These will be used by what I am uncreatively calling a "JSONPushProxy"</p> <div class="highlight"><pre><code class="python"><span class="k">class</span> <span class="nc">JSONPushProxy</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">baseuri</span><span class="p">,</span> <span class="n">ttl</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">http</span> <span class="o">=</span> <span class="n">AsyncHTTPClient</span><span class="p">()</span> <span class="bp">self</span><span class="o">.</span><span class="n">ttl</span> <span class="o">=</span> <span class="n">ttl</span> <span class="bp">self</span><span class="o">.</span><span class="n">baseuri</span> <span class="o">=</span> <span class="n">baseuri</span> <span class="bp">self</span><span class="o">.</span><span class="n">uris</span> <span class="o">=</span> <span class="p">{}</span> <span class="bp">self</span><span class="o">.</span><span class="n">ioloop</span> <span class="o">=</span> <span class="n">tornado</span><span class="o">.</span><span class="n">ioloop</span><span class="o">.</span><span class="n">IOLoop</span><span class="o">.</span><span class="n">instance</span><span class="p">()</span> <span class="k">def</span> <span class="nf">try_callback</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">callback</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot;</span> <span class="sd"> Wraps a callback for some special error handling (borrowed from</span> <span class="sd"> Tornado)</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="k">if</span> <span class="n">callback</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span> <span class="k">return</span> <span class="bp">None</span> <span class="k">if</span> <span class="n">args</span> <span class="ow">or</span> <span class="n">kwargs</span><span class="p">:</span> <span class="n">callback</span> <span class="o">=</span> <span class="n">functools</span><span class="o">.</span><span class="n">partial</span><span class="p">(</span><span class="n">callback</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span> <span class="k">try</span><span class="p">:</span> <span class="k">return</span> <span class="n">callback</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> <span class="k">except</span> <span class="ne">Exception</span><span class="p">,</span> <span class="n">e</span><span class="p">:</span> <span class="c"># TODO: logging</span> <span class="k">raise</span> <span class="n">e</span> <span class="k">return</span> <span class="n">wrapper</span> <span class="k">def</span> <span class="nf">subscribe</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">uri</span><span class="p">,</span> <span class="n">callback</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot;</span> <span class="sd"> Subscribe to content for a URI relative to the Base URI of this PushProxy.</span> <span class="sd"> New subscribers will receive an immediate content push followed by pushes</span> <span class="sd"> of content every time the regularly scheduled check detect a change.</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="c"># If there are no subscribers to this uri</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">uri</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">uris</span><span class="p">:</span> <span class="c"># Build a new URI</span> <span class="n">new_uri</span> <span class="o">=</span> <span class="n">URI</span><span class="p">(</span><span class="n">callbacks</span><span class="o">=</span><span class="p">[</span><span class="n">callback</span><span class="p">])</span> <span class="bp">self</span><span class="o">.</span><span class="n">uris</span><span class="p">[</span><span class="n">uri</span><span class="p">]</span> <span class="o">=</span> <span class="n">new_uri</span> <span class="c"># Do the first fetch run</span> <span class="bp">self</span><span class="o">.</span><span class="n">fetch</span><span class="p">(</span><span class="n">uri</span><span class="p">)</span> <span class="c"># Otherwise just append a callback and send the current content</span> <span class="k">else</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">uris</span><span class="p">[</span><span class="n">uri</span><span class="p">]</span><span class="o">.</span><span class="n">callbacks</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">callback</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">alert</span><span class="p">(</span><span class="n">callback</span><span class="p">,</span> <span class="n">uri</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">uris</span><span class="p">[</span><span class="n">uri</span><span class="p">]</span><span class="o">.</span><span class="n">content</span><span class="p">)</span> <span class="k">def</span> <span class="nf">unsubscribe</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">uri</span><span class="p">,</span> <span class="n">callback</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot;</span> <span class="sd"> Unsubscribe a callback for a URI</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="bp">self</span><span class="o">.</span><span class="n">uris</span><span class="p">[</span><span class="n">uri</span><span class="p">]</span><span class="o">.</span><span class="n">callbacks</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="n">callback</span><span class="p">)</span> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">uris</span><span class="p">[</span><span class="n">uri</span><span class="p">]</span><span class="o">.</span><span class="n">callbacks</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span> <span class="c"># If no one is subscribing remove uri from the dictionary</span> <span class="k">del</span> <span class="bp">self</span><span class="o">.</span><span class="n">uris</span><span class="p">[</span><span class="n">uri</span><span class="p">]</span> <span class="k">def</span> <span class="nf">fetch</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">uri</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot;</span> <span class="sd"> When running, this will be called periodically to update the cache and</span> <span class="sd"> push it&#39;s contents to any subscribers.</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="n">r</span> <span class="o">=</span> <span class="n">HTTPRequest</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">baseuri</span><span class="o">+</span><span class="n">uri</span><span class="p">,</span> <span class="n">prepare_curl_callback</span><span class="o">=</span><span class="n">ssl_no_verify</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">http</span><span class="o">.</span><span class="n">fetch</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="n">callback</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">try_callback</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">on_response</span><span class="p">,</span> <span class="n">uri</span><span class="p">))</span> <span class="c"># Schedule the next run if we&#39;re still going</span> <span class="bp">self</span><span class="o">.</span><span class="n">ioloop</span><span class="o">.</span><span class="n">add_timeout</span><span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span> <span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">ttl</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">try_callback</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">fetch</span><span class="p">,</span> <span class="n">uri</span><span class="p">))</span> <span class="k">def</span> <span class="nf">on_response</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">uri</span><span class="p">,</span> <span class="n">response</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot;</span> <span class="sd"> Called when an HTTP response is available</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="n">new_content</span> <span class="o">=</span> <span class="n">response</span><span class="o">.</span><span class="n">body</span> <span class="k">try</span><span class="p">:</span> <span class="k">if</span> <span class="n">new_content</span> <span class="o">!=</span> <span class="bp">self</span><span class="o">.</span><span class="n">uris</span><span class="p">[</span><span class="n">uri</span><span class="p">]</span><span class="o">.</span><span class="n">content</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">uris</span><span class="p">[</span><span class="n">uri</span><span class="p">]</span><span class="o">.</span><span class="n">content</span> <span class="o">=</span> <span class="n">new_content</span> <span class="c"># Push update</span> <span class="k">for</span> <span class="n">callback</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">uris</span><span class="p">[</span><span class="n">uri</span><span class="p">]</span><span class="o">.</span><span class="n">callbacks</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">alert</span><span class="p">(</span><span class="n">callback</span><span class="p">,</span> <span class="n">uri</span><span class="p">,</span> <span class="n">new_content</span><span class="p">)</span> <span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span> <span class="c"># This can happen if the URI has been disabled since the request</span> <span class="c"># was initiated</span> <span class="k">pass</span> <span class="k">def</span> <span class="nf">alert</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">callback</span><span class="p">,</span> <span class="n">uri</span><span class="p">,</span> <span class="n">content</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot;</span> <span class="sd"> Call a callback</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="k">try</span><span class="p">:</span> <span class="n">callback</span><span class="p">(</span><span class="n">uri</span><span class="p">,</span> <span class="n">content</span><span class="p">)</span> <span class="k">except</span> <span class="ne">Exception</span><span class="p">,</span> <span class="n">e</span><span class="p">:</span> <span class="c"># Unsubscribe this callback</span> <span class="bp">self</span><span class="o">.</span><span class="n">unsubscribe</span><span class="p">(</span><span class="n">uri</span><span class="p">,</span> <span class="n">callback</span><span class="p">)</span> <span class="k">raise</span> <span class="n">e</span> </code></pre> </div> <p>Calling that "rough around the edges" would be pretty generous, but it mostly works, and at least shows what I was trying to do.</p> <p>For getting content to the browser, I used Tornado's new-ish WebSocketHandler</p> <div class="highlight"><pre><code class="python"><span class="k">class</span> <span class="nc">WebSocket</span><span class="p">(</span><span class="n">tornado</span><span class="o">.</span><span class="n">websocket</span><span class="o">.</span><span class="n">WebSocketHandler</span><span class="p">):</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">application</span><span class="p">,</span> <span class="n">request</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">uri</span> <span class="o">=</span> <span class="bp">None</span> <span class="bp">self</span><span class="o">.</span><span class="n">ganeti</span> <span class="o">=</span> <span class="n">application</span><span class="o">.</span><span class="n">settings</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;ganeti&#39;</span><span class="p">)</span> <span class="nb">super</span><span class="p">(</span><span class="n">WebSocket</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">__init__</span><span class="p">(</span><span class="n">application</span><span class="p">,</span> <span class="n">request</span><span class="p">)</span> <span class="k">def</span> <span class="nf">open</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">receive_message</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">on_message</span><span class="p">)</span> <span class="k">def</span> <span class="nf">on_message</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">message</span><span class="p">):</span> <span class="n">obj</span> <span class="o">=</span> <span class="n">tornado</span><span class="o">.</span><span class="n">escape</span><span class="o">.</span><span class="n">json_decode</span><span class="p">(</span><span class="n">message</span><span class="p">)</span> <span class="k">if</span> <span class="n">obj</span><span class="p">[</span><span class="s">&#39;action&#39;</span><span class="p">]</span> <span class="o">==</span> <span class="s">&#39;subscribe&#39;</span><span class="p">:</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">uri</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">ganeti</span><span class="o">.</span><span class="n">proxy</span><span class="o">.</span><span class="n">unsubscribe</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">uri</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">gnt_callback</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">uri</span> <span class="o">=</span> <span class="n">obj</span><span class="p">[</span><span class="s">&#39;uri&#39;</span><span class="p">]</span> <span class="bp">self</span><span class="o">.</span><span class="n">ganeti</span><span class="o">.</span><span class="n">proxy</span><span class="o">.</span><span class="n">subscribe</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">uri</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">gnt_callback</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">receive_message</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">on_message</span><span class="p">)</span> <span class="k">def</span> <span class="nf">gnt_callback</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">):</span> <span class="n">to_send</span> <span class="o">=</span> <span class="s">&quot;{</span><span class="se">\&quot;</span><span class="s">uri</span><span class="se">\&quot;</span><span class="s">: </span><span class="se">\&quot;</span><span class="si">%s</span><span class="se">\&quot;</span><span class="s">, </span><span class="se">\&quot;</span><span class="s">content</span><span class="se">\&quot;</span><span class="s">: </span><span class="si">%s</span><span class="s">}&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">write_message</span><span class="p">(</span><span class="n">to_send</span><span class="p">)</span> </code></pre> </div> <p>Note that I limited the client to being subscribed to one URI at a time.</p> <p>As far as what happens client-side, that code is a total mess at the moment, but the idea is this: when the page is loaded, the browser opens a web socket to the handler above. When the user navigates, for example, to the list of Nodes, the browser sends a message over the web socket that looks something like this</p> <div class="highlight"><pre><code class="javascript"><span class="s1">&#39;{</span> <span class="s1"> &quot;action&quot;: &quot;subscribe&quot;,</span> <span class="s1"> &quot;uri&quot;: &quot;/nodes&quot;</span> <span class="s1">}&#39;</span> </code></pre> </div> <p>The response will specify the URI that it is from and the actual contents of the API response. From the contents I build a table that is shown to the user. If we add a node (and assuming someone is viewing the node list), within 5 seconds the JSONPushProxy should pick up on this and message every subscriber, where the table will be re-built with the new data.</p> <p>There are, however, a few downsides to this approach:</p> <p>First, the use of Web Sockets, which to my knowledge are only supported by Chrome at this point.</p> <p>Second, there are some issues with displaying the data client-side: I want the user to be able to sort and filter the table, but most of the libraries I've seen for doing this aren't designed to handle changing data. I've managed to get sorting working with the jQuery <a href="http://tablesorter.com/docs/">Tablesorter</a> plugin, but there seems to be some performance issue that I have yet to diagnose. Even after that, usability issues remain. For example, a user might be about to click on a node when a new node is brought online, shifting the list and causing them to click the wrong one. Not the end of the world, and I'm honestly not sure how this is handled desktop applications, but its something to think about.</p> WordPress with Nginx and PostgreSQL 2010-02-20T00:00:00+00:00 http://russellhaering.com/2010/02/20/wordpress-with-ngingx-and-postgresql <p>It took a while, but it has finally become obvious to me that I need to spend less time making my blog work and more time doing interesting things to blog about. In other words, I need a blog that "just works". WordPress.</p> <p>The two complicating factors are that it needs play nice with nginx and postgres. The solution is to use FastCGI and the <a href="http://wordpress.org/extend/plugins/postgresql-for-wordpress/">PostgreSQL for Wordpress</a> (pg4wp) plugin.</p> <p>We'll need PHP, with CGI support and spawn-fcgi. On Gentoo:</p> <div class="highlight"><pre><code class="bash"><span class="nb">echo</span> <span class="s2">&quot;dev-lang/php cgi&quot;</span> &gt;&gt; /etc/portage/package.use emerge php spawn-fcgi <span class="c"># Go have a beer, PHP takes a long time to compile</span> </code></pre> </div> <p>First we need WordPress</p> <div class="highlight"><pre><code class="bash"><span class="c"># Create a directory for this installation</span> <span class="nb">cd</span> /var/www mkdir new.russellhaering.com <span class="nb">cd </span>new.russellhaering.com <span class="c"># Download WordPress</span> wget http://wordpress.com/latest.tar.gz <span class="c"># And extract it</span> tar -xf latest.tar.gz rm latest.tar.gz </code></pre> </div> <p>Then the pg4wp plugin</p> <div class="highlight"><pre><code class="bash"><span class="c"># Download</span> wget http://downloads.wordpress.org/plugin/postgresql-for-wordpress.1.1.0.zip <span class="c"># Extract</span> unzip postgresql-for-wordpress.1.1.0.zip <span class="c"># Install</span> mv postgresql-for-wordpress/pg4wp wordpress/wp-content/plugins/ <span class="nb">cd </span>wordpress/wp-content cp plugins/pg4wp/db.php ./ </code></pre> </div> <p>Now we need to configure spawn-fcgi. Create /etc/conf.d/spawn-fcgi.wp which should look something like this</p> <div class="highlight"><pre><code class="bash"><span class="c"># The path to the socket</span> <span class="nv">FCGI_SOCKET</span><span class="o">=</span>/var/run/spawn-fcgi/russellhaering_com <span class="c"># WordPress uses PHP</span> <span class="nv">FCGI_PROGRAM</span><span class="o">=</span>/usr/bin/php-cgi <span class="c"># I believe that with the configuration below 1 child is fine</span> <span class="nv">FCGI_CHILDREN</span><span class="o">=</span>1 <span class="c"># It is convenient to run as nginx, but you could use a different user</span> <span class="nv">FCGI_USER</span><span class="o">=</span>nginx <span class="nv">FCGI_GROUP</span><span class="o">=</span>nginx <span class="c"># PHP will spawn off this many children</span> <span class="nv">PHP_FCGI_CHILDREN</span><span class="o">=</span>2 <span class="c"># Evidently PHP processes should be restarted every so often</span> <span class="nv">PHP_FCGI_MAX_REQUESTS</span><span class="o">=</span>500 <span class="nv">ALLOWED_ENV</span><span class="o">=</span><span class="s2">&quot;PATH PHP_FCGI_CHILDREN PHP_FCGI_MAX_REQUESTS&quot;</span> </code></pre> </div> <p>Then create an nginx vhost</p> <div class="highlight"><pre><code class="nginx"><span class="k">server</span> <span class="p">{</span> <span class="kn">listen</span> <span class="mi">80</span><span class="p">;</span> <span class="kn">server_name</span> <span class="s">new.russellhaering.com</span><span class="p">;</span> <span class="kn">index</span> <span class="s">index.php</span> <span class="s">index.html</span> <span class="s">index.htm</span><span class="p">;</span> <span class="kn">location</span> <span class="s">/</span> <span class="p">{</span> <span class="kn">root</span> <span class="s">/var/www/new.russellhaering.com/wordpress</span><span class="p">;</span> <span class="c1"># absolute path to your WordPress installation</span> <span class="c1"># this serves static files that exist without running other rewrite tests</span> <span class="kn">if</span> <span class="s">(-f</span> <span class="nv">$request_filename</span><span class="s">)</span><span class="p">{</span> <span class="kn">expires</span> <span class="s">30d</span><span class="p">;</span> <span class="kn">break</span><span class="p">;</span> <span class="p">}</span> <span class="c1"># this sends all non-existing file or directory requests to index.php</span> <span class="kn">if</span> <span class="s">(!-e</span> <span class="nv">$request_filename</span><span class="s">)</span> <span class="p">{</span> <span class="kn">rewrite</span> <span class="s">^(.+)</span>$ <span class="s">/index.php?q=</span><span class="nv">$1</span> <span class="s">last</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="kn">location</span> <span class="p">~</span> <span class="sr">\.php$</span> <span class="p">{</span> <span class="kn">fastcgi_pass</span> <span class="s">unix:/var/run/spawn-fcgi/russellhaering_com-1</span><span class="p">;</span> <span class="c1"># port where FastCGI processes were spawned</span> <span class="kn">fastcgi_index</span> <span class="s">index.php</span><span class="p">;</span> <span class="kn">fastcgi_param</span> <span class="s">SCRIPT_FILENAME</span> <span class="s">/var/www/new.russellhaering.com/wordpress/</span><span class="nv">$fastcgi_script_name</span><span class="p">;</span> <span class="c1"># same path as above</span> <span class="kn">fastcgi_param</span> <span class="s">QUERY_STRING</span> <span class="nv">$query_string</span><span class="p">;</span> <span class="kn">fastcgi_param</span> <span class="s">REQUEST_METHOD</span> <span class="nv">$request_method</span><span class="p">;</span> <span class="kn">fastcgi_param</span> <span class="s">CONTENT_TYPE</span> <span class="nv">$content_type</span><span class="p">;</span> <span class="kn">fastcgi_param</span> <span class="s">CONTENT_LENGTH</span> <span class="nv">$content_length</span><span class="p">;</span> <span class="kn">fastcgi_param</span> <span class="s">SCRIPT_NAME</span> <span class="nv">$fastcgi_script_name</span><span class="p">;</span> <span class="kn">fastcgi_param</span> <span class="s">REQUEST_URI</span> <span class="nv">$request_uri</span><span class="p">;</span> <span class="kn">fastcgi_param</span> <span class="s">DOCUMENT_URI</span> <span class="nv">$document_uri</span><span class="p">;</span> <span class="kn">fastcgi_param</span> <span class="s">DOCUMENT_ROOT</span> <span class="nv">$document_root</span><span class="p">;</span> <span class="kn">fastcgi_param</span> <span class="s">SERVER_PROTOCOL</span> <span class="nv">$server_protocol</span><span class="p">;</span> <span class="kn">fastcgi_param</span> <span class="s">GATEWAY_INTERFACE</span> <span class="s">CGI/1.1</span><span class="p">;</span> <span class="kn">fastcgi_param</span> <span class="s">SERVER_SOFTWARE</span> <span class="s">nginx/</span><span class="nv">$nginx_version</span><span class="p">;</span> <span class="kn">fastcgi_param</span> <span class="s">REMOTE_ADDR</span> <span class="nv">$remote_addr</span><span class="p">;</span> <span class="kn">fastcgi_param</span> <span class="s">REMOTE_PORT</span> <span class="nv">$remote_port</span><span class="p">;</span> <span class="kn">fastcgi_param</span> <span class="s">SERVER_ADDR</span> <span class="nv">$server_addr</span><span class="p">;</span> <span class="kn">fastcgi_param</span> <span class="s">SERVER_PORT</span> <span class="nv">$server_port</span><span class="p">;</span> <span class="kn">fastcgi_param</span> <span class="s">SERVER_NAME</span> <span class="nv">$server_name</span><span class="p">;</span> <span class="c1"># required if PHP was built with --enable-force-cgi-redirect</span> <span class="kn">fastcgi_param</span> <span class="s">REDIRECT_STATUS</span> <span class="mi">200</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <p>Go ahead and start things up..</p> <div class="highlight"><pre><code class="bash">/etc/init.d/spawn-fcgi.wp start /etc/init.d/nginx restart </code></pre> </div> <p>Now you should be able to perform a fairly standard WordPress installation. Just use your Postgres database and password when it asks for DB info (or when modifying the config file).</p> Dynamic Blogging 2010-01-26T00:00:00+00:00 http://russellhaering.com/2010/01/26/dynamic-blogging <p>So after my brief stint using <a href="http://github.com/lakshmivyas/hyde">Hyde</a> I've decided that its just not quite right for me.</p> <p>That being said I still hoped to avoid the A, M and P pillars of the LAMP stack, so I wasn't going to just roll out a Wordpress or Drupal instance. I investigated a few existing Django blog solutions but in the end decided to just make my own.</p> <p>So here it is. Its nowhere near done, but as I work I keep finding topics to blog about, so I finally decided to just get something online so at least I can use it in the process.</p> <p>The application is a collection of Django 'apps' that handle comments, blog posts, navigation and static pages.</p> <p>The application is running on the <a href="http://www.cherrypy.org/wiki/WSGI">CherryPy WSGI Server</a> using Peter Baumgartner's <a href="http://lincolnloop.com/blog/2008/mar/25/serving-django-cherrypy/">django_cpserver</a>. Public connections are proxied through nginx, which also serves static media and will eventually be used for limited caching. Configuring this was trivial, merely a matter of sropping django_cpserver into the site root, running './manage.py runcpserver' then adding a proxypass line to the nginx config.</p> <p>Also I've imported the two posts I made on my static blog.</p> <p>More to come on all of this...</p> Static Blogging 2009-12-22T00:00:00+00:00 http://russellhaering.com/2009/12/22/static-blogging <p>I've decided to give blogging another shot. Since I only have 360MB of RAM on my VPS, and plan to use it for a number of other services, I want to keep resource use to a minimum. I've been playing with nginx and love it, but what to use for the blog? I'm not going to run MySQL just for a blog, which rules out Wordpress and Drupal. I really don't like SQLite. And as much as I like Django (which supports Postgres which I'm running anyway), the Django blogging apps I looked at just weren't quite what I wanted.</p> <p>So I started looking at static website generators. I've heard a lot of good things about <a href="http://github.com/mojombo/jekyll">Jekyll</a>, but I'm not ready to install Ruby just to generate a static site. Then I found <a href="http://github.com/lakshmivyas/hyde">Hyde</a>. It uses the Django template engine, can parse a bunch of different markup formats and doesn't make a lot of assumptions about how you will use it. It's a little confusing at first and I'm still getting it set up, but I'm going to put the whole thing in a public git repo in case anyone is curious.</p> Running cgit under nginx 2009-12-22T00:00:00+00:00 http://russellhaering.com/2009/12/22/running-cgit-under-nginx <p>Like many in the open source community, I've developed a bit of an addiction to git. I use git for keeping track of class notes, homework, this blog, nearly everything we do on the <a href="http://osuosl.org/">OSU Open Source Lab</a> Systems team and, of course, coding. I have hosted a number of repositories (mostly for long dead programming projects on <a href="http://github.com/russellhaering">GitHub</a> but while it has a lot of great features, sometimes I just want a level of control that github just doesn't offer.</p> <p>But when you do want to share your git repositories, you probably want a way to browse them via the web. And there just aren't that many options. <a href="http://git.or.cz/gitwiki/Gitweb">Gitweb</a> is probably the most popular and runs on kernel.org, with <a href="http://hjemli.net/git/cgit/">cgit</a> in second. Of the two I prefer cgit because it is light and fast with excellent caching. But both cgit and GitWeb are CGI based, and nginx doesn't support CGI.</p> <p>The solution I came up with is to route requests through a little program called <a href="http://github.com/gnosek/fcgiwrap/">fcgiwrap</a>. To begin with, you'll need to download and install it.</p> <div class="highlight"><pre><code class="bash">git clone git://github.com/gnosek/fcgiwrap.git <span class="nb">cd </span>fcgiwrap make make install </code></pre> </div> <p>However fcgiwrap alone can't handle FastCGI requests, so we need to set upsomething like spawn-fcgi. At least on Gentoo this is fairly easy.</p> <div class="highlight"><pre><code class="bash"><span class="nb">echo</span> <span class="s2">&quot;www-servers/spawn-fcgi&quot;</span> &gt;&gt; /etc.portage/package.keywords emerge -av spawn-fcgi ln -s spawn-fcgi /etc/init.d/spawn-fcgi.cgit cp /etc/conf.d/spawn-fcgi /etc/conf.d/spawn-fcgi.cgit </code></pre> </div> <p>Because you might want to do this process for multiple CGI scripts the init script is designed to be symlinked for each instance. Now you need to edit /etc/conf.d/spawn-fcgi.cgit to look something like this</p> <div class="highlight"><pre><code class="bash"><span class="c"># I used a unix socket since nginx and cgit are on the same machine</span> <span class="nv">FCGI_SOCKET</span><span class="o">=</span>/var/run/spawn-fcgi/code_russellhaering_com <span class="c"># Again, I don&#39;t need these</span> <span class="nv">FCGI_ADDRESS</span><span class="o">=</span> <span class="nv">FCGI_PORT</span><span class="o">=</span> <span class="c"># Tell spawn-fcgi what program to execute</span> <span class="nv">FCGI_PROGRAM</span><span class="o">=</span>/usr/local/bin/fcgiwrap <span class="c"># You don&#39;t really need to change any of these</span> <span class="nv">FCGI_CHILDREN</span><span class="o">=</span>1 <span class="nv">FCGI_CHROOT</span><span class="o">=</span> <span class="nv">FCGI_CHDIR</span><span class="o">=</span> <span class="c"># You probably don&#39;t want cgit running as root</span> <span class="nv">FCGI_USER</span><span class="o">=</span>nginx <span class="nv">FCGI_GROUP</span><span class="o">=</span>nginx <span class="c"># I&#39;m not sure if this is really essential</span> <span class="nv">ALLOWED_ENV</span><span class="o">=</span><span class="s2">&quot;PATH&quot;</span> </code></pre> </div> <p>I found that I had to 'chown -R nginx:nginx /var/run/spawn-fcgi since both programs accessing the socket will be running as user nginx.</p> <p>Of course you'll also need cgit itself</p> <div class="highlight"><pre><code class="bash">wget http://hjemli.net/git/cgit/snapshot/cgit-stable.tar.gz tar -xf cgit-stable.tar.gz make get-git <span class="o">&amp;&amp;</span> make <span class="c"># Create a web root</span> mkdir -p /var/www/code.russellhaering.com/htdocs <span class="c"># And copy over the important stuff</span> cp cgit /var/www/code.russellhaering.com/htdocs/ cp cgit.css /var/www/code.russellhaering.com/htdocs/ cp cgit.png /var/www/code.russellhaering.com/htdocs/ </code></pre> </div> <p>Now edit /etc/cgitrc</p> <div class="highlight"><pre><code class="ini"><span class="c"># enable caching of up to 1000 output entries</span> <span class="na">cache-size</span><span class="o">=</span><span class="s">1000</span> <span class="c"># page title for the root page (repo listing)</span> <span class="na">root-title</span><span class="o">=</span><span class="s">Russell&#39;s Git Repositories</span> <span class="c"># description for the root page</span> <span class="na">root-desc</span><span class="o">=</span> <span class="c"># link to css file</span> <span class="na">css</span><span class="o">=</span><span class="s">/cgit.css</span> <span class="c"># link to logo file</span> <span class="na">logo</span><span class="o">=</span><span class="s">/cgit.png</span> <span class="c"># root path for cached output</span> <span class="na">cache-root</span><span class="o">=</span><span class="s">/var/cache/cgit</span> <span class="c"># time-to-live settings: specify how long (in minutes) different pages should</span> <span class="c"># be cached. specify 0 for instant expiration and -1 for immortal pages</span> <span class="c"># ttl for root page (repo listing)</span> <span class="na">cache-root-ttl</span><span class="o">=</span><span class="s">0</span> <span class="c"># ttl for repo summary page</span> <span class="na">cache-repo-ttl</span><span class="o">=</span><span class="s">0</span> <span class="c"># ttl for other dynamic pages</span> <span class="na">cache-dynamic-ttl</span><span class="o">=</span><span class="s">0</span> <span class="c"># ttl for static pages (addressed by SHA-1)</span> <span class="na">cache-static-ttl</span><span class="o">=</span><span class="s">0</span> <span class="c"># allow download of tar.gz and tar.bz2 files</span> <span class="na">snapshots</span><span class="o">=</span><span class="s">tar.gz tar.bz2</span> <span class="na">include</span><span class="o">=</span><span class="s">/etc/cgitrc.local</span> </code></pre> </div> <p>Notice how I left caching disabled. Thats because I'm going to use the built in nginx fastcgi caching. You'll also need to edit /etc/cgitrc.local - this is what I have right now.</p> <div class="highlight"><pre><code class="ini"><span class="na">repo.url</span><span class="o">=</span><span class="s">russellhaering.com</span> <span class="na">repo.desc</span><span class="o">=</span><span class="s">Hyde configuration for russellhaering.com</span> <span class="na">repo.path</span><span class="o">=</span><span class="s">/var/lib/git/russellhaering.com.git</span> <span class="na">repo.owner</span><span class="o">=</span><span class="s">russell_h</span> <span class="na">repo.clone-url</span><span class="o">=</span><span class="s">git://code.russellhaering.com/russellhaering.com</span> </code></pre> </div> <p>And of course you'll also need to set up nginx itself.</p> <p>I added a line like this to the 'http' section of my nginx.conf</p> <div class="highlight"><pre><code class="nginx"><span class="c1"># Use up to 10 Megabytes for the &#39;code&#39; cache, keep inactive cache around for 1h</span> <span class="k">fastcgi_cache_path</span> <span class="s">/var/cache/nginx</span> <span class="s">levels=1:2</span> <span class="s">keys_zone=code:10m</span> <span class="s">inactive=1h</span> <span class="s">max_size=100m</span><span class="p">;</span> </code></pre> </div> <p>You may need to create and set appropriate permissions on that cache directory</p> <p>And added a 'server' section like this</p> <div class="highlight"><pre><code class="nginx"><span class="k">server</span> <span class="p">{</span> <span class="kn">listen</span> <span class="mi">80</span><span class="p">;</span> <span class="kn">server_name</span> <span class="s">code.russellhaering.com</span><span class="p">;</span> <span class="c1"># Serve static files</span> <span class="kn">location</span> <span class="p">~</span><span class="sr">*</span> <span class="s">^.+\.(css|png|ico)</span>$ <span class="p">{</span> <span class="kn">root</span> <span class="s">/var/www/code.russellhaering.com/htdocs</span><span class="p">;</span> <span class="kn">expires</span> <span class="s">30d</span><span class="p">;</span> <span class="p">}</span> <span class="kn">location</span> <span class="s">/</span> <span class="p">{</span> <span class="kn">rewrite</span> <span class="s">^/</span>$ <span class="s">/cgit</span> <span class="s">permanent</span><span class="p">;</span> <span class="kn">rewrite</span> <span class="s">^/cgit/</span>$ <span class="s">/cgit</span> <span class="s">permanent</span><span class="p">;</span> <span class="kn">fastcgi_cache</span> <span class="s">code</span><span class="p">;</span> <span class="kn">fastcgi_cache_valid</span> <span class="mi">200</span> <span class="mi">5m</span><span class="p">;</span> <span class="kn">fastcgi_cache_use_stale</span> <span class="no">off</span><span class="p">;</span> <span class="kn">fastcgi_pass</span> <span class="s">unix:/var/run/spawn-fcgi/code_russellhaering_com-1</span><span class="p">;</span> <span class="kn">fastcgi_read_timeout</span> <span class="mi">5m</span><span class="p">;</span> <span class="kn">fastcgi_index</span> <span class="s">/</span><span class="p">;</span> <span class="kn">fastcgi_param</span> <span class="s">DOCUMENT_ROOT</span> <span class="s">/var/www/code.russellhaering.com/htdocs</span><span class="p">;</span> <span class="kn">fastcgi_param</span> <span class="s">SCRIPT_FILENAME</span> <span class="s">/var/www/code.russellhaering.com/htdocs/cgit</span><span class="p">;</span> <span class="kn">fastcgi_param</span> <span class="s">QUERY_STRING</span> <span class="nv">$query_string</span><span class="p">;</span> <span class="kn">fastcgi_param</span> <span class="s">REQUEST_METHOD</span> <span class="nv">$request_method</span><span class="p">;</span> <span class="kn">fastcgi_param</span> <span class="s">CONTENT_TYPE</span> <span class="nv">$content_type</span><span class="p">;</span> <span class="kn">fastcgi_param</span> <span class="s">CONTENT_LENGTH</span> <span class="nv">$content_length</span><span class="p">;</span> <span class="kn">fastcgi_param</span> <span class="s">GATEWAY_INTERFACE</span> <span class="s">CGI/1.1</span><span class="p">;</span> <span class="kn">fastcgi_param</span> <span class="s">SERVER_SOFTWARE</span> <span class="s">nginx</span><span class="p">;</span> <span class="kn">fastcgi_param</span> <span class="s">SCRIPT_NAME</span> <span class="nv">$fastcgi_script_name</span><span class="p">;</span> <span class="kn">fastcgi_param</span> <span class="s">REQUEST_URI</span> <span class="nv">$request_uri</span><span class="p">;</span> <span class="kn">fastcgi_param</span> <span class="s">DOCUMENT_URI</span> <span class="nv">$document_uri</span><span class="p">;</span> <span class="kn">fastcgi_param</span> <span class="s">DOCUMENT_ROOT</span> <span class="nv">$document_root</span><span class="p">;</span> <span class="kn">fastcgi_param</span> <span class="s">SERVER_PROTOCOL</span> <span class="nv">$server_protocol</span><span class="p">;</span> <span class="kn">fastcgi_param</span> <span class="s">REMOTE_ADDR</span> <span class="nv">$remote_addr</span><span class="p">;</span> <span class="kn">fastcgi_param</span> <span class="s">REMOTE_PORT</span> <span class="nv">$remote_port</span><span class="p">;</span> <span class="kn">fastcgi_param</span> <span class="s">SERVER_ADDR</span> <span class="nv">$server_addr</span><span class="p">;</span> <span class="kn">fastcgi_param</span> <span class="s">SERVER_PORT</span> <span class="nv">$server_port</span><span class="p">;</span> <span class="kn">fastcgi_param</span> <span class="s">SERVER_NAME</span> <span class="nv">$server_name</span><span class="p">;</span> <span class="p">}</span> <span class="kn">access_log</span> <span class="s">/var/log/nginx/code.russellhaering.com/transfer/access_log</span> <span class="s">combined</span><span class="p">;</span> <span class="kn">error_log</span> <span class="s">/var/log/nginx/code.russellhaering.com/error/error_log</span> <span class="s">warn</span><span class="p">;</span> <span class="p">}</span> </code></pre> </div> <p>At this point you should be set to go, excepting any permissions, etc that I neglected to mention. Go ahead and try it out</p> <div class="highlight"><pre><code class="bash">/etc/init.d/spawn-fcgi.cgit start /etc/init.d/nginx restart </code></pre> </div> <p>I suppose I should mention that I am seeing an issue that I think is related to the caching, where every so often when you click a link in cgit the address in the address bar updates but the page doesn't actually change. Clicking the link again has always fixed the problem. I looked in the logs and didn't see anything unusual, so it may be an issue in Chromium, but for the moment I'm content to leave it. If you happen to know what this is about and want to share, please do.</p>