<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;DkAAR3k5cCp7ImA9WhVSGEg.&quot;"><id>tag:blogger.com,1999:blog-1611368930203661681</id><updated>2012-03-16T00:39:06.728+01:00</updated><category term="rest" /><category term="google app engine" /><category term="android outofmemory" /><category term="android" /><category term="java" /><category term="dns" /><category term="spring" /><category term="twitter" /><category term="security" /><category term="othello legends" /><category term="jenkins" /><category term="dropbox" /><category term="eclipse memory analyzer" /><category term="adb" /><category term="maven" /><category term="oauth" /><category term="hudson" /><category term="twitter4j" /><category term="profiling" /><category term="rankington" /><category term="google apps" /><title>MacGyver Development</title><subtitle type="html" /><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://macgyverdev.blogspot.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://macgyverdev.blogspot.com/" /><author><name>Johan Norén</name><uri>http://www.blogger.com/profile/14636265442070759165</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://3.bp.blogspot.com/-rDRt8dDpk8g/Th9Qq6DmMPI/AAAAAAAAADk/6ha9_J9-2iU/s220/IMG_0461.JPG" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>12</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/MacgyverDevelopment" /><feedburner:info uri="macgyverdevelopment" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:emailServiceId>MacgyverDevelopment</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><entry gd:etag="W/&quot;DEIEQHwyeSp7ImA9WhRWFU4.&quot;"><id>tag:blogger.com,1999:blog-1611368930203661681.post-6371833604939272544</id><published>2012-01-02T21:35:00.000+01:00</published><updated>2012-01-02T21:35:01.291+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-01-02T21:35:01.291+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="rest" /><category scheme="http://www.blogger.com/atom/ns#" term="spring" /><category scheme="http://www.blogger.com/atom/ns#" term="google app engine" /><category scheme="http://www.blogger.com/atom/ns#" term="othello legends" /><title>Create Spring REST service for Google App Engine in 15 minutes</title><content type="html">Here's how you setup a REST service deployed in the Google App Engine cloud in 15 minutes. The use case in this example is a highscore backend service for my Android game&amp;nbsp;&lt;a href="https://market.android.com/details?id=se.noren.android.othello" target="_blank"&gt;Othello Legends&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-size: large;"&gt;Requirements:&lt;/span&gt;&lt;br /&gt;
We want to create a REST interface for these resources representing a highscore service.&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;GET&amp;nbsp;http://myapp.appspot.com/api/highscores/&lt;/span&gt;&lt;br /&gt;
Fetch all applications backed by the highscore service since we want to reuse this for multiple games.&lt;br /&gt;
&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;GET http://myapp.appspot.com/api/highscores/myappname&lt;/span&gt;&lt;br /&gt;
Fetch a sorted listed of highscores for a particular application &lt;i&gt;myappname&lt;/i&gt;.&lt;br /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;POST http://myapp.appspot.com/api/highscores/myappname&lt;/span&gt;&lt;br /&gt;
Post a potential new highscore to service. If it makes it to the highscore list it will be saved in database. The data will be sent as query parameters.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-size: large;"&gt;Ingredients of the solution:&lt;/span&gt;&lt;br /&gt;
Google App Engine runs Java and Python. This example will use the Java infrastructure.&lt;br /&gt;
So what we'll do is to create a standard Java J2EE web application built for deployment in App Engine backed by a simple DAO to abstract the Google BigTable databases. By using Spring REST together with Jackson we can communicate with JSON in a RESTful manner with minimum effort.&lt;br /&gt;
&lt;br /&gt;
Sounds complicated? Not at all, here's how you do it!&lt;br /&gt;
&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-size: large;"&gt;Prerequisities:&lt;/span&gt;&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Java knowledge&lt;/li&gt;
&lt;li&gt;Eclipse and some basic skills of working in it.&lt;/li&gt;
&lt;li&gt;A Google account&lt;/li&gt;
&lt;li&gt;The Eclipse plugin for Google App Engine. If you ain't got it, the install is easy, follow the instruction at&amp;nbsp;&lt;a href="http://code.google.com/intl/sv-SE/appengine/docs/java/tools/eclipse.html"&gt;http://code.google.com/intl/sv-SE/appengine/docs/java/tools/eclipse.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div&gt;&lt;span style="font-size: large;"&gt;REST Implementation:&lt;/span&gt; &lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-_cWO_kq2isw/Trw4GT3f3oI/AAAAAAAAANw/8SlJ1Ctp3zI/s1600/newproj.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/-_cWO_kq2isw/Trw4GT3f3oI/AAAAAAAAANw/8SlJ1Ctp3zI/s1600/newproj.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;So to create an App Engine web app, click the &lt;i&gt;New Web Application Project&lt;/i&gt; icon. Deselect Google Web Toolkit if you don't intend to use it.&lt;br /&gt;
&lt;br /&gt;
Now, we're going to use Spring REST for the REST heavy weight lifting. Download Spring Framework 3 or later from&amp;nbsp;&lt;a href="http://www.springsource.org/download" target="_blank"&gt;http://www.springsource.org/download&lt;/a&gt;. While at it, download the Jackson JSON library from&amp;nbsp;&lt;a href="http://jackson.codehaus.org/" target="_blank"&gt;http://jackson.codehaus.org/&lt;/a&gt;. Put the downloaded jars in the&amp;nbsp;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;/war/WEB-INF/lib/&lt;/span&gt;&amp;nbsp;folder and add them to the classpath of your web application.&lt;br /&gt;
&lt;br /&gt;
Now, to bootstrap Spring to handle your incoming servlet requests you should edit the&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt; web.xml &lt;/span&gt;file of your web application found in &lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;war/WEB-INF/&lt;/span&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;&lt;web-app version="2.5" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemalocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"&gt;

&lt;servlet&gt;
   &lt;servlet-name&gt;api&lt;/servlet-name&gt;
   &lt;servlet-class&gt;
      org.springframework.web.servlet.DispatcherServlet
   &lt;/servlet-class&gt;
   &lt;load-on-startup&gt;1&lt;/load-on-startup&gt;
&lt;/servlet&gt;
  
&lt;servlet-mapping&gt;
   &lt;servlet-name&gt;api&lt;/servlet-name&gt;
   &lt;url-pattern&gt;/api/*&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;

&lt;welcome-file-list&gt;
   &lt;welcome-file&gt;index.html&lt;/welcome-file&gt;
&lt;/welcome-file-list&gt;
&lt;/web-app&gt;
&lt;/pre&gt;&lt;br /&gt;
That will put Spring in charge of everything coming in under path &lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;/api/*&lt;/span&gt;. Spring must now which packages to scan for Spring annotated classes. We add a Spring configuration file for this and also add some Spring/Jackson config for specifying how to convert from our Java POJOs to JSON. Put this stuff in a file called&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt; api-servlet.xml&lt;/span&gt; in &lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;war/WEB-INF&lt;/span&gt;.&lt;br /&gt;
&lt;pre class="java" name="code"&gt;&lt;beans xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemalocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-3.0.xsd"&gt;

 &lt;context:component-scan base-package="se.noren.othello"&gt;

 &lt;bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"&gt;
  &lt;property name="messageConverters"&gt;
   &lt;list&gt;
    &lt;ref bean="jsonHttpMessageConverter"&gt;
   &lt;/ref&gt;&lt;/list&gt;
  &lt;/property&gt;
 &lt;/bean&gt;

 &lt;bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" id="jsonHttpMessageConverter"&gt;

 &lt;bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"&gt;
  &lt;property name="mediaTypes"&gt;
   &lt;map&gt;
    &lt;entry key="json" value="application/json"&gt;
   &lt;/entry&gt;&lt;/map&gt;
  &lt;/property&gt;
  &lt;property name="defaultContentType" value="application/json"&gt;
  &lt;property name="defaultViews"&gt;
   &lt;list&gt;
    &lt;bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView"&gt;
   &lt;/bean&gt;&lt;/list&gt;
  &lt;/property&gt;
 &lt;/property&gt;&lt;/bean&gt;

&lt;/bean&gt;&lt;/context:component-scan&gt;&lt;/beans&gt;

&lt;/pre&gt;&lt;br /&gt;
Without going into detail, this config pretty much tells Spring to convert POJOs to JSON as default using Jackson for servlet responses. If you're not interested in the details just grab it, but you must adjust the &lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;lt;context:component-scan base-package="se.noren.othello" /&amp;gt;&lt;/span&gt; to match your package names.&lt;br /&gt;
&lt;br /&gt;
Now to the fun part, mapping Java code to the REST resources we want to expose. We need a controller class to annotate how our Java methods should map to the exposed HTTP URIs. Create something similar to&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;import java.util.Date;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

/**
 * Controller for Legends app family highscore services.
 */
@Controller
@RequestMapping("/highscores")
public class LegendsHighScoreController {
 private static final long serialVersionUID = 1L;

 @Autowired
 HighScoreService highScoresService;

 /**
  * @return Fetch all registered applications in the highscore database.
  */
 @RequestMapping(value = "/", method = RequestMethod.GET)
 public ModelAndView getAllApplications() {
  List&amp;lt;String&amp;gt; allApplications = highScoresService.getAllApplications();
  return new ModelAndView("highScoresView", BindingResult.MODEL_KEY_PREFIX + "applications", allApplications);
 }
 
 /**
  * Fetch all highscores for a particular application.
  * @param application Name of application
  * @return
  */
 @RequestMapping(value = "/{application}", method = RequestMethod.GET)
 public ModelAndView getAllHighScores(@PathVariable String application) {
  List&amp;lt;HighScore&amp;gt; allHighScores = highScoresService.getAllHighScores(application);
  return new ModelAndView("highScoresView", BindingResult.MODEL_KEY_PREFIX + "scores", allHighScores);
 }
 
 /**
  * Add a new highscore to the database if it makes it to the high score list.
  * @param application Name of application
  * @param owner Owner of the highscore
  * @param score Score as whole number
  * @param level Level of player reaching score.
  * @return The created score.
  */
 @RequestMapping(value = "/{application}", method = RequestMethod.POST)
 public ModelAndView addHighScores(@PathVariable String application,
                             @RequestParam String owner,
                             @RequestParam long score,
                             @RequestParam long level
                             ) {
  
  HighScore highScore = new HighScore(owner, score, application, new Date().getTime(), level);
  highScoresService.addHighScores(highScore);
  return new ModelAndView("highScoresView", BindingResult.MODEL_KEY_PREFIX + "scores", highScore);
 }
}

&lt;/pre&gt;&lt;br /&gt;
So what's the deal with all the annotations? They're pretty self explanatory once you start matching the Java methods to the three HTTP REST URIs we wanted to create, but in short:&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;@Controller&lt;/span&gt; - The usual Spring annotation to tell Spring that this is a controller class that should be managed by the Spring container. All RESTful stuff is contained within the this class.&lt;/li&gt;
&lt;li&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;@RequestMapping("/highscores")&lt;/span&gt; - This means that this controller class should accept REST calls under the path &lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;/highscores&lt;/span&gt;. Since we deployed the servlet under servlet mapping &lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;/api&lt;/span&gt; in the &lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;web.xml &lt;/span&gt;this means all REST resources resides under &lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;http://host.com/api/highscores&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;@Autowired&amp;nbsp;HighScoreService highScoresService&lt;/span&gt;&amp;nbsp;- Our backing service class to do real business logic. Agnostic that we're using a RESTful front.&lt;/li&gt;
&lt;li&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;@RequestMapping(value = "/{application}", method = RequestMethod.GET) public ModelAndView getAllHighScores(@PathVariable String application)&amp;nbsp;&lt;/span&gt;- &amp;nbsp;A method annotated like this creates a REST resource &lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;/api/highscores/&lt;i&gt;dynamicAppName&lt;/i&gt;&lt;/span&gt; where the value given for&amp;nbsp;&lt;i&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;dynamicAppName &lt;/span&gt;&lt;/i&gt;is given via the path variable &lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;application&lt;/span&gt;. The request method specifies that this Java method will be called if this URI is requested via HTTP GET. All ordinary HTTP verbs are supported.&lt;/li&gt;
&lt;li&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;@RequestParam String owner&lt;/span&gt; - If you wish to pass query parameters like &lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;myvar1=foo&amp;amp;myvar2=bar&lt;/span&gt; you can use the request param annotation.&lt;/li&gt;
&lt;li&gt;The Java class returned in the&amp;nbsp;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;ModelAndView &lt;/span&gt;response will be automatically marshalled to JSON by Jackson on the same structure as the Java POJO.&lt;/li&gt;
&lt;/ul&gt;&lt;div&gt;&lt;span style="font-size: large;"&gt;Database&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Google App Engine uses the Google BigTables behind the scenes to store data. You can abstract this by using the standard JPA annotations on your POJOs. The similar JDO standard can be used as well. I've used JDO in previous projects and it works very well. For this simple server application we will however use the query language to directly access the document database. Here's the code for the first method to fetch all highscores for a particular Legends application. The database can filter and sort via API methods in the query.&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;@Service
public class HighScoreServiceImpl implements HighScoreService {

 @Override
 public List&amp;lt;HighScore&amp;gt; getAllHighScores(String application) {
  ArrayList&amp;lt;HighScore&amp;gt; list = new ArrayList&amp;lt;HighScore&amp;gt;();
  DatastoreService datastore = DatastoreServiceFactory
    .getDatastoreService();

  // The Query interface assembles a query
  Query q = new Query("HighScore");
  q.addFilter("application", Query.FilterOperator.EQUAL, application);
  q.addFilter("score", FilterOperator.GREATER_THAN_OR_EQUAL, 0);
  q.addSort("score", SortDirection.DESCENDING);

  // PreparedQuery contains the methods for fetching query results
  // from the datastore
  PreparedQuery pq = datastore.prepare(q);

  for (Entity result : pq.asIterable()) {
   String owner = (String) result.getProperty("owner");
   Long date = (Long) result.getProperty("date");
   Long score = (Long) result.getProperty("score");
   Long level = (Long) result.getProperty("level");
   list.add(new HighScore(owner, score, application, date, level));
  }

  return list;
 }
&lt;/pre&gt;&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
That's pretty much it. Run the project locally by right-clicking it and choose &lt;i&gt;Run As -&amp;gt; Web application&lt;/i&gt;. Once you are ready to go live create a cloud application by going to&amp;nbsp;&lt;a href="https://appengine.google.com/" target="_blank"&gt;https://appengine.google.com/&lt;/a&gt;&amp;nbsp;and &lt;i&gt;Create new application&lt;/i&gt;&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-TPqRUB-DNbE/TwIRj_HkWII/AAAAAAAAAO8/xNRXsplVDkg/s1600/newapp.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://1.bp.blogspot.com/-TPqRUB-DNbE/TwIRj_HkWII/AAAAAAAAAO8/xNRXsplVDkg/s320/newapp.png" width="263" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;Now in Eclipse, right click on your project and choose Google -&amp;gt; Deploy to Google App Engine.&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-VugaJX4TNOM/TwIR6VDvEcI/AAAAAAAAAPM/uOcnjpx8GWI/s1600/deploygae.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="276" src="http://3.bp.blogspot.com/-VugaJX4TNOM/TwIR6VDvEcI/AAAAAAAAAPM/uOcnjpx8GWI/s320/deploygae.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;You will be asked to supply the name you created in the App Engine administration interface. Wait a few seconds and the application will be deployed in the cloud.&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1611368930203661681-6371833604939272544?l=macgyverdev.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/0Xw5SsOyHvq5pTe_EcrkkHIPw7c/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/0Xw5SsOyHvq5pTe_EcrkkHIPw7c/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/0Xw5SsOyHvq5pTe_EcrkkHIPw7c/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/0Xw5SsOyHvq5pTe_EcrkkHIPw7c/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MacgyverDevelopment/~4/6Qe2tbP0TM4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://macgyverdev.blogspot.com/feeds/6371833604939272544/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://macgyverdev.blogspot.com/2012/01/create-spring-rest-service-for-google.html#comment-form" title="10 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1611368930203661681/posts/default/6371833604939272544?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1611368930203661681/posts/default/6371833604939272544?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/MacgyverDevelopment/~3/6Qe2tbP0TM4/create-spring-rest-service-for-google.html" title="Create Spring REST service for Google App Engine in 15 minutes" /><author><name>Johan Norén</name><uri>http://www.blogger.com/profile/14636265442070759165</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://3.bp.blogspot.com/-rDRt8dDpk8g/Th9Qq6DmMPI/AAAAAAAAADk/6ha9_J9-2iU/s220/IMG_0461.JPG" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-_cWO_kq2isw/Trw4GT3f3oI/AAAAAAAAANw/8SlJ1Ctp3zI/s72-c/newproj.png" height="72" width="72" /><thr:total>10</thr:total><feedburner:origLink>http://macgyverdev.blogspot.com/2012/01/create-spring-rest-service-for-google.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0YEQnk_eCp7ImA9WhRVF0w.&quot;"><id>tag:blogger.com,1999:blog-1611368930203661681.post-1232262971495048598</id><published>2011-12-11T22:36:00.002+01:00</published><updated>2012-01-16T11:51:43.740+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-01-16T11:51:43.740+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="android" /><category scheme="http://www.blogger.com/atom/ns#" term="othello legends" /><title>Othello Legends 1.0 in Android Market!</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-Ie7Ik3rP6sE/TuT5qxEceMI/AAAAAAAAAOg/qF6hc7oVaSk/s1600/sc1.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://3.bp.blogspot.com/-Ie7Ik3rP6sE/TuT5qxEceMI/AAAAAAAAAOg/qF6hc7oVaSk/s200/sc1.png" width="120" /&gt;&lt;/a&gt;&lt;/div&gt;A first version of Othello Legends is now published to the Android Market, check out&amp;nbsp;&lt;a href="https://market.android.com/details?id=se.noren.android.othello"&gt;https://market.android.com/details?id=se.noren.android.othello&lt;/a&gt;!&amp;nbsp;The game is the classic Othello/Reversi with a twist for unlocking harder levels when beating opponents. Compete with other players by increasing your total scores.&lt;br /&gt;
&lt;br /&gt;
The aim of the project was to learn how to build an Android application of some complexity with features like OpenGL rendering, integration with backend server, Google AdMob for serving ads, Google Analytics tracking of application usage, SD storage and possibility to run on both phones and tablets with appealing layout and performance.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Lesson learned 1 - 3D&lt;/b&gt;&lt;br /&gt;
Accelerated 3D graphics is hard even if you're experienced with desktop OpenGL. To make OpenGL ES work on all devices requires a lot of testing which you can't do without devices. And generally debugging accelerated graphics in the Android emulator is no fun.&amp;nbsp;Therefore, use a library to leverage yourself from the low level details.&lt;br /&gt;
&lt;a href="http://4.bp.blogspot.com/-b2Zt6vESsk8/TuT0aQh0rBI/AAAAAAAAAOI/Cr6Jm-gilKY/s1600/blender.png" imageanchor="1" style="clear: right; display: inline !important; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="224" src="http://4.bp.blogspot.com/-b2Zt6vESsk8/TuT0aQh0rBI/AAAAAAAAAOI/Cr6Jm-gilKY/s320/blender.png" width="320" /&gt;&lt;/a&gt;I looked into &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;jMonkeyEngine&lt;/span&gt;, &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;libgdx &lt;/span&gt;which are general and large frameworks with quite massive APIs which probably would have worked out great but seemed to have some threshold for a newcomer to overcome.&lt;br /&gt;
&lt;br /&gt;
In the end I decided to work with the more limited &lt;a href="http://www.jpct.net/jpct-ae/" target="_blank"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;jPCT &lt;/span&gt;&lt;/a&gt;which has worked out very well. A stable and reliable library with an active community. jPCT handles 3DS-models well which makes it easy to create environments via some tooling.&lt;br /&gt;
I used the open source modeller Blender which is free and has support for all you would need as sculpt modelling and texture UV coordinate tooling. Antother appealing feature of jPCT is that is developed both for Android and as standard Java for desktop so you can port your apps between them without great effort.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Lesson 2 - Revenue model&lt;/b&gt;&lt;br /&gt;
If you haven't decided whether charging for your app or using ads I can only say that Ads are easy! If you're familiar with Google AdSense for creating ads on your websites you'll find it intuitive to work with Google AdMob. If you have an Android Activity made up of standard Android layouts you can simply add an&amp;nbsp;&lt;span class="typ" style="background-color: white; color: #660066; font-family: monospace; font-size: 12px; line-height: 15px; white-space: pre;"&gt;AdView&lt;/span&gt;&amp;nbsp;view to your layout and the Ad library will populate the container with ads.&lt;br /&gt;
&lt;br /&gt;
Compared to the standard Google AdSense interfaces for managing and following up your ad reports AdMob is more limited and not as well polished but who cares? Will revenues be larger with mobile app ads than with ordinary web ads? I'll come back later on that.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Lesson 3 - Mobile is not desktop&lt;/b&gt;&lt;br /&gt;
Memory is scarse when you go down the 3D pathway. I early discovered that you must be cheap on your textures and the polygon levels of your meshes. The devices have no problem with rendering polygon heavy meshes with impressive framerates, but you soon run out of memory if you don't do clever texture unloads when you don't need them. My lesson here was: Create a game engine with strict modules for each game state so that you can be sure to deallocate all resources when you change state and use more low res textures than you usually would.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Lesson 4 - Tune your app after how your users use it&lt;/b&gt;&lt;br /&gt;
So in this game each level becomes more difficult and it seems like a good tuning approach to make the &amp;nbsp;difficulties of the first two levels easy enough to to make sure all players passes them. After that it should be exponentially more difficult. How to know how your app users are doing? I notices that the game was way too hard when I tried it on people. Some sort of surveillance would be nice without intruding in the users' privacy. Lesson here is to not invent anything new. By using Google Analytics you can track how users travel around in your application by marking different states as you would use Google Analytics to mark web pages in a web site to follow traffic around your site and adapt your game to how users respond.&lt;br /&gt;
&lt;div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-IZWP2qeJVdc/TuTxf6WXjDI/AAAAAAAAAOA/AAsO-qq0xqA/s1600/analytics.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="166" src="http://3.bp.blogspot.com/-IZWP2qeJVdc/TuTxf6WXjDI/AAAAAAAAAOA/AAsO-qq0xqA/s400/analytics.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;b&gt;Lesson 5 - Android is not Java&lt;/b&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;Another more depressive lesson learned is that when you plan to reuse some Java library first make a Google search whether anyone has had difficulties using it on the Android platform. For example the JSON marshaller Jackson proved to be hard to use.&lt;/div&gt;&lt;br /&gt;
&lt;div style="text-align: left;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1611368930203661681-1232262971495048598?l=macgyverdev.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/27-Zctv1OjQs6VGJQLjckOY6v2c/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/27-Zctv1OjQs6VGJQLjckOY6v2c/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/27-Zctv1OjQs6VGJQLjckOY6v2c/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/27-Zctv1OjQs6VGJQLjckOY6v2c/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MacgyverDevelopment/~4/G1f355nQqKA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://macgyverdev.blogspot.com/feeds/1232262971495048598/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://macgyverdev.blogspot.com/2011/12/othello-legends-10-in-android-market.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1611368930203661681/posts/default/1232262971495048598?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1611368930203661681/posts/default/1232262971495048598?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/MacgyverDevelopment/~3/G1f355nQqKA/othello-legends-10-in-android-market.html" title="Othello Legends 1.0 in Android Market!" /><author><name>Johan Norén</name><uri>http://www.blogger.com/profile/14636265442070759165</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://3.bp.blogspot.com/-rDRt8dDpk8g/Th9Qq6DmMPI/AAAAAAAAADk/6ha9_J9-2iU/s220/IMG_0461.JPG" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-Ie7Ik3rP6sE/TuT5qxEceMI/AAAAAAAAAOg/qF6hc7oVaSk/s72-c/sc1.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://macgyverdev.blogspot.com/2011/12/othello-legends-10-in-android-market.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkMEQ308cCp7ImA9WhRTFEQ.&quot;"><id>tag:blogger.com,1999:blog-1611368930203661681.post-6790723154711136577</id><published>2011-11-05T11:06:00.001+01:00</published><updated>2011-11-05T11:53:22.378+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-11-05T11:53:22.378+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="android outofmemory" /><category scheme="http://www.blogger.com/atom/ns#" term="profiling" /><category scheme="http://www.blogger.com/atom/ns#" term="android" /><category scheme="http://www.blogger.com/atom/ns#" term="eclipse memory analyzer" /><title>Android - track down memory leaks</title><content type="html">&lt;div class="separator" style="clear: both; text-align: left;"&gt;My current Android application project is starting to make sense. Unfortunately it crasches after a few levels of playing due to&amp;nbsp;&lt;b style="background-color: white; font-family: monospace; text-align: -webkit-auto; white-space: pre;"&gt;java.lang.OutOfMemoryError&lt;/b&gt;. Up to that point I hadn't put much thinking into the memory model of Android applications and simply consumed memory without hesitations. I've now been forced to rewrite some critical parts of the application and i thought I'll write a few words to remember the most useful tools I came across.&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;First of all, Android apps have small heaps. And with different sizes, it's up to the vendor of the device to decide. Here's a few numbers I came across:&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;/div&gt;&lt;ul style="background-attachment: initial; background-clip: initial; background-color: white; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; font-size: 14px; line-height: 18px; list-style-image: initial; list-style-position: initial; margin-bottom: 1em; margin-left: 30px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline;"&gt;&lt;li style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline; word-wrap: break-word;"&gt;G1 = 16 Mb&lt;/li&gt;
&lt;li style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline; word-wrap: break-word;"&gt;Droid = 24 Mb&lt;/li&gt;
&lt;li style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline; word-wrap: break-word;"&gt;Nexus One = 32 Mb&lt;/li&gt;
&lt;li style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline; word-wrap: break-word;"&gt;Xoom = 48 Mb&lt;/li&gt;
&lt;li style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline; word-wrap: break-word;"&gt;GalaxyTab = 64 Mb&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
So you see that allocated heaps are far from using the entire RAM of the devices since no application should be able to clog the system.&amp;nbsp;The natural approach to solving a memory problem would be to increase the heap but that is not so easy.&amp;nbsp;If you have a rooted phone you may edit&lt;br /&gt;
&lt;br /&gt;
&lt;span class="Apple-style-span" style="background-color: #eeeeee; font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif; font-size: 14px; line-height: 18px;"&gt;/system/build.props&lt;/span&gt;&lt;br /&gt;
and set the heap size via&lt;br /&gt;
&lt;span class="pln" style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif; font-size: 14px; line-height: 18px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; vertical-align: baseline; white-space: pre;"&gt;dalvik&lt;/span&gt;&lt;span class="pun" style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif; font-size: 14px; line-height: 18px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; vertical-align: baseline; white-space: pre;"&gt;.&lt;/span&gt;&lt;span class="pln" style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif; font-size: 14px; line-height: 18px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; vertical-align: baseline; white-space: pre;"&gt;vm&lt;/span&gt;&lt;span class="pun" style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif; font-size: 14px; line-height: 18px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; vertical-align: baseline; white-space: pre;"&gt;.&lt;/span&gt;&lt;span class="pln" style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif; font-size: 14px; line-height: 18px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; vertical-align: baseline; white-space: pre;"&gt;heapsize&lt;/span&gt;&lt;span class="pun" style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif; font-size: 14px; line-height: 18px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; vertical-align: baseline; white-space: pre;"&gt;=&lt;/span&gt;&lt;span class="lit" style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; color: maroon; font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif; font-size: 14px; line-height: 18px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; vertical-align: baseline; white-space: pre;"&gt;24m&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
Or, if you're running on a tablet (3.x) Android version there is a manifest setting to ask for a large heap&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="lang-java prettyprint" style="background-attachment: initial; background-clip: initial; background-color: white; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif; font-size: 14px; line-height: 18px; margin-bottom: 10px; max-height: 600px; overflow-x: auto; overflow-y: auto; padding-bottom: 5px; padding-left: 5px; padding-right: 5px; padding-top: 5px; text-align: left; vertical-align: baseline; width: auto;"&gt;&lt;code style="background-attachment: initial; background-clip: initial; background-color: #eeeeee; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline;"&gt;&lt;span class="pun" style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline;"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pln" style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline;"&gt;application android&lt;/span&gt;&lt;span class="pun" style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline;"&gt;:&lt;/span&gt;&lt;span class="pln" style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline;"&gt;label&lt;/span&gt;&lt;span class="pun" style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline;"&gt;=&lt;/span&gt;&lt;span class="str" style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; color: maroon; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline;"&gt;"@string/app_name"&lt;/span&gt;&lt;span class="pln" style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline;"&gt; android&lt;/span&gt;&lt;span class="pun" style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline;"&gt;:&lt;/span&gt;&lt;span class="pln" style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline;"&gt;hardwareAccelerated&lt;/span&gt;&lt;span class="pun" style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline;"&gt;=&lt;/span&gt;&lt;span class="str" style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; color: maroon; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline;"&gt;"true"&lt;/span&gt;&lt;span class="pln" style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline;"&gt; android&lt;/span&gt;&lt;span class="pun" style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline;"&gt;:&lt;/span&gt;&lt;span class="pln" style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline;"&gt;largeHeap&lt;/span&gt;&lt;span class="pun" style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline;"&gt;=&lt;/span&gt;&lt;span class="str" style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; color: maroon; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline;"&gt;"true"&lt;/span&gt;&lt;span class="pln" style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline;"&gt; android&lt;/span&gt;&lt;span class="pun" style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline;"&gt;:&lt;/span&gt;&lt;span class="pln" style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline;"&gt;debuggable&lt;/span&gt;&lt;span class="pun" style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline;"&gt;=&lt;/span&gt;&lt;span class="str" style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; color: maroon; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline;"&gt;"true"&lt;/span&gt;&lt;span class="pun" style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline;"&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;a href="http://4.bp.blogspot.com/-B-Z2rmN0XHk/TrT-gg26nNI/AAAAAAAAAM4/3NiujdDGjrc/s1600/changevmheap.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="320" src="http://4.bp.blogspot.com/-B-Z2rmN0XHk/TrT-gg26nNI/AAAAAAAAAM4/3NiujdDGjrc/s320/changevmheap.png" width="202" /&gt;&lt;/a&gt;but that is no guarantee and you will instead be punished with longer GC cycle times.&lt;br /&gt;
&lt;br /&gt;
On the other hand, changing the VM heap size in your emulator is easy, and could be a good thing in order to verify that your app works on devices with smaller heaps. To do that, fire up your Android SDK and AVD Manager and click edit on your virtual device. Under hardware, there is a setting&lt;i&gt; Max VM application heap size&lt;/i&gt;.&lt;br /&gt;
&lt;br /&gt;
So the conclusion is that you have to live with small heaps and limited memory. How to get an estimate of &amp;nbsp;your consumed memory and how much there is available then?&lt;br /&gt;
Run your application in the emulator or connect your real device via USB and use the Android Debug Bridge (adb). It's located in your Android SDK tools folder.&lt;br /&gt;
&lt;br /&gt;
To dump memory info for all your running applications use&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;$&amp;gt;adb shell dumpsys meminfo&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
or for your specific application&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;$&amp;gt;adb shell dumpsys meminfo se.noren.android.othello&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Applications Memory Usage (kB):&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Uptime: 8979886 Realtime: 8979886&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;** MEMINFO in pid 1073 [se.noren.android.othello] **&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; native &amp;nbsp; dalvik &amp;nbsp; &amp;nbsp;other &amp;nbsp; &amp;nbsp;total&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; size: &amp;nbsp; &amp;nbsp;24648 &amp;nbsp; &amp;nbsp;10119 &amp;nbsp; &amp;nbsp; &amp;nbsp;N/A &amp;nbsp; &amp;nbsp;34767&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;allocated: &amp;nbsp; &amp;nbsp;10869 &amp;nbsp; &amp;nbsp; 7335 &amp;nbsp; &amp;nbsp; &amp;nbsp;N/A &amp;nbsp; &amp;nbsp;18204&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; free: &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;2 &amp;nbsp; &amp;nbsp; 2784 &amp;nbsp; &amp;nbsp; &amp;nbsp;N/A &amp;nbsp; &amp;nbsp; 2786&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;(Pss): &amp;nbsp; &amp;nbsp; 2857 &amp;nbsp; &amp;nbsp; 8568 &amp;nbsp; &amp;nbsp; 9385 &amp;nbsp; &amp;nbsp;20810&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; (shared dirty): &amp;nbsp; &amp;nbsp; 1508 &amp;nbsp; &amp;nbsp; 4092 &amp;nbsp; &amp;nbsp; 2556 &amp;nbsp; &amp;nbsp; 8156&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; (priv dirty): &amp;nbsp; &amp;nbsp; 2656 &amp;nbsp; &amp;nbsp; 6020 &amp;nbsp; &amp;nbsp; 7732 &amp;nbsp; &amp;nbsp;16408&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;Objects&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;Views: &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;0 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;ViewRoots: &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;0&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;AppContexts: &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;0 &amp;nbsp; &amp;nbsp; &amp;nbsp; Activities: &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;0&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Assets: &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;2 &amp;nbsp; &amp;nbsp;AssetManagers: &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;2&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp;Local Binders: &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;6 &amp;nbsp; &amp;nbsp;Proxy Binders: &amp;nbsp; &amp;nbsp; &amp;nbsp; 10&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Death Recipients: &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;0&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;OpenSSL Sockets: &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;0&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;SQL&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; heap: &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;0 &amp;nbsp; &amp;nbsp; &amp;nbsp; memoryUsed: &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;0&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;pageCacheOverflo: &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;0 &amp;nbsp;largestMemAlloc: &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;0&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
To understand this table we must know that you have a managed heap, &lt;i&gt;dalvik&lt;/i&gt;, and a native heap. For example some graphics are stored in native heap. But important, it is the sum of these heaps that can not exceed the VM heap size. so you can't fool the runtime by putting more stuff in either native or managed heap. So to me, the most important numbers are the number under &lt;i&gt;dalvik &lt;/i&gt;and &lt;i&gt;total&lt;/i&gt; above. The dalvik heap is the managed VM heap and the &lt;i&gt;native &lt;/i&gt;numbers are memory allocated by native libraries (malloc).&lt;br /&gt;
You'll probably see these numbers fluctating each time you run the command, that is because objects are allocated by the runtime all the time but GCs are not run particularly often. So, in order to know that you really have garbage collected all unused objects you must either wait for the Android debug log in logcat to say something like&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;GC_FOR_MALLOC&lt;/span&gt; or &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;GC_EXTERNAL_MALLOC&lt;/span&gt; or similar to that which indicates that the GC has been invoked. Still, this does not mean that all unused memory has been released since the inner workings of the GC might not have done a complete sweep.&lt;br /&gt;
&lt;br /&gt;
You can of course ask for a GC programmatically by&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;System.gc()&lt;/span&gt;;&lt;br /&gt;
But that is never a good option. You should have trust in the VM to garbage collect for you. If you for example want to allocate a large memory chunk the gc will be invoked if necessary.&lt;br /&gt;
&lt;br /&gt;
You can force a gc using the Dalvik Debug Monitor (DDMS). Either start it from Eclipse or from the ddms tool in the Android SDK installation folders.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-q-KSOVCzI3k/TrT-jRS0c6I/AAAAAAAAANQ/csLK11wNbtA/s1600/gc.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="203" src="http://4.bp.blogspot.com/-q-KSOVCzI3k/TrT-jRS0c6I/AAAAAAAAANQ/csLK11wNbtA/s640/gc.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
If you can't see your process right away, go to menu &lt;i&gt;Actions &lt;/i&gt;and &lt;i&gt;Reset adb&lt;/i&gt;. After that you can turn on heap updates via the green icon &lt;i&gt;Show heap updates&lt;/i&gt;. To force a GC, click on &lt;i&gt;Cause GC&lt;/i&gt;.&lt;br /&gt;
&lt;br /&gt;
If you wish to monitor the memory usage programmatically there are a few APIs you can use.&lt;br /&gt;
&lt;br /&gt;
&lt;span class="Apple-style-span" style="background-color: white; font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; font-size: 14px; line-height: 18px;"&gt;ActivityManager.getMemoryInfo()&amp;nbsp;&lt;/span&gt;can be used to get an idea of how the memory situation is for the whole Android system. If running low on the gauges you can expect background processes to be killed off soon.&lt;br /&gt;
&lt;br /&gt;
To start inspecting your process in particular use the Debug APIs&amp;nbsp;&lt;a href="http://developer.android.com/intl/de/reference/android/os/Debug.html#getMemoryInfo(android.os.Debug.MemoryInfo" target="_blank"&gt;http://developer.android.com/intl/de/reference/android/os/Debug.html#getMemoryInfo(android.os.Debug.MemoryInfo&lt;/a&gt;. There's an excellent explanation of the data you can retrieve from this here&amp;nbsp;&lt;a href="http://stackoverflow.com/questions/2298208/how-to-discover-memory-usage-of-my-application-in-android" target="_blank"&gt;http://stackoverflow.com/questions/2298208/how-to-discover-memory-usage-of-my-application-in-android&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
For example, to see how much memory is allocated in the native heap, use:&lt;br /&gt;
&lt;span class="Apple-style-span" style="background-color: #eeeeee; font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif; font-size: 14px; line-height: 18px;"&gt;Debug.getNativeHeapAllocatedSize()&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-UIaPXx5XvTk/TrT-jMeF3UI/AAAAAAAAANE/cAYb_BftNMs/s1600/dumpheap.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/-UIaPXx5XvTk/TrT-jMeF3UI/AAAAAAAAANE/cAYb_BftNMs/s1600/dumpheap.png" /&gt;&lt;/a&gt;&lt;/div&gt;So back to DDMS.&amp;nbsp;This tool can also create heap dumps which are particulary useful when tracing down memory leaks. To dump the heap, click on the icon &lt;i&gt;Dump HPROF file&lt;/i&gt;. There are many tools for analyzing heap dumps but the one I'm most familiar is the Eclipse Memory Analyzer (MAT). Download it from&amp;nbsp;&lt;a href="http://www.eclipse.org/mat/" target="_blank"&gt;http://www.eclipse.org/mat/&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
MAT can't handle the DDMS heap dumps right away, but there is a converter tool in the Android SDK.&lt;br /&gt;
So simply run this command.&lt;br /&gt;
&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;C:\Temp\hprof&amp;gt;hprof-conv rawdump.hprof converteddump.hprof&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Then you can open the converted heap dump in MAT. An important concept here is &lt;i&gt;retained size&lt;/i&gt;. The retained size of an object found in the heap is how much memory could be freed if this object could be garbage collected. That includes the object itself, but also child objects which no other objects outside of the retained set has references to.&lt;br /&gt;
&lt;br /&gt;
MAT gives you an overview of where your memory is allocated and has some good tooling on finding suspicious allocations that could be memory leaks.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-LXQ9fRWho9E/TrUHsa6JtgI/AAAAAAAAANo/80BXbLj3hiY/s1600/mat.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="552" src="http://2.bp.blogspot.com/-LXQ9fRWho9E/TrUHsa6JtgI/AAAAAAAAANo/80BXbLj3hiY/s640/mat.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;a href="http://1.bp.blogspot.com/-X2UZcTVmn-0/TrT-kVkF-SI/AAAAAAAAANU/kbphriOaX4U/s1600/leak.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="117" src="http://1.bp.blogspot.com/-X2UZcTVmn-0/TrT-kVkF-SI/AAAAAAAAANU/kbphriOaX4U/s400/leak.png" width="400" /&gt;&lt;/a&gt;So to find my memory leak, I used the dominator tree tab which sorts the allocated objects by retained heap&lt;br /&gt;
and I soon discovered that the GLRendered object held far too many references to a large 512x512 texture.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-ZlY4z90xhVA/TrT-k83PZtI/AAAAAAAAANc/yWUVN-vwu-I/s1600/whereisit.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="125" src="http://2.bp.blogspot.com/-ZlY4z90xhVA/TrT-k83PZtI/AAAAAAAAANc/yWUVN-vwu-I/s400/whereisit.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;The tool becomes even more valuable when the leaking objects are small but many. The dominator tree tell you right away that you have a single object holding a much larger retained heap than you would expect it to.&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;br /&gt;
If you want to learn more, check out this speech by Patrick Dubroy on Android memory management from Google IO 2011 where he explains the Android memory model in more detail.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://1.gvt0.com/vi/_CruQY55HOk/0.jpg" height="266" width="320"&gt;&lt;param name="movie" value="http://www.youtube.com/v/_CruQY55HOk&amp;fs=1&amp;source=uds" /&gt;&lt;param name="bgcolor" value="#FFFFFF" /&gt;&lt;embed width="320" height="266"  src="http://www.youtube.com/v/_CruQY55HOk&amp;fs=1&amp;source=uds" type="application/x-shockwave-flash"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1611368930203661681-6790723154711136577?l=macgyverdev.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/eMn2cW3G0Nyd7znXEgct_6uBzyM/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/eMn2cW3G0Nyd7znXEgct_6uBzyM/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/eMn2cW3G0Nyd7znXEgct_6uBzyM/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/eMn2cW3G0Nyd7znXEgct_6uBzyM/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MacgyverDevelopment/~4/PQLU_8U-z3o" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://macgyverdev.blogspot.com/feeds/6790723154711136577/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://macgyverdev.blogspot.com/2011/11/android-track-down-memory-leaks.html#comment-form" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1611368930203661681/posts/default/6790723154711136577?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1611368930203661681/posts/default/6790723154711136577?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/MacgyverDevelopment/~3/PQLU_8U-z3o/android-track-down-memory-leaks.html" title="Android - track down memory leaks" /><author><name>Johan Norén</name><uri>http://www.blogger.com/profile/14636265442070759165</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://3.bp.blogspot.com/-rDRt8dDpk8g/Th9Qq6DmMPI/AAAAAAAAADk/6ha9_J9-2iU/s220/IMG_0461.JPG" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-B-Z2rmN0XHk/TrT-gg26nNI/AAAAAAAAAM4/3NiujdDGjrc/s72-c/changevmheap.png" height="72" width="72" /><thr:total>3</thr:total><feedburner:origLink>http://macgyverdev.blogspot.com/2011/11/android-track-down-memory-leaks.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEQEQH4zfSp7ImA9WhdaEEg.&quot;"><id>tag:blogger.com,1999:blog-1611368930203661681.post-182999992055533607</id><published>2011-10-19T22:31:00.000+02:00</published><updated>2011-10-19T22:31:41.085+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-10-19T22:31:41.085+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="android" /><category scheme="http://www.blogger.com/atom/ns#" term="adb" /><title>adb - getting inside your Android simulator</title><content type="html">I've been working with persistent state between launches of my Android application and wanted to easily inspect my application data between launches. I discovered the power of the&amp;nbsp;Android Debug Bridge or&amp;nbsp;&lt;i&gt;adb&lt;/i&gt; tool. In short a dev tool that lets you hook up against a running Android simulator.&lt;br /&gt;
&lt;br /&gt;
You'll find it in your sdk folder under platform-tools. On my machine:&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;C:\Program Files (x86)\Android\android-sdk\platform-tools\adb.exe&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
A few handy commands:&lt;br /&gt;
&lt;br /&gt;
- List currently running devices:&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;gt;adb devices&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;List of devices attached&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;emulator-5554 &amp;nbsp; device&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
- Launch a terminal shell against a running device&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;gt;adb -s emulator-5554 shell&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Then you can do your ordinary linux stuff like &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;cd, ls, cat&lt;/span&gt; etcetera to get to know your Android device.&lt;br /&gt;
My happiest discovery was that the preference file you fetch and save via the SharedPreference API:&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;SharedPreferences settings = getSharedPreferences("OthelloLegendsPrefs", 0);&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
are located under the path&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;/data/data/se.noren.android.othello/shared_prefs/OthelloLegendsPrefs.xml&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
and is a simple XML file which you can inspect and edit.&lt;br /&gt;
&lt;br /&gt;
So, to pull a file from the simulator to your development computer&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;gt;adb -s emulator-5554 pull &amp;lt;remotefile&amp;gt; &amp;lt;localfile&amp;gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Similarly, to upload a file to the device&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;gt;adb -s emulator-5554 push &amp;lt;localfile&amp;gt; &amp;lt;remotefile&amp;gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
For more info on adb,&amp;nbsp;&lt;a href="http://developer.android.com/guide/developing/tools/adb.html"&gt;http://developer.android.com/guide/developing/tools/adb.html&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1611368930203661681-182999992055533607?l=macgyverdev.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/BlGmdhEJPJk69llvHlTMfiS6MSk/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/BlGmdhEJPJk69llvHlTMfiS6MSk/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/BlGmdhEJPJk69llvHlTMfiS6MSk/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/BlGmdhEJPJk69llvHlTMfiS6MSk/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MacgyverDevelopment/~4/UIXKitBXNko" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://macgyverdev.blogspot.com/feeds/182999992055533607/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://macgyverdev.blogspot.com/2011/10/adb-getting-inside-your-android.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1611368930203661681/posts/default/182999992055533607?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1611368930203661681/posts/default/182999992055533607?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/MacgyverDevelopment/~3/UIXKitBXNko/adb-getting-inside-your-android.html" title="adb - getting inside your Android simulator" /><author><name>Johan Norén</name><uri>http://www.blogger.com/profile/14636265442070759165</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://3.bp.blogspot.com/-rDRt8dDpk8g/Th9Qq6DmMPI/AAAAAAAAADk/6ha9_J9-2iU/s220/IMG_0461.JPG" /></author><thr:total>0</thr:total><feedburner:origLink>http://macgyverdev.blogspot.com/2011/10/adb-getting-inside-your-android.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A04GQXk6fyp7ImA9WhdWEUg.&quot;"><id>tag:blogger.com,1999:blog-1611368930203661681.post-3421415795246236480</id><published>2011-09-04T20:12:00.000+02:00</published><updated>2011-09-04T20:12:00.717+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-04T20:12:00.717+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="java" /><category scheme="http://www.blogger.com/atom/ns#" term="google app engine" /><title>How to get character encoding correct on Google App Engine</title><content type="html">Character encodings can be a primary trigger for&amp;nbsp;stomach ulcers. My Swedish web applications deployed on Google App Engine have had great difficulties to behave when presented with user input containing for example Swedish characters Å, Ä and Ö.&lt;br /&gt;
&lt;br /&gt;
So here's a recipe for treating such characters with respect.&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Don't use&amp;nbsp;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;ISO-8859-1&lt;/span&gt; as character encoding. Just &lt;b&gt;don't&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;Instead specify you JSP's to use &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;UTF-8&lt;/span&gt; with something like&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;lt;%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;However, this might not be enough unfortunately. Current browsers might not set a character encoding even if specified in the HTML page or form.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;So if you aren't already using Spring, add spring-web to your application and add one of the Spring filters &lt;b&gt;first &lt;/b&gt;in your filter chain.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;	&lt;/span&gt;&amp;lt;filter&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;	&lt;/span&gt; &amp;nbsp; &amp;nbsp;&amp;lt;filter-name&amp;gt;SetCharacterEncoding&amp;lt;/filter-name&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;	&lt;/span&gt; &amp;nbsp; &amp;nbsp;&amp;lt;filter-class&amp;gt;org.springframework.web.filter.CharacterEncodingFilter&amp;lt;/filter-class&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;	&lt;/span&gt; &amp;nbsp; &amp;nbsp;&amp;lt;init-param&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;	&lt;/span&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;lt;param-name&amp;gt;encoding&amp;lt;/param-name&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;	&lt;/span&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;lt;param-value&amp;gt;UTF-8&amp;lt;/param-value&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;	&lt;/span&gt; &amp;nbsp; &amp;nbsp;&amp;lt;/init-param&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;	&lt;/span&gt; &amp;nbsp; &amp;nbsp;&amp;lt;init-param&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;	&lt;/span&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;param-name&amp;gt;forceEncoding&amp;lt;/param-name&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;	&lt;/span&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;param-value&amp;gt;true&amp;lt;/param-value&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;	&lt;/span&gt; &amp;nbsp; &amp;nbsp;&amp;lt;/init-param&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;	&lt;/span&gt;&amp;lt;/filter&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;	&lt;/span&gt; &amp;lt;filter-mapping&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;	&lt;/span&gt; &amp;nbsp; &amp;nbsp;&amp;lt;filter-name&amp;gt;SetCharacterEncoding&amp;lt;/filter-name&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;	&lt;/span&gt; &amp;nbsp; &amp;nbsp;&amp;lt;url-pattern&amp;gt;/*&amp;lt;/url-pattern&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;	&lt;/span&gt;&amp;lt;/filter-mapping&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;That's all folks.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1611368930203661681-3421415795246236480?l=macgyverdev.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/LVfgi9abfvYFc5DvrdLTj25CCGQ/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/LVfgi9abfvYFc5DvrdLTj25CCGQ/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/LVfgi9abfvYFc5DvrdLTj25CCGQ/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/LVfgi9abfvYFc5DvrdLTj25CCGQ/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MacgyverDevelopment/~4/MlwhBwRAcuw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://macgyverdev.blogspot.com/feeds/3421415795246236480/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://macgyverdev.blogspot.com/2011/09/how-to-get-character-encoding-correct.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1611368930203661681/posts/default/3421415795246236480?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1611368930203661681/posts/default/3421415795246236480?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/MacgyverDevelopment/~3/MlwhBwRAcuw/how-to-get-character-encoding-correct.html" title="How to get character encoding correct on Google App Engine" /><author><name>Johan Norén</name><uri>http://www.blogger.com/profile/14636265442070759165</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://3.bp.blogspot.com/-rDRt8dDpk8g/Th9Qq6DmMPI/AAAAAAAAADk/6ha9_J9-2iU/s220/IMG_0461.JPG" /></author><thr:total>0</thr:total><feedburner:origLink>http://macgyverdev.blogspot.com/2011/09/how-to-get-character-encoding-correct.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ck4DQnY7eyp7ImA9WhdREU8.&quot;"><id>tag:blogger.com,1999:blog-1611368930203661681.post-2036442348805587022</id><published>2011-07-31T16:02:00.000+02:00</published><updated>2011-07-31T16:02:53.803+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-07-31T16:02:53.803+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="dns" /><category scheme="http://www.blogger.com/atom/ns#" term="google apps" /><category scheme="http://www.blogger.com/atom/ns#" term="google app engine" /><title>Mapping DNS domain to Google App Engine application</title><content type="html">I have been working on a small application for promoting and selling books on popular science. I made a prototype and bought a domain name so I can setup all trackers and Amazon Affiliate accounts. I deployed the prototype as a Google App Engine application. Using GAE you can have a site up in 3 minutes.&lt;br /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;When you deploy your GAE app, it will automatically get DNS mapped as&amp;nbsp;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;yourappid.appspot.com&lt;/span&gt;. So in my case the app resides as&amp;nbsp;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;http://popularsciencemedia.appspot.com&lt;/span&gt;&amp;nbsp;in the Google cloud. From the Swedish DNS supplier Loopia I had bought the domain&amp;nbsp;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;popularsciencemedia.com&lt;/span&gt;&amp;nbsp;and now simply wanted to DNS remap&amp;nbsp;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;popularsciencemedia.appspot.com&amp;nbsp;&lt;/span&gt;to my own&amp;nbsp;&lt;a href="http://www.popularsciencemedia.com/"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;http://www.&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;popularsciencemedia.com&lt;/span&gt;&lt;/a&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;&lt;b&gt;This is not as easy as you might think!&lt;/b&gt; The obvious solution would be to create a DNS CNAME entry with an alias to reroute traffic to&amp;nbsp;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;www.&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;popularsciencemedia.com&lt;/span&gt;&amp;nbsp;to&amp;nbsp;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;http://popularsciencemedia.appspot.com but&amp;nbsp;&lt;/span&gt;but Google won't allow that. So I thought I'd write a few lines on how you do it to avoid you from the same misery. The purpose here is to map &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;i&gt;www&lt;/i&gt;.popularsciencemedia.com&lt;/span&gt; since Google App Engine only can be mapped to sub domains. &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;www &lt;/span&gt;is good enough for me.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;You need to sign up for Google Apps for you application. Go to&amp;nbsp;&lt;a href="https://www.google.com/a/cpanel/domain/new"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;https://www.google.com/a/cpanel/domain/new&lt;/span&gt;&lt;/a&gt;&amp;nbsp;and enter your registrered domain name and fill in the forms. You will in this process create a system user for this Google App. In my case I created the user admin@popularsciencemedia.com. Google Apps let's you create emails, calenders and much more for your app.&lt;/li&gt;
&lt;li&gt;This process will need you to verify that you own the domain. There are several ways to do this, I thought the easiest way was to add a Google verification code in the DNS registry as a TXT entry. The DNS record now looks like this. If you can't create TXT records with your DNS provider Google has other mechanisms for verifying that you own the domain.&lt;/li&gt;
&lt;/ul&gt;&lt;div&gt;&lt;pre style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; white-space: pre-wrap; word-wrap: break-word;"&gt;&lt;span class="Apple-style-span" style="font-size: x-small;"&gt;$ORIGIN popularsciencemedia.com.
$TTL 300
@ SOA ns1.loopia.se. registry.loopia.se. (
    1312117911
    3H ; Refresh after three hours
    1H ; Retry after one hour
    1W ; Expire after one week
    1D ) ; Minimum one day TTL

@    IN 3600 NS ns1.loopia.se.
@    IN 3600 NS ns2.loopia.se.

@    IN 3600 A 194.9.94.85
@    IN 3600 A 194.9.94.86
@    IN 3600 TXT google-site-verification=Uy4magKHIasdeEOasdgs6b7qYt8tR8

*    IN 3600 CNAME ghs.google.com.

www    IN 3600 CNAME ghs.google.com.&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;Notice the additions. The TXT entry maps against the value you get from the Google App sign up. This makes it possible for Google Apps to verify that you own the domain you claim to own. Then, add also a CNAME mapping to&amp;nbsp;&lt;span class="Apple-style-span" style="font-family: monospace; white-space: pre-wrap;"&gt;ghs.google.com&lt;/span&gt;&amp;nbsp;for the subdomain &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;www&lt;/span&gt;&amp;nbsp;since this is the sub domain we want to use.&lt;/li&gt;
&lt;li&gt;Now to to your Google App account, go to something like&amp;nbsp;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;https://www.google.com/a/cpanel/popularsciencemedia.com&lt;/span&gt;. Under Sites-&amp;gt; Services -&amp;gt; YourAppId (App Engine) there should be a possibility to add a new address under the domain you have registrered.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;I add&amp;nbsp;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;www&amp;nbsp;&lt;/span&gt;so my app is mapped as&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt; www.popularsciencemedia.com&lt;/span&gt;.&lt;/li&gt;
&lt;li&gt;After a waiting a very short while all DNS changes seems to be working and I can surf to&amp;nbsp;http://www.popularsciencemedia.com/&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1611368930203661681-2036442348805587022?l=macgyverdev.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/gIrpIuu9I8GmPEH4no_g-czWSWw/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/gIrpIuu9I8GmPEH4no_g-czWSWw/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/gIrpIuu9I8GmPEH4no_g-czWSWw/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/gIrpIuu9I8GmPEH4no_g-czWSWw/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MacgyverDevelopment/~4/O16SLwkyjTw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://macgyverdev.blogspot.com/feeds/2036442348805587022/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://macgyverdev.blogspot.com/2011/07/mapping-dns-domain-to-google-app-engine.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1611368930203661681/posts/default/2036442348805587022?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1611368930203661681/posts/default/2036442348805587022?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/MacgyverDevelopment/~3/O16SLwkyjTw/mapping-dns-domain-to-google-app-engine.html" title="Mapping DNS domain to Google App Engine application" /><author><name>Johan Norén</name><uri>http://www.blogger.com/profile/14636265442070759165</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://3.bp.blogspot.com/-rDRt8dDpk8g/Th9Qq6DmMPI/AAAAAAAAADk/6ha9_J9-2iU/s220/IMG_0461.JPG" /></author><thr:total>1</thr:total><feedburner:origLink>http://macgyverdev.blogspot.com/2011/07/mapping-dns-domain-to-google-app-engine.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkcERng5eyp7ImA9WhdTF0s.&quot;"><id>tag:blogger.com,1999:blog-1611368930203661681.post-9013498542350970281</id><published>2011-07-15T22:00:00.000+02:00</published><updated>2011-07-15T22:00:07.623+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-07-15T22:00:07.623+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="profiling" /><category scheme="http://www.blogger.com/atom/ns#" term="android" /><title>Profiling an Android application tutorial</title><content type="html">I'm spending some spare time on an Android Reversi game which could need some performance tuning. After figuring out how the tooling works for Android profiling it works like a charm.&lt;br /&gt;
&lt;br /&gt;
There are two ways to profile an application, using the debugging server&amp;nbsp;&lt;a href="http://developer.android.com/guide/developing/debugging/ddms.html#profiling"&gt;DDMS&lt;/a&gt; or manually decide which parts of the code base are interesting for inspection. DDMS could be useful if you are inspecting code you might not be able to recompile. DDMS can also be used to inspect memory usage and more.&lt;br /&gt;
&lt;br /&gt;
The easiest approach however is to use the debug interface provided by the Android API in your sources to specify when to start generating profiling information and when to end.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;public int[] alphabeta(Board b, int maxDepth, long maxTime) {
      
      Debug.startMethodTracing("othello_profiling");

      // Here goes code to profile
  
      Debug.stopMethodTracing();
      return result;
   }
&lt;/pre&gt;&lt;br /&gt;
Run your program and you'll see in the VM logs when the profiler kicks in. (As usual the performance of your app in the emulator will sink to the bottom when profiling is enabled)&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-2wdnzd-9LtM/TiCXbgeP4CI/AAAAAAAAAEA/IASgljAxYwQ/s1600/logs.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="164" src="http://2.bp.blogspot.com/-2wdnzd-9LtM/TiCXbgeP4CI/AAAAAAAAAEA/IASgljAxYwQ/s640/logs.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
Now you got your profiling info written to the SD card of your Android emulator device.&amp;nbsp;If you run into permission issues when writing to the SD card, add something like this to your Android Manifest.&lt;br /&gt;
&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;lt;uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /&amp;gt; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
To fetch the file to your development computer use the adb tool that comes with the Android SDK. On my Windows machine I did something like this.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;C:\Program Files (x86)\Android\android-sdk\platform-tools&amp;gt;adb pull /sdcard/othel&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;lo_profiling.trace c:\temp\othello_profiling.trace&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;126 KB/s (2266911 bytes in 17.491s)&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
The tool traceview can interprete the file.&lt;br /&gt;
&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;C:\Program Files (x86)\Android\android-sdk\tools&amp;gt;traceview.bat c:\Temp\othello_p&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;rofiling.trace&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Voila! You get a profiling view similar to what you get from common profilers like JProfiler, hprof etcetera.&amp;nbsp;Here you can see each methods execution time and which parents and children methods it has connection to and much more.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-uErfxI6_N8Y/TiCZMDgkAaI/AAAAAAAAAEE/EqCM-nEhZP4/s1600/trace.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="272" src="http://4.bp.blogspot.com/-uErfxI6_N8Y/TiCZMDgkAaI/AAAAAAAAAEE/EqCM-nEhZP4/s640/trace.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Theres more you can do with the trace file. &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Traceview &lt;/span&gt;can also show you each threads exectution and calls in chronological order. You can simple zoom in on the interesting parts.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-_-_2ZiTVKxw/TiCbfJpmD7I/AAAAAAAAAEI/XnTPY9QW43I/s1600/threadexec.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="108" src="http://2.bp.blogspot.com/-_-_2ZiTVKxw/TiCbfJpmD7I/AAAAAAAAAEI/XnTPY9QW43I/s640/threadexec.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
You may also want to try the tool&amp;nbsp;dmtracdedump to create graphs over your call stack. See the&lt;a href="http://developer.android.com/guide/developing/debugging/debugging-tracing.html"&gt; Android documentation&lt;/a&gt; for more information.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1611368930203661681-9013498542350970281?l=macgyverdev.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/LjpxqcVcQpyRTxVj3yRRI0UZXJk/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/LjpxqcVcQpyRTxVj3yRRI0UZXJk/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/LjpxqcVcQpyRTxVj3yRRI0UZXJk/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/LjpxqcVcQpyRTxVj3yRRI0UZXJk/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MacgyverDevelopment/~4/PFpWG0c_7q8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://macgyverdev.blogspot.com/feeds/9013498542350970281/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://macgyverdev.blogspot.com/2011/07/profiling-android-application-tutorial.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1611368930203661681/posts/default/9013498542350970281?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1611368930203661681/posts/default/9013498542350970281?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/MacgyverDevelopment/~3/PFpWG0c_7q8/profiling-android-application-tutorial.html" title="Profiling an Android application tutorial" /><author><name>Johan Norén</name><uri>http://www.blogger.com/profile/14636265442070759165</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://3.bp.blogspot.com/-rDRt8dDpk8g/Th9Qq6DmMPI/AAAAAAAAADk/6ha9_J9-2iU/s220/IMG_0461.JPG" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-2wdnzd-9LtM/TiCXbgeP4CI/AAAAAAAAAEA/IASgljAxYwQ/s72-c/logs.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://macgyverdev.blogspot.com/2011/07/profiling-android-application-tutorial.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEMNQ3c5cSp7ImA9WhZQF0s.&quot;"><id>tag:blogger.com,1999:blog-1611368930203661681.post-1983768376921099022</id><published>2011-04-25T20:45:00.003+02:00</published><updated>2011-04-25T22:14:52.929+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-04-25T22:14:52.929+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="security" /><category scheme="http://www.blogger.com/atom/ns#" term="dropbox" /><title>Dropbox - is it safe to put you files in the cloud?</title><content type="html">I really like the simplicity of sharing files with&amp;nbsp;&lt;a href="http://www.dropbox.com/"&gt;Dropbox&lt;/a&gt;. I haven't gone full circle yet, but I have been moving a substantial part of my personal stuff there.&lt;br /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;I hadn't thought much about the security of it, but when listening to Steve Gibson and Leo Laporte&amp;nbsp;on the Security Now podcast (&lt;a href="http://www.grc.com/securitynow.htm"&gt;http://www.grc.com/securitynow.htm&lt;/a&gt;, or search for it on iTunes) examining Dropbox, I got an eye-opener that you can't assume an awesome service to by definition have awesome security.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;First of all, Dropbox have claimed that not even their employees are able to see your data. Great! But in a recent change in the terms of agreement it says that the authorities due to US regulations can ask Dropbox to decrypt your data in certain crime investigations. Allright, I'm a good guy so that's not a problem for me. &lt;i&gt;But that means&lt;/i&gt; that Dropbox must keep my encryption key in their vaults instead of me doing a client side encryption/decryption of my data. Interesting, that means that a bad apple Dropbox employee also have the possibility to look at my data without my knowledge. Not to mention what would happen if Dropbox would lose the table of private keys in some master planned hacking or insider heist.&lt;/div&gt;&lt;div&gt;So, the lesson should be. If you have some valuable or sensitive data, you should probably encrypt it before even dropping it into Dropbox.&lt;/div&gt;&lt;div&gt;Well, that applies to companies or people with more valuables than family photos like me.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;Issue two might be more concerning, Derek Newton,&amp;nbsp;&lt;a href="http://dereknewton.com/2011/04/dropbox-authentication-static-host-ids/"&gt;http://dereknewton.com/2011/04/dropbox-authentication-static-host-ids/&lt;/a&gt;,&amp;nbsp;has looked into how your Dropbox client authenticates against the cloud service. It seems like all you need is a config file which is set up at install time. That file contains your hostid which is your authentication token against Dropbox. The bad thing is that if someone by social engineering, a trojan or other malware gets a copy of this file, they can access your Dropbox account from any machine. Changing your password is not enough since this is an access token. You must remove your own machine as a valid host from Dropbox to stop the bad guy from using your account. Most probably you won't even know someone is eavesdropping on you.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;These guys also seems to trust the cloud a bit too naively&lt;br /&gt;
&lt;a href="https://forums.aws.amazon.com/thread.jspa?threadID=65649&amp;amp;tstart=0"&gt;https://forums.aws.amazon.com/thread.jspa?threadID=65649&amp;amp;tstart=0&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1611368930203661681-1983768376921099022?l=macgyverdev.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/EqlP-oCIfDcxrioaeweW3jjNFy0/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/EqlP-oCIfDcxrioaeweW3jjNFy0/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/EqlP-oCIfDcxrioaeweW3jjNFy0/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/EqlP-oCIfDcxrioaeweW3jjNFy0/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MacgyverDevelopment/~4/cWAAr6Gim2w" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://macgyverdev.blogspot.com/feeds/1983768376921099022/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://macgyverdev.blogspot.com/2011/04/dropbox-is-it-safe-to-put-you-files-in.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1611368930203661681/posts/default/1983768376921099022?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1611368930203661681/posts/default/1983768376921099022?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/MacgyverDevelopment/~3/cWAAr6Gim2w/dropbox-is-it-safe-to-put-you-files-in.html" title="Dropbox - is it safe to put you files in the cloud?" /><author><name>Johan Norén</name><uri>http://www.blogger.com/profile/14636265442070759165</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://3.bp.blogspot.com/-rDRt8dDpk8g/Th9Qq6DmMPI/AAAAAAAAADk/6ha9_J9-2iU/s220/IMG_0461.JPG" /></author><thr:total>0</thr:total><feedburner:origLink>http://macgyverdev.blogspot.com/2011/04/dropbox-is-it-safe-to-put-you-files-in.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUMGRXY7fCp7ImA9WhZQFEo.&quot;"><id>tag:blogger.com,1999:blog-1611368930203661681.post-358665957780565366</id><published>2011-04-17T22:40:00.002+02:00</published><updated>2011-04-22T15:03:44.804+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-04-22T15:03:44.804+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="twitter" /><category scheme="http://www.blogger.com/atom/ns#" term="twitter4j" /><category scheme="http://www.blogger.com/atom/ns#" term="oauth" /><title>Twitter integration using OAuth</title><content type="html">&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;So my Rankington application hosted on Google App Engine wants to read tweets containing mentions of keyword 'rankington' and also update the status of the Twitter system user Rankington on certain occations. There are loads of&amp;nbsp;&lt;a href="http://dev.twitter.com/pages/libraries"&gt;Twitter APIs&lt;/a&gt;&amp;nbsp;out there for Java. Here's a short guide on how get going with Twitter4j and some handy knowledge of OAuth.&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Download Twitter4j from&amp;nbsp;&lt;a href="http://twitter4j.org/en/index.html"&gt;http://twitter4j.org/en/index.html&lt;/a&gt;. If you wish to do only reads against Twitter you don't need to authenticate in any way, but if you wish to post status updates or similar you must provide credentials for the Twitter user you are using. In the past you could authenticate against the Twitter REST APIs using user/password but this has been shut down by Twitter since August 2010. So don't try that via the Twitter4J API which has not deprecated that code yet.&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;OAuth&lt;/span&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Twitter is now using&amp;nbsp;&lt;a href="http://en.wikipedia.org/wiki/OAuth"&gt;OAuth&lt;/a&gt;&amp;nbsp;as access mechanism. If you are familiar with OpenID you could compare OAuth to OpenID in the sense that OpenID is a decentralised identification infrastructure, whereas OAuth solves authorisation in a decentralised way. OAuth is a RFC under standardization of the IETF. For example the Google Docs API and other Google APIs have recently added OAuth as access restriction mechanims for their REST APIs.&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;An example of using OAuth would be that you have some resources on a site A, say some private photos. You wish to let another site B access those photos in order to incorporate them into a photo stream or whatever, but you don't want to hand your identification credentials for site A to site B for obvious security reasons. Instead you wish to delegate authorisation to site B to access resources on site A.&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;What will happen is that site A and site B shares a common OAuth secret. Without going into the handshaking details you will surf against site B and an authorisation request will redirect you to site A where you will be asked to grant site B permission to the appropriate resources. Once redirecting back to site B, an OAuth token will be handed to site B which can be used from now on to access precisely the set of resources granted from site A using the access token. Check out the RFC if interested at&amp;nbsp;&lt;a href="http://tools.ietf.org/html/rfc5849"&gt;http://tools.ietf.org/html/rfc5849&lt;/a&gt;.&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Twitter4J and OAuth&lt;/span&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;So, in our case, Twitter is site A and Rankington is site B (service provider and client). To create the shared secret which will be generated by site A (= Twitter), go to&amp;nbsp;&lt;span class="Apple-style-span" style="font-size: 14px;"&gt;&lt;a href="http://twitter.com/oauth_clients/new" style="text-decoration: underline;"&gt;http://twitter.com/oauth_clients/new&lt;/a&gt;&amp;nbsp;and create a pair of secret keys. A consumer key and a consumer secret.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;span class="Apple-style-span" style="font-size: 14px;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-size: 14px;"&gt;A very basic test of this could be:&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;public class TwitterBridge {
    
    private static String key = "abcaasdkj1231231lkj123";
    private static String secret = "asdkj7987asdjl12312lkj4323423423";
    
    public static void main(String[] argv) throws TwitterException {

        Twitter twitter = new TwitterFactory().getInstance();
        twitter.setOAuthConsumer(key, secret);
        RequestToken requestToken = twitter.getOAuthRequestToken();
        System.out.println(requestToken.getAuthorizationURL());
        
        // Breakpoint here and update value of pin from what
        // what you get in browser when surfing against
        // authorisation URL above.
        String pin = "7117195";
        
        AccessToken accessToken = twitter.getOAuthAccessToken(requestToken, pin);
        
        System.out.println("Token: " + accessToken.getToken());
        System.out.println("Token secret: " + accessToken.getTokenSecret());
        
        Query query = new Query();
        query.setQuery("rankington");
        
        QueryResult queryResult = twitter.search(query);
        
        List&amp;lt;Tweet&amp;gt; tweets = queryResult.getTweets();
        for (Tweet t : tweets) {
            System.out.println("From: " + t.getFromUser());
            System.out.println("Time: " + t.getCreatedAt());
            System.out.println("Text: " + t.getText());
        }
        
        twitter.updateStatus("New tweet!");
    }
}
&lt;/pre&gt;So put in your consumer key and secret in the code above as key and secret. Then debug these lines of code.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;System.out.println(requestToken.getAuthorizationURL());&lt;/pre&gt;&lt;br /&gt;
will print something like&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;http://api.twitter.com/oauth/authorize?oauth_token=51IMwiqF8MfEdcNDZxzUgV3guqCpQK6VbFZasdl&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-rZqHHDDUUI8/TatD31bYsmI/AAAAAAAAABs/xq56SxvBx3A/s1600/twitter1.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="240" src="http://3.bp.blogspot.com/-rZqHHDDUUI8/TatD31bYsmI/AAAAAAAAABs/xq56SxvBx3A/s320/twitter1.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;a href="http://2.bp.blogspot.com/-_cjqMKdGPo8/TatD4FsML5I/AAAAAAAAABw/V5iRWan-63Y/s1600/twitter2.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="128" src="http://2.bp.blogspot.com/-_cjqMKdGPo8/TatD4FsML5I/AAAAAAAAABw/V5iRWan-63Y/s320/twitter2.png" width="320" /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Open that link in a browser and you will be prompted&lt;br /&gt;
to identify yourself against Twitter, unless you are logged in automatically. In the same dialog you allow the client application "rankington" to access Twitter in your stead.&lt;br /&gt;
&lt;br /&gt;
A bit confusingly, we login as user "rankington" on Twitter and allow application "rankington" access. This is just conincidence that the names are the same in this example.&lt;br /&gt;
&lt;br /&gt;
When granted access, a verification code will be shown. Copy this code and either rerun the Java program or insert it into via the debugger as the variable "pin".&lt;br /&gt;
&lt;br /&gt;
Continue to run the program and the Twitter4J API will verify the pin code against the Twitter REST API and receive an access token. We print this and it will look something like&lt;br /&gt;
&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Token: 45335176-3NEtmOsdfsacZROM9ow3sdfsdfHm5dfu0ShNGTdN2CKw&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Token secret: nOXQish8asfasiq4tZINEOJuDasdYDQC4dBJiAM3k&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
All good. These are the important values which we can use to create a new AccessToken in the future.&lt;br /&gt;
The end of the program makes a query for tweets concerning status updates containing "rankington". That would print: &lt;br /&gt;
&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;From: rankington&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Time: Sun Apr 17 01:10:48 CEST 2011&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Text: Rankington alpha is out! Follow progress at http://macgyverdev.blogspot.com&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;#rankington&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-8fGgSJRbZCE/TatHrJHZXqI/AAAAAAAAAB0/Df3_lqwOC60/s1600/twitter3.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="179" src="http://4.bp.blogspot.com/-8fGgSJRbZCE/TatHrJHZXqI/AAAAAAAAAB0/Df3_lqwOC60/s320/twitter3.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;And in the end, the program makes a status update in the name of the Twitter user "rankington".&lt;br /&gt;
&lt;br /&gt;
So, now we got all ingredients, all keys and secrets for OAuth authority delegation. The piece of code we can use in our real application is the following.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;private static String key         = "3649LZ3sasdasdpXWFHkHxaWQQ";
   private static String secret      = "aFOb86GmafgKtTasdasq3CpcwQw7bA";
   private static String token       = "28326asdasdasdD1ZxVDDL5Mqe7H";
   private static String tokensecret = "7Klyasdasdasdwpc8Xbtm0IsiRA";

   public static void main(String[] argv) throws TwitterException {
        
        AccessToken accessToken = new AccessToken(token, tokensecret);
        
        ConfigurationBuilder confBuilder = new ConfigurationBuilder(); 
        confBuilder.setOAuthAccessToken(accessToken.getToken()) 
                   .setOAuthAccessTokenSecret(accessToken.getTokenSecret()) 
                   .setOAuthConsumerKey(key)
                   .setOAuthConsumerSecret(secret); 

        Twitter twitter = new TwitterFactory(confBuilder.build()).getInstance(); 
        
        Query query = new Query();
        query.setQuery("rankington");
        
        QueryResult queryResult = twitter.search(query);
        
        List&lt;tweet&gt; tweets = queryResult.getTweets();
        for (Tweet t : tweets) {
            System.out.println("From: " + t.getFromUser());
            System.out.println("Time: " + t.getCreatedAt());
            System.out.println("Text: " + t.getText());
        }
        
        twitter.updateStatus("New tweet again!");
    }
&lt;/tweet&gt;&lt;/pre&gt;&lt;br /&gt;
You see that we create the AccessTokens needed to authenticate against Twitter using the keys and secrets previously negotiated as a one time routine.&amp;nbsp;The application can now tweet in eternity unless the user revokes the authorisation for the client.&lt;br /&gt;
&lt;br /&gt;
With my basic understanding of OAuth, it feels like a great standard for interconnecting all service providers we got in the cloud.&amp;nbsp;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1611368930203661681-358665957780565366?l=macgyverdev.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/IKtlAAZvFDLDEJ_o72QZOcLUD5E/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/IKtlAAZvFDLDEJ_o72QZOcLUD5E/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/IKtlAAZvFDLDEJ_o72QZOcLUD5E/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/IKtlAAZvFDLDEJ_o72QZOcLUD5E/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MacgyverDevelopment/~4/pAU2tbo-ckc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://macgyverdev.blogspot.com/feeds/358665957780565366/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://macgyverdev.blogspot.com/2011/04/twitter-integration-with-google-app.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1611368930203661681/posts/default/358665957780565366?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1611368930203661681/posts/default/358665957780565366?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/MacgyverDevelopment/~3/pAU2tbo-ckc/twitter-integration-with-google-app.html" title="Twitter integration using OAuth" /><author><name>Johan Norén</name><uri>http://www.blogger.com/profile/14636265442070759165</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://3.bp.blogspot.com/-rDRt8dDpk8g/Th9Qq6DmMPI/AAAAAAAAADk/6ha9_J9-2iU/s220/IMG_0461.JPG" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-rZqHHDDUUI8/TatD31bYsmI/AAAAAAAAABs/xq56SxvBx3A/s72-c/twitter1.png" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://macgyverdev.blogspot.com/2011/04/twitter-integration-with-google-app.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUQNQ34yeCp7ImA9WhZQFEo.&quot;"><id>tag:blogger.com,1999:blog-1611368930203661681.post-6797555440142904307</id><published>2011-04-16T23:13:00.002+02:00</published><updated>2011-04-22T15:03:12.090+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-04-22T15:03:12.090+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="jenkins" /><category scheme="http://www.blogger.com/atom/ns#" term="hudson" /><category scheme="http://www.blogger.com/atom/ns#" term="maven" /><category scheme="http://www.blogger.com/atom/ns#" term="google app engine" /><title>Hudson - Continuous Integration for a Google App Engine application</title><content type="html">&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;The last blog post described how to configure a Maven project for a Google App Engine application.&amp;nbsp;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;To build and deploy the Maven artifacts you will need some command line hacking.&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Hudson CI can be used to escape the command line for all this. Hudson is a continuous integration system for building and testing your projects. It has some cool features as distributed building and much more.&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;To install &lt;a href="http://hudson-ci.org/"&gt;Hudson&lt;/a&gt;, or&amp;nbsp;&lt;a href="http://jenkins-ci.org/"&gt;Jenkins&lt;/a&gt;&amp;nbsp;as the main fork has rebranded it now after Oracle came into clinch with the open source community, simply download the war-archive from either the Jenkins or Hudson web site. My installation is old so I use a Hudson build.You can either deploy it in a web container such as Tomcat or simply use the built in bootstrap. To bootstrap the war archive&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;java -Dhudson.udp=32850 -jar hudson.war --httpPort=9090 --daemon --logfile=/home/johan/hudson/hudson.log&lt;/span&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class="separator" style="clear: both; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-F2akXvNKCKM/Tamfwj_PaII/AAAAAAAAAAo/_WXeV8sZzAk/s1600/hudson.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="187" src="http://2.bp.blogspot.com/-F2akXvNKCKM/Tamfwj_PaII/AAAAAAAAAAo/_WXeV8sZzAk/s400/hudson.png" style="cursor: move;" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Access Hudson via&amp;nbsp;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;http://localhost:8080/&lt;/span&gt;.&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Goto Manage Hudson -&amp;gt; Configure System and enable Maven under the Maven subsection. Goto Manage Hudson -&amp;gt; Manage Plugins and install the plugins you need. In my case I have installed Cobertura Plugin for code coverage, Findbugs for static code analysis, Maven2 Project Plugin, checkstyle for Java code validation and the CVS plugin. You might need to bring in some sub dependencies like Static Analysis Collector Plug-In, static Analysis Utilities.&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Now create a new job, under your job, click Configure and setup appropriate version control config. In my case&amp;nbsp;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;CVSROOT=:pserver:rankington:xxxx@213.xxx.xxx.xxx:/cvsrepo&lt;/span&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;and correct CVS module and branch.&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Under Build, choose correct Maven installation, probably &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;/usr/bin/mvn&lt;/span&gt;. Then setup your Maven build goals. In this case the goals are, enable debug, clean, compile, test, create war archive and generate reports.&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;-X&lt;/span&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;clean&lt;/span&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;package&lt;/span&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;findbugs:findbugs&lt;/span&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;checkstyle:checkstyle&lt;/span&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class="separator" style="clear: both; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-LanAxLjiMwY/TamizFl7NkI/AAAAAAAAAAs/VIoo0reGSos/s1600/mavenbuild.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/-LanAxLjiMwY/TamizFl7NkI/AAAAAAAAAAs/VIoo0reGSos/s1600/mavenbuild.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;This will make Maven checkout your code, compile it, run the Maven plugins for creating xml reports for Findbugs and Checkstyle.&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Now add Post-build Actions to integrate these reports into Hudson. Enable Publish Findbugs analysis results from file&amp;nbsp;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;**/target/findbugsXml.xml&lt;/span&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Add similar report integrations by enabling publishing the following reports&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;target/surefire-reports/*.xml&lt;/span&gt;&amp;nbsp;for JUnit tests,&amp;nbsp;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;**/target/site/cobertura/coverage.xml&lt;/span&gt;&amp;nbsp;for Cobertura code coverage and so forth.&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;a href="http://1.bp.blogspot.com/-2nWukf-eHUc/TamjjIvDTHI/AAAAAAAAAAw/ceEzxN0b2uk/s1600/cobertura.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/-2nWukf-eHUc/TamjjIvDTHI/AAAAAAAAAAw/ceEzxN0b2uk/s1600/cobertura.png" style="cursor: move;" /&gt;&amp;nbsp;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-YZEMpxHnbeQ/TamnEOcqI7I/AAAAAAAAAA0/bD-R3xo2JBc/s1600/coverage1.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="366" src="http://1.bp.blogspot.com/-YZEMpxHnbeQ/TamnEOcqI7I/AAAAAAAAAA0/bD-R3xo2JBc/s400/coverage1.png" style="cursor: move;" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Now build your Hudson job by clicking Build Now and the Maven goals are executed to checkout, build, test and creates reports of the project. Then the post action goals kicks in and updates the dashboards of Hudson to show the results of the build.&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Cobertura allows you to see on a package level the unit test coverage and the possibility to drill down on package and file level. Similar graphs and drilling can be done on Findbugs, Checkstyle and JUnit test reports.&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-bottom: 0.5em; margin-left: auto; margin-right: auto; padding-bottom: 6px; padding-left: 6px; padding-right: 6px; padding-top: 6px; text-align: center;"&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;a href="http://1.bp.blogspot.com/-R02euLATJbs/TamnRW3bmNI/AAAAAAAAAA4/IQ7enNd9biU/s1600/covergae2.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="75" src="http://1.bp.blogspot.com/-R02euLATJbs/TamnRW3bmNI/AAAAAAAAAA4/IQ7enNd9biU/s640/covergae2.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="tr-caption" style="font-size: 13px; padding-top: 4px; text-align: center;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Cobertura reporting on file level&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-bottom: 0.5em; margin-left: 1em; padding-bottom: 6px; padding-left: 6px; padding-right: 6px; padding-top: 6px; text-align: right;"&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;a href="http://3.bp.blogspot.com/-ReA0OQ-7X2Q/TamqLkQQAxI/AAAAAAAAAA8/Mw_6n4S0KI8/s1600/junit.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="138" src="http://3.bp.blogspot.com/-ReA0OQ-7X2Q/TamqLkQQAxI/AAAAAAAAAA8/Mw_6n4S0KI8/s320/junit.png" style="cursor: move;" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="tr-caption" style="font-size: 13px; padding-top: 4px; text-align: center;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;JUnit test reports over time, red indicates test cases have failed during those builds.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-eJwBO66c3HY/TaoNtKxNa7I/AAAAAAAAABE/f3yJqChQdLs/s1600/checkstyle.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="218" src="http://1.bp.blogspot.com/-eJwBO66c3HY/TaoNtKxNa7I/AAAAAAAAABE/f3yJqChQdLs/s400/checkstyle.png" width="400" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Checkstyle reports Java code issues and the interface makes drilling easy.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;In this example Hudson will generate a war archive ready for deployment to a web or application server.&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;
&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;The Maven build could be extended to deploy the war at a local server to also run the web tests as a part of the Hudson job.&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;/div&gt;&lt;br /&gt;
If you wish to also incorporate production deployment in the Hudson process you could use the Google App Engine scripting possibilities. Add a build step which does something like this to upload your newly built war archive to GAE.&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="border-collapse: separate; color: black; font-family: 'Times New Roman'; font-size: small; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Helvetica, Arial, sans-serif; font-size: x-small;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;pre style="background-color: #fafafa; border-bottom-color: rgb(187, 187, 187); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(187, 187, 187); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(187, 187, 187); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(187, 187, 187); border-top-style: solid; border-top-width: 1px; color: #007000; font-family: monospace; font-size: 9pt; line-height: 15px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 1em; overflow-x: auto; overflow-y: auto; padding-bottom: 0.99em; padding-left: 0.99em; padding-right: 0.99em; padding-top: 0.99em; word-wrap: break-word;"&gt;appengine-java-sdk\bin\appcfg.cmd --email user@gmail.com --passin password update myapp/war&lt;/pre&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;The same script can be used to download logs from production via&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="border-collapse: separate; color: black; font-family: 'Times New Roman'; font-size: small; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Helvetica, Arial, sans-serif; font-size: x-small;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;pre style="background-color: #fafafa; border-bottom-color: rgb(187, 187, 187); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(187, 187, 187); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(187, 187, 187); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(187, 187, 187); border-top-style: solid; border-top-width: 1px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 1em; overflow-x: auto; overflow-y: auto; padding-bottom: 0.99em; padding-left: 0.99em; padding-right: 0.99em; padding-top: 0.99em; word-wrap: break-word;"&gt;&lt;span class="Apple-style-span" style="font-family: Helvetica, Arial, sans-serif; font-size: x-small;"&gt;&lt;span class="Apple-style-span" style="white-space: normal;"&gt;&lt;pre style="background-color: #fafafa; border-bottom-color: rgb(187, 187, 187); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(187, 187, 187); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(187, 187, 187); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(187, 187, 187); border-top-style: solid; border-top-width: 1px; color: #007000; font-family: monospace; font-size: 9pt; line-height: 15px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 1em; overflow-x: auto; overflow-y: auto; padding-bottom: 0.99em; padding-left: 0.99em; padding-right: 0.99em; padding-top: 0.99em; word-wrap: break-word;"&gt;appengine-java-sdk/bin/appcfg.sh request_logs myapp/war mylogs.txt&lt;/pre&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Theres a bunch of more handy commands for scripting GAE if you run through the docs at&amp;nbsp;&lt;a href="http://code.google.com/intl/sv-SE/appengine/docs/java/tools/uploadinganapp.html"&gt;http://code.google.com/intl/sv-SE/appengine/docs/java/tools/uploadinganapp.html&lt;/a&gt;.&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Next time I'll try to describe what the production environment offers in addition to the local development environment.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1611368930203661681-6797555440142904307?l=macgyverdev.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/zQoEGFR4HCAkJWhZ5xTeTGey-Fw/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/zQoEGFR4HCAkJWhZ5xTeTGey-Fw/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/zQoEGFR4HCAkJWhZ5xTeTGey-Fw/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/zQoEGFR4HCAkJWhZ5xTeTGey-Fw/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MacgyverDevelopment/~4/2NGSsLeSYsQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://macgyverdev.blogspot.com/feeds/6797555440142904307/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://macgyverdev.blogspot.com/2011/04/hudson-continous-integration-for-google.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1611368930203661681/posts/default/6797555440142904307?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1611368930203661681/posts/default/6797555440142904307?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/MacgyverDevelopment/~3/2NGSsLeSYsQ/hudson-continous-integration-for-google.html" title="Hudson - Continuous Integration for a Google App Engine application" /><author><name>Johan Norén</name><uri>http://www.blogger.com/profile/14636265442070759165</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://3.bp.blogspot.com/-rDRt8dDpk8g/Th9Qq6DmMPI/AAAAAAAAADk/6ha9_J9-2iU/s220/IMG_0461.JPG" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-F2akXvNKCKM/Tamfwj_PaII/AAAAAAAAAAo/_WXeV8sZzAk/s72-c/hudson.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://macgyverdev.blogspot.com/2011/04/hudson-continous-integration-for-google.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUQBRnk_fyp7ImA9WhZQFEo.&quot;"><id>tag:blogger.com,1999:blog-1611368930203661681.post-6706345111964594654</id><published>2011-04-16T16:52:00.001+02:00</published><updated>2011-04-22T15:02:37.747+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-04-22T15:02:37.747+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="rankington" /><category scheme="http://www.blogger.com/atom/ns#" term="hudson" /><category scheme="http://www.blogger.com/atom/ns#" term="maven" /><category scheme="http://www.blogger.com/atom/ns#" term="google app engine" /><title>Development environment for a Google App Engine application</title><content type="html">&lt;a href="http://4.bp.blogspot.com/-ARhLzswxy-8/TamDeWIe0xI/AAAAAAAAAAg/60xSYXfNMm8/s1600/ide.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="400" src="http://4.bp.blogspot.com/-ARhLzswxy-8/TamDeWIe0xI/AAAAAAAAAAg/60xSYXfNMm8/s400/ide.png" width="250" /&gt;&lt;/a&gt;There is integration with Google App Engine for several IDEs and you can also interact with the deployment tools from plain command line if you have that disposition.&lt;br /&gt;
&lt;br /&gt;
I'm an Eclipse guy, and here's how I've done to get a good setup to be able to develop in an efficient way using Eclipsen, Maven, CVS, Hudson and GAE.&lt;br /&gt;
&lt;br /&gt;
Fisrt of all, install the Google App Engine SDK from &lt;a href="http://code.google.com/intl/sv-SE/appengine/docs/java/tools/eclipse.html"&gt;http://code.google.com/intl/sv-SE/appengine/docs/java/tools/eclipse.html&lt;/a&gt; and follow the install instructions.&lt;br /&gt;
&lt;br /&gt;
You should get a new toolbar in Eclipse with icons for creating GAE web projects, profiling them and for deploying to the cloud. If you create a new GAE application you can have it deployed in Googles data centers in a few minutes. All you need is a standard Google Account.&lt;br /&gt;
&lt;br /&gt;
A few tips:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;You access your application at http://localhost:8888/ by default.&lt;/li&gt;
&lt;li&gt; In your dev environment a light weight data store will be kept in&lt;span style="font-size: small;"&gt; &lt;/span&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: small;"&gt;WEB-INF\appengine-generated\local_db.bin&lt;/span&gt;. This store is permanent over server restarts and must be deleted if you wish a clean state. Indicies are automatically generated in&lt;span style="font-size: small;"&gt; &lt;/span&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: small;"&gt;WEB-INF\appengine-generated\datastore-indexes-auto.xml&lt;/span&gt;&lt;span style="font-size: small;"&gt;.&lt;/span&gt; You access the development console at&lt;span style="font-size: small;"&gt; &lt;/span&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: small;"&gt;http://localhost:8888/_ah/admin&lt;/span&gt;&lt;span style="font-size: small;"&gt; &lt;/span&gt;where you can inspect and make queries on the data store and work with task queues and more.&lt;/li&gt;
&lt;li&gt;In addition to the standard JEE files such as web.xml there are a few specific config files for GAE projects. Inspect&lt;span style="font-size: small;"&gt; &lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span style="font-size: small;"&gt;WEB-INF\appengine-web.xml&lt;/span&gt; &lt;/span&gt;&lt;/span&gt;where you setup logging and turn on sessions. By default you will not create web sessions when accessing you application&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;pre class="xml" name="code"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;appengine-web-app xmlns="http://appengine.google.com/ns/1.0"&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;application&amp;gt;rankington&amp;lt;/application&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;version&amp;gt;1&amp;lt;/version&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; 
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;sessions-enabled&amp;gt;true&amp;lt;/sessions-enabled&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; 
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;!-- Configure java.util.logging --&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;system-properties&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/system-properties&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; 
&amp;lt;/appengine-web-app&amp;gt; 
&lt;/pre&gt;&lt;br /&gt;
If you wish to complicate your environment with some continous integration for compilation, bug, test and coverage reporting I'll outline how you could integrate your GAE application with Hudson.&lt;br /&gt;
&lt;br /&gt;
I've fiddled around with the default directory structure to make a more Maven compliant tree for dependency management. That is no problem since the App Engine plugin uses the Eclipse&amp;nbsp;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;.classpath&lt;/span&gt; to resolve the structure.&amp;nbsp; So create &lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;src/main/java src/main/resources /src/test/java src/test/resources and src/main/webapp&lt;/span&gt;. Create a pom.xml in your project root and setup your Maven dependencies. To make this easy, install the Eclipse Maven plugin which makes pom-editing a no-brainer. You will need a few special dependencies for App Engine. Be careful about which version of the plugin you use to match the Maven dependency.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="xml" name="code"&gt;&amp;lt;dependency&amp;gt;
&amp;nbsp;&amp;nbsp; &amp;lt;groupId&amp;gt;com.google.appengine&amp;lt;/groupId&amp;gt;
&amp;nbsp;&amp;nbsp; &amp;lt;artifactId&amp;gt;appengine-api-1.0-sdk&amp;lt;/artifactId&amp;gt;
&amp;nbsp;&amp;nbsp; &amp;lt;version&amp;gt;1.4.0&amp;lt;/version&amp;gt;
&amp;nbsp;&amp;nbsp; &amp;lt;type&amp;gt;jar&amp;lt;/type&amp;gt;
&amp;nbsp;&amp;nbsp; &amp;lt;scope&amp;gt;compile&amp;lt;/scope&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/pre&gt;&lt;br /&gt;
My Maven dependencies are:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-zVQuHNtwCBA/TamFMD5DgsI/AAAAAAAAAAk/M3xXKI-xWBA/s1600/mavenpom.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="232" src="http://4.bp.blogspot.com/-zVQuHNtwCBA/TamFMD5DgsI/AAAAAAAAAAk/M3xXKI-xWBA/s640/mavenpom.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;It took me some time to figure out how to add unit testing, findbugs reporting and code coverage to my Maven build. So hopefully you can reuse something like this for your pom file. I've removed most of the code dependencies specific for my project for clarity&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="xml" name="code"&gt;&amp;lt;project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
&amp;nbsp; xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"&amp;gt;
&amp;nbsp; &amp;lt;modelVersion&amp;gt;4.0.0&amp;lt;/modelVersion&amp;gt;
&amp;nbsp; &amp;lt;groupId&amp;gt;se.noren.rankington&amp;lt;/groupId&amp;gt;
&amp;nbsp; &amp;lt;artifactId&amp;gt;Rankington&amp;lt;/artifactId&amp;gt;
&amp;nbsp; &amp;lt;packaging&amp;gt;war&amp;lt;/packaging&amp;gt;
&amp;nbsp; &amp;lt;version&amp;gt;1.0-SNAPSHOT&amp;lt;/version&amp;gt;
&amp;nbsp; &amp;lt;name&amp;gt;Maven Quick Start Archetype&amp;lt;/name&amp;gt;
&amp;nbsp; &amp;lt;url&amp;gt;http://maven.apache.org&amp;lt;/url&amp;gt;
&amp;nbsp; &amp;lt;ciManagement&amp;gt;
&amp;nbsp; &amp;lt;/ciManagement&amp;gt;
&amp;nbsp; &amp;lt;build&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;plugins&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;plugin&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;artifactId&amp;gt;maven-compiler-plugin&amp;lt;/artifactId&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;version&amp;gt;2.3.2&amp;lt;/version&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;configuration&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;source&amp;gt;1.5&amp;lt;/source&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;target&amp;gt;1.5&amp;lt;/target&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/configuration&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/plugin&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;plugin&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;groupId&amp;gt;org.codehaus.mojo&amp;lt;/groupId&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;artifactId&amp;gt;surefire-report-maven-plugin&amp;lt;/artifactId&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;version&amp;gt;2.0-beta-1&amp;lt;/version&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/plugin&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;plugin&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;artifactId&amp;gt;maven-checkstyle-plugin&amp;lt;/artifactId&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;version&amp;gt;2.6&amp;lt;/version&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/plugin&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;plugin&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;groupId&amp;gt;org.codehaus.mojo&amp;lt;/groupId&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;artifactId&amp;gt;cobertura-maven-plugin&amp;lt;/artifactId&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;version&amp;gt;2.2&amp;lt;/version&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;configuration&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;formats&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;format&amp;gt;xml&amp;lt;/format&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/formats&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/configuration&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;executions&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;execution&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;phase&amp;gt;package&amp;lt;/phase&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;goals&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;goal&amp;gt;cobertura&amp;lt;/goal&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/goals&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/execution&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/executions&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/plugin&amp;gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; 
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/plugins&amp;gt;
&amp;nbsp; &amp;lt;/build&amp;gt;
&amp;nbsp; &amp;lt;dependencies&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;dependency&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;groupId&amp;gt;junit&amp;lt;/groupId&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;artifactId&amp;gt;junit&amp;lt;/artifactId&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;version&amp;gt;4.4&amp;lt;/version&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/dependency&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;dependency&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;artifactId&amp;gt;spring-beans&amp;lt;/artifactId&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;version&amp;gt;3.0.5.RELEASE&amp;lt;/version&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;type&amp;gt;jar&amp;lt;/type&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;scope&amp;gt;compile&amp;lt;/scope&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/dependency&amp;gt;&lt;/pre&gt;&lt;pre class="xml" name="code"&gt;&amp;nbsp; &amp;lt;/dependencies&amp;gt;
&amp;nbsp; &amp;lt;reporting&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;plugins&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;plugin&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;artifactId&amp;gt;maven-checkstyle-plugin&amp;lt;/artifactId&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;version&amp;gt;2.6&amp;lt;/version&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/plugin&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;plugin&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;groupId&amp;gt;org.codehaus.mojo&amp;lt;/groupId&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;artifactId&amp;gt;findbugs-maven-plugin&amp;lt;/artifactId&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;version&amp;gt;2.3.1&amp;lt;/version&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;configuration&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;findbugsXmlOutput&amp;gt;true&amp;lt;/findbugsXmlOutput&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;findbugsXmlWithMessages&amp;gt;true&amp;lt;/findbugsXmlWithMessages&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;xmlOutput&amp;gt;true&amp;lt;/xmlOutput&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;!-- Optional directory to put findbugs xml report --&amp;gt; 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;findbugsXmlOutputDirectory&amp;gt;target&amp;lt;/findbugsXmlOutputDirectory&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;effort&amp;gt;Max&amp;lt;/effort&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;threshold&amp;gt;Low&amp;lt;/threshold&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/configuration&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/plugin&amp;gt;
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/plugins&amp;gt;
&amp;nbsp; &amp;lt;/reporting&amp;gt;&lt;/pre&gt;&lt;pre class="xml" name="code"&gt;&amp;lt;/project&amp;gt;
&lt;/pre&gt;So once alright in Eclipse, commit your application to a version control system. I use CVS, but this will work fine with Subversion as well. In the example below I commited App Engine project Rankington on branch Develop.&lt;br /&gt;
&lt;br /&gt;
Now you could build your GAE application from a command line system with Maven installed with something like&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="xml" name="code"&gt;CVSROOT=:pserver:rankington@213.xxx.xxx.xxx:/cvsrepo
export CVSROOT
cvs login
cvs co -r Develop Rankington
cd Rankington/
mvn package
&lt;/pre&gt;&lt;br /&gt;
This will generate a directory structure with your compiled classes, a packaged war-file ready for deployment to Google App Engine, directories with reports of your unit tests, static code analysis bug possibilities and code coverage.&lt;br /&gt;
&lt;br /&gt;
Go to subdirectory target and you'll find everything&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;drwxr-xr-x 11 johan johan &amp;nbsp; &amp;nbsp; 4096 2011-04-16 14:28 ./&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;drwxr-xr-x &amp;nbsp;8 johan johan &amp;nbsp; &amp;nbsp; 4096 2011-04-16 14:28 ../&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;drwxr-xr-x &amp;nbsp;3 johan johan &amp;nbsp; &amp;nbsp; 4096 2011-04-16 14:28 classes/&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;drwxr-xr-x &amp;nbsp;2 johan johan &amp;nbsp; &amp;nbsp; 4096 2011-04-16 14:28 cobertura/&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;drwxr-xr-x &amp;nbsp;3 johan johan &amp;nbsp; &amp;nbsp; 4096 2011-04-16 14:28 generated-classes/&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;drwxr-xr-x &amp;nbsp;2 johan johan &amp;nbsp; &amp;nbsp; 4096 2011-04-16 14:28 maven-archiver/&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;drwxr-xr-x &amp;nbsp;8 johan johan &amp;nbsp; &amp;nbsp; 4096 2011-04-16 14:28 Rankington-1.0-SNAPSHOT/&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;-rw-r--r-- &amp;nbsp;1 johan johan 23081938 2011-04-16 14:28 Rankington-1.0-SNAPSHOT.war&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;drwxr-xr-x &amp;nbsp;3 johan johan &amp;nbsp; &amp;nbsp; 4096 2011-04-16 14:28 site/&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;drwxr-xr-x &amp;nbsp;2 johan johan &amp;nbsp; &amp;nbsp; 4096 2011-04-16 14:28 surefire-reports/&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;drwxr-xr-x &amp;nbsp;3 johan johan &amp;nbsp; &amp;nbsp; 4096 2011-04-16 14:28 test-classes/&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;drwxr-xr-x &amp;nbsp;3 johan johan &amp;nbsp; &amp;nbsp; 4096 2011-04-16 14:28 war/&lt;/span&gt;&lt;br /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;Next time we'll look at how to integrate this into Hudson.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1611368930203661681-6706345111964594654?l=macgyverdev.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/2xfrrHt2Otd8aY9rnkdWsRk2KeQ/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/2xfrrHt2Otd8aY9rnkdWsRk2KeQ/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/2xfrrHt2Otd8aY9rnkdWsRk2KeQ/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/2xfrrHt2Otd8aY9rnkdWsRk2KeQ/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MacgyverDevelopment/~4/BLYqfh2PG4s" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://macgyverdev.blogspot.com/feeds/6706345111964594654/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://macgyverdev.blogspot.com/2011/04/development-environment-for-google-app.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1611368930203661681/posts/default/6706345111964594654?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1611368930203661681/posts/default/6706345111964594654?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/MacgyverDevelopment/~3/BLYqfh2PG4s/development-environment-for-google-app.html" title="Development environment for a Google App Engine application" /><author><name>Johan Norén</name><uri>http://www.blogger.com/profile/14636265442070759165</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://3.bp.blogspot.com/-rDRt8dDpk8g/Th9Qq6DmMPI/AAAAAAAAADk/6ha9_J9-2iU/s220/IMG_0461.JPG" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-ARhLzswxy-8/TamDeWIe0xI/AAAAAAAAAAg/60xSYXfNMm8/s72-c/ide.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://macgyverdev.blogspot.com/2011/04/development-environment-for-google-app.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUQFQHw5fyp7ImA9WhZQFEo.&quot;"><id>tag:blogger.com,1999:blog-1611368930203661681.post-4476709400722979182</id><published>2011-03-17T22:39:00.002+01:00</published><updated>2011-04-22T15:01:51.227+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-04-22T15:01:51.227+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="rankington" /><category scheme="http://www.blogger.com/atom/ns#" term="google app engine" /><title>Building a Google App Engine application</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-c7osZSfMuWM/Tal-QEByrcI/AAAAAAAAAAc/ZDpPNT1lYcA/s1600/dump.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="135" src="http://3.bp.blogspot.com/-c7osZSfMuWM/Tal-QEByrcI/AAAAAAAAAAc/ZDpPNT1lYcA/s200/dump.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
I've been poking around lately with an application for creating competitions and tournaments with your friends. The main purpose is to keep track of results and creating rankings.&lt;br /&gt;
&lt;br /&gt;
Similar to chess ratings, when you beat a better ranked opponent you will gain a lot of ranking while beating a less ranked opponent will not affect your ranking as much.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-SpUN7_7TcVw/Tal9eCTtuxI/AAAAAAAAAAY/bH61p1I7Jcw/s1600/graph.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="400" src="http://1.bp.blogspot.com/-SpUN7_7TcVw/Tal9eCTtuxI/AAAAAAAAAAY/bH61p1I7Jcw/s400/graph.png" width="355" /&gt;&lt;/a&gt;&lt;/div&gt;For test data I've been using Premier League results over some time period and calculating the teams rankings as functions of the teams performance. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
I've been using Google App Engine (GAE) as deploy target and trying to take advantage of what the GAE infrastructure can offer. I'd thought I'll write some blog posts about pros and cons of using GAE and how my technology stack has worked out.&lt;br /&gt;
&lt;br /&gt;
So what is GAE? GAE is a cloud based hosting service running applications written in Java or Python. In contrast to for example Amazon EC2 you have no control over what OS or web/application server you are running on. This might be a problem if you wish to have total control. For my purposes it is perfect since I don't need to worry about server and OS upgrades etcetera.&lt;br /&gt;
&lt;br /&gt;
The main advantage is that you can focus entirely on the application and infrastructure like scaling of more servers and database backends comes for free. The pricing model is also very generous. App Engine is free to use up to certain quota limits of daily traffic measured on server requests, data store accesses, memcache data transfers etcetera. These limits are so ridiculously high that an average small to medium application can never breach them. If your application becomes a success you will start paying for the extra traffic.&lt;br /&gt;
&lt;br /&gt;
So what can you run on GAE? If focusing on Java applications you must conform to the JEE container specs. What we got to work with is a JEE servlet container supporting most of the specification. Your application is a standard Java web application contained in war archive with the ordinary directory structure of WEB-INF etcetera.&lt;br /&gt;
&lt;br /&gt;
A GAE application runs in a sandboxed environment with constraints like no forking of threads, no access of the file system and other IO tasks and not a complete Java SE library to work with, the AWT libraries are for example not bundled. But there are work arounds for most practical problems. For asynchronous jobs there is for example an API for creating long running tasks. Similarly there are alternatives to lacking JEE standards like JMS messages queues.&lt;br /&gt;
&lt;br /&gt;
Naturally, you don't want to write to much code relying on propritary Google APIs to be able to move to other hosting systems. So when you hear that there is no standard relational SQL database to work with you might fell uneasy!&lt;br /&gt;
&lt;br /&gt;
Data storing relies on Googles BigTable which you might see as a big and extremely fast lookup table. Fortunately GAE provides different access mechanisms to BigTable. If you're familiar with Java ORM frameworks like Hibernate or JPA you can feel at rest since there are JPA and JDO bindings so that you can be data store agnostic in your code.&lt;br /&gt;
&lt;br /&gt;
Obviously, you can leverage a lot of Google APIs inside your application. For example reusing the the identification framework of Google Accounts or using Gmail as email infrastructure. &lt;br /&gt;
&lt;br /&gt;
I'll post some notes on examples of the APIs and more info on GAE deployment.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1611368930203661681-4476709400722979182?l=macgyverdev.blogspot.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/SQAlRMPJewsNu0UhotdL0Bkx-c4/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/SQAlRMPJewsNu0UhotdL0Bkx-c4/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/SQAlRMPJewsNu0UhotdL0Bkx-c4/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/SQAlRMPJewsNu0UhotdL0Bkx-c4/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MacgyverDevelopment/~4/PKnkWHahx4s" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://macgyverdev.blogspot.com/feeds/4476709400722979182/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://macgyverdev.blogspot.com/2011/03/iphone-3d-game-how-much-work.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1611368930203661681/posts/default/4476709400722979182?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1611368930203661681/posts/default/4476709400722979182?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/MacgyverDevelopment/~3/PKnkWHahx4s/iphone-3d-game-how-much-work.html" title="Building a Google App Engine application" /><author><name>Johan Norén</name><uri>http://www.blogger.com/profile/14636265442070759165</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://3.bp.blogspot.com/-rDRt8dDpk8g/Th9Qq6DmMPI/AAAAAAAAADk/6ha9_J9-2iU/s220/IMG_0461.JPG" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-c7osZSfMuWM/Tal-QEByrcI/AAAAAAAAAAc/ZDpPNT1lYcA/s72-c/dump.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://macgyverdev.blogspot.com/2011/03/iphone-3d-game-how-much-work.html</feedburner:origLink></entry></feed>

