Monday, September 3, 2012

Serializing a JBox2D Shape with GSON

For JBox2D 2.1.2, The JBox2D Shape is an abstract class which does not have a default no-arg constructor.  Serializing it with GSON works fine, but trying to deserialize it will throw the following exception:

java.lang.RuntimeException: 
Failed to invoke public org.jbox2d.collision.shapes.Shape() with no args ...
Caused by: java.lang.InstantiationException ...

The Shape constructor takes a ShapeType argument - this is stored as the field m_type.  In order to deserialize a Shape you need to read the m_type field out of the JSON string and instantiate the concrete class based on the ShapeType.  The code to do this is given below:


package com.exposure101.ubik.shared.io.json;

import java.lang.reflect.Type;

import org.jbox2d.collision.shapes.CircleShape;
import org.jbox2d.collision.shapes.PolygonShape;
import org.jbox2d.collision.shapes.Shape;
import org.jbox2d.collision.shapes.ShapeType;

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;

public class ShapeTypeAdapter implements JsonDeserializer<Shape> {
  
  @Override
  public Shape deserialize(JsonElement element, Type type, 
        JsonDeserializationContext context) throws JsonParseException {
    final JsonObject wrapper = (JsonObject) element;
    final ShapeType shapeType = ShapeType.valueOf(wrapper.get("m_type").getAsString());
    Type actualType = getClass(shapeType);
    return context.deserialize(element, actualType);
  }

  private Class<? extends Shape> getClass(ShapeType shapeType) {
    switch (shapeType) {
    case CIRCLE:
      return CircleShape.class;
    case POLYGON:
      return PolygonShape.class;
    default:
      return null;
    }
  }
}

Tuesday, August 28, 2012

Registering a Close Handler for a newly opened Window in GWT

It seems that GWT doesn't have any built in way to add handlers to a new Window opened with the standard Window.open(...) call. In all honesty, I don't have any real useful reason for doing this, but here's a simple example of how to in case it ever needs to happen. keep in mind this is a really trivial example meant to show how to do this as simply as possible

Here's the setup - I created a simple project and dropped the exposure101 logo into the war file. I added a button on the screen which opens the picture in a new window whenever the button is clicked.

Currently, When you use Window.open(...) in GWT, it does not give you a handle to the new Window. According to Joel Webber from 2006 (This is actually the most recent explanation I could find), "GWT cannot allow you to pass objects easily back and forth, because the methods and properties on those objects are compressed and unpredictable (i.e. Foo.bar() in Java is probably called something like x.y() in Javascript). There are also other optimizations that the compiler performs that would make this problematic." So, in order to actually get a handle to it we will have to drop into JSNI.

First thing is a method to get a handle to the Window. (the syntax formatter is having trouble with the JSNI blocks - they aren't comments)

native JavaScriptObject openWindow(String url) /*-{
  return $wnd.open(url, 'blank');
}-*/;  

Next thing to do is to create a Java callback that the new Window JavaScriptObject can call when it's closed.

private void onWindowClosed() {
  // do something useful
}

Next thing is to add another JSNI function to listen for the window close event.  I'm not a big Javascript guy and from what I've read on stackoverflow it seems like this function may not be the best way to handle window close events.  So far I've been using this on both Chrome and Firefox and haven't seen an issue - that being said people that are way better at Javascript than I am have said it's unreliable - so if you actually do need to create this type of listener, you might need to look further into handling window close events with Javascript.  Anyway, for this example, here's the function I've been using.

native JavaScriptObject registerHandlers(Jsni jsni, JavaScriptObject window) /*-{
  window.onbeforeunload = doOnbeforeunload;
  function doOnbeforeunload() {
    jsni.@com.exposure101.examples.jsni.client.Jsni::onWindowClosed()();
  }
}-*/;

* There's something kind of tricky here.  the JSNI documentation says you can call Java instance methods from JSNI using both an instance of the class or this.  I have not been able to call instance methods using this.  I could not get anything to happen using this:
 this.@com.exposure101.examples.jsni.client.Jsni::onWindowClosed()();

Anyway, those 3 functions are pretty much the bulk of it.  The complete code is given below.  I don't know how useful this will really be to anybody but I couldn't find any examples of how to actually implement this anywhere.

package com.exposure101.examples.jsni.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.VerticalPanel;

/**
 * Entry point classes define <code>onModuleLoad()</code>.
 */
public class Jsni implements EntryPoint {

  /**
   * This is the entry point method.
   */
  public void onModuleLoad() {
    final Button openButton = new Button("open");
    final VerticalPanel panel = new VerticalPanel();
    panel.add(openButton);
    RootPanel.get("center").add(panel);

    openButton.addClickHandler(new ClickHandler() {

      @Override
      public void onClick(ClickEvent event) {
        final String url = GWT.getHostPageBaseURL() + "exposure101-logo.png";
        final JavaScriptObject window = openWindow(url);
        registerHandlers(Jsni.this, window);
      }
    });
  }

  native JavaScriptObject openWindow(String url) /*-{
    return $wnd.open(url, 'blank');
  }-*/;
  
  native JavaScriptObject registerHandlers(Jsni jsni, JavaScriptObject window) /*-{
    window.onbeforeunload = doOnbeforeunload;
    function doOnbeforeunload() {
      jsni.@com.exposure101.examples.jsni.client.Jsni::onWindowClosed()();
    }
  }-*/;
  
  private void onWindowClosed() {
    Window.alert("nice catch blanco nino");
  }
}




Wednesday, July 18, 2012

A really simple Android Jersey/Gson example


Last weekend I wanted to start playing with REST on android.  I set up a really simple Google App Engine server that basically runs nothing more than a Hello World server - you send it a shared User object with nothing but an emailAddress and it will give it back to you.

To recreate this you're going to need to download the following libraries: (the downloads are at the bottom - the jersey-json was a nightmare to track down the first time)

gson (i used 2.2.2):
jersey-client (i used 1.12):
jersey-core (i used 1.12):
jersey-json (i used 1.12):

The first thing I did was create a simple network handler.

There's 2 points to this that took me a minute to find:
  1. Android doesn't allow you to run Network Code from the main thread.  In order to return anything it would be necessary to have some type of Callback method.
  2. Certain versions of Jersey and Android don't play well together.  See this post: http://stackoverflow.com/questions/9342506/jersey-client-on-android-nullpointerexception.  Basically you will have to provide your own Implementation of Jersey's ServiceIteratorProvider class - Android apparently cannot load the one in the META-INF folder of the Jersey Jar. The code to get around this is copied directly out of this post and works perfectly - it is attached below.
So - the very first thing I decided to do before creating my NetworkHandler class was to create a simple Callback interface as shown below.

package com.exposure101.example.shared;

public interface Callback<T> {

  void callback(T t); 
} 

Next I made 2 classes that extend Android's AsyncTask class to do the actual GET and POST calls.  I made a simple enum to wrap the MIME types for JSON.  The source code is given below

Here's the MIMETypes enum

package com.exposure101.example.shared;

public enum MIMETypes {

  APPLICATION_JSON("application/json");
  
  private final String name;
  
  private MIMETypes(String name) {
    this.name = name;
  }
  
  public String getName() {
    return name;
  }
}

Here's the Get Task

package com.exposure101.example.json;

import android.os.AsyncTask;

import com.exposure101.example.shared.Callback;
import com.exposure101.example.shared.MIMETypes;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;

public class GetTask extends AsyncTask<String, String, String> {

  private final String url;
  private final Callback<String> callback;

  GetTask(String url, Callback<String> callback) {
    this.url = url;
    this.callback = callback;
  }

  @Override
  protected String doInBackground(String... params) {
    final Client client = Client.create();
    final WebResource resource = client.resource(url);
    final ClientResponse response = resource.accept(MIMETypes.APPLICATION_JSON.getName())
        .get(ClientResponse.class);
    return response.getEntity(String.class);
  }

  @Override
  protected void onPostExecute(String result) {
    callback.callback(result);
    super.onPostExecute(result);
  }
}

And here's the Post Task

package com.exposure101.example.json;

import android.os.AsyncTask;

import com.exposure101.example.shared.Callback;
import com.exposure101.example.shared.MIMETypes;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;

public class PostTask extends AsyncTask<String, String, String> {

  private final String url;
  private final String requestBody;
  private final Callback<String> callback;

  PostTask(String url, String requestBody, Callback<String> callback) {
    this.url = url;
    this.requestBody = requestBody;
    this.callback = callback;
  }

  @Override
  protected String doInBackground(String... params) {
    final Client client = Client.create();
    final WebResource resource = client.resource(url);
    final ClientResponse response = resource.type(MIMETypes.APPLICATION_JSON.getName())
        .post(ClientResponse.class, requestBody);
    if (response.getStatus() != 201 && response.getStatus() != 200) {
      throw new RuntimeException("failed: http error code = " + response.getStatus());
    }
    final String responseEntity = response.getEntity(String.class).replaceAll("\\\\", "");
    return responseEntity.substring(1, responseEntity.length() - 1);
  }

  @Override
  protected void onPostExecute(String result) {
    callback.callback(result);
    super.onPostExecute(result);
  }
}

With this I kind of cheated the system using the POST request - honestly I have no idea if you should do it like this.  On the server side, as soon as the POST is received I query the datastore to see if a User with the given email address exists.  If it's not already in the datastore I create one, either way I return it back across the wire as JSON with the google datastore id in it.  There's 2 things that I found happen.
  1. The entire response is broken up by \ marks. An example would be {\"id"\:\1001\,\"emailAddress"\: ... \}
  2. The entire JSON string is wrapped in quotation marks - as a quick hack to get around this I simply took a substring.
Now that I have some simple tasks up and ready I made a really simple NetworkHandler interface to read and write requests. 

package com.exposure101.example.json;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.exposure101.example.jersey.AndroidServiceIteratorProvider;
import com.exposure101.example.shared.Callback;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.sun.jersey.spi.service.ServiceFinder;

public class NetworkHandler {

  private static NetworkHandler instance;

  public static synchronized NetworkHandler getInstance() {
    if (instance == null) {
      instance = new NetworkHandler();
    }
    return instance;
  }

  @SuppressWarnings("rawtypes")
  private NetworkHandler() {
    ServiceFinder.setIteratorProvider(new AndroidServiceIteratorProvider());
  }

  public <T> void read(final String url, final Class<T> clazz, final Callback<T> callback) {
    new GetTask(url, new Callback<String>() {

      @Override
      public void callback(String result) {
        callback.callback(new GsonBuilder().create().fromJson(result, clazz));
      }
    }).execute();
  }

  public <T> void readList(final String url, final Class<T[]> clazz, final Callback<List<T>> callback) {
    new GetTask(url, new Callback<String>() {

      @Override
      public void callback(String result) {
        final T[] array = new GsonBuilder().create().fromJson(result, clazz);
        callback.callback(new ArrayList<T>(Arrays.asList(array)));
      }
    }).execute();
  }

  public <T> void write(final String url, final Class<T> clazz, final T t, final Callback<T> callback) {
    final Gson gson = new GsonBuilder().create();
    new PostTask(url, gson.toJson(t), new Callback<String>() {

      @Override
      public void callback(String result) {
        callback.callback(gson.fromJson(result, clazz));
      }
    }).execute();
  }
}


That's pretty much it for the Network code.

All that's left is to create an activity to show the user accounts and send it to the server.

Basically there's only 2 things I want to happen here:
  1. Have all the accounts on the phone come up in a ListView on the top part of the screen.  (Note: the accounts on your phone aren't unique - there may be multiple accounts with the same email - we should only show unique email addresses)
  2. Have a button on the bottom to send the selected email address to the server.  If we receive a response we can then launch a new Intent to switch the Activity. (Note: while the server is processing we should indicate that to the user with a loading icon)
The xml layout for this is as follows:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <Button
        android:id="@+id/select_user_account_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:text="@string/login" />

    <ListView
        android:id="@+id/user_accounts_list_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@id/select_user_account_button"
        android:layout_alignParentTop="true" />

</RelativeLayout>

and the actual Android Activity code is as follows:


package com.exposure101.example.activities;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Patterns;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;

import com.exposure101.example.R;
import com.exposure101.example.json.NetworkHandler;
import com.exposure101.example.json.UserServiceURLs;
import com.exposure101.example.shared.Callback;
import com.exposure101.example.shared.entity.User;

public class UserAccountSelectActivity extends Activity {

  private ListView userAccountsListView;

  private Button selectUserAccountButton;

  private String selectedEmailAddress;

  /**
   * 
   */
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
    setContentView(R.layout.activity_user_account_select);
    userAccountsListView = (ListView) findViewById(R.id.user_accounts_list_view);
    selectUserAccountButton = (Button) findViewById(R.id.select_user_account_button);
    final Account[] accounts = initializeAccounts();
    final String[] emailAddresses = initializeEmailAddresses(accounts);
    initializeView(emailAddresses);
  }

  /**
   * get the accounts from android's account manager
   * 
   * @return
   */
  private Account[] initializeAccounts() {
    final Pattern emailPattern = Patterns.EMAIL_ADDRESS;
    final List<Account> accounts = new ArrayList<Account>(Arrays.asList(AccountManager.get(this)
        .getAccounts()));
    for (final Iterator<Account> it = accounts.iterator(); it.hasNext();) {
      final Account account = it.next();
      if (emailPattern.matcher(account.name).matches() == false) {
        it.remove();
      }
    }
    return accounts.toArray(new Account[accounts.size()]);
  }

  /**
   * get the email addresses from the accounts
   * 
   * @param accounts
   * @return
   */
  private String[] initializeEmailAddresses(Account[] accounts) {
    final List<String> emailAddresses = new ArrayList<String>(accounts.length);
    for (final Account account : accounts) {
      emailAddresses.add(account.name);
    }
    final Set<String> uniqueEmailAddresses = new HashSet<String>(emailAddresses);
    return uniqueEmailAddresses.toArray(new String[uniqueEmailAddresses.size()]);
  }

  /**
   * 
   * @param accounts
   */
  private void initializeView(String[] emailAddresses) {
    selectUserAccountButton.setEnabled(false);
    userAccountsListView.setAdapter(new ArrayAdapter<String>(this,
        android.R.layout.simple_list_item_single_choice, emailAddresses));
    userAccountsListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
    userAccountsListView.setOnItemClickListener(new OnItemClickListener() {

      @Override
      public void onItemClick(AdapterView<?> adapter, View view, int position, long arg3) {
        selectedEmailAddress = (String) userAccountsListView.getItemAtPosition(position);
        selectUserAccountButton.setEnabled(true);
      }
    });

    selectUserAccountButton.setOnClickListener(new OnClickListener() {

      @Override
      public void onClick(View v) {
        login();
      }
    });
  }

  /**
   * 
   * @param emailAddress
   */
  private void login() {
    final NetworkHandler networkHandler = NetworkHandler.getInstance();
    final String url = UserServiceURLs.POST.getURL();
    networkHandler.write(url, User.class, new User(selectedEmailAddress), new Callback<User>() {

      @Override
      public void callback(User user) {
        handleLogin();
      }
    });
  }

  /**
   * your call was successful
   */
  private void handleLogin() {
    setProgressBarIndeterminateVisibility(false);
    selectUserAccountButton.setEnabled(true);
    /*
     * startActivity(new Intent(UserAccountSelectActivity.this, TheNextExampleActivity.class));
     */
  }
}
 

And that's about it. 

Here's all the required libraries:  (The jersey-json one was a little hard to track down)

gson
jersey-client-1.2
jersey-core-1.2
json-jar-1.2

And here's the AndroidServiceIteratorProvider class.

package com.exposure101.example.jersey;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import android.util.Log;

import com.sun.jersey.spi.service.ServiceFinder.ServiceIteratorProvider;

public class AndroidServiceIteratorProvider<T> extends ServiceIteratorProvider<T> {

  private static final String TAG = AndroidServiceIteratorProvider.class.getSimpleName();
  private static final String MESSAGE = "Unable to load provider";

  private static final HashMap<String, String[]> SERVICES = new HashMap<String, String[]>();

  private static final String[] com_sun_jersey_spi_HeaderDelegateProvider = {
      "com.sun.jersey.core.impl.provider.header.MediaTypeProvider",
      "com.sun.jersey.core.impl.provider.header.StringProvider" };

  private static final String[] com_sun_jersey_spi_inject_InjectableProvider = {};

  private static final String[] javax_ws_rs_ext_MessageBodyReader = {
      "com.sun.jersey.core.impl.provider.entity.StringProvider",
      "com.sun.jersey.core.impl.provider.entity.ReaderProvider" };

  private static final String[] javax_ws_rs_ext_MessageBodyWriter = {
      "com.sun.jersey.core.impl.provider.entity.StringProvider",
      "com.sun.jersey.core.impl.provider.entity.ReaderProvider" };

  static {
    SERVICES.put("com.sun.jersey.spi.HeaderDelegateProvider",
        com_sun_jersey_spi_HeaderDelegateProvider);
    SERVICES.put("com.sun.jersey.spi.inject.InjectableProvider",
        com_sun_jersey_spi_inject_InjectableProvider);
    SERVICES.put("javax.ws.rs.ext.MessageBodyReader", javax_ws_rs_ext_MessageBodyReader);
    SERVICES.put("javax.ws.rs.ext.MessageBodyWriter", javax_ws_rs_ext_MessageBodyWriter);
    SERVICES.put("jersey-client-components", new String[] {});
    SERVICES.put("com.sun.jersey.client.proxy.ViewProxyProvider", new String[] {});
  }

  @SuppressWarnings("unchecked")
  @Override
  public Iterator<Class<T>> createClassIterator(Class<T> service, String serviceName,
      ClassLoader loader, boolean ignoreOnClassNotFound) {

    String[] classesNames = SERVICES.get(serviceName);
    int length = classesNames.length;
    ArrayList<Class<T>> classes = new ArrayList<Class<T>>(length);
    for (int i = 0; i < length; i++) {
      try {
        classes.add((Class<T>) Class.forName(classesNames[i]));
      } catch (ClassNotFoundException e) {
        Log.v(TAG, MESSAGE, e);
      }
    }
    return classes.iterator();
  }

  @Override
  public Iterator<T> createIterator(Class<T> service, String serviceName, ClassLoader loader,
      boolean ignoreOnClassNotFound) {

    String[] classesNames = SERVICES.get(serviceName);
    int length = classesNames.length;
    ArrayList<T> classes = new ArrayList<T>(length);
    for (int i = 0; i < length; i++) {
      try {
        classes.add(service.cast(Class.forName(classesNames[i]).newInstance()));
      } catch (IllegalAccessException e) {
        Log.v(TAG, MESSAGE, e);
      } catch (InstantiationException e) {
        Log.v(TAG, MESSAGE, e);
      } catch (ClassNotFoundException e) {
        Log.v(TAG, MESSAGE, e);
      }
    }

    return classes.iterator();
  }
}



Hope this helps.

Intro

I've been using blogs as my primary resource for almost everything i've done in basically the last 5 years.  I got through school on pretty much nothing but posts and music on blogspot.  I figure its my turn to post some stuff to hopefully help some of you guys out.

Also - It's hard to find a lot of the best practices with newer technologies out on the web - if you see that I've done something wrong or if there's a pattern or a better way to do something please by all means let me know.  I hope that i can get some feedback to make my own projects better as I'm posting information for you guys.

Lastly, the only 2 things I really care about are music and programming - there may be an occasional music post on here from time to time if something's just that awesome.  ask me and i'll remove anything up here, please don't sue me.