I Think I'm Becoming an Android
Serializing Objectify Keys with Restlet/Jackson/GWT/ Print E-mail

In a current project, I've got a typical architecture:  DB Store, API, Server talking to a variety of clients, including Mobile, and Web (GWT).

I wanted very much to use a single POJO model for my API and at least my web app, potentially my Android app as well.  I made the following technology choices, running on Google App Engine:

  • Objectify for ORM - this maps POJOs directly to the GAE datastore.  Light, fast, nice.
  • Restlet for API and client-server communication - well-supported versions for GAE, GWT, and Android

My hope was that this stack would meet my requirements, and not paint me into any corners with respect to other platforms (such as iOS) later.  I wanted my API to be able to use multiple transport protocols (e.g., GWT RPC, JSON, XML, etc.).

Eventually I ran into issues with Jackson (the default OOTB JSON serialization framework that comes with Restlet) not being able to serialize Objectify key objects.  This was due to the Objectify Key class having a self-referencing loop on the root property.  Since this is a dynamically generated property, it is safe to remove it from the serialization rules used by Jackson.  It took me a fair amount of research to figure out how to do that, and it's my hope that this post will help someone else figure out how to get by this issue.

The basic idea is that you need to replace the OOTB JacksonCoverter with your own custom converter that makes use of the concept of Jackson Mixins to ignore the getRoot method on the Objectify Key class.  Mixins are a very cool Jackson concept that allow you to, in effect, inject annotations into a class that is handled by the Jackson serialization framework.  Normally, you would need to get the source for the Key class, add annotations to the root property, and then recompile.  Mixins allow you to do the equivalent without access to the original source.

You will need to create three classes.  You can pretty much just copy them as I have them here.  If you're using Restlet, you will also need to register your new ConverterHelper with the Restlet infrastructure.  I did this in my Restlet Application class in the createInboundRoot method:

RestletApi.java


 /**
* Creates a root Restlet that will receive all incoming calls.
*/
@Override
public Restlet createInboundRoot() {
	Router router = new Router(getContext());
	// Defines only one route
	router.attach("/foo", FooServerResource.class);
	router.setRoutingMode(Router.MODE_BEST_MATCH);
	 
	// add the GWT mapping in the metadata service
	getMetadataService().setDefaultMediaType(MediaType.APPLICATION_JSON);
	getMetadataService().addExtension("gwt", MediaType.APPLICATION_JAVA_OBJECT_GWT);


	// replace the OOTB JacksonConverter with the a converter that will work with the objectify Key class
	replaceConverter(JacksonConverter.class, new FixedJacksonConverter());


	return router;
}
 
/**
* Registers a new converter with the Restlet engine, after removing the first
* registered converter of the given class.
*
* Taken from:
* http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId
* =2716118
*/
static void replaceConverter(
	Class<? extends ConverterHelper> converterClass,
	ConverterHelper newConverter) {
 
	ConverterHelper oldConverter = null;
	List<ConverterHelper> converters = Engine.getInstance().getRegisteredConverters();
	for (ConverterHelper converter : converters) {
		if (converter.getClass().equals(converterClass)) {
			converters.remove(converter);
			oldConverter = converter;
			break;
		}
	}
	
	converters.add(newConverter);
	if (oldConverter == null) {
		System.err.println("Added Converter to Restlet Engine: " + newConverter.getClass().getName());
	} else {
		System.err.println("Replaced Converter "+oldConverter.getClass().getName()+" with "+
		newConverter.getClass().getName()+" in Restlet Engine");
	}
}

FixedJacksonConverter.java


import java.util.List;
import org.restlet.data.MediaType;
import org.restlet.engine.resource.VariantInfo;
import org.restlet.ext.jackson.JacksonConverter;
import org.restlet.ext.jackson.JacksonRepresentation;
import org.restlet.representation.Representation;
import org.restlet.representation.Variant;


public class FixedJacksonConverter extends JacksonConverter {
  private static final VariantInfo VARIANT_JSON = new VariantInfo(MediaType.APPLICATION_JSON);


  @Override
  protected <T> JacksonRepresentation<T> create(MediaType mediaType, T source) {
    return new FixedJacksonRepresentation<T>(mediaType, source);
  }


  @Override
  protected <T> JacksonRepresentation<T> create(Representation source, Class<T> objectClass) {
    return new FixedJacksonRepresentation<T>(source, objectClass);
  }
  @Override
  public List<Class<?>> getObjectClasses(Variant source) {
    List<Class<?>> result = null;
    if(VARIANT_JSON.isCompatible(source)){
      result=addObjectClass(result, Object.class);
      result=addObjectClass(result, FixedJacksonRepresentation.class);
    }
    return result;
  }
}

FixedJacksonRepresentation.java











 
import org.codehaus.jackson.map.ObjectMapper;
import org.restlet.data.MediaType;
import org.restlet.ext.jackson.JacksonRepresentation;
import org.restlet.representation.Representation;
 
import com.googlecode.objectify.Key;
 
public class FixedJacksonRepresentation<T> extends JacksonRepresentation<T> {
 
  public FixedJacksonRepresentation(MediaType mediaType, T object) {
    super(mediaType, object);
  }
 
  public FixedJacksonRepresentation(T object) {
    super(object);
  }
 
  public FixedJacksonRepresentation(Representation representation, Class<T> objectClass) {
    super(representation, objectClass);
  }
 
  @Override
  protected ObjectMapper createObjectMapper() {
    ObjectMapper ret = super.createObjectMapper();
 
    // inject the mixin that will allow us to properly serialize Objectify Key objects...
    ret.getSerializationConfig().addMixInAnnotations(Key.class, JacksonMixIn.class);
    ret.getDeserializationConfig().addMixInAnnotations(Key.class, JacksonMixIn.class);
 
    return ret;
  }
 
 
 
}


JacksonMixin.java


import org.codehaus.jackson.annotate.JsonIgnore;
 
import com.googlecode.objectify.Key;
 
public interface JacksonMixIn {
 @JsonIgnore <V> Key<V> getRoot();
}

 
Debugging on Amazon Kindle Fire - No Thanks to Amazon Print E-mail

Me:  Hey Amazon, can I buy a Kindle Fire to test my apps on before you ship to the general public?

Amazon: No.  BTW, we've forked Android, but to get you started here's a two-paragraphs description of the features of the Fire so that you can create a VM image and "test" your apps.

Me: OK.  I know that you test everyone's apps yourself.  Do you know if my app works on Fire?

Amazon: It does not.  That's as much as we're going to tell you, however.  Good luck, and be sure to see our two paragraphs that describe the features of the Fire.

Me:  That sucks.  How can I test my app on an actual device?

Amazon: We recommend that you buy a Kindle Fire, so that you can develop new apps for the Fire.  Have you seen Angry Birds?  It works on the Fire.  You should get your app to work there too.  Make sure that you update the price of your app.

Me:  OK.  I got my Fire from FedEx today.  I plugged it in and it doesn't seem to have any way enable debugging.  What do you recommend?

Amazon: See our FAQ.  It has a two-paragraph description of the features of the Fire.  Use that to create a VM image on which you can test.  Have you seen Angry Birds?  You should write apps for the Fire.

Me: Hey, XDA, does anyone here know how to debug on the Kindle Fire?

XDA: Sure.  Just do this (http://blog.actlocalmedia.com/2011/11/developing-on-kindle-fire.html).

 
My App's Users Hate Me Print E-mail
Before I get started on this post, let me say that I love the users of my Android apps.  I've gotten over 6,000 ratings on the Android Market for Screebl, and nearly 10,000 emails and forum postings over the past two years.  The majority of my contact with users results in positive interactions, and I have a long list of requested feature enhancements, bug reports, and ideas from the hundreds-of-thousands of users that find my apps helpful.  It really is fun interacting with a healthy community of users.

However, I particularly love hearing from those users that hate me, and there seem to be a lot of those too.  Something about my personality makes it impossible to ignore the many creative insults that come my way.  I'm pretty sure that my responses and the ensuing interactions are not productive (and sometimes downright unhealthy), but it entertains me. In the last few weeks I was the lucky recipient of a market ratings ambush by a competing app to CrazyCat HD, so some of this anger may be artificial, but I like to think that there are a fair number of people out there that genuinely dislike me and what I do.

So here's a list of the ten most amusing hate mails that I've received over the last two years.  I've edited names and obscenities, but otherwise these are cut and pasted from my inbox...

Read more...
 
The NEW Way to Game the Android Market -- Pay Up Print E-mail

 

It's been a while since I've tried to market a new app.  With the introduction of CrazyCat on the Android Market, I've started paying attention again, and things have changed significantly.  While the new Android market looks really slick, I must say that Google has really kicked the small developer in the teeth with the format of the new market.  

Most notably, there is absolutely no way for devs to get a paid app in front of customers using only the market.  In the past, Google prominently promoted a "Just In" category on the market.  If you released or updated an app, it would show up (with certain caveats) in this highly-visible place on the Market.  If your app was good, lots of people would download it, it would start trending up in the market, and money would start showing up in your Google Checkout account.  It's only been a couple of short years since the Android Market started allowing the sale of apps, but already those are the good old days...

Read more...
 
CrazyCat HD Innovates With Game Control Print E-mail

A pet project that we've been working on for the past few months has finally matured enough to make it onto the major Android markets.  CrazyCat HD is a simple Android game based on the very fine AndEngine gaming engine that has some pretty cool and innovative features.

The premise of the game is very simple.  Your cat chases animated critters around on the screen and scores points based on how fast they are moving when her paw makes contact.  Yeah that's right, it's an app for your cat.  We're not the first ones to think of such a twisted thing, it's been done on the iPad, and in Flash form for Android, but we wanted to see what a native Android version could do while we played with AndEngine and explored some other ideas that we have around app marketing and advertising.  We'll be getting into those last two in some other posts.

Read more...
 
Amazon Appstore Shows 30% Conversion Rates Print E-mail

 It's only been a bit over two days, but I'm very encouraged about a couple of things happening over at the Amazon Appstore.  First, it appears that the rankings are very dynamic, changing continually throughout the day.  Rankings have been a complaint of developers selling on the Google Android Market almost from day one.  There have been big changes on the Android Market, but the rankings are still a bit static and just as difficult to understand as they ever where.  There is no more transparency on Amazon, but I like how they call out "Hot New Releases" and "Movers and Shakers" as sub-categories under "Best Sellers".  They offer lots of ways for an app developer to get their app on some kind of temporal and constantly updated list...

Read more...
 
Android Users Tend to Be Democrats Print E-mail

 

On Jan 25, 2011, KeyesLabs conducted the first in a series of live experiments to determine how users interact with mobile devices while watching television.    Our goal was simple and targeted - create an application that would enhance the experience of watching the 2011 State of the Union Address, and measure how users interacted with the app during the live address.  Click here, to see an interactive replay of the experiment.  You can also read the details about what we discovered...

 

Read more...
 
Hacking the State of the Union Print E-mail

KeyesLabs is excited to release an experimental application, called State of the Union 2011, that is intended to explore the combination of TV and mobile computing devices.   Available immediately on the Google Android Market the app is designed to enhance the viewing experience for those who watch the State of the Union Address.

This is an unusual application in that it has a life span of about one hour.  It is designed for use during the State of the Union Address, which typically lasts somewhere between thirty and ninety minutes.  During that time we're hoping to gather some really interesting information about how mobile devices can enhance and augment the television viewing experience...

Read more...
 
Great Map of Entrepreneurs' Brain Print E-mail

Recently saw a terrific visualization of the typical entrepreneur's brain.  It is only partially being done tongue-in-cheek.  What I want to see is a subset of this map that shows the brain of someone who likes to think of themselves as an entrepreneur, but is lacking in success.  Mostly ego, I would think...

Read more...
 
<< Start < Prev 1 2 3 Next > End >>

Page 1 of 3