<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"><channel><title>GAE Planet</title><link>http://gae-planet.appspot.com/</link><description>Google App Engine Community Blog Aggregator.</description><language>en-us</language><lastBuildDate>Wed, 03 Jun 2009 12:58:32 -0000</lastBuildDate><item><title>IMified Example - An IM bot using Google App Engine</title><link>http://blogs.voxeo.com/voxeodeveloperscorner/2009/06/03/imified-example-an-im-bot-using-google-app-engine/</link><description>&lt;p&gt;&lt;a href="http://blogs.voxeo.com/voxeotalks/2009/05/27/voxeo-acquires-imified-and-gains-ivr-type-self-service-for-im/"&gt;&lt;img src="http://blogs.voxeo.com/voxeodeveloperscorner/files/2009/06/imifiedlogo.jpg" alt="imifiedlogo.jpg" border="0" width="150" height="60" align="right" /&gt;&lt;/a&gt;As you may have noticed last week, we announced &lt;a href="http://blogs.voxeo.com/voxeotalks/2009/05/27/voxeo-acquires-imified-and-gains-ivr-type-self-service-for-im/"&gt;the acquisition of a company called IMified&lt;/a&gt; and with that we brought in more opportunities and options for developers. Now you can create instant messaging &amp;#8220;bots&amp;#8221; (or &amp;#8220;agents&amp;#8221; or whatever you want to call them) that allow you to create applications that interact with users via IM. (More about what you can do with IMified in &lt;a href="http://blogs.voxeo.com/voxeotalks/2009/05/27/voxeo-acquires-imified-and-gains-ivr-type-self-service-for-im/"&gt;the acquisition announcement&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;Developer accounts are free over on &lt;a href="http://www.imified.com/"&gt;www.imified.com&lt;/a&gt;.  Please do sign up and check it out.  We&amp;#8217;d love to hear what you think of the service.  (And you can safely assume that we&amp;#8217;ll be evolving the service and adding more to it over the months ahead.)&lt;/p&gt;
&lt;p&gt;One aspect of IMified that is a bit different from either Voxeo&amp;#8217;s &lt;a href="http://evolution.voxeo.com/"&gt;Evolution XML developer portal&lt;/a&gt; or our &lt;a href="http://www.tropo.com/"&gt;Tropo.com site&lt;/a&gt; is that with IMified you do need to host your application on a web server somewhere.  With both Evolution and Tropo, you &lt;em&gt;can&lt;/em&gt; host your application on your own server and simply point Evolution or Tropo to the URL of your app, but there is also the option with both services of hosting your apps directly within our hosted infrastructure.  With IMified you do need to host your application somewhere and then provide the URL inside IMified when configuring your bot.&lt;/p&gt;
&lt;p&gt;The beautiful thing about this, of course, is that it means you can write the bot application in whatever language you want on whatever operating system you want and using whatever tools you want.  All your application has to do is use &lt;a href="http://www.imified.com/developers/api"&gt;the IMified API&lt;/a&gt; to communicate with the IMified platform.&lt;/p&gt;
&lt;p&gt;One place developers can host a web services app these days is certainly &lt;a href="http://appengine.google.com/_ah/login%3Fcontinue%3Dhttp://appengine.google.com/"&gt;Google App Engine&lt;/a&gt;, and the IMified team &lt;a href="http://help.imified.com/faqs/building-imified-bots/google-app-engine"&gt;just posted a knowledge base article showing an example bot in python hosted on Google App Engine&lt;/a&gt;.  Courtesy of an IMified user named Barry, the source code &lt;a href="http://help.imified.com/faqs/building-imified-bots/google-app-engine"&gt;is available in the IMified knowledge base&lt;/a&gt;.  The IMified team notes that it&amp;#8217;s also a good example of performing HTTP Authentication in python.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(And rather than include the source code here, I&amp;#8217;ll just point you over to &lt;a href="http://help.imified.com/faqs/building-imified-bots/google-app-engine"&gt;the IMified site&lt;/a&gt;.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We&amp;#8217;re excited about adding IMified to the Voxeo family of services and we&amp;#8217;re definitely looking forward to seeing what people develop on the platform.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;If you found this post interesting or helpful, please consider either &lt;a href="http://blogs.voxeo.com/voxeodeveloperscorner/feed/"&gt;subscribing via RSS&lt;/a&gt;, becoming &lt;a href="http://www.voxeo.com/facebook"&gt;a fan on Facebook&lt;/a&gt; or &lt;a href="http://twitter.com/voxeo/"&gt;following us on Twitter&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;!-- Technorati Tags Start --&gt;&lt;/p&gt;
&lt;p&gt;Technorati Tags:
&lt;a href="http://technorati.com/tag/im" rel="tag"&gt;im&lt;/a&gt;, &lt;a href="http://technorati.com/tag/imified" rel="tag"&gt;imified&lt;/a&gt;, &lt;a href="http://technorati.com/tag/google" rel="tag"&gt;google&lt;/a&gt;, &lt;a href="http://technorati.com/tag/googleappengine" rel="tag"&gt;googleappengine&lt;/a&gt;, &lt;a href="http://technorati.com/tag/gae" rel="tag"&gt;gae&lt;/a&gt;, &lt;a href="http://technorati.com/tag/python" rel="tag"&gt;python&lt;/a&gt;, &lt;a href="http://technorati.com/tag/voxeo" rel="tag"&gt;voxeo&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;&lt;!-- Technorati Tags End --&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://sharethis.com/item?publisher=c0af778a-87ab-41b4-be56-8ec058cd3e46&amp;title=IMified+Example+-+An+IM+bot+using+Google+App+Engine&amp;url=http%3A%2F%2Fblogs.voxeo.com%2Fvoxeodeveloperscorner%2F2009%2F06%2F03%2Fimified-example-an-im-bot-using-google-app-engine%2F"&gt;ShareThis&lt;/a&gt;&lt;/p&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Dan York</dc:creator><pubDate>Wed, 03 Jun 2009 12:58:32 -0000</pubDate><guid>http://blogs.voxeo.com/voxeodeveloperscorner/2009/06/03/imified-example-an-im-bot-using-google-app-engine/</guid></item><item><title>Random thoughts after Google I/O</title><link>http://feedproxy.google.com/%7Er/AppEngineFan/%7E3/qYu5Iu-OLWE/random-thoughts-after-google-io.html</link><description>Just like the year before, I was in the fortunate position of being able to attend Google I/O. When I was still living in Virginia, such a trip would have been only wishful thinking, so I am very grateful for such opportunities. The meetings were amazing, and smarter and more eloquent people than myself have already reported more than enough about it. I will try not to bore anyone by yet another rehash of the Wave demo, or all the other cool things that have been announced. In other words, I will keep this short ;-)&lt;br /&gt;&lt;br /&gt;One of the talks I attended was called &lt;a href="http://code.google.com/events/io/sessions/WritingRealTimeGamesAndroid.html"&gt;Writing Real-Time Games for Android&lt;/a&gt;, and it turned out one of my favourite sessions. I will not pretend to have understood everything (Chris Pruett certainly lost me when he started talking about the different graphics features on the device), but it was inspiring to see the quality of output that could be achieved on such a device. And it was all in Java, a language that I actually understood.&lt;br /&gt;&lt;br /&gt;When I first blogged about my new hobby-project of &lt;a href="http://blog.appenginefan.com/2009/05/my-summer-project-let-games-begin.html"&gt;making the JOGRE gaming engine work with an App Engine backend&lt;/a&gt;, I got asked why I was going after a framework that used a Java client, when Javascript and HTML canvas seemed the new way to go. My response was relatively bland. Bascially, I did not really care about building a state-of-the-art client; I more cared about porting an existing backend. Now after this talk I start wondering: could I achieve both? JOGRE is Java -- Android is Java!&lt;br /&gt;&lt;br /&gt;JOGRE builds clients on a regular Java framework with a Swing or AWT based frontend. I am already working on making that client work "on the web", by replacing socket-based messaging with an http protocol. Beyond that, how hard would it be to refactor the client to work on Android backends, rather than regular PCs? Would it be possible to "mobilize" the games with little effort? Imagine having a standard gaming platform for multiplayer games, backed by App Engine and running on your cell phone? How easy would that make writing new online games, and pushing them to the Android marketplace?&lt;br /&gt;&lt;br /&gt;So... I guess I've got to ask: any "Android fan" reading this blog who could audit the client side of JOGRE and give me some feedback?&lt;div class="blogger-post-footer"&gt;&lt;img width="1" height="1" src="//blogger.googleusercontent.com/tracker/5502193732299119611-5753588447860042885?l=blog.appenginefan.com" /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/NilYgnSzV16NuuTSEtJcCcnfxKY/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/NilYgnSzV16NuuTSEtJcCcnfxKY/0/di" border="0" ismap="true" /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/NilYgnSzV16NuuTSEtJcCcnfxKY/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/NilYgnSzV16NuuTSEtJcCcnfxKY/1/di" border="0" ismap="true" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds2.feedburner.com/~ff/AppEngineFan?a=qYu5Iu-OLWE:jPDGNWmpLEo:yIl2AUoC8zA"&gt;&lt;img src="http://feeds2.feedburner.com/~ff/AppEngineFan?d=yIl2AUoC8zA" border="0" /&gt;&lt;/a&gt; &lt;a href="http://feeds2.feedburner.com/~ff/AppEngineFan?a=qYu5Iu-OLWE:jPDGNWmpLEo:4cEx4HpKnUU"&gt;&lt;img src="http://feeds2.feedburner.com/~ff/AppEngineFan?i=qYu5Iu-OLWE:jPDGNWmpLEo:4cEx4HpKnUU" border="0" /&gt;&lt;/a&gt; &lt;a href="http://feeds2.feedburner.com/~ff/AppEngineFan?a=qYu5Iu-OLWE:jPDGNWmpLEo:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds2.feedburner.com/~ff/AppEngineFan?i=qYu5Iu-OLWE:jPDGNWmpLEo:gIN9vFwOqvQ" border="0" /&gt;&lt;/a&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">The App Engine Fan (noreply@blogger.com)</dc:creator><pubDate>Sun, 31 May 2009 21:28:00 -0000</pubDate><guid>http://feedproxy.google.com/~r/AppEngineFan/~3/qYu5Iu-OLWE/random-thoughts-after-google-io.html</guid></item><item><title>Reconsidering my Combinators</title><link>http://www.kablambda.org/blog/2009/05/25/reconsidering-my-combinators/</link><description>&lt;p&gt;Since writing my set of function combinators for matching HTTP requests I&amp;#8217;ve realised that there are better approaches.&lt;/p&gt;

&lt;p&gt;While the combinators read nicely, once you start extracting attributes from the request things become messy, due to the &lt;code&gt;hAnd&lt;/code&gt; combinator composing results as nested pairs.&lt;/p&gt;

&lt;p&gt;Here&amp;#8217;s an example:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;skitchPutHandler =
    let
        storeImage p =
            let (p2, path) = p;
                (maybeContentType,imageData) = p2;
                ...
            in ...
    in
        matchMethod "PUT" `hThen` matchUrl "/skitch" `hThen`
            (getHeader "Content-Type") `hAnd` requireRawData `hAnd` requirePathInfo `hApply` storeImage;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Unpacking the values extracted from the request is cumbersome, and because many are &lt;code&gt;String&lt;/code&gt;s, error prone.&lt;/p&gt;

&lt;p&gt;A better approach is to simply use the &lt;code&gt;Maybe&lt;/code&gt; monad with the same extraction functions:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;simpleSkitchPutHandler req =
    let
        maybeContentType = requireHeader "Content-Type" req;
        ...
    in
        matchMethod "PUT" req `anonymousBind` matchUrl "/skitch" req `anonymousBind`
            requireRawData req `bind` (\rawData -&amp;gt;
            requirePathInfo req `bind` (\path -&amp;gt;
            return $ storeImage path rawData (actualContentType path)));
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Optional values are extracted in let expressions, and required values and tests are composed with the monadic &lt;code&gt;bind&lt;/code&gt; operators in the main expression.
You can make the function a bit more concise and &amp;#8216;point free&amp;#8217; by writing a monad which composes the &lt;code&gt;Maybe&lt;/code&gt; and &lt;code&gt;Reader&lt;/code&gt; monads to supply the HTTP request parameter to functions implicitly:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;readerMonadSkitchPutHandler =
    let
        ...
    in
        runHttpRequestM (
        method "PUT" `anonymousBind` matchUrlM "/skitch" `anonymousBind`
            rawData `bind` (\rawData -&amp;gt;
            pathInfo `bind` (\path -&amp;gt;
            (optional (header "Content-Type")) `bind` (\maybeContentType -&amp;gt;
            return $ storeImage path rawData (actualContentType path maybeContentType)))));
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The function &lt;code&gt;optional&lt;/code&gt; has the type &lt;code&gt;HttpRequestM a -&amp;gt; HttpRequestM (Maybe a)&lt;/code&gt;, that is, it allows a function to succeed by wrapping its result in a &amp;#8216;Just&amp;#8217;.&lt;/p&gt;

&lt;p&gt;I&amp;#8217;m not convinced that the improvement in readability gained by removing the request parameter is worth the increase in complexity.&lt;/p&gt;

&lt;p&gt;So I&amp;#8217;ll remove the combinators from my &lt;code&gt;Http&lt;/code&gt; module and will rewrite Hangman using just the Maybe monad. I&amp;#8217;ll keep the more complicated Monad on the back-burner in case I discover a compelling advantage.&lt;/p&gt;

&lt;p&gt;I&amp;#8217;d be interested to know what someone with a deeper understanding of functional programming would say.&lt;/p&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Tom Davies</dc:creator><pubDate>Mon, 25 May 2009 12:00:14 -0000</pubDate><guid>http://www.kablambda.org/blog/2009/05/25/reconsidering-my-combinators/</guid></item><item><title>An initial refactoring</title><link>http://feedproxy.google.com/%7Er/AppEngineFan/%7E3/r1iNzEDzWzM/initial-refactoring.html</link><description>This is the first technical post in my new &lt;a href="http://blog.appenginefan.com/search/label/JOGRE"&gt;JOGRE series &lt;/a&gt;. As mentioned in the &lt;a href="http://blog.appenginefan.com/2009/05/my-summer-project-let-games-begin.html"&gt;previous article&lt;/a&gt;, the goal behind this project is to explore how App Engine can efficiently host online games that scale to many users. Since I do not want to reinvent the wheel, I am basing the work on an existing Java-based platform (&lt;a href="http://jogre.sourceforge.net/main.htm" target="_blank"&gt;http://jogre.sourceforge.net/main.htm&lt;/a&gt;) that already happens to come with a lot of prewritten games. How hard is it going to be to retrofit that base to the App Engine platform? Over the next couple of weeks, I will hopefully find out.&lt;br /&gt;&lt;br /&gt;The post is a little bit longer than usual, so I'll do my best to put in a few meaningful headlines inbetween. This way, readers can skip to the parts that are of particular interest to them.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;A hobbyist's code of conduct to refactoring&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;A lot of my activities over the next couple of weeks will be about &lt;a href="http://en.wikipedia.org/wiki/Refactoring"&gt;refactoring&lt;/a&gt;  code in JOGRE, so I'd like to start out by summarizing the guidelines that I will try to follow in this work. &lt;i&gt;Note that the &lt;b&gt;"you" &lt;/b&gt;in the following paragraphs does not address the reader but myself&lt;/i&gt;. I know -- talking to myself is a bad habit, but it makes it easier to write the rules down. I will to try my best to abide by them in this and other refactoring posts.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;The cardinal rule: Don't be a jerk&lt;br /&gt;&lt;/h3&gt;&lt;br /&gt;Life's too short to get angry, so why would anyone want to work with someone he or she does not like? This is even more important to consider when there is no transfer of money involved: people work on this kind of software because they have a passion for it and it is fun. So, try to avoid spoiling the project for anyone else!&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Corollary #1: If it ain't broke, don't fix it.&lt;/h3&gt;&lt;br /&gt;You might have an extensive background in methodology xyz. You might have read &lt;a href="http://www.amazon.com/gp/product/0201633612?ie=UTF8&amp;tag=apenfa-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=0201633612"&gt;the GOF book&lt;/a&gt;&lt;img src="http://www.assoc-amazon.com/e/ir?t=apenfa-20&amp;l=as2&amp;o=1&amp;a=0201633612" alt="" border="0" width="1" height="1" /&gt;, &lt;a href="http://www.amazon.com/gp/product/0201485672?ie=UTF8&amp;tag=apenfa-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=0201485672"&gt;Martin Fowler's Refactoring&lt;/a&gt;&lt;img src="http://www.assoc-amazon.com/e/ir?t=apenfa-20&amp;l=as2&amp;o=1&amp;a=0201485672" alt="" border="0" width="1" height="1" /&gt;, and you might have quoted from &lt;a href="http://www.amazon.com/gp/product/0321356683?ie=UTF8&amp;tag=apenfa-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=0321356683"&gt;Effective Java&lt;/a&gt;&lt;img src="http://www.assoc-amazon.com/e/ir?t=apenfa-20&amp;l=as2&amp;o=1&amp;a=0321356683" alt="" border="0" width="1" height="1" /&gt; on occasion. You may think you know how to spot "&lt;a href="http://en.wikipedia.org/wiki/Code_smell"&gt;code smells&lt;/a&gt;", or that you have read and written a ton of code. Good for you, but do yourself a favor: forget all of this right now! Unless you are a glutton for punishment, you have chosen to work on this project in your spare time because you like it, it works well 90% of the time, and you'd like to make it even better. This means that if you run into something that does not comply with what you might consider quasi-standards of good coding, it is probably like that for one of the following reasons:&lt;br /&gt;       &lt;ul&gt;&lt;li&gt;The code has evolved historically towards the way it currently is, and it has never been too much of a problem.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The project team has a different philosophy or architectural view towards development than you. Happens all the time, and that doesn't make it bad code. Feel free to ask the team &lt;i&gt;why&lt;/i&gt; it is the way it is, but do not expect them to change it for you.&lt;br /&gt;       &lt;/li&gt;&lt;li&gt;You ran into a bug or an area with "TODOs". If it's an issue and the team agrees, feel free to improve it -- as long as you do it with the consent of the others.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Bear in mind that long before you joined the project, other people have spent a lot of &lt;i&gt;their&lt;/i&gt; spare time contributing and building the foundation of what you are using right now. Criticizing the code or calling any of it "smelly" could be considered impolite or disrespectful to other contributors.&lt;br /&gt; &lt;br /&gt;&lt;h3&gt;Corollary #2: Honor the code's spirit and document the intentions of changes you make.&lt;/h3&gt;&lt;br /&gt;&lt;a href="http://www.osnews.com/story/19266/WTFs_m"&gt;It's funny 'cause it's true:&lt;/a&gt;&lt;a href="http://www.osnews.com/story/19266/WTFs_m" target="_blank"&gt;&lt;/a&gt; nobody should be looking at your changes and ask, "why the heck did he do that?!?"  There are several things that can significantly increase the WTFs per minute ratio:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;changes that would make sense -- if the author had only documented &lt;i&gt;why&lt;/i&gt; he or she is doing them&lt;/li&gt;&lt;li&gt;too many concurrent modifications in one iteration&lt;/li&gt;&lt;li&gt;combining a refactoring with writing new features&lt;/li&gt;&lt;li&gt;incomplete or speculative refactorings (in other words, starting a change because "it might come in handy later" but never following through on that)&lt;/li&gt;&lt;li&gt;major changes to the contract of a core class that require code changes throughout the project&lt;/li&gt;&lt;li&gt;random changes just for the heck of it, or because one does not like the way a particular piece of code looks &lt;i&gt;(see corollary #1)&lt;/i&gt;. While it sometimes makes sense to clean up something small while you're in a particular class anyway, there is a fine line between cleanup and major modifications.&lt;/li&gt;&lt;/ul&gt;Refactorings can be like solving a Rubik's cube: while the overall change might be complex, it can usually be comprised of several smaller transformations, each of them simple and easy to understand. It often makes sense to document these smaller steps by checking them in independently into revision control (for example, today's refactoring steps can be traced from revisions &lt;a href="http://code.google.com/p/gae-ogre/source/detail?r=5"&gt;5&lt;/a&gt; &lt;a href="http://code.google.com/p/gae-ogre/source/detail?r=5" target="_blank"&gt;&lt;/a&gt; till &lt;a href="http://code.google.com/p/gae-ogre/source/detail?r=16"&gt;16&lt;/a&gt; &lt;a href="http://code.google.com/p/gae-ogre/source/detail?r=16" target="_blank"&gt;&lt;/a&gt; of the open source project.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Corollary #3: Contributorship does not imply ownership&lt;/h3&gt;&lt;br /&gt;Your open source work is branched off an existing project (&lt;i&gt;Note: I contacted Bob Marks before I started any of this. We agreed to keep my work separate for now, but if I am successful and he finds the changes useful and beneficial, we will merge them back into the main project&lt;/i&gt;). The overall amount of changes you are going to make will most likely affect not even ten percent of the overall codebase. The new features you add will probably be even fewer. Therefore, do not imply that you "built" this platform, or forget to give credit where credit is due. If you build a new feature, feel free to put your name into the author tag -- but if all you are doing is pulling code from one class into an other class, that doesn't make you the author. Give credit where credit is due! Also, do not expect that your changes will definitely make it back verbatim into the main branch (in other words, don't get defensive if the other coders require some additional modifications first). Do not remove any branding that the original authors might have put in and replace it with your own -- it might be ok to do that in customized deploys, but not in the open source project itself. That being said -- of course you are free to take some liberties with the project, as long as you comply with its open source license (GPL v2 or higher in case of JOGRE). Just don't forget about the cardinal rule.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Corollary #4: Failure IS an option&lt;/h3&gt;&lt;br /&gt;Sometimes, as you get deeper into the code base, you might discover that the project does not really do what you need it to do. It might be designed in a way that you cannot easily adapt, there could be fundamental disagreements between you and the other team members -- or you simply happen to run out of time and need to focus on something else. It happens on occasion, so move on if you need to move on. However, if you choose to do so, communicate this clearly and in a nice manner. Do not promise to implement a particular feature and just never do it. Do not write a scathing goodbye post that explains why project xyz is &lt;i&gt;so&lt;/i&gt; much better. Sometimes, the real problem actually lies somewhere between the keyboard and the seat.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;The overall goal of my refactorings&lt;/h2&gt;&lt;br /&gt;Simply put, the goal of my initial work is to rework any code I might find in JOGRE that would prevent it from running on App Engine. Since I am just starting out in this effort, I do not have a very good idea yet what that might be. However, I am aware of a few things that work in a regular Java program but would be challenging in App Engine, such as:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;spawning threads&lt;/li&gt;&lt;li&gt;network communication through anything but http&lt;/li&gt;&lt;li&gt;storing data in files&lt;/li&gt;&lt;li&gt;keeping data in a static field and expecting it to stick around (the next request might hit a completely different instance of my App Engine app)&lt;/li&gt;&lt;li&gt;connecting via JDBC to a database&lt;/li&gt;&lt;li&gt;longer living background processes&lt;/li&gt;&lt;li&gt;anything that requires a "restart" of the system&lt;/li&gt;&lt;li&gt;anything equivalent to a "global scan" (will the client ever need to get the list of all users?)&lt;/li&gt;&lt;li&gt;anything with near-realtime requirements that relies on the accuracy of timestamps&lt;/li&gt;&lt;li&gt;anything that uses native code or expects certain operating system commands to be available&lt;/li&gt;&lt;li&gt;code that uses any class that may not be whitelisted&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;There's probably more stuff to look out for, but those are the first things that come to my mind. Some of these limitations will only be triggered (if ever) as I do the first tentative steps towards the port to App Engine, but others might be obvious from looking at the code. In the first couple of weeks, I will do my best to identify those more obvious areas and refactor them. I will do so in a way that is downwards compatible (it should be possible to merge those changes back into the main project, and older deployments of JOGRE should still work) and does not break the existing unit tests. If I break a unit test, I will revert my change and accomplish the goal in another way (minor changes to the test to cover API modifications are ok, though).&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Today's refactoring in pictures&lt;/h2&gt;&lt;br /&gt;After having successfully built the server and played a game of Connect4 with myself, I started looking into the main method of the server code. From the documentation, I knew that the client-server protocol was xml based, which is good. I did not know however in which way those messages were exchanged. Many gaming engines use TCP directly (or, for better throughput, even UDP), since it provides a lot of nice properties (like establishing an keeping up a connection) that are more than sufficient for their needs.&lt;br /&gt;&lt;br /&gt;I took a peek and found my expectations confirmed: JOGRE was using a well-established pattern that could have been straight from &lt;a href="http://www.amazon.com/gp/product/0321334205?ie=UTF8&amp;tag=apenfa-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=0321334205"&gt;The Java Tutorial&lt;/a&gt;&lt;img src="http://www.assoc-amazon.com/e/ir?t=apenfa-20&amp;l=as2&amp;o=1&amp;a=0321334205" alt="" border="0" width="1" height="1" /&gt; (see &lt;a href="http://java.sun.com/docs/books/tutorial/networking/sockets/clientServer.html" target="_blank"&gt;http://java.sun.com/docs/books/tutorial/networking/sockets/clientServer.html&lt;/a&gt;):&lt;br /&gt;&lt;ul&gt;&lt;li&gt;a main class (JogreServer) creates a ServerSocket and listens in a loop to incoming connections&lt;/li&gt;&lt;li&gt;for each incoming connection, it starts a new Thread (ServerConnectionThread) that then handles all the gaming logic.&lt;/li&gt;&lt;/ul&gt;Here is how the connection thread looks like:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_ZXENRUvBme8/ShawRwPgd-I/AAAAAAAAATo/WpN-SPnNxg4/s1600-h/refactoring_base.jpg"&gt;&lt;img src="http://4.bp.blogspot.com/_ZXENRUvBme8/ShawRwPgd-I/AAAAAAAAATo/WpN-SPnNxg4/s320/refactoring_base.jpg" alt="" id="BLOGGER_PHOTO_ID_5338648227348510690" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;ServerConnectionThread is a subclass of AbstractConnectionThread, which contains all the logic of how to fetch data from the connection, maintain the lifecycle of the socket, update connection state, and remember the name of the current user that owns the connection. AbstractConnectionThread, a subclass of java.lang.Thread, also had a couple of other subclasses that shared the connection handling logic but did other things with the arriving data. How could I start squeezing http support in here without breaking anything?&lt;br /&gt;&lt;br /&gt;There is a very good chapter in &lt;a href="http://www.amazon.com/gp/product/0131177052?ie=UTF8&amp;tag=apenfa-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=0131177052"&gt;Working Effectively with Legacy Code&lt;/a&gt;&lt;img src="http://www.assoc-amazon.com/e/ir?t=apenfa-20&amp;l=as2&amp;o=1&amp;a=0131177052" alt="" border="0" width="1" height="1" /&gt; that introduces a concept that is called the Single-Responsibility Principle:&lt;br /&gt;&lt;blockquote&gt;"Every class should have a single responsibility: It should have a single purpose in the system, and there should be only one reason to change it."&lt;br /&gt;&lt;/blockquote&gt;Using that as the foundation of my first refactoring, I decided to break parts of the AbstractConnectionThread out into a new class called SocketBasedMessageBus:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_ZXENRUvBme8/ShawwnGCAoI/AAAAAAAAATw/gmgSZs0YdNg/s1600-h/refactoring_v5.jpg"&gt;&lt;img src="http://1.bp.blogspot.com/_ZXENRUvBme8/ShawwnGCAoI/AAAAAAAAATw/gmgSZs0YdNg/s320/refactoring_v5.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5338648757468791426" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The refactoring itself was pretty straightforward: I took the content of the thread's run()-method, plus everything that used the socket object, and copied it into the new class. The thread's constructor would simply wrap the original Socket into the new object, and it's start/stop/run-methods would delegate calls to their equivalents in the new object.&lt;br /&gt;&lt;br /&gt;I compiled the code and ran the unit tests -- everything still worked :-). The next step was to simply kick out those delegating, hollowed-out methods. I added a "getMessageBus" method to the base class and had all subclasses use that to get access to the new MessageBus object, and called the moved methods in the subclasses:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_ZXENRUvBme8/Shaw9a86DiI/AAAAAAAAAT4/w0BHS4G5CYg/s1600-h/refactoring_v8.jpg"&gt;&lt;img src="http://4.bp.blogspot.com/_ZXENRUvBme8/Shaw9a86DiI/AAAAAAAAAT4/w0BHS4G5CYg/s320/refactoring_v8.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5338648977547595298" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Now that the the SocketBasedMessageBus was doing all the socket-based work, AbstractConnectionThread no longer needed to subclass the Thread class. This enabled me to get rid of the run() method, and encapsulate the use of Threads in the message bus:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_ZXENRUvBme8/ShaxFzV1KuI/AAAAAAAAAUA/weDtyskMYhA/s1600-h/refactoring_v11.jpg"&gt;&lt;img src="http://1.bp.blogspot.com/_ZXENRUvBme8/ShaxFzV1KuI/AAAAAAAAAUA/weDtyskMYhA/s320/refactoring_v11.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5338649121533536994" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;While looking at the new class, I realized that the names of the methods I had moved into SocketBasedMessageBus were focused around threading and loops -- but that was not necessarily what the class was responsible for. As the name said, SocketBasedMessageBus isolated the exchange of messages via a Java socket -- so its public API should reflect that. I decided to rename the methods accordingly:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_ZXENRUvBme8/ShaxTR5cTyI/AAAAAAAAAUI/dqLkGUrY2yA/s1600-h/refactoring_v12.jpg"&gt;&lt;img src="http://2.bp.blogspot.com/_ZXENRUvBme8/ShaxTR5cTyI/AAAAAAAAAUI/dqLkGUrY2yA/s320/refactoring_v12.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5338649353074265890" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;While probably still not single responsibility, I now had a situation where the AbstractConnectionThread class no longer had any particular knowledge that it was using a socket -- except for its constructor. That was good enough for me (after all, if it ain't broke...), so I decided to wrap it up. One final cleanup step remained: the constructor of our base class should not have to know about sockets. Nor would our MessageBus really have to care about the user name of the connection thread, as long as there was a parse- and a cleanup method. I therefore chose to extract those aspects of the class into interfaces and have the base classes depend on those rather than the concrete implementations:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_ZXENRUvBme8/ShaxbJAzCgI/AAAAAAAAAUQ/b5_KI6sqj0w/s1600-h/refactoring_v13.jpg"&gt;&lt;img src="http://4.bp.blogspot.com/_ZXENRUvBme8/ShaxbJAzCgI/AAAAAAAAAUQ/b5_KI6sqj0w/s320/refactoring_v13.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5338649488128150018" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;At the end of the day, the refactoring of AbstractConnectionThread resulted in the following modification:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;instead of managing sockets and threads, an AbstractConnectionThread is connected to a generic MessageBus object, which may choose any transport protocol (sockets, udp, http) it likes. The class no longer has any dependencies on sockets.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;AbstractConnectionThread has two remaining responsibilities: to manage the name of the user that the connection belongs to, and to provide a generalization for how to react to incoming messages from the MessageBus. The latter responsibility is represented by the MessageParser interface, which is what the MessageBus interfaces with.&lt;/li&gt;&lt;li&gt;concrete subclasses like ServerConnectionThread still accept Sockets in their constructor (thus remaining compatible with the rest of the codebase), but they wrap the socket in a MessageBus before pushing it into the base constructor. I might choose to refactor that in a later stage, but if I do so, I can do it on an individual class basis, without breaking any of the other peer classes.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Hardly a spectacular change, but that was never the goal in the first place ;-). The main target was to make the socket communication replaceable without major rewrites of the system. Now that that's done, I can keep scanning the code for other things that might be tough to do on App Engine.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;The refactoring in code&lt;/h2&gt;&lt;br /&gt;For those amongst us who'd rather just read code, here are the classes affected by this refactoring:&lt;br /&gt;&lt;br /&gt;&lt;span&gt;The original class&lt;/span&gt;&lt;br /&gt;&lt;a href="http://code.google.com/p/gae-ogre/source/browse/trunk/api/src/org/jogre/common/AbstractConnectionThread.java?r=2" target="_blank"&gt;http://code.google.com/p/gae-ogre/source/browse/trunk/api/src/org/jogre/common/AbstractConnectionThread.java?r=2&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;Main refactoring targets&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://code.google.com/p/gae-ogre/source/diff?spec=svn5&amp;old=2&amp;r=16&amp;format=side&amp;path=%2Ftrunk%2Fapi%2Fsrc%2Forg%2Fjogre%2Fcommon%2FAbstractConnectionThread.java"&gt;AbstractConnectionThread (refactored)&lt;/a&gt;&lt;a href="http://code.google.com/p/gae-ogre/source/diff?spec=svn5&amp;old=2&amp;r=16&amp;format=side&amp;path=%2Ftrunk%2Fapi%2Fsrc%2Forg%2Fjogre%2Fcommon%2FAbstractConnectionThread.java" target="_blank"&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://code.google.com/p/gae-ogre/source/browse/trunk/api/src/org/jogre/common/MessageBus.java?r=15"&gt;MessageBus (new)&lt;/a&gt;&lt;a href="http://code.google.com/p/gae-ogre/source/browse/trunk/api/src/org/jogre/common/MessageBus.java?r=15" target="_blank"&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://code.google.com/p/gae-ogre/source/browse/trunk/api/src/org/jogre/common/SocketBasedMessageBus.java?r=15"&gt;SocketBasedMessageBus (new)&lt;/a&gt;&lt;a href="http://code.google.com/p/gae-ogre/source/browse/trunk/api/src/org/jogre/common/SocketBasedMessageBus.java?r=15" target="_blank"&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;span&gt;Affected classes (minor changes)&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://code.google.com/p/gae-ogre/source/diff?spec=svn6&amp;old=2&amp;r=16&amp;format=side&amp;path=%2Ftrunk%2Fserver%2Fsrc%2Forg%2Fjogre%2Fserver%2Fadministrator%2FAdminClientConnectionThread.java"&gt;AdminClientConnectionThread&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://code.google.com/p/gae-ogre/source/diff?spec=svn9&amp;old=2&amp;r=12&amp;format=side&amp;path=%2Ftrunk%2Fserver%2Fsrc%2Forg%2Fjogre%2Fserver%2Fadministrator%2FAdminConnectionPanel.java"&gt;AdminConnectionPanel&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://code.google.com/p/gae-ogre/source/diff?spec=svn5&amp;old=2&amp;r=16&amp;format=side&amp;path=%2Ftrunk%2Fserver%2Fsrc%2Forg%2Fjogre%2Fserver%2Fadministrator%2FAdminClientConnectionThread.java"&gt;AdminConnectionThread&lt;/a&gt;&lt;a href="http://code.google.com/p/gae-ogre/source/diff?spec=svn5&amp;old=2&amp;r=16&amp;format=side&amp;path=%2Ftrunk%2Fserver%2Fsrc%2Forg%2Fjogre%2Fserver%2Fadministrator%2FAdminClientConnectionThread.java" target="_blank"&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://code.google.com/p/gae-ogre/source/diff?spec=svn5&amp;old=2&amp;r=16&amp;format=side&amp;path=%2Ftrunk%2Fapi%2Fsrc%2Forg%2Fjogre%2Fclient%2FClientConnectionThread.java"&gt;ClientConnectionThread&lt;/a&gt;&lt;a href="http://code.google.com/p/gae-ogre/source/diff?spec=svn5&amp;old=2&amp;r=16&amp;format=side&amp;path=%2Ftrunk%2Fapi%2Fsrc%2Forg%2Fjogre%2Fclient%2FClientConnectionThread.java" target="_blank"&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://code.google.com/p/gae-ogre/source/diff?spec=svn9&amp;old=2&amp;r=12&amp;format=side&amp;path=%2Ftrunk%2Fapi%2Fsrc%2Forg%2Fjogre%2Fclient%2Fawt%2FGameConnectionPanel.java"&gt;GameConnectionPanel&lt;/a&gt;&lt;a href="http://code.google.com/p/gae-ogre/source/diff?spec=svn9&amp;old=2&amp;r=12&amp;format=side&amp;path=%2Ftrunk%2Fapi%2Fsrc%2Forg%2Fjogre%2Fclient%2Fawt%2FGameConnectionPanel.java" target="_blank"&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://code.google.com/p/gae-ogre/source/diff?spec=svn10&amp;r=10&amp;format=side&amp;path=/trunk/api/src/org/jogre/common/util/JogreLogger.java&amp;old_path=/trunk/api/src/org/jogre/common/util/JogreLogger.java&amp;old=2#"&gt;JogreLogger&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://code.google.com/p/gae-ogre/source/diff?spec=svn9&amp;old=2&amp;r=12&amp;format=side&amp;path=%2Ftrunk%2Fserver%2Fsrc%2Forg%2Fjogre%2Fserver%2FJogreServer.java"&gt;JogreServer&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://code.google.com/p/gae-ogre/source/diff?spec=svn16&amp;old=2&amp;r=16&amp;format=side&amp;path=%2Ftrunk%2Fserver%2Fsrc%2Forg%2Fjogre%2Fserver%2FServerConnectionThread.java"&gt;ServerConnectionThread&lt;/a&gt;&lt;br /&gt; &lt;/li&gt;&lt;li&gt;&lt;a href="http://code.google.com/p/gae-ogre/source/diff?spec=svn6&amp;old=2&amp;r=12&amp;format=side&amp;path=%2Ftrunk%2Fserver%2Fsrc%2Forg%2Fjogre%2Fserver%2Fcontrollers%2FServerGameController.java"&gt;ServerGameController&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://code.google.com/p/gae-ogre/source/diff?spec=svn5&amp;old=2&amp;r=14&amp;format=side&amp;path=%2Ftrunk%2Fserver%2Ftest%2Fsrc%2Forg%2Fjogre%2Fprotocol%2FTestConnectionThread.java"&gt;TestConnectionThread&lt;/a&gt;&lt;a href="http://code.google.com/p/gae-ogre/source/diff?spec=svn5&amp;old=2&amp;r=14&amp;format=side&amp;path=%2Ftrunk%2Fserver%2Ftest%2Fsrc%2Forg%2Fjogre%2Fprotocol%2FTestConnectionThread.java" target="_blank"&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width="1" height="1" src="//blogger.googleusercontent.com/tracker/5502193732299119611-7695054699383231164?l=blog.appenginefan.com" /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/4Xy9tQkwJZyyMLUIV8UXe2prd9Q/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/4Xy9tQkwJZyyMLUIV8UXe2prd9Q/0/di" border="0" ismap="true" /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/4Xy9tQkwJZyyMLUIV8UXe2prd9Q/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/4Xy9tQkwJZyyMLUIV8UXe2prd9Q/1/di" border="0" ismap="true" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds2.feedburner.com/~ff/AppEngineFan?a=r1iNzEDzWzM:jSAjMx7iA3I:yIl2AUoC8zA"&gt;&lt;img src="http://feeds2.feedburner.com/~ff/AppEngineFan?d=yIl2AUoC8zA" border="0" /&gt;&lt;/a&gt; &lt;a href="http://feeds2.feedburner.com/~ff/AppEngineFan?a=r1iNzEDzWzM:jSAjMx7iA3I:4cEx4HpKnUU"&gt;&lt;img src="http://feeds2.feedburner.com/~ff/AppEngineFan?i=r1iNzEDzWzM:jSAjMx7iA3I:4cEx4HpKnUU" border="0" /&gt;&lt;/a&gt; &lt;a href="http://feeds2.feedburner.com/~ff/AppEngineFan?a=r1iNzEDzWzM:jSAjMx7iA3I:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds2.feedburner.com/~ff/AppEngineFan?i=r1iNzEDzWzM:jSAjMx7iA3I:gIN9vFwOqvQ" border="0" /&gt;&lt;/a&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">The App Engine Fan (noreply@blogger.com)</dc:creator><pubDate>Thu, 21 May 2009 20:39:00 -0000</pubDate><guid>http://feedproxy.google.com/~r/AppEngineFan/~3/r1iNzEDzWzM/initial-refactoring.html</guid></item><item><title>My summer project: let the games begin</title><link>http://feedproxy.google.com/%7Er/AppEngineFan/%7E3/R-IywDWKgaI/my-summer-project-let-games-begin.html</link><description>I finally got my &lt;a href="http://blog.appenginefan.com/search/label/Schluesselmeister"&gt;Schluesselmeister&lt;/a&gt; app to a point where I feel comfortable moving away from my desktop app and into the cloud (the final missing piece was sharing, so that my wife and I could use the same key database). Now that that's done, I need another hobby project :-) The search criteria were as following&lt;br /&gt;&lt;ul&gt;&lt;li&gt;It should be as far away from my day job as it could possibly be, yet still involve Google App Engine.&lt;/li&gt;&lt;li&gt;It should force me to look at the tools from a different angle and broaden my perspective.&lt;/li&gt;&lt;li&gt;It should be about something I can "occasionally" mention in this blog and that people will hopefully enjoy reading.&lt;/li&gt;&lt;li&gt;It should be something that I find fun to do.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;At first, I thought along the lines of building an enterprise application. Ages ago (at least in software years ;-), when I was at a previous job writing software for public transportation, &lt;a href="http://www.masstransitmag.com/print/Mass-Transit/Technology-Update/1$675"&gt;I had claimed that&lt;/a&gt;:&lt;br /&gt;&lt;blockquote&gt;Even if Google happens to stay out of the market, its products will help lower the entry-level for this industry even more. By using its products, a new breed of systems, based on Internet technologies and open standards will reach the market. Greater competition will improve the overall quality of the solutions offered, and the end user is going to benefit.&lt;/blockquote&gt;I was briefly contemplating putting that statement to the test and writing such an application. After all, App Engine seemed like the perfect backend of moving such software into the cloud. The problem with it: it seemed more like work than fun. Also, I wasn't sure if I could squeeze any good articles out of such a project ;-)&lt;br /&gt;&lt;br /&gt;After some more soul searching, I decided to focus on something else instead: online gaming. A recent lightning talk I saw on Buddypoke inspired me: obviously, App Engine is a great backend to scale fun applications to millions of users. But how? What works, and what doesn't? What are best practices to build such a fun and massively scalable application?&lt;br /&gt;&lt;br /&gt;Naturally, I did not want to reinvent the wheel. I searched a little bit around and found &lt;a href="http://jogre.sourceforge.net/main.htm"&gt;JOGRE&lt;/a&gt;, a Java-based gaming engine that is open source, seems to have decent test coverage and comes with a ton of pre-implemented games. How hard would it be to run this backend on App Engine? I am going to find out over the next months (yes, &lt;span&gt;months&lt;/span&gt; -- I have no idea how hard it is going to be; and the weather is way to beautiful outside to be coding all weekend ;-). I am starting with the 0.3 source snapshot, which I uploaded to &lt;a href="http://code.google.com/p/gae-ogre/"&gt;http://code.google.com/p/gae-ogre/&lt;/a&gt;. I doubt I will have anything runnable for quite a while, but I hope the journey of getting there will yield some interesting posts. Wish me luck :-)&lt;div class="blogger-post-footer"&gt;&lt;img width="1" height="1" src="http://res1.blogblog.com/tracker/5502193732299119611-8341279700061103209?l=blog.appenginefan.com" /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/SvtrCRf6Vf2rC8TqBpX5Nau50JI/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/SvtrCRf6Vf2rC8TqBpX5Nau50JI/0/di" border="0" ismap="true" /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/SvtrCRf6Vf2rC8TqBpX5Nau50JI/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/SvtrCRf6Vf2rC8TqBpX5Nau50JI/1/di" border="0" ismap="true" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds2.feedburner.com/~ff/AppEngineFan?a=R-IywDWKgaI:z5IpvpwgNLk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds2.feedburner.com/~ff/AppEngineFan?d=yIl2AUoC8zA" border="0" /&gt;&lt;/a&gt; &lt;a href="http://feeds2.feedburner.com/~ff/AppEngineFan?a=R-IywDWKgaI:z5IpvpwgNLk:4cEx4HpKnUU"&gt;&lt;img src="http://feeds2.feedburner.com/~ff/AppEngineFan?i=R-IywDWKgaI:z5IpvpwgNLk:4cEx4HpKnUU" border="0" /&gt;&lt;/a&gt; &lt;a href="http://feeds2.feedburner.com/~ff/AppEngineFan?a=R-IywDWKgaI:z5IpvpwgNLk:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds2.feedburner.com/~ff/AppEngineFan?i=R-IywDWKgaI:z5IpvpwgNLk:gIN9vFwOqvQ" border="0" /&gt;&lt;/a&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">The App Engine Fan (noreply@blogger.com)</dc:creator><pubDate>Sun, 17 May 2009 18:55:00 -0000</pubDate><guid>http://feedproxy.google.com/~r/AppEngineFan/~3/R-IywDWKgaI/my-summer-project-let-games-begin.html</guid></item></channel></rss>