Lukasvan3L

A pragmatic programmer

0 notes &

Class not enhanced correctly - Siena

When calling my EnhancedModel.findById(Object) function, I get this error:

UnsupportedOperationException occured : Class not enhanced correctly

Turns out this is because inside the EnhancedModel I created an overload of the findById function with two parameters: findById(String, String).

This is also the case with getByKey(Object) function; if you create an overload with other parameters; class not enhanced correctly.

0 notes &

PlayFramework & GAE localization editor

Play Framework includes the messages-file way of handling the translation of values. Unfortunately, this won’t do for the site I’m creating: it has 7 different languages and editors need to be able to translate on-the-fly.

So I thought I’d share the solution that’s now implemented: translations via the BigTable datastore.

Datastore
First of all, there’s a model:

@Entity
public class Translation extends EnhancedModel {

  @Id(Generator.AUTO_INCREMENT)
  public Long id;

  public String locale;
  public String name;
  public String translation;

  @Index(“qidx”)
  public List<String> q;

  @PreSave
  @PreInsert
  @PreUpdate
  public void updateSearchFields()
  {
    if (this.translation != null && this.translation.isEmpty())
      this.translation = null;
    this.q = Utils.splitSearchWords(String.format(“%s %s”, name, translation));
  }
}

I’m using Siena 2.0.7, that’s where the annotations are coming from. The “q” field gets indexed and the editors can search on that field when they’re looking for a translation. OnSave of a record this field also gets updated.

PlayPlugin
Next up is the code that actually translates the &{‘translatable message’} that the views are asking for. This is going to be a PlayPlugin overriding the getMessage function:

public class MessagesDbPlugin extends PlayPlugin {
  @Override
  public String getMessage(String locale, Object key, Object… args) {   
    return String.format(getTranslation(locale, key.toString()), args);
  }

Now we’re set up to get the actual translations from the database. Of course we need to also cache them using the gae memcached client, as it’s much faster then the actual datastore.

private static String getTranslation(String locale, String key)
{
  if (Utils.isNullOrEmpty(key))
    return “”;

  // look up in cache
  String value = Cache.get(Translation.generateCacheKey(locale, key), String.class);
  if (!Utils.isNullOrEmpty(value))
    return value;

  // look up in database
  Translation t = Translation.all().filter(“locale”, locale).filter(“name”, key).get();
  if (t != null)
  {
    cache(t);
    return Utils.isNullOrEmpty(t.translation) ? t.name : t.translation;
  }

  // validate the locale: is it possible?
  if (!Play.langs.contains(locale))
  {
    Logger.error(“Invalid language: %s”, locale);
    return “”;
  }

  // not found: insert into the database and the cache for each language
  for (String lang : Play.langs)
  {
    Translation newT = new Translation();
    newT.locale = lang;
    newT.name = key;
    newT.translation = null;
    newT.save();
    cache(newT);
  }

  // now return the key, as the value is empty

  return key;
}

That’s it, you’re set! Each time a translation is requested that doesn’t yet exist in the database, it gets inserted. So if you accidentally skip one and don’t enter it in the database, is pops up in the cms anyway.

CMS
I’m not going to display all the javascript, html and css involved in making the translations editable, but it’s fairly easy. Getting the translations you searched for is plain and simple:
Translation.all().filter(“locale”, locale).filter(“q”, q.toLowerCase().trim()).fetch();
And don’t forget to invalidate the cache entry after you saved a new translation!
That’s it! I might make a Play Module out of it some day, if enough people ask me to :)

0 notes &

Set play framework id for gae:deploy

We’re working on setting up our playframework / google appengine project with a full DTAP roadmap. The way we did that at Q42 is first to integrate our project in Teamcity; teamcity now even does automatic deploys to google appengine!

One problem I encountered was that I wanted to set a specific Framework ID when executing the gae:deploy command. Turns out, you can just append the id just as you would on a run command. So:

play gae:deploy - -%gae-dev

or

play gae:deploy - -%gae-prod

Now you can have different values in your application.conf for different appengine environments, for instance

%gae-dev.application.baseUrl=http://project-dev.appspot.com/

or

%gae-prod.application.baseUrl=http://project.appspot.com/

0 notes &

Museumify - facial recognition

For the Museums and the Web 2012 Hackathon that I participated in, I created Museumify. You input a picture of yourself, and your face will be inserted on top of the face of a piece of art. Here’s obama:

The technique behind it is fairly simple: it uses face.com where you input the URL to an image and it returns JSON containing the location of the eyes, nose, mouse, etc. Here’s a sample:

{"width":140, "height":140, "tags":[{"recognizable":false, 
"width":77.86, "height":77.86, "center":{"x":48.93,"y":53.93},
"eye_left":{"x":29.65,"y":37.36}, "eye_right":{"x":67.52,"y":38.95},
"mouth_left":{"x":30.09,"y":73.59}, "mouth_center":{"x":46.26,"y":78.64}, "mouth_right":{"x":65.45,"y":75.99},
"nose":{"x":44.89,"y":61.78}, "ear_left":null, "ear_right":null,
"chin":null, "yaw":4.43, "roll":3.56, "pitch":-5.05,
"attributes":{"face":{"value":"true","confidence":96}, "gender":{"value":"male","confidence":37},
"glasses":{"value":"true","confidence":96}, "smiling":{"value":"true","confidence":95}}}]}

With the eye, mouth and nose information, you can find out what direction the face is looking at, if it’s en profile etc. The museumify site only crops the picture and overlays it, but a lot more is possible this way. Think of overlaying your picture on an art object that’s looking the same direction.

Maybe in the future I’ll pick up the project again to include this. But for now, it was a nice experiment and will just be sitting there :)

0 notes &

PlayFramework + GAE OnApplicationStart code

Google App Engine works in mysterious ways! The default @OnApplicationStart doJob of play framework apparantly doesn’t work. But there’s a workaround if you want to have code executed when your application is booted. For instance, to inject bootstrap data in your database.

The solution I found makes use of the Plugins functionality of play. A plugin can execute code on application start, exactly what we need!

So here’s my code to create a one-class-plugin:

package plugins;

import play.*;
import play.Play.Mode;
import play.test.Fixtures;

public class Bootstrap extends PlayPlugin {

  @Override
  public void onApplicationStart()
  {
    if (Play.mode == Mode.DEV)
    {
      Logger.info(“Loading data………….”);
      Fixtures.deleteAllModels();
      Fixtures.loadModels(“data.yml”);
    }
    Logger.info(“App loaded!”);
  }

}

And then one little extra: put a “play.plugins” file in your /app directory with the following contents:

4242:plugins.Bootstrap

This registers the plugin with a priority of 4242 with Play. SpringPlugin, for instance, has a priority of 1000.

That’s it! No ugly url’s to call after a deploy, this code gets executed automagically.

0 notes &

Addthis bug!

Addthis updated its code somewhere in the past days. At two specific places in their code they set “d = document”. Because there’s no “var” in front of this, window.d gets set to document. This code conflicts with a library we use (spiffy) that also binds to window.d.

The symptoms are  javascript breaking, and when you open the console you get js errors on every mouse move.

1 note &

AirPlay crashes my router dir-635

During my honeymoon in America, june last year, I bought an iPad 2 and an AppleTV 2. In theory, that would enable me to wirelessly stream music and video to my television, using AirPlay.

Unfortunately, every time I tried it, I heard 5 seconds of audio and then my router gave up. It seemed to be rebooting, both my wired pc and wireless laptop didn’t receive a signal from the router.

During my last attempt before I was going to buy a new router (and an expensive one this time!) I updated the firmware of my d-link dir-635 from 1.09W to 1.13b. And low-and-behold: it works! I’ve been listening my spotify playlist via iPad for the past hour or so, unobstructed.

In the release notes I can’t find the change that fixed it. If you find out, please let me know! For now, I’m happy :)

28 notes &

log4net 1.2.11 dependency hell

Whowh, that’s a nice error:

Looks like you forgot to register the http module Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule
To fix this add
<add name="PerRequestLifestyle" type="Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule, Castle.Windsor" />
to the <httpModules> section on your web.config.
Windsor also detected you're running IIS in Integrated Pipeline mode. This means that you also need to add the module to the <modules> section under <system.webServer>.
Alternatively make sure you have Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35 assembly in your GAC (it is installed by ASP.NET MVC3 or WebMatrix) and Windsor will be able to register the module automatically without having to add anything to the config file.

That doesn’t make sense… After some fumbling around I got the next error message:

An error occurred creating the configuration section handler for common/logging: Unable to create type 'Common.Logging.Log4Net.Log4NetLoggerFactoryAdapter, Common.Logging.Log4Net'

And after that another error in log4net configuration. Eventually colleague Mark helped me figure out the following was the underlying problem:

log4net version 1.2.11 has a different publicKeyToken then 1.2.10

So when I upgraded my nuget files, log4net 1.2.11 got downloaded and referenced. But castle.windsor was looking for 1.2.10, and didn’t see the similarity with 1.2.11. And so it epically failed :( For some reason it did work on my machine, but not on the live server, which didn’t help.

What can we learn from this?
It took me 2 hours to figure out what was wrong. And all that because I let castle.windsor do the dependency injection via public properties. This just ended up with CollectionObjectService being null:

public CollectionObjectService CollectionObjectService { get; set; }
public SynchronizerService()
{
}

When I re-wrote it so the services use constructors to get their dependencies injected, the error that actually told me what was wrong turned up:

public CollectionObjectService CollectionObjectService { get; private set; }
public SynchronizerService(CollectionObjectService coService)
{
  this.CollectionObjectService = coService;
}

6 notes &

Google mail vs Google apps

My current e-mail setup is as follows:

  • Mail sent to my 3l.nl mailaccount is received in a pop3 mailbox
  • My gmail account is set up to download the mail from the pop3 mailbox periodically
  • My father and mother both have 3l.nl mailaddresses, and receive the pop3 mails directly in outlook
  • My brother and sister both have 3l.nl mailaddresses, but they’re just aliasses and their mail is directly sent to their gmail or hotmail (yup, some people still use it…) address.

A change of heart

I’m thinking of a new setup using Google Apps. This enables my parents to open their mail directly in a webmail environment (they travel a lot) and on their phone. And everywhere the mail is synchronized (which it is not at the moment).

Also it eliminates the need of an additional gmail address for myself, as it’s unneccessary and my mail arrives delayed, because the pop3 server is pinged periodically (5 minutes, but still).

And last but not least, we’ll be using our awesome 3l.nl e-mail adresses to log in, how cool is that?!

The downside

  • All the mail, contacts, calendar and documents I currently have resides in my gmail account. I would like to keep that and move it to the new 3l.nl google apps account.
  • My gmail account has analytics access, picasa albums uploaded, etc. All that will have to be setup again using my new 3l.nl google apps account
  • The aliasses that I set up for my brother and sister won’t work anymore. I’ll have to create emailadresses for them, and install filters that forward all incoming mail to their own gmail / hotmail address. And that process includes creating and validating forward addresses, etc…

Do you see any solutions to one of my problems? I could also just use e-mail forwarding rules for my parents, and create private gmail accounts for them. But I think this is definitely worth a shot!