»
March 10, 2010
»

Gaeds 1.2-SNAPSHOT Changes #3 - GaedsKey<T>

Gaeds is my tiny AppEngine for Java low-level DatastoreService wrapper. This is third post in three post series about new features that will form 1.2 final release.

The first two are:

Google AppEngine Datastore Entity primary keys are Key instances. Each Key is composed from String kind and either long id or String keyName. Also Key can be encoded as String and then decoded back to Key instance using KeyFactory.

For me the most important new gaeds feature is GaedsKey<T> generic class that wraps Key and respective model class. It is impossible to create GaedsKey with mismatched model class (see notes below about inheritance).

GaedsKey interface is declared like this:

public interface GaedsKey<T> extends Comparable<GaedsKey<T>> {

  Key getKey();

  Class<? extends T> getModelClass();

  <R> GaedsKey<R> cast(Class<R> type);

  <P> GaedsKey<P> getParent(Class<? extends P> model);

  String encode();
  
  // and few other methods

}

To create GaedsKey from Key or from encoded String GaedsKeyFactory must be used:

GaedsKeyFactory kf = session.key(); // or @Inject GaedsKeyFactory directly

// from String or Key
GaedsKey<Task> key = kf.create(string, Task.class);       // from encoded datastore key
GaedsKey<Task> key = kf.create(datastoreKey, Task.class); // from datastore key

// Root keys
GaedsKey<Task> key = kf.create(Task.class, "something");  // with keyName
GaedsKey<Task> key = kf.create(Task.class, 102l);         // with id

// Keys with parent Key or GaedsKey
GaedsKey<Task> key = kf.create(parent, Task.class, "something"); // with keyName
GaedsKey<Task> key = kf.create(parent, Task.class, 102l);        // with id

First two DatastoreKeyFactory#create calls ensures that provided key matches model class by comparing Key kind to kind declared for model. If kind don’t match, GaedsKeyRuntimeException is thrown. This behavior ensures that encoded keys coming from web request matches expected model kind. If kinds doesn’t match, exception is thrown before Datastore get/put/query operation (fail fast). By using Key directly get operations would quite possibly just fail with ClassCastException after touching Datastore.

String string = keyToString(createKey("User", 101));

// Creates "User(101)" key
Key key = stringToKey(string);
// Throws ModelNotFoundException or ClassCastException after get.
Task task = sess.get(key);

// Throws GaedsKeyRuntimeException
GaedsKey<Task> key = kf.create(string, Task.class);
// Not reached
Task task = sess.get(key);

Gaeds models has direct relationship to Entity keyNames. It is possible to use GaedsKey<T> without any changes to Model classes but more beneficial they are if @PrimaryKey, @ParentKey and all @Property Key declarations also are declared as GaedsKey<T>. Lets consider 2 models – Person and Task:

@Model
public static class Person {

  @PrimaryKey
  private GaedsKey<Person> key;

  // getters & setters
  
}

@Model
public static class Task {

  @PrimaryKey
  private GaedsKey<Task> key;

  @Property
  private String title;

  @Property
  private GaedsKey<Person> owner;

  // getters & setters

}

And some straight-forward operations on them:

// First request

// create person
Person person = new Person();
sess.put(person);

// create task with person set as owner
Task task = new Task();
task.setTitle("Hello world");
task.setOwner(person.getKey());
sess.put(task);

String encoded = task.getKey().encode();

// Second request

// Create Task key from encoded string (will throw if encoded Key is not for Task kind)
GaedsKey<Task> taskKey = sess.key().create(encoded, Task.class);

// get Task, get Person
Task task = sess.get(taskKey);
Person person = sess.get(task.getOwner());
Task{
  key=GaedsKey{Task(2) com.ampatspell.gaeds.test.Task}, 
  title='Hello world', 
  owner=GaedsKey{Person(1) com.ampatspell.gaeds.test.Person}
}

Person{
  key=GaedsKey{Person(1) com.ampatspell.gaeds.test.Person}
}

As we can see, the GaedsKey Session API is the same as for Key and that we needed to access GaedsKeyFactory only once — for string decoding. While GaedsKey API is identical to Key based there are a bit more going on behind the scenes.

Not only GaedsKey creation is validated to conform model-kind relationship, also all model attributes that has GaedsKey type are validated before put and after get, query.

Put

Lets consider a bit different example involving gaeds model inheritance:

@Model
public static class Person {

  @PrimaryKey
  private GaedsKey<? extends Person> key;

}

@Model(discriminator = "administrator")
public static class Administrator extends Person {
}

We have a new class Administrator which extends Person. Gaeds implements inheritance by transparently adding "_discriminator" property to saved entities and because there is no way knowing which entity type is before getting actual Entity, GaedsKey can be created with incorrect key+type pair:

Administrator admin = new Administrator();
sess.put(admin);

Person person = new Person();
sess.put(person);

String key = person.getKey().encode();

// creating a key with Person Datastore Key and Administrator type
GaedsKey<Administrator> admin = kf.create(key, Administrator.class);

// Throws an GaedsKeyRuntimeException
sess.get(admin);

After Entity is retrieved from Datastore, its type is compared to required type for GaedsKey, if GaedsKey type is not the same or subclass of fetched Model’s class, GaedsKeyRuntimeException is thrown. This prevents getting ClassCastException or incorrect Model when sess.get returns. This also works for messed up GaedsKey unchecked casts:

@Model
public static class Task {

  // ...

  @Property
  private GaedsKey<Administrator> owner;

}
Person person = new Person();
sess.put(person);

// Unchecked cast
GaedsKey<Administrator> ownerKey = (GaedsKey) person.getKey();

Task task = new Task();
task.setOwner(ownerKey);

// throws GaedsKeyRuntimeException
sess.put(task);
com.ampatspell.gaeds.internal.key.GaedsKeyRuntimeException: 
  GaedsKey{ExamplePerson(2) com.ampatspell.gaeds.test.Person} model class is not assignable 
  to com.ampatspell.gaeds.test.Administrator

Also GaedsKey exposes cast(T targetType) method that allows up- and down-casting GaedsKey instances but it enforces that:

  • GaedsKey<Person> cannot be up-casted to GaedsKey<Administrator>
  • GaedsKey<Administrator> can be down-casted to GaedsKey<Person> and then up-casted back to GaedsKey<Administrator>

But note: it is possible to create GaedsKey<Administrator> with Person Key and Administrator type, save it in Datastore as GaedsKey<Administrator>. Try fetching entity using that GaedsKey before, only if fetch succeeds, we can be confident that given GaedsKey<Administrator> actually points to Administrator.

Get & Query

For gets and queries, model @Property attributes there is a bit different story. Lets start with creating Task with Zeeba as an owner instead of Administrator:

Entity task = new Entity("Task");
task.setProperty("owner", createKey("Zeeba", "fake"));
Key dsKey = ds.put(task);

GaedsKey<Task> key = kf.create(dsKey, Task.class);
Task model = sess.get(key);

assertTrue(Gaeds.asModel(model).getIncompatibleProperties().contains("owner"));
WARNING: Entity to model mapping cast errors
Model:  com.ampatspell.gaeds.test.Task
Entity: Task(1)
 * 'owner' 
      com.google.appengine.api.datastore.Key => 
      com.ampatspell.gaeds.GaedsKey com.ampatspell.gaeds.test.Administrator (Zeeba("fake"))

As we can see, this is also considered incompatibility because Session wouldn’t allow persisting GaedsKey<Zeeba> instead of GaedsKey<Administrator>. Useful for Entity migration.

API

  • All Session and Session#query() methods now takes either Key or GaedsKey
  • GaedsKeyFactory can be injected (@Inject) directly or accessed from Session#key()
  • GaedsBpSupport and GaedsDaoSupport inherits from AbstractGaedsSupport which also implements #key()

GaedsKey @Property, @PrimaryKey and @ParentKey can be declared as:

  • GaedsKey<Task>
  • GaedsKey<? extends Task>
  • GaedsKey<T> for generic classes (@Model class Task<T> { ... })

Release

I’ve migrated 10 or so models to GaedsKey for one of the projects I’m currently working on. All tests pass, everything looks fine but I’ll wait few more days before releasing 1.2 final.

March 09, 2010
»

Gaeds 1.2-SNAPSHOT Changes #2 - Converters

Gaeds is my tiny AppEngine for Java low-level DatastoreService wrapper. This is second post in three post series about new features that will form 1.2 final release.

Since first release of gaeds it has Model property value conversion support from-to Entity properties using Converter. There are few default converters and its easy to write additional ones. To declare that property needs converter, @Property annotation has type setting:

@Model
public class Something {

  @Property(type = Text.class)
  private String text;

}

For Something.text property before creating or updating Entity Converter<String, Text> will kick in and convert String value to Text and after get or query operations, the same converter will convert Text back to String and set it to model property.

But now lets consider the following property:

@Model
public class Something {

  @Property(field = Serializable.class, type = Blob.class)
  private Map<String, Integer> paymentDetails;

}

The Converter lookup is done using newly added @Property.field, if it is not provided, using actual field type. Because in this example @Property.field is set, the Converter<Serializable, Blob> is used instead of Converter<Map, Blob>.

Also see Gaeds 1.2-SNAPSHOT Changes #2 — Converters to learn about changes in Converter configuration.

March 04, 2010
»

Gaeds 1.2-SNAPSHOT Changes #1 - Configuration

Gaeds is my tiny AppEngine for Java low-level DatastoreService wrapper. I’ve started the work on version 1.2 what will have few nice additions but for now a small breaking change.

Previous versions was configured something like this:

public class GaedsModule extends AbstractModule {

  @Override
  protected void configure() {
    install(new GaedsBaseModule());
    
    bind(DatastoreService.class).annotatedWith(Gaeds.class).
      toProvider(DatastoreServiceProvider.class);
      
    bind(IncompatiblePropertiesLogger.class).
      to(IncompatiblePropertiesLoggerImpl.class);
  }

  @Provides
  @Singleton
  public List<Converter> provideConverters(
      @Named("default") List<Converter> defaultConverters) {
      
    return defaultConverters;
  }

  @Provides
  @ModelClasses
  @Singleton
  public List<Class<?>> provideModelClasses() {
    List<Class<?>> classes = new ArrayList<Class<?>>();

    classes.add(Zeeba.class);
    classes.add(Larry.class);

    return classes;
  }

}

Now configuration looks a lot better:

public class GaedsModule extends AbstractGaedsModelsModule {

  @Override
  protected void configureModels() {
    install(new GaedsBaseModule());

    bind(DatastoreService.class).annotatedWith(Gaeds.class).
      toProvider(DatastoreServiceProvider.class);
    
    bind(IncompatiblePropertiesLogger.class).
      to(IncompatiblePropertiesLoggerImpl.class);

    // Models

    model(Zeeba.class);
    model(Larry.class);

    // Converters

    useDefaultConverters(true);

    converter(SomeCustomConverter.class);
  }

}

In addition to to cleaner gaeds module, model classes and converters are bound using Multibinder so it’s perfectly fine to install multiple AbstractGaedsModule derived modules in one Guice Injector. There are only two things to keep in mind:

  • Model class and Converter duplicates are not allowed (guice will complain)
  • you may call useDefaultConverters(boolean) only once (in one AbstractGaedsModule)
March 04, 2010
»

Models Library Current Status

This post is about Models — the GWT library which hopefully will greatly help to build client-server communication, model properties-to-view binding and in application architecture in general by simplifying all boilderplate code needed to manage downloaded object graphs, presenting them to users and allowing users to act on them. What I have currently is initial implementation of KVC, KVO, Bindings, ForwardingModel, ListController, ObjectController and DataSource (with DataStore). Nothing are release ready. Controllers and DataSource are so badly implemented I’m a bit ashamed to show them.

The main purpose of post (aside from loud-thinking) is to try to get some feedback before API is locked. So if you have some comments, critique or just want to say hi, feel free to mail me to ampatspell@gmail.com.

Lets start from lowest level API — Key-Value Coding and Key-Value Observing (the terms come from Foundation framework in Cocoa). Basically this is easy to use property change observation and property value setting by name support for classes. In whole Model library context the classes are model objects either fetched from DataSource (server) or created locally.

Observing

The basic model property change observation looks like this:

User user = GWT.create(User.class);

ObserverRegistration registration = asReflection(user).observe("address.country", 
  String.class, 
  new Observer<String>() {
    public void onValueChange(KeyPath keyPath, Operation operation, IndexSet indexes, 
                              String oldValue, 
                              String newValue) {
      // called after value change
    }
  });

registration.remove();

ObservableReflection reflection = ((Observable) entry).getReflection();

Observer<T> callback is called when either of following are called:

  • user.setAddress(newAddress);
  • user.getAddress().setCountry("New country");

Callback is not called with initial value. See bindings why this is not needed.

Operation can be one of SET, INSERT, REMOVE, CLEAR and so on (SET for properties, others for List operations).

IndexSet is Range like class for defining List operation indexes (list.addAll(int index, E element) notifies observers about INSERT operation and IndexSet with single index).

asReflection(Object) is statically imported helper method what just casts user instance to Observable.

Value setting by keypath

The second low-level building block is Key-Value Coding that allows to set Observable object properties not only by using setters directly but also by key path. “address.country” in previous code snippet is key path.

Lets look at small example using the same user entity:

asReflection(user).setValueForKeyPath("address.country", "Zeebaburg");

this is the same as calling user.getAddress().setCountry("Zeebaburg") (with null checking) but can be used to dynamically set values for arbitrary keys (properties).

GWT.create

Of course for both of those things to work GWT generator is used. This is the reason why GWT.create() is required and so this code will only work in GWT runtime and unittests run with GWTTestCase. Currently I don’t have Java reflection and cglib based implementations for Observable but it can be created to support standalone JUnit unittests. For now I’m ignoring this while I’m not finished with API design.

Bindings

Next low-level API is Bindings. This feature allows to “bind” two targets (BindingTarget) so if one of them is changed, the other is notified with new optionally converted value. Also when binding is created initial value is set from “left” target to “right”.

Currently I have three notable binding targets:

  • ObservableTarget — for Observable models
  • HasValueTarget<T> — for view bindings
  • HasTextTarget — for view bindings

Others are easy to write.

As you might guessed this main purpose is to bind model properties to GWT Widgets. But actually it turns out that binding ObservableTarget to other ObservableTarget is as important as Widget targeted ones.

Lets look at two low-level Bindings usage examples:

Observable to Observable:

bindings.bind(
  observable(list, "selection", BlogEntry.class), 
  observable(selected, "content", BlogEntry.class)
);

Observable to HasValue

bindings.bind(
  observable(model, "key", String.class), 
  hasText(view.key())
);

// transformer is required because binding target types differ
// otherwide the code won't compile
bindings.bind(
  observable(model, "status", Integer.class), 
  hasText(view.status()), 
  new StatusToStringArrayTransformer()
);

"bindings" variable is instance of Bindings which allows to create and bind new bindings (and store them in DeferredCollection which allows concurrent modifications by deferring adds, removes, clear after iterator has finished) and unbind them with one bindings.clear() call.

The binding itself is created like this:

Binding<L, R> binding = new Binding<L, R>(left, right, transformer);
binding.bind();
// ...
binding.unbind();

Bindings and Binding by themselselves doesn’t require GWT.create at least now but ObservableTarget needs Observable of course.

Forwarding

The last low-level tool is Forwarding which is Observable proxy object that represents other Observable object while trying to be invisible for its clients. This is sometimes needed for current DataSource API implementation and I think this won’t change.

To create Model (Observable) and it’s Forwarding instance we start with 2 classes. GWT.create doesn’t take parameters so Forwarding must be additional class (or better yet interface. But for now it is class.)

public class BlogEntry implements IsModel {
  
  String title;
  
  public String getTitle() {
    return title;
  }
  
  public void setTitle(String title) {
    this.title = title;
  }
  
}

public class BlogEntryForwarding extends BlogEntry implements IsModel, IsForwarding {
}

Now we can GWT.create them and use:

BlogEntry model = GWT.create(BlogEntry.class);

BlogEntry proxy = GWT.create(BlogEntryForwarding.class);
// asForwarding is just "safe" cast to Forwarding w/ assert

Forwarding proxyForwarding = Forwardings.asForwarding(proxy);
proxyForwarding.setForwardingTarget(model);

// the same for model observers (except setForwardingTarget change)
Observables.asReflection(proxy).observe("title", String.class, new Observer<String>(){
  void onValueChange(KeyPath keyPath, Operation operation, IndexSet indexes, 
                     T oldValue, T newValue) {
                    
    // 1st property change
    assertEquals(null,       oldValue);
    assertEquals("Title #1", newValue);
    
    // 2nd property change
    assertEquals("Title #1", oldValue);    
    assertEquals("Title #2", newValue);
    
    // result of setForwardingTarget. Old model is _not_ notified about this change obviosly
    assertEquals("Title #2", oldValue);
    assertEquals("New Title", newValue);
  }
});

// model property change

model.setTitle("Title #1");
assertEquals("Title #1", proxy.getTitle());

// proxy property change

proxy.setTitle("Title #2");
assertEquals("Title #2", model.getTitle());

// proxy target change

BlogEntry newModel = GWT.create(BlogEntry.class);
newModel.setTitle("New Title");

proxyForwarding.setForwardingTarget(newModel);

And that’s it. We can move on to Model Controllers.

Model Controllers

Before we talk about Model controllers I need to draw a distinction between Observable and Model. First Model is Observable, second it has status represented as int bitfield (yes, not EnumSet or whatever. This I’m almost certain should be called premature optimization). Status is observable property.

Ok, finally model controllers.

Now API has 2 (really badly implemented) ModelControllers:

  • ListController
  • ObjectController

Both should be created using GWT.create to enable observation support for them.

ListController has “content” and “selection” (only single selection currently). Both properties are observable, both publicly available. When ListController content changes, selection is set to null.

ObjectController has observable “content” property.

// bind ListController.selection to ObjectController.content
bindings.bind(observable(list, "selection", BlogEntry.class), observable(selected, "content", BlogEntry.class));

// observe ObjectController.content changes
asReflection(selected).observe("content", BlogEntry.class, new Observer<BlogEntry>() {
  public void onValueChange(KeyPath keyPath, Operation operation, IndexSet indexes, 
                            BlogEntry oldValue, BlogEntry newValue) {

    if (newValue != null)
      ds.fetchDetails(newValue);
  }
});

Also both controllers allows Presenters (or whatever else) to add itself as Target. List controller target has callbacks to:

  • clear – called when list is cleared (maybe speeds up DOM modifications, haven’t checked)
  • create – called when Widget for new list controller entry should be created. It is passed to bind afterwards
  • bind – called when Widget needs to be rebound to new entry (only bound, no rebinding yet)
  • remove, insert and other things not exposed yet

Here is one ListController to UI Target implementation (declared in Presenter):

blogEntries.addTarget(new ListControllerTarget<BlogEntry, BlogEntryListView>() {

  public void clear() {
    view.clearListEntries();
  }

  public BlogEntryListView create() {
    return view.addListEntry();
  }

  public void bind(Bindings bindings, BlogEntry model, BlogEntryListView entry) {

    bindings.bind(
      observable(model, "title", String.class), 
      hasText(entry.subject())
    );
    
    bindings.bind(
      observable(model, "published", Boolean.class), 
      hasText(entry.marker()), 
      new BooleanToPublishedStatusTransformer()
    );

    // when rebind will be supported, HandlerRegistration will be managed by ListController
    entry.selectionClickHandlers().addClickHandler(new ClickHandler() {
      public void onClick(ClickEvent clickEvent) {
        if (view != null) {
          int index = view.getListEntryIndex(clickEvent);
          blogEntries.setSelection(index);
        }
      }
    });
  }
});

hasText is statically imported method which asks HasText and returns HasTextTarget.

While this ListControllerTarget interface is too specific for controller-view binding it suffices for now.

From example we can see how easy is to bind to list entries and how terrible current bindings api look. For Model-* related bindings I’m planning to implement easier to use and reuse API. See below.

ObjectController Target interface is similar:

registration = selection.addTarget(new ObjectControllerTarget<BlogEntry>() {
  public void bind(Bindings bindings, final BlogEntry model) {

    // general info

    bindings.bind(
      observable(model, "key", String.class), 
      hasText(view.key())
    );
    
    bindings.bind(
      observable(model, "status", Integer.class), 
      hasText(view.status()), 
      new StatusToStringArrayTransformer()
    );
    
    bindings.bind(
      observable(model, "title", String.class), 
      hasText(view.title())
    );

    // 
    bindings.bind(
      observable(model, "hasDetails", Boolean.class), 
      hasBoolean(view.showDetails())
    );
      
    // details

    bindings.bind(
      observable(model, "body", String.class), 
      hasText(view.body())
    );
    
    bindings.bind(
      observable(model, "created", Date.class), 
      hasText(view.created()), 
      new DateToStringTransformer()
    );
    
    bindings.bind(
      observable(model, "published", Boolean.class), 
      hasText(view.published()), 
      new BooleanToPublishedStatusTransformer()
    );
    
  }
});

Called when “content” is changed. null value will have separate callback also create callback will be here to support switching UI from null placeholder to actual view.

For general Target API — specific user Target API I’m thinking of something along those lines:

registration = selection.addTarget(simpleBindableTarget(new SimpleBindableTarget<BlogEntry(){
  public void onBind(Bindings bindings, BlogEntry model) {
    /// ...
  }
}));

where simpleBindableTarget is statically imported method that takes simplified, more targeted to usage interface and implements general ObjectControllerTarget.

The other approach would be just using ObjectController observation directly (still by using some simpleBindableTarget as a helper) and to bind UI against ObjectController itself (which would forward events to and from its content). Not yet decided.

So far we have observable models and controllers that helps to map model graph and selection to UI components. Now I’ll try to describe DataSource and Model state.

DataSource

As stated previously, Model has state integer. It has the following values:

int READY = 1 << 1;
int DIRTY = 1 << 2;
int BUSY = 1 << 3;
int ERROR = 1 << 4;

int LOADING = 1 << 5;
int SAVING = 1 << 6;
int DESTROYING = 1 << 7;
int REFRESHING = 1 << 8;

int NEW = 1 << 9;
int DESTROYED = 1 << 10;

When model is locally created (transient), it has “NEW | DIRTY” state, when it is currently loading from DataSource (async), the state is set to “BUSY | LOADING”. The state is observable, so view can show “Please wait” while loading, refreshing or deleting model objects.

DataSource has a bit different approach managing asynchronous operations. Basically all DataSource methods are seemingly synchronous. Lets look how the list fetching happens:

public class BlogEntryDataSource extends DispatchDataSource<BlogEntry> {

  public List<BlogEntry> fetchAll() {
    return fetchList(
      new GetBlogEntriesAction(), 
      new DispatchDataSourceCallback<GetBlogEntriesResult, DataSourceList<BlogEntry>>() {
        public void prepare(DataSourceList<BlogEntry> model, GetBlogEntriesResult result) {
          populateList(model, result.getEntries());
        }
      });
  }

}

As we can see from previous code snippet, fetchAll() returns immediately. It returns empty ListModel<BlogEntry> instance which can be set as a content to some ListController. Also the list implements IsModel so it has state property. It is set to “BUSY | LOADING”. When asynchronous call returns, DispatchDataSourceCallback#prepare is called with Result (Command pattern) and the same list what method returned previously. Now its time to add entries to the list. When it happens, List → ListController → Presenter → UI chain is invoked so new entries automatically appears in view.

In similar fashion also single models are fetched:

public class BlogEntryDataSource extends DispatchDataSource<BlogEntry> {

  public BlogEntry fetchLatestEntry() {
    return fetchSingle(
      new GetLatestBlogEntry(), 
      new DispatchDataSourceCallback<GetBlogEntryResult, BlogEntry>() {
        public void prepare(BlogEntry model, GetLatestBlogEntryResult result) {
          populateModel(model, result.getBlogEntry());
        }
      });
  }

}

Returned BlogEntry has all properties set to null and state “BUSY | LOADING”. All would be fine but this way if fetchLatestBlogEntry() is called multiple times, multiple BlogEntry instances for one actual blog entry row/entity would be created. And not always unique model primary key is known prior fetching it (as in this example).

So here comes small trick – instead of returning actual BlogEntry, the BlogEntryForwarding proxy is returned with null or dummy BlogEntry as target. When async returns, DispatchDataSourceCallback#populateModel(BlogEntry, GetLatestBlogEntryResult) is called and it updates BlogEntry key what can be checked for uniqueness in local DataStore. If model was already fetched previously, its properties are updated with new ones, BlogEntryForwarding proxy target is changed to that previous model instance but latest fetched is simply discarded. This process also works for each list item fetched.

Key comparison is done using IsModel#getKey() method what must be implemented by each model.

If model key is known prior fetching (fetchBlogEntryByKey(String key)), DataSource returns model instance without creating a proxy.

The benefits are:

  • zero or one model instance for each key (not counting possible proxies)
  • subsequent fetches will automatically notify view about updated model properties
  • DataSource can transparently cache models

The things that needs to be implemented are:

  • predicates/query names so list fetching can be cached (“fetchLatestEntry” – DataStore should know that “latestEntry” is fetched already before fetching it and not only after fetch find out that model with given key already exists in DataStore. This also applies to lists (“fetchAllEntries” with GAE DataStore cursor or Hibernate/JDBC page number)
  • fetch v.s. refresh (DataSource should be aware of difference)

Prettier Bindings For UI

While basic binding api can look so technical, bindings from observable/model to view should have special approach. Firstly bindings are both target type agnostic, for model-view bindings left side is always model.

Previously I had model-view bindings what looks like this:

binder.model(User.class).with(model(new ModelBuilderDelegate<User>() {
 public void bind(ModelBinder<User> b, User model) {

   b.property("key", String.class).
     to(hasValue(view.getKey())).
     to(hasText(view.getKeyValue()));

   b.property("name", String.class).
     to(hasValue(view.getName())).
     to(hasText(view.getNameValue()));

   b.property("login", String.class).
     to(hasValue(view.getLogin())).
     to(hasText(view.getLoginValue()));

   b.model("address", Address.class).with(model(new ModelBuilderDelegate<Address>() {
     public void bind(ModelBinder<Address> b, Address model) {

       b.property("street", String.class).
         to(hasValue(view.getStreet())).
         to(hasText(view.getStreetValue()));

       b.property("country", String.class).
         to(hasValue(view.getCountry())).
         to(hasText(view.getCountryValue()));

       b.model("details", Details.class).with(model(new ModelBuilderDelegate<Details>() {
         public void bind(ModelBinder<Details> b, Details model) {

           b.property("description", String.class).
             to(hasValue(view.getAddressDescription())).
             to(hasText(view.getAddressDescriptionValue()));

         }
       }));
     }
   }));
 }
}));

Now I haven’t touched that code yet but here is that test form running (also with list handling): http://models.latest.ampatspell-test.appspot.com/

Few notes about validation

While gwt-pectin and nearly all other binding frameworks also handle validation I feel that validation support should/must be separated from model-view bindings simply because it would be great to run the same validation definition in server-side. I deem it very useful. Also if validators are not instantiated directly, few validators what can not be run in GWT, can be skipped there and only run at server-side.

bones-validator does that (but API is far from great).

public class UserValidationModule extends AbstractValidationModule<UserDto> {

  @Override
  public void prepare(UserDto o) {
    field("name", o.getName()).with(Unique.class); // runs only in server side
    field("name", o.getName()).with(NotBlank.class);
    // allowBlank() is EmailValidator instance method call for this particular field
    field("email", o.getEmail()).with(Email.class).allowBlank();
  }

}

If model-view bindings are aware of validation (if needs to be), I think it can be done while keeping bindings and validation APIs clean.

February 24, 2010
»

Maven Archetypes Updated

Just deployed latest SNAPSHOT of maven archetypes:

  • bones-google-quickstart-archetype (Google GWT & AppEngine Web App With Bones and Gaeds)
  • google-quickstart-archetype (Google GWT & AppEngine Web App)

They’re finally updated to latest dependencies and even compile and run.

mvn archetype:generate -DarchetypeCatalog=http://www.amateurinmotion.com/repository

To try them out make sure you have Maven 2.2.1 and run:

$ mvn archetype:generate -DarchetypeCatalog=http://www.amateurinmotion.com/repository

...

Choose archetype:
1: http://www.amateurinmotion.com/repository -> google-quickstart-archetype               ↩ 
    (Google GWT & AppEngine Web App)
2: http://www.amateurinmotion.com/repository -> bones-google-quickstart-archetype         ↩
    (Google GWT & AppEngine Web App With Bones and Gaeds)
Choose a number:  (1/2): 2

...

Define value for groupId: : com.zeeba
Define value for artifactId: : zeeba
Define value for version:  1.0-SNAPSHOT: : 
Define value for package:  com.zeeba: : 
Confirm properties configuration:
groupId: com.zeeba
artifactId: zeeba
version: 1.0-SNAPSHOT
package: com.zeeba
 Y: : 

...

$ cd zeeba
$ mvn install # compiles GWT module, packs war ready for AppEngine deployment

...

[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] ------------------------------------------------------------------------
[INFO] Bones Quickstart ...................................... SUCCESS [1.631s]
[INFO] Quickstart -- Shared .................................. SUCCESS [1.480s]
[INFO] Quickstart -- GWT ..................................... SUCCESS [30.084s]
[INFO] Quickstart -- Server .................................. SUCCESS [2.208s]
[INFO] Quickstart -- War ..................................... SUCCESS [4.255s]
[INFO] ------------------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 40 seconds

...

$ cd zeeba-gwt
$ mvn google:gwt-run          # to run GWT in Development Mode

$ cd zeeba-war
$ mvn google:appengine-run    # to run AppEngine local server with compiled GWT module

Deploy to AppEngine

To deploy app to AppEngine, set your login, password in ~/.m2/settings.xml:

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
                      http://maven.apache.org/xsd/settings-1.0.0.xsd">
  <profiles>
    <profile>
      <id>appengine</id>
      <properties>
        <appengine-email> ... </appengine-email>
        <appengine-password> ... </appengine-password>
      </properties>
    </profile>
  </profiles>
  <activeProfiles>
    <activeProfile>appengine</activeProfile>
  </activeProfiles>
</settings>

Run:

$ mvn deploy # needs maven distributionManagement/repository

or 

$ mvn install
$ cd zeeba-war
$ mvn google:appengine-deploy

Info about google-maven-plugin can be found at plugin information site.

Plugin and Bones also can be used with Springframework outside AppEngine environment.

February 24, 2010
»

ListController 1st Working Prototype

Just got working the first prototype implementation of ListController for GWT (name comes from Cocoa NSArrayController).

Here is small screencast that shows List<BlogEntry> observation and “reaction” to element adds (ListController) and element property changes (Bindings). Video has no sound, better viewed full-screen.

ListController<T> takes content observable List<T> and observes ADD (later will also handle REMOVE, REPLACE, CLEAR) operations. When it happens, calls:

  • ListControllerTarget#create that should add the widget to the screen
  • ListControllerTarget#bind that should bind widget to model properties
blogEntries.addDelegate(new ListControllerTarget<BlogEntry, BlogEntryListView>() {

  public BlogEntryListView create() {
    return view.addListEntry();
  }

  public void bind(Bindings bindings, BlogEntry model, BlogEntryListView view) {
    // bind BlogEntry#title to HasText subject()
    bindings.bind(observable(model, "title", String.class), hasText(view.subject()));
    
    // bind BlogEntry#published to HasText marker() using boolean to marker text transformer
    // (which transforms true to "Published" and false to "Draft")
    bindings.bind(observable(model, "published", Boolean.class), hasText(view.marker()), 
      new BooleanToMarkerTransformer());
  }
});

ObservableList<T> is an ArrayList<T> subclass which implements IsObservable and overrides add, remove mutators and notifies about element changes.

BlogEntry implements IsModel which extends IsObservable and also is observable.

Observation and Bindings are based on IsObservable » Observable:

BlogEntry entry = GWT.create(BlogEntry.class) // needs GWT generator

ObserverRegistration reg = ((Observable) entry).getReflection().observe("title", String.class, new Observer<String>() {
  public void onValueChange(KeyPath keyPath, Operation operation, IndexSet indexes, String oldValue, String newValue) {
    // "title" value has changed
  }
});

reg.remove(); // to remove observer when it is not needed anymore

Next: ListController selection, ObjectController, DataSource a la NSManagedObjectContext / SproutCode DataSource or something like that.

February 09, 2010
»

Regarding GWT-MooTools

For those who don’t know, after spending a lot of time trying to write my own GWT-only animation framework and after trying to integrate Scriptaculous, later JQuery, I settled to MooTools as my animation library of choice and wrote small GWT wrapper around it.

Now along with already existing FX wrappers for:

  • Morph
  • Tween

Today I upgaded it to mootools 1.2.4 and added:

  • Slide
  • Reveal
  • Scroll

Live demo

gwt-mootools can be downloaded from project downloads, other stuff nearby.

February 01, 2010
»

GWT History Management With Places

This is a second post in two post series about History Management in GWT. In first part I introduced the definition of Place and few necessary “design patterns” which makes it easy to implement history aware rich applications in GWT. The only missing piece of puzzle I left till the next time was cleaning up the implementation of SimpleHistoryManager.

In first part the SimpleHistoryManager is the central compnent in whole application History architecture. It:

  • calls History.addItem(String)
  • handles history token value changes
  • handles application Events which should be turned in history items
  • fires application Events for each History Place

While for small applications this central history manager class may suffice, it is not flexible enough and it only grows in size as application functionalty grows. This “doesn’t scale”. We need to split HistoryManager in logical parts that can be easily managed independently. For history management, the smallest part is Place class and for each application place, Place implementation should:

  • fire application Events on place change
  • handle application Events which should be turned in History items

Also there are few things what Place implementations should not do:

  • parse, compare string tokens
  • create new string tokens
  • call History methods directly

Those tasks can be perfectly be abstracted away in Place library that can be reused (and improved separately) between projects.

It happens I’ve written Places library and the following will explain how to setup and use its public API. If you’re currious about implementation details, feel free to dig in to the sources.

Downloading

Currently Places is at 1.0-SNAPSHOT state. I’ll be releasing 1.0 soon.

Using Maven

If you’re using Maven, add the following repository to your pom (or better in your settings.xml / Nexus):

<repositories>
  <repository>
    <id>amateurinmotion</id>
    <name>amateurinmotion m2 repository</name>
    <url>http://www.amateurinmotion.com/repository</url>
  </repository>
</repositories>

And add dependencies:

<dependency>
  <groupId>com.ampatspell.places</groupId>
  <artifactId>places-client</artifactId>
  <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
  <groupId>com.ampatspell.places</groupId>
  <artifactId>places-client</artifactId>
  <version>1.0-SNAPSHOT</version>
  <classifier>sources</classifier>
  <scope>compile</scope>
</dependency>

Warning: Places library declares few dependencies which are not in central Maven repository like GIN, GWT, Guice version which works in AppEngine. They reside in my Maven repository and will be included in compile classpath. If it is not desired, maven dependency exclusions are your friends.

Manually

Places and its dependencies:

Now that you have all necessary jars we can move on GWT and GIN configuration.

Setup

Add the inherits to gwt.xml:

<module>
  <inherits name="com.ampatspell.bones.core.Core"/>
  <inherits name="com.ampatspell.places.Places"/>
</module>

Install PlacesModule in your GIN Module:

public class ExampleGinModule extends AbstractGinModule {

  @Override
  protected void configure() {
    // Bones (optional)
    install(new BonesCoreGinModule());
    bind(DispatchServiceAsync.class).to(DispatchServiceAsyncImpl.class);
    
    // Places Service
    install(new PlaceServiceModule());
    bind(PlacesLogger.class).to(GWTPlacesLogger.class);
    bind(EventBus.class).to(BaseEventBus.class); // or your EventBus implementation
  }
  
  @Provides
  @Singleton
  public List<Place> providePlaces() {
    List<Place> places = new ArrayList<Place>();
    return places;
  }  

}

If you don’t want to bring all Bones functionality (post about Bones library comming soon), you may install only PlacesModule and bind EventBus.class to your HandlerManager implementation that poses as EventBus in your application. No other dependencies exist.

Also, as we can see, there’s binding to empty List of Place instances bound. There we should list all application places.

Place

Lets start with Place interface and talk about what is Place scope is and what implementations should do:

public interface Place {

  /**
   * History token url sub-pattern for this {@link Place}.
   *
   * @return string pattern for {@link Place}. May be blank or null
   */
  String getPattern();

  /**
   * Returns if {@link Place#getPattern()} overrides it's parent pattern.
   *
   * @return <code>true</code> if pattern overrides parent, <code>false</code> otherwise
   */
  boolean overridesParent();

  /**
   * @return parent {@link Place} or null
   */
  Place getParent();

  /**
   * Parameters which will be added to token
   *
   * @return additional parameter names or null
   */
  String[] getAdditionalParameters();

  /**
   * Called before first place is invoked
   *
   * @param binding binding context for moving to next {@link Place} and 
   *                for registering event handlers
   */
  void bind(PlaceBindingContext binding);

  void unbind();

  /**
   * Called when History token matches given {@link Place} ir one of the 
   * child {@link Place}s.
   * <p/>
   * Note: {@code invocation.invoke()} <b>must</b> be called in each invoke call
   *
   * @param invocation context for getting current parameters (from pattern or from 
   *                   additional parameters) and continuing invocation chain
   */
  void invoke(PlaceInvocationContext invocation);

}

Place implementation does three things (three scopes):

  • Matching — provides information about History token pattern parts which this place manages
  • Binding — handles application Events which should add next History item
  • Invocation — fires application Events to restore place
Scope Method group Description
Matching getPattern() Relative History token pattern string
(may be blank or null)
Matching overridesParent() Whether pattern returned by getPattern() overrides
its parent pattern
Matching getParent() Parent Place (may be null)
Binding bind(PlaceBindingContext) Callback method for adding application Event handlers that should be
turned into new History items
Binding unbind() Invoked on HistoryService unbind. Nearly always useless
Invocation invoke(PlaceInvocationContext) Called when new history token matches this Place pattern
Binding & Invocation getAdditionalParameters() Additional parameter names of which values will be stored
after main token as name=value pairs (a la url parameters)
and should be added for consequent invocations

And my apologies for over-complicated explanation. As we’ll see later, this thing is easy to use.

Matching

Token matching is done using pattern and parent. A pattern can be composed by constant string parts and parameters using {name} syntax. If Place declares:

Pattern Parent Overrides parent
/person/show/{key} null false

The place will match:

Token Matches Key value
/person/show/zeeba true “zeeba”
/person/show/larry true “larry”
/person/show/ false
/person/show/zeeba/ false1

1 — the default parameter matcher uses the /^([a-zA-Z0-9\-_]+)(.*)$/ regex to find parameter value range in string. Let me know if it is too limiting.

When Place implementation returns other place as its parent things start to get more interesting. Those are all Place patterns used in demo application:

Place Pattern Parent Overrides parent
DefaultRedirectPlace("/index") null null false
IndexPlace /index SectionPlace true
PeoplePlace /people SectionPlace true
ShowPersonPlace /show PersonKeyPlace false
EditPersonPlace /edit PersonKeyPlace false
NewPersonPlace /new PersonPlace false
Parents
BasePlace null null false
SectionPlace /{section} BasePlace false
PersonPlace /person BasePlace false
PersonKeyPlace /{key} PersonPlace false

The resuling “full” place patterns are:

places.png

By the way, this UI is included in library. See “Registered Places” section below.

Where for example /person/zeeba/show will match ShowPersonPlace that has the following full parent-child hierarchy:

  • ShowPersonPlace
  • PersonKeyPlace
  • PersonPlace
  • BasePlace

Each of parent places takes a part in all three Place scopes: matching, binding and invocation. It makes sense because this way place binding and invocation can be greatly simplified for end-level Place implementations. Multiple Places can share common parent that can also be bound as @Singleton.

Also while all shown patterns are “one-slash with string or parameter”, its perfectly fine to have pattern with more than one parameter with some delimiter (“/” or something else) between (for example /list/{all}/{page} with PeoplePlace as parent and few additional parameters will work just fine).

Invocation

When Place matches current History token, invoke(PlaceInvocationContext) callbacks are invoked for whole this Place parent-child hierarchy. Invocations are starting with root parent.

For ShowPersonPlace invocation chain contents are:

// BasePlace
String title = invocation.get("title", "Places Example");
invocation.fireEvent(new SetTitleEvent(title));
invocation.proceed();

// PersonPlace
invocation.fireEvent(new WorkspaceSetContentEvent(people, new Command() {
  public void execute() {
    invocation.fireEvent(new MenuSetActiveEvent("people"));
    invocation.proceed();
  }
}));

// PersonKeyPlace
String key = invocation.get("key");
invocation.fireEvent(new ListSetActiveEvent(key));
invocation.proceed();

// ShowPersonPlace
final String key = invocation.get("key");
invocation.fireEvent(new PeopleSetContentEvent(show, new Command() {
  public void execute() {
    invocation.fireEvent(new PersonShowEvent(key));
  }
}));

PlaceInvocationContext (source) has the proceed() method which needs more clarification.

As we’ve seen in post’s first part, Place setup Events must not be fired one after another in one “batch”, instead next Event should only be fired after previous event is handled and presenter hierarchy is able to handle Events (see On-Ready Event Callback).

For this to work in Place parent-child hierarchy PlaceService needs to be notified when next Place.invoke(PlaceInvocationContext) can be safely called. This is done using proceed() method.

So while BasePlace invokes proceed() immediately, PersonPlace invokes it only after PeoplePresenter is bound and ready to receive events because otherwise ListSetActiveEvent won’t be handled.

Other PlaceInvocationMethods should be obvious (see javadocs for interface for more info).

Binding

Place.bind(PlaceBindingContext) callback is used to register application specific Event handlers for events which should be turned into the places. This is done using provided PlaceBindingContext which allows to register to EventBus and takes care of removing handlers on PlacesService unbind. Also PlaceBindingContext (sources) declares StateBuilder state() method which allows in declared event handlers perform transition to next History place (add new History item). StateBuilder (sources) allows to set values for declared pattern parameters and also parameters that are added after “?” as key=value pairs).

One of in ShowPersonPlace.onBind declared event handlers is:

binding.addHandler(PersonShowPlaceEvent.getType(), new PersonShowPlaceHandler() {
  public void onPersonShowPlace(PersonShowPlaceEvent event) {
    binding.state().set("key", event.getKey()).invoke();
  }
});

So when ShowPersonPlaceEvent is fired, this is handled here and History.newItem is called with ShowPersonPlace full history token and event provided person key.

/person/{key}/show
/person/zeeba/show

At some later time StateBuilder will have invokeCurrent() method which will allow to stay in “current” place and only change some additional parameters.

Additional Parameters

Sometimes it can be useful to declare some parameters as “persistent” between place invocations. grid=true comes in mind as a decent example. If all Place implementations share directly or indirectly single BasePlace parent with grid as a additional parameter, the grid value will be added for each and every place invocation and the same BasePlace can fire SetGridVisible event in invoke to show the grid (useful for laying out UI components).

Demo application has title declared as additional parameter in BasePlace and in invoke it fires SetTitleEvent:

@Singleton
public class BasePlace extends AbstractPlace {

  @Inject
  protected BasePlace() {
    addAdditionalParameter("title");
  }

  @Override
  public void onBind(final PlaceBindingContext binding) {
    binding.addHandler(SetTitlePlaceEvent.getType(), new SetTitlePlaceHandler() {
      public void onSetTitlePlace(SetTitlePlaceEvent event) {
        binding.state().set("title", event.getTitle()).invoke();
      }
    });
  }

  @Override
  public void onInvoke(PlaceInvocationContext invocation) {
    String title = invocation.get("title", "Places Example");
    invocation.fireEvent(new SetTitleEvent(title));
    invocation.proceed();
  }

}

To see the functionality in action, open demo application with ‘Additional Parameters’ as title and click on few links. The result is that the string “Additional Parameters” follows the place changes without explicit set in each binding().state() call.

Registered Places

Library also contains “Registered Places” UI for visualizing currently registered Placepattern bindings in application. Basic usage:

import com.ampatspell.places.client.api.PlacesService;
import com.ampatspell.places.client.ui.places.PlacesPresenter;

public class Example implements EntryPoint {

  public void onModuleLoad() {
    ExampleGinjector injector = GWT.create(ExampleGinjector.class);

    PlacesService placeService = injector.getPlacesService();
    placeService.bind();

    PlacesPresenter placesPresenter = injector.getPlacesPresenter();
    placesPresenter.bind();
    RootPanel.get().add(placesPresenter.getView().getViewWidget());
    placesPresenter.show();
  }

}

EOF

I hope this will help someone at least a bit to understand this approach of GWT History management in general and use of PlacesService. I’ve spent a lot of time trying to come up with some simple and useful history management guidelines for my projects and this is best approach I’m using with good results.

One more time few links:

Questions? Feel free to mail me directly to email address which can be found in about section.

January 28, 2010
»

Mercurial Branch in Bash Prompt

Git has nice bash function for showing current branch and status in bash prompt. Here is the branch only version for Mercurial:

hg_branch.png

__hg_ps1 ()
{
  if [ "$(hg root 2> /dev/null)" ]; then
    printf "$1" "$(hg branch)"
  fi
}

export PS1='\u:\w$(__hg_ps1 " (%s)\$ '

and PS1 version with colors:

export PS1='\u:\w$(__hg_ps1 " \[\033[1;31m\](%s)\[\033[0m\]")\$ '
January 17, 2010
»

GWT History Management

This is a first post in two post series about one history management approach for non-trivial GWT applications. This approach I deem done right (in contrast to good enough).

In the following I assume that general application architecture follows MVP (or similar) design and uses some global event or notification system (for example EventBus based on HandlerManager or something similar to NSNotificationCenter) used in Cocoa applications. Examples has GIN annotations where appropriate.

Fictional Application

Lets agree on one concrete application as a basis of discussion of history and places.

history-1/01.png

The application consists of two sections (managed by “root” WorkspacePresenter) that are represented with two top tabs, each section has root presenter and people section has nested child presenters:

Section Container Presenter
Index Content Index
People Master Person list
Detail Blank
Show person
Edit person

Screen shot is shows People section with “Show person” detail presenter open, linked site uses Places library (see “Terrible Footnote” below, but basic concepts described here apply).

History token — Place

From previous table we can gather a list of application states where permanent links (using history token) are needed. Lets call those states places.

Section State History token / Place pattern
Index /index
People No person selected /people
Showing person /person/{key}/show 1
Showing edit person form /person/{key}/edit 1

1 where {key} is Person model primary key.

In GWT the history management can be implemented using History singleton class what manages the part of the URL that comes after #. It allows us to:

  • register to token value changes,
  • change token (that will fire value change event) and
  • fire current token state.

When History#fireCurrentHistoryState() is called, it notifies handlers about new token value. Token itself is a String that can be parsed and reacted upon (think routes in Rails applications or Servlet mapping in web.xml).

This can be done in HistoryService class that must know about each and every history token pattern used in application and should be able to react on them. This way it is easy to implement GWT.runAsync based application code splitting where application initial download contains only HistoryService and split point loaders that initiates additional code loading when needed.

SimpleHistoryManager

Lets take a look at History manager simplified implementation (in next blog post this class will be replaced with sane implementation, other concepts still apply):

@Singleton
public class SimpleHistoryManager implements ValueChangeHandler<String> {

  private final EventBus eventBus;

  @Inject
  public SimpleHistoryManager(EventBus eventBus) {
    this.eventBus = eventBus;
  }

  public void bind() {
    History.addValueChangeHandler(this);
    History.fireCurrentHistoryState();
  }

  public void onValueChange(ValueChangeEvent<String> stringValueChangeEvent) {
    String token = stringValueChangeEvent.getValue();
    onTokenChanged(token);
  }

  private void onTokenChanged(String token) {
    if (token.equals("/index") || token.equals("")) {
      showIndexSection();
    } else if (token.equals("/people")) {
      showPeopleSection();
    } else if (token.startsWith("/person/")) {
      String[] array = token.split("/");
      String key = array[2];
      String action = array[3];

      if (action.equals("/show")) {
        showPerson(key);
      } else if (action.equals("/edit")) {
        showEditPerson(key);
      }
    }
  }

  private void showIndexSection() {
  }

  private void showPeopleSection() {
  }

  private void showPerson(String key) {
  }

  private void showEditPerson(String key) {
  }

}

When bind() is called, the instance registers itself as token value change handler and fires first value change event which in turn invokes onTokenChanged() for the first time in application current life cycle. onTokenChanged parses (parsing is incomplete and without any error handling) token string and delegates actual work to methods listed below. Fine, what’s next?

Events.

To presenter “Index section”:

  • WorkspacePresenter must show IndexPresenter by setting IndexPresenter#getView() to it’s View#setContentView(BaseView),
  • MenuPresenter must set “index” as active menu item.

Menu is easy:

eventBus.fireEvent(new MenuSetActiveEvent("index"));

But before we implement WorkspacePresenterIndexPresenter relations we have one design decision to make about Presenter hierarchy.

Presenter Hierarchy

Does WorkspacePresenter keeps reference to Provider<IndexPresenter> and all other presenter providers it may need?

It surely can be done (by using SectionService#getSectionByKey(String) or something like that) but it unnecessary complicates presenter and sometimes makes it harder to implement history support and GWT.runAsync’able code splitting. Instead of WorkspacePresenter (and presenters in general), HistoryManager should manage dynamic presenter hierarchy. By dynamic I mean those child presenters that can be replaced by different presenters in application runtime in contrast to static ones that can be safely bound in parent — for example MenuPresenter and LogPresenter is bound directly in WorkspacePresenterImpl.

So now we need an event that SimpleHistoryManager fires and WorkspacePresenter handles, gets Presenter instance from it, calls bind() and sets in its own View as sub-view:

BasePresenter<?> indexPresenter = ...;
eventBus.fireEvent(new WorkspaceSetContentEvent(indexPresenter));
public void onWorkspaceSetContent(final WorkspaceSetContentEvent event) {
  setContent(event.getPresenter());
}

public void setPresenter(BasePresenter<?> presenter) {
  presenter.bind();
  view.setContentView(presenter.getView());
  presenter.show();
}

But there is one small caveat with HandlerManager which nearly always is used as EventBus implementation. WorkspacePresenter content Presenter may register itself to @Singleton EventBus as an event handler. But if event handler is added to HandlerManager inside fired event from the same HandlerManager, that handler will be live only after original event handler has returned.

Basically it means that:

eventBus.addHandler(WorkspaceSetContentEvent.getType(), new WorkspaceSetContentHandler() {
  public void onWorkspaceSetContent(WorkspaceSetContentEvent event) {

    // this handler will handle PersonShowEvent only after this 
    // WorkspaceSetContentEvent handler will return
    eventBus.addHandler(PersonShowEvent.getType(), new PersonShowHandler() {
      public void onPersonShow(PersonShowEvent event) {
      }
    });

    // this event won't be handled by previous handler
    eventBus.fireEvent(new PersonShowEvent("foo"));
  }
});

To overcome this we need small design pattern. I’ll even give it a fancy name.

On-Ready Event Callback

Simply put it is an GwtEvent with additional Command parameter. This Command callback must be called when presenter’s bind() is called and bound presenter is ready to handle events (by using DeferredCommand if needed).

public class WorkspaceSetContentEvent extends GwtEvent<WorkspaceSetContentHandler> {

  // TYPE, ivars removed -- less noise

  public WorkspaceSetContentEvent(PresenterProvider<?> presenter, Command callback) {
    this.presenter = presenter;
    this.callback = callback;
  }

  public PresenterProvider<?> getPresenter() {
    return presenter;
  }

  public Command getCallback() {
    return callback;
  }

}

See WorkspaceSetContentEvent for full source code of this Event.

Lets ignore PresenterProvider interface for a moment — it needs it’s own small section.

Now we can implement all SimpleHandlerManager#show* methods:

private void showIndexSection() {
  fireMenuSetActive("index");
  fireWorkspaceSetContent(indexPresenterProvider, null);
}

private void showPeopleSection() {
  showPeopleSection(blankPresenterProvider, null, null);
}

private void showPerson(final String key) {
  showPeopleSection(showPresenterProvider, key, new Command() {
    public void execute() {
      eventBus.fireEvent(new PersonShowEvent(key));
    }
  });
}

private void showEditPerson(final String key) {
  showPeopleSection(editPresenterProvider, key, new Command() {
    public void execute() {
      eventBus.fireEvent(new PersonEditEvent(key));
    }
  });
}

private void showPeopleSection(final PresenterProvider<?> content, final String key, 
    final Command command) {
    
  fireMenuSetActive("people");
  fireWorkspaceSetContent(peoplePresenterProvider, new Command() {
    public void execute() {
      eventBus.fireEvent(new ListSetActiveEvent(key));
      eventBus.fireEvent(new PeopleSetContentEvent(content, command));
    }
  });
}

private void fireMenuSetActive(String key) {
  eventBus.fireEvent(new MenuSetActiveEvent(key));
}

private void fireWorkspaceSetContent(PresenterProvider<?> presenter, Command command) {
  eventBus.fireEvent(new WorkspaceSetContentEvent(presenter, command));
}

And for example WorkspaceSetContentHanlder inside WorkspacePresenter looks like this:

public void onWorkspaceSetContent(final WorkspaceSetContentEvent event) {
  content.set(event.getPresenter(), event.getCallback());
}

Next we need to define what is:

  • content from previous handler code listing,
  • PresenterProvider<P extends BasePresenter<?>>

They both forms one simple concept described in next section.

PresenterProvider & PresenterSwitch

Let’s start with some reasons behind PresenterProvider interface and what part it plays in overall architecture. Let’s return to two place implementations: “showing person” and “showing edit person form”:

private void showPerson(final String key) {
  showPeopleSection(showPresenterProvider, new Command() {
    public void execute() {
      eventBus.fireEvent(new PersonShowEvent(key));
    }
  });
}

private void showEditPerson(final String key) {
  showPeopleSection(editPresenterProvider, new Command() {
    public void execute() {
      eventBus.fireEvent(new PersonEditEvent(key));
    }
  });
}

private void showPeopleSection(final PresenterProvider<?> content, final Command command) {
  fireMenuSetActive("people");
  fireWorkspaceSetContent(peoplePresenterProvider, new Command() {
    public void execute() {
      eventBus.fireEvent(new PeopleSetContentEvent(content, command));
    }
  });
}

As we can see from code, both place implementations sets PeoplePresenter as WorkspacePresenter content and only changes PeoplePresenter content.

For the time being we have taken into consideration only one of two possible times when History value change handler is invoked:

  • after History.fireCurrentHistoryState() as a part of EntryPoint#onModuleLoad invocation,
  • any time after History.newItem(String token) is called.

When application is launched, WorkspacePresenter content view will be empty. It is History manager responsibility to fill it with requested-by-history-token Presenter-View. But when switching for example from “Showing person” to “Showing edit person form” places only PersonPresenter content needs to be switched from ShowPresenter to EditPresenter. The WorkspacePresenter content must not be replaced by new instance of the same PersonPresenter (otherwise content will blink because of DeferredCommand that is needed after Presenter#bind() and just because this is simply plain useless).

Though SimpleHistoryManager shouldn’t and doesn’t keep track of current presenter hierarchy the decision if “current” presenter in all presenter hierarchy levels needs to be done Presenters side because it is easier to implement and allows us to modify presenter hierarchy also without history support if such need arises (as not all application states needs history token).

PresenterProvider

GIN allows to inject class dependencies in two ways:

  • Instance directly
  • Instance via provider (using Provider<T>)

The Provider<T> interface declaration is:

public interface Provider<T> {
  T get();
}

This interface requires us construct a new Presenter in order to get the instance class that can be used in comparison between “current” Presenter and Presenter which is received as a part of *SetContentEvent. To remove this possibly unnecessary Presenter construction the minor extension to Provider<T> interface is needed:

public interface PresenterProvider<P extends BasePresenter<?>> 
  extends Provider<P>, HasInstanceClass {
}

public interface HasInstanceClass {
  Class<?> getInstanceClass();
}

Then by using PresenterProvider<P> we can efficiently handle *SetContentEvents:

private Class<?> currentPresenterInstanceClass;

public void onAnySetContentEvent(AnySetContentEvent event) {
  PresenterProvider<?> presenterProvider = event.getPresenter();

  Class<?> nextInstanceClass = presenterProvider.getInstanceClass();
  if (currentPresenterInstanceClass != nextInstanceClass) {
    BasePresenter<?> next = presenterProvider.get();
    currentPresenterInstanceClass = nextInstanceClass;
    // unbind previous presenter
    // bind, view.setSomethingView(next.getView()), show
    // DeferredCommand.addCommand(event.getCallback());
  }
}

Now we can construct and initialize Presenter instance only if it is not the same as current. But there is too much noise in event handler.

PresenterSwitch

Let’s create PresenterSwitch helper class that (fully, comparing to previous code snippet) implements and encapsulates the logic which resides in onAnySetContentEvent in previous snippet:

public class PresenterSwitch {

  public interface SwitchCallback {
    void onSetView(BasePresenter<?> presenter);
  }

  private SwitchCallback callback;
  private Class<?> presenterClass;
  private BasePresenter<?> presenter;

  public void bind(SwitchCallback callback) {
    this.callback = callback;
  }

  public void unbind() {
    callback = null;
    presenterClass = null;
    if (presenter != null) {
      presenter.unbind();
      presenter = null;
    }
  }

  public void set(PresenterProvider<?> provider, Command onBoundCommand) {
    Class<?> instance = provider != null ? provider.getInstanceClass() : null;
    if (presenterClass != instance) {
      final BasePresenter<?> next = provider != null ? provider.get() : null;

      if (presenter != null) {
        presenter.unbind();
      }

      presenter = next;
      presenterClass = instance;

      if (next != null) {
        next.bind();
        callback.onSetView(next);
        next.show();
        if (onBoundCommand != null)
          addDeferredCommand(onBoundCommand);
      } else {
        callback.onSetView(null);
        if (onBoundCommand != null)
          onBoundCommand.execute();
      }
    } else {
      if (onBoundCommand != null)
        onBoundCommand.execute();
    }
  }

  private void addDeferredCommand(Command command) {
    DeferredCommand.addCommand(command);
  }

}

And bind it inside WorkspacePresenter:

public class WorkspacePresenterImpl extends AbstractBasePresenter<WorkspaceView> 
  implements WorkspacePresenter, WorkspaceSetContentHandler {

  private final PresenterSwitch content = new PresenterSwitch();

  @Inject
  public WorkspacePresenterImpl(WorkspaceView view, EventBus eventBus) {
    super(view, eventBus);
  }

  @Override
  public void onBind() {
    register(eventBus.addHandler(WorkspaceSetContentEvent.getType(), this));
    content.bind(new SwitchCallback() {
      public void onSetView(BasePresenter<?> presenter) {
        view.setContentView(presenter != null ? presenter.getView() : null);
      }
    });
  }

  @Override
  public void onUnbind() {
    content.unbind();
  }

  @Override
  public void onShow() {
  }

  public void onWorkspaceSetContent(final WorkspaceSetContentEvent event) {
    content.set(event.getPresenter(), event.getCallback());
  }

}

The same goes with PeoplePresenter and its PeopleSetContentEvent.

Lastly we need to construct PresenterProvider instances for each Presenter in SimpleHistoryManager so lets create a PresenterProviderFactory interface:

public interface PresenterProviderFactory {

  <P extends BasePresenter<?>> PresenterProvider<P> create(Class<?> clazz, 
                                                           Provider<P> provider);

}

and define full constructor for SimpleHistoryManager:

private final EventBus eventBus;
private final PresenterProvider<IndexPresenter> indexPresenterProvider;
private final PresenterProvider<PeoplePresenter> peoplePresenterProvider;
private final PresenterProvider<BlankPresenter> blankPresenterProvider;
private final PresenterProvider<ShowPresenter> showPresenterProvider;
private final PresenterProvider<EditPresenter> editPresenterProvider;

@Inject
public SimpleHistoryManager(EventBus eventBus, PresenterProviderFactory f,
                            Provider<IndexPresenter> indexPresenterProvider,
                            Provider<PeoplePresenter> peoplePresenterProvider,
                            Provider<BlankPresenter> blankPresenterProvider,
                            Provider<ShowPresenter> showPresenterProvider,
                            Provider<EditPresenter> editPresenterProvider) {
  this.eventBus = eventBus;

  this.indexPresenterProvider = f.create(IndexPresenter.class, indexPresenterProvider);
  this.peoplePresenterProvider = f.create(PeoplePresenter.class, peoplePresenterProvider);
  this.blankPresenterProvider = f.create(BlankPresenter.class, blankPresenterProvider);
  this.showPresenterProvider = f.create(ShowPresenter.class, showPresenterProvider);
  this.editPresenterProvider = f.create(EditPresenter.class, editPresenterProvider);
}

Looks terrible, doesn’t it? See “Terrible Footnote” below.

Also note that get() may or may not be instance of getInstanceClass(). This actually calls for rename of getInstanceClass() to something different more adequate.

Changing place — Place Events

So we have implemented the complicated side of history management — reaction to history tokens. Last thing what’s missing is adding new history items.

If SimpleHistoryService parses history token, it also must create new tokens to encapsulate token string related functionality in one place and to hide token strings from other parts of application. If it creates tokens, it needs to know when create them. So we need few new events that can be fired from application presenters and handled by SimpleHistoryService. Full list for this application is:

  • MenuClickEvent
  • PeopleShowPlaceEvent
  • PeopleEditPlaceEvent
  • PersonShowPlaceEvent

For example MenuClickEvent is fired from MenuPresenter:

String key = ...;
eventBus.fireEvent(new MenuClickEvent(key)); 

And other events are fired similarly. Lets add bindHandlers() method to service and call it from bind():

public void bind() {
  History.addValueChangeHandler(this);
  History.fireCurrentHistoryState();

  bindHandlers();
}

private void bindHandlers() {
  eventBus.addHandler(PeopleShowPlaceEvent.getType(), new PeopleShowPlaceHandler() {
    public void onPeopleShowPlace(PeopleShowPlaceEvent event) {
      newItem("/people");
    }
  });
  eventBus.addHandler(PersonEditPlaceEvent.getType(), new PersonEditPlaceHandler() {
    public void onPersonEditPlace(PersonEditPlaceEvent event) {
      newItem("/person/" + event.getKey() + "/edit");
    }
  });
  eventBus.addHandler(PersonShowPlaceEvent.getType(), new PersonShowPlaceHandler() {
    public void onPersonShowPlace(PersonShowPlaceEvent event) {
      newItem("/person/" + event.getKey() + "/show");
    }
  });
  eventBus.addHandler(MenuClickEvent.getType(), new MenuClickHandler() {
    public void onMenuClick(MenuClickEvent event) {
      newItem("/" + event.getKey());
    }
  });
}

private void newItem(String token) {
  History.newItem(token);
}

Each of those event handlers will add new History item that will fire History value change handler logic inside onTokenChanged() method.

And this prototype of history aware application is working just fine.

That Terrible Footnote

Regarding those ugly SimpleHistoryManager bindHandlers(), constructor and string parsing: next blog post will be about Places library which will replace SimpleHistoryManager with more sophisticated solution — list of hierarchic Place instances where each manages only few Presenter providers, few events and only sub-part of history token pattern with or without parameters.

 
Internet Explorer 6
Are you serious?