Sunday, November 08, 2009

Gachi - Query interface added

GAE/j has a built-in Query concept which I have extended into a Gachi Query interface through which all queries must be executed.

At my very first test on the newly implemented interface I ran into a well known "feature" of GAE/j, the "can't operate on multiple entity groups in a single transaction" problem. This exception is thrown when you try to operate in more than one entity-group within a single transaction (as the text describes pretty well :-) ), but I simply just queried for all Wishlists (my example), which are all entity-roots, and then I did a DatastoreService.get(key) using the key returned from the query. "WTF!" I thought - "Can't I query multiple entity-roots? Then how will I list all Wishlists?".. This is infact quite tricky, but I've added a "keysOnly" switch to my Query interface, which will, if set, only work within a single entity-root, but if unset, you may Query mutiple entity-roots, but only see properties (as of now)..

Next up - lazy load of ManyAssociation items (currently they are all instanciated when first accessed)

Friday, November 06, 2009

Gachi - cascade deletes implemented

I've written a roadmap for Gachi and one of the biggest steps was to implement cascade deletes and it's DONE! (as in working :-) )

Since Google App Engine's datastore is kinda hierarchical but then not really, you have to delete all child entities yourself, when you delete the "parent" entity. I'm really pleased with my implementation where I've introduced a meta-info concept where I, when deleting an entity, knows which child entities I must also delete.

Next up - Query interface

Sunday, November 01, 2009

Gachi - first look

Well - as mentioned in previous blog entry I've written a tiny framework to make it easier to store (kinda) POJO's in Google App Engine for Java. This will try to explain what I've got running so far:


@Test
public void testCreateWishlistAndAddRecipient() {
UnitOfWork uow = new GAEUnitOfWork();

All work with entities are done in the scope of a unit of work (UnitOfWork) which is transactional and only for a single entity-group at a time, since GAE/J can only handle transactions within a single entity-group, its very important not to hide this fact from users.

Date created = new Date();

Wishlist wishlist = uow.newEntity(Wishlist.class);
wishlist.title().set("Wishlist for christmas 2009");
wishlist.comment().set("No soft presents");
wishlist.created().set(created);

To create a new entity (in this case entity-group as there are no parents) you use "newEntity" which will return an instance of the class given as second argument.

uow.complete();

"uow.complete" will make all changes persistent using GAE low-level API.

uow = new GAEUnitOfWork();
wishlist = uow.get(wishlist.getEntityReference());
assertEquals("Wishlist for christmas 2009", wishlist.title().get());
assertEquals("No soft presents", wishlist.comment().get());
assertEquals(created, wishlist.created().get());

Well - check to make sure all is persistent as expected...

Recipient recipient1 = uow.newEntity(wishlist, Recipient.class);
recipient1.name().set("Test Person #1");
recipient1.email().set(new Email("testperson1@email.com"));

In this example a Wishlist holds a ManyAssociation which is a collection of Recipient objects. The above creates a new Recipient, which will be stored as a child GAE Entity of the Wishlist Entity (not a Key reference) and therefore it's parent (wishlist) if given as argument in "newEntity(wishlist, Recipient.class)

Recipient recipient2 = uow.newEntity(wishlist, Recipient.class);
recipient2.name().set("Test Person #2");
recipient2.email().set(new Email("testperson2@email.com"));

wishlist.recipients().add(recipient1);
wishlist.recipients().add(recipient2);

uow.complete();

Finally the newly created 2 Recipient objects are added to the "wishlist.recipients()" ManyAssociation (collection) and UnitOfWork is complete (all objects persistet)

uow = new GAEUnitOfWork();
wishlist = uow.get(wishlist.getEntityReference());
ManyAssociation recipients = wishlist.recipients();
assertEquals(2, recipients.getCount());
assertEquals("Test Person #1", recipients.get(0).name().get());
assertEquals("testperson1@email.com", recipients.get(0).email().get().getEmail());
assertEquals("Test Person #2", recipients.get(1).name().get());
assertEquals("testperson2@email.com", recipients.get(1).email().get().getEmail());
}

... and again tests to check all were persistet as expected.

Next up - a look under the hood where child objects are all lazy loaded.

Saturday, October 31, 2009

Gachi - "ORM" for Google App Engine

I've been working on a framework for Google App Engine, the Java version.

Gachi as I've named it, is a tiny framework where I've taken some ideas from QI4J and hence domain driven development for storing entities (POJO's) in the Google App Engine Datastore using the low-level API.

This is one of my testcases:


@Test
public void testCreateWishlistAndAddRecipient() {
UnitOfWork uow = new GAEUnitOfWork();

Date created = new Date();

Wishlist wishlist = uow.newEntity(Wishlist.class);
wishlist.title().set("Wishlist for christmas 2009");
wishlist.comment().set("No soft presents");
wishlist.created().set(created);

uow.complete();

uow = new GAEUnitOfWork();
wishlist = uow.get(wishlist.getEntityReference());
assertEquals("Wishlist for christmas 2009", wishlist.title().get());
assertEquals("No soft presents", wishlist.comment().get());
assertEquals(created, wishlist.created().get());

Recipient recipient1 = uow.newEntity(wishlist, Recipient.class);
recipient1.name().set("Test Person #1");
recipient1.email().set(new Email("testperson1@email.com"));

Recipient recipient2 = uow.newEntity(wishlist, Recipient.class);
recipient2.name().set("Test Person #2");
recipient2.email().set(new Email("testperson2@email.com"));

wishlist.recipients().add(recipient1);
wishlist.recipients().add(recipient2);

uow.complete();

uow = new GAEUnitOfWork();
wishlist = uow.get(wishlist.getEntityReference());
ManyAssociation recipients = wishlist.recipients();
assertEquals(2, recipients.getCount());
assertEquals("Test Person #1", recipients.get(0).name().get());
assertEquals("testperson1@email.com", recipients.get(0).email().get().getEmail());
assertEquals("Test Person #2", recipients.get(1).name().get());
assertEquals("testperson2@email.com", recipients.get(1).email().get().getEmail());
}


... more to come

Friday, May 29, 2009

EU-politiks grimme ansigt

Det er ikke ofte jeg blogger om politik. Ikke fordi jeg ikke interesserer mig for politik, men jeg mener ikke at jeg nødvendigvis skal pådutte andre min mening i tide og utide, men idag vil jeg alligevel komme med et halvsurt opstød.

I den tidlige TV-avis (mener jeg det var), var der et indslag omkring Europa-Parlementets "rejsecircus". Ifølge indslaget SKAL Europa-Parlamentet mødes 18 gange i Strassbourg og uendelige kasser med materiale og rigtigt mange mennesker skal flyttes disse 18 gange. Prisen? Ja - godt og vel 1,5 MILLIARDER kroner og lad os så slet ikke tænke på de miljømæssige omkostninger (hvor EU politikernes mange mange fly-rejser jo ikke just hjælper i forvejen)

Hvorfor spørger du? Ja - tilsyneladende fordi det (på den ene eller anden måde) er lykkedes Frankrig at få dette skrevet ind i Lisabon traktaten.. Hva f... er nu det - hvordan i alverden kan det lykkedes Frankrig at få dette tilføjet EU's "grundlov" UDEN at de mange politikere man ellers hører i fjernsynet har brokket sig højlydt over dette?

Husk at stemme søndag d. 7 juni - og lad os håbe at vi gøre bare en lille forskel (selv om en stemme kastet ned i dette EU maskineri ikke helt føles som at udøve sin demokratiske ret og pligt)

jeg er rystet...

Tuesday, May 19, 2009

Det digitale Danmark

Selv om der er sket meget i Danmark og rigtigt mange "services" fra det offentlige er blevet digitaliseret, så undrer det mig dog at indberetningen/udbetalingen af SP pengene fra ATP ikke kan foregå digital. Istedet sender ATP 2,9 millioner breve ud til folk, som så skal sende svar tilbage til ATP såfremt de ønsker at hæve deres SP penge i løbet af 2009. Dvs. i værste tilfælde (eller bedste for PostDanmark) skal der sendes 5,8 millioner breve af en pris på 5,50 Dkr hvilket er 31,9 millioner ALENE i forsendelsesomkostninger (worst case). Man kan allerede idag gå ind hos ATP og se ens SP opsparing - hvorfor ikke lave en knap "HÆV NU!" og sende pengene til personens nem-konto?
Ja det er en forunderlig verden vi lever i, men helt digital er den endnu ikke.

Monday, April 13, 2009

Wicket on Google App Engine

Like many others I am using Wicket on Google App Engine re-writing one of my pet projects. Google App Engine does not allow for user applications to start new threads so in order to get Wicket working, you need to disable the ModificiationWatcher by setting the resource poll frequency to NULL. This has a huge disadvantage when you're in development mode, because you'd have to restart the servlet engine each and every time you make a change in one of your HTML or other resource files.
Well - I dug into the Wicket internals and found out the ModificationWatcher was NOT meant to be plugable.. Yikes!.. Well I tried different approaches but found one that worked like a charm and it was dead simple:

Create the "org.apache.wicket.util.watch" package in your project, add a Class called ModificationWatcher and make it a copy of the ModificationWatcher from the original Wicket sources. Remove all threading but keep the base logic in the "start(Duration pollFrequency)" method.

Now override "newWebRequest" in your WebApplication class like this:

@Override
protected WebRequest newWebRequest(HttpServletRequest servletRequest) {
getResourceSettings().getResourceWatcher(true).start(getResourceSettings().getResourcePollFrequency());
return super.newWebRequest(servletRequest);
}

and vupti you've got reload of resource files again, even on Google App Engine. Remember this is only for development, but I cant see any other ways of doing it.

And to the wicket developers - please make the ModificationWathcer extendable someway.