Monday, September 13, 2010

SimpleDS 1.0 is released

Gotta love 1.0 releases
This week we are releasing version 1.0 of SimpleDS. SimpleDS is a simple persistence framework for Google AppEngine that provides an alternative to JDO or JPA.

This release brings a lot of new features:

Cached queries


This is the star feature of this release. Starting with 1.0, all query results can be cached. To maximize cache performance only the returned primary keys will be cached, so you still have to use @Cacheable to cache the entity itself.

There are cases were this feature is killer, e.g. to retrieve a User given the email:

User user = entityManager.createQuery(User.class)
  .equal("email", googleUser.getEmail())
  .withCacheSeconds(3600)
  .asSingleResult()
  ;

DEBUG CacheManagerImpl - Level 2 cache hit: qdata{kind=User,pred=[email = test@example.com]}
DEBUG CacheManagerImpl - Level 2 cache hit: User(5)
DEBUG Level1Cache - Level 1 cache hit: User(5)

Cached queries work with FetchOptions (cursors, limit and offset) and support only count(), asSingleEntity(), asList() and asPagedList(). Any invocation to asIterable() / asIterator() will ignore the cache. To clear cached data, just prepare the same query and invoke clearCache().

More about cache here.

Better fluent interfaces


The syntax has been simplified from this (older version):

SimpleQuery query = entityManager.createQuery(User.class).equal("name", "foo");
return entityManager.find(query);

To this:

return entityManager.createQuery(User.class)
  .equal("email", email)
  .equal("enabled", true)
  .sortAsc("email")
  .asList()
  ;

Now we rarely use more than one line for most queries. In this example, if email is null it will just be ignored.

Several methods have been added: asList(), asSingleResult(), asIterator(), asIterable() and PagedQuery.asPagedList(). The old EntityManager methods have been deprecated.

More about queries here.

New Functions


We are also adding some new Functions for transforming persistent entities. Some examples of use:

// save space for your relationships by storing Set instead of Set
Set userIds = user.getFriends();
Collection userKeys = Collections2.transform(userIds, new IdToKeyFunction(User.class));
Collection users = entityManager.get(userKeys);

// transform back
CompositeFunction func = new CompositeFunction(
 new EntityToKeyFunction(User.class), 
 new KeyToIdFunction()
);
user.setFriends(Collections2.transform(users, func));

// just return a collection of email addresses
return Collections2.transform(users, new EntityToPropertyFunction(User.class, "email"));

More about functions and transformations here.

Added JRebel support


I personally use JRebel for development, which means that I rarely restart my development server. This was a problem with SimpleDS, which was unable to detect changes such as new persistent attributes, etc. SimpleDS can now be used with JRebel just by adding this to your startup code:

if (SystemProperty.environment.get().equals("Development"))
   ClassMetadataReloader.register();

Then in eclipse ("Go to your launcher config -> Arguments -> VM Arguments")

-javaagent:/usr/local/java/appengine-java-sdk/lib/agent/appengine-agent.jar -noverify
"-javaagent:${env_var:REBEL_HOME}/jrebel.jar"

Minor changes


  • Added @Property.converter to override the default converter for one persistent property. You can, for example, store a String attribute as Text.
  • Added new methods SimpleQuery.withReadPolicy(ReadPolicy) and SimpleQuery.withDeadline(double)
  • Added new methods SimpleQuery.withStartCursor(Cursor) and SimpleQuery.withEndCursor(Cursor)
  • Cache settings will be ignored when invoked within a transaction.
  • We have moved from commons-logging to slf4j. This may break binary compatibility, but is a huge boost in performance.

Let me get off-topic for a while


Last week this very same blog reached the 400-subscribers mark! It's awesome to find so many people interested, and I find extremely rewarding the contacts I receive every now and then, sometimes just to say hi. Last week it came from Australia! (hey Ángel).

So this is me, taking a small detour to say thanks. And yes, I will try to post more often :))

Monday, September 06, 2010

The current state of logging frameworks

Logging is a commodity, which is argot to mean that I simply don't care as long as it gets out of the way. Recently I had to revisit my own logging decisions based on a single tip: Google Closure Compiler (the library we are using to minify our javascript files) was ignoring my logging setup.

It's like opening the closet you buried ten years ago when log4j was the one and only available choice.

First, the basics

Logging reliably since 1999

One thing is the logging framework (the thingie that writes stuff out):

  • log4j: Started by Ceki Gülcü back in the day, it was the framework used by the Java community until Sun decided to include a different logging implementation in the JRE. After Ceki split from the project, it has been mostly dead in the water.
  • Java Util Logging (JUL): First included with JRE 1.4.2, it is a simple (some would say naïve) implementation that includes just a handful of possible logging setups. You can extend it, but you should code it yourself.
  • Logback: It seems that Ceki Gülcü didn't stop after leaving the Apache Software Foundation. Logback is an implementation of the SLF4J API that can replace log4j or JUL.

A separate concept is the logging isolation library, because when you are implementing a library you cannot require (or expect) a specific logging framework:

  • Commons Logging: Supported by the Apache Software Foundation, this framework has been sadly well-known because of their memory leaks. At the time of this writing their last release dates back to 2007.
  • SLF4J: A logging API layer by Ceki Gülcü that includes adapters for Log4J, JUL, and almost anything you can think of. The adapter plugin mechanism is quite well-thought, just drop your jar in the classpath and you are ready to go.

The Google case


Google makes a point of not including external dependencies with their libraries, and when they do the package name is jarjar'ed to avoid version conflicts. This would not be needed in an ideal world, but as long as framework designers keep making backwards-incompatible changes I think it's a sane decision. Anyone that has used ASM or even Google Collections / Guava knows what I'm talking about (fun trivia fact: the earliest occurrence of this practice I can recall was inside Weblogic 8.1 in 2003, but my memory is not what I would call a trustable source :)

With this strategy logging becomes a problem because at some point you cannot keep renaming classes. This makes the JDK logging the only remaining alternative - for Google, that is.

The choice


A bit of history here: when log4j was out, it could write anywhere: JMS, Database, Email, the OS event system, Grandma's Cheesecacke Recipes Book... But that was soooo 1999. Today we have aspects, servlet filters and all kind of interceptor mechanisms that make these options obsolete. Of the handful times I have configured a log that goes anywhere other than the standard output, I have always ended up replacing it with a more elaborate solution.

"Zillions of appenders" is thus not exactly an important feature in my book. Performance, stability and support are. A quick search using Google pretty much confirms that the competition is over. SLF4J is being widely used by other frameworks, includes an awesome support (with releases every couple of months, the latest at the time of this writing was July 2010), no major known glitches, and fits perfectly with all the logging frameworks mentioned before.

About the logging framework... well, I will keep playing with JUL and Logback, but log4j is left behind as far as I am concerned.

UPDATE: You can also see a longer explanation by the SpringSource team here.