Monthly Archives: November 2011

Erfahre, wann wettertechnisch der beste Reisemonat ist.

Die Grafik zeigt den besten Reisemonat

Bester Reisemomat für den chin. Süd-Westen


Heute habe ich INI um eine Funktion erweitert, die mir endlich zeigt, wann ich nach Koh Phangan (oder einen anderen Ort der Erde) fliegen sollte und wann lieber nicht.

Wenn man auf die Karte klickt, bekommt man ein Fenster zu sehen, das die besten Reisemonate mit den jeweiligen Werten zeigt und einen Chart – der die Temperatur zu dem Niederschlag in Verhältnis setzt. Die Berechnungsgrundlage ist im Moment noch fest hinterlegt und besagt, dass ich eine Temperatur von 25° und sehr wenig Regen mag.

In der nächsten Version, wird der Nutzer einstellen können, ob ihm die Temperatur oder der Regen vor Ort wichtiger ist und welche Werte er als „ideal“ empfindet. Aber für heute reicht es…

It’s nice in… Ja, wo eigentlich?

Ini's Oberfläche


…ein Bekannter von mir und ich haben uns diese Frage vor ca. einem Jahr gestellt. Wir waren zuvor beide in Thailand; er zwei Wochen später als ich. Obwohl wir unseren Urlaub auf der selben Insel verbracht haben, hatte er deutlich schlechteres Wetter erwischt als ich. Ich wusste z.B. nicht, dass Anfang September dort die Regenzeit losgeht.

Und das ist auf Koh Phangan nicht lustig, weil nur wenige Strasse befestigt sind und in der Regenzeit schonmal das eine oder andere Dörfchen von dem Rest der Insel abgeschnitten ist.

Also haben wir uns It’s nice in… (Ini) ausgedacht. Die Anwendung ist noch nicht fertig, zeigt aber schonmal die Richtung, in die es gehen soll. Sie beantwortet nämlich die Frage: Wenn ich z.B. im Januar verreisen will, wo ist das Wetter angenehm genug für mich?

Dafür greift Ini auf die Wetterdaten der Vergangenheit zurück und erstellt für jeden Monat zwei Arten von Karten:

  1. Temeperatur Karte (blau = kalt, rot = heiß)
  2. Karte vom Niederschlag (rot = gar nicht, blau = viel)

Will ich also wissen wann es in Thailand am wenigsten regnet, wähle ich die Karte mit dem Niederschlag aus und schiebe den Monats-Regler solange hin und her, bis ich das wenigste blau sehe. Oder, wenn ich es nicht so heiß mag aber nach Australien möchte, dann den Button mit der Sonnenbrille drücken und den Monats-Regler bewegen, bis es nicht mehr ganz so rot ist.

Ich bin mit dem Projekt noch nicht fertig und werde noch 1-2 Features umsetzen, die mir unter den Fingernägeln brennen. Oder hast du eine Funktion im Kopf, die Ini unbedingt haben muss ? 😉

Maven führt Grails Tests doppelt aus

Neulich hat sich mein TeamCity-Server mit einem java.lang.OutOfMemoryError Fehler bei mir gemeldet – und das trotz meiner großzügigen Erlaubnis etwas mehr Speicher (XX:MaxPermSize=256m -Xmx1024m) für die Builds nutzen zu dürfen.

Der Fehler trat auf, als TeamCity mein Grails-Plugin mit einem mvn install in das lokale Maven Repository installiert sollte. Im Log sah ich dann, dass die Tests zweimal unmittelbar nacheinander ausgeführt wurden und auch die Test-Reports wurden zwei mal geschrieben. Meine Recherche ergab bis auf diesen Blog-Eintrag nicht viel. Den Grund, weshalb die Tests doppelt ausgeführt werden, habe ich noch nicht gefunden. Aber eine Lösung, die das Problem löst:

Statt einem mvn install führe ich nun mvn grails:test-app install -Dmaven.test.skip=true aus. maven.test.skip=true bewirkt, dass Maven die Test-Phase überspringt.

DomainClass.afterUpdate und StaleObjectstateException im Service

Heute hat mich eine StaleObjectstateException: Row was updated or deleted by another transaction den halben Tag gekostet. Ich hatte das folgende Problem: Jedes mal, wenn meine Domain-Klasse gespeichert oder aktualisiert wurde und über eine ID verfügt, möchte ich in meinem Service die Inhalte einer bestimmten Variable in das Dateisystem schreiben. Ich habe mir überlegt das Problem über die GORM Callbacks afterInsert & afterUpdate zu lösen.

class CmsNode {
  ...
  def afterInsert = {
    cmsService.writeNodeToFilesystem(this.id)
  }
  def afterUpdate = {
    cmsService.writeNodeToFilesystem(this.id)
  }
 
  def afterDelete = {
    cmsService.deleteNodeFromFilesystem(this.id)
  }
 
  static def getCmsService() {
    if (!service) { 
      service = ApplicationHolder.application.mainContext.getBean('cartyCmsService')  
    }
 
    service
  }
  ...
}

Sobald ich im Service die referenzierte ID angefasst habe, wurde die StaleObjectstateException geworfen.

Was ich nicht bedacht habe ist, dass um die Methode des Dienstes eine Transaktionsklammer liegt und Hibernate auf Autoflush (grails.gorm.autoFlush = true) konfiguriert war.

Es passiert nämlich folgendes:

  • Die Callbacks (afterInsert, afterUpdate, afterDelete) werden dann erst aufgerufen, wenn die Änderungen in der Hibernate-Session in die Datenbank überführt werden – also beim flush. In dem Aufruf der Callbacks ist der Prozess aber noch nicht abgeschlossen, sondern mitten drin.
  • Rufe ich aus dem Callback heraus eine transaktionsgeschützte Methode eines Dienstes auf und führe einen lesenden Zugriff auf eine Domain Klasse durch, greift das Autoflush und Hibernate versucht die Session erneut zu flushen, was die Exception auslöst.

Ich habe dieses Problem gelöst, indem ich die Transaktionsklammer um die Methode des Dienstes weggelassen und stattdessen mit einem Transaktionsblock (DomainClass.withTransaction {...}) gearbeitet habe.

class CartyCmsService implements DisposableBean {
  static transactional = false
  def threadPool
 
  CartyCmsService() {
    threadPool = Executors.newFixedThreadPool(2)
  }
 
  public void writeNodeToFilesystem(long nodeId) {
    threadPool.submit({
      CmsNode.withTransaction {
        def node = CmsNode.get(nodeId)
        def file = new File(...)
        ...
        file.write(node.content)
        ...
      }
    })
  }
  ...
  void destroy() {
    if (!threadPool.isShutdown()) {
      threadPool.shutdown()
    }
  }
}