Migration von Grails 1.3.7 nach 3.1.9 - Probleme und deren Behebung - Teil 1

von Sebastian Jäger

In den letzten Monaten hat es zu meinen Hauptaufgaben gezählt, eine große Business-Applikation von Grails 1.3.7 auf die Version 3 zu migrieren. In diesem Blog Post will ich auf allgemeine Schwierigkeiten und Lösungen eingehen, die in unserem Fall aufgetreten sind.
In den folgenden Posts werde ich zum einen auf die selbst entwickelte Lösung zum Erstellen von Testdaten eingehen und zum anderen die Probleme bei der Migration der einzelnen Testarten erläutern.

Für die Migration wurde in folgender Reihenfolge vorgegangen:

  1. Neues Projekt mit der gewünschten Grails Version erstellen und konfigurieren
  2. Einbinden aller Plugins in der neusten verfügbaren Version, die verwendet werden sollen
  3. Migrieren der Domain-/ Java-/ Groovy-Klassen, sowie TagLibs und i18n- Ressourcen
  4. Einspielen von Testdaten
  5. Migrieren der Services
  6. Migrieren der Controller
  7. Migrieren der Views und alle zugehörigen Ressourcen wie JavaScript und CSS Dateien
  8. Migrieren der Testdateien
Nach jedem Schritt wurden automatisierte Tests, die die schon migrierten Teile der Applikation testen, ebenfalls migriert, um möglichst früh Fehler zu erkennen. Wie oben beschrieben werde ich im dritten Teil dieser Serie genauer darauf eingehen.

Hibernate naming_strategy

Nachdem die Domainklassen sowie sämtliche Hilfsklassen migriert waren, ist durch das Starten mit einer bestehenden Datenbank, aufgefallen, dass das automatisch erstellte Datenbankschema nach dem Starten der Applikation sich von dem Ursprünglichen hinsichtlich der Tabellennamen unterscheidet. Das Anpassen der hibernate.naming_strategyhat für mich das Problem gelöst. In der Hibernate-Dokumentation für das Interface NamingStrategy können implementierte Strategien gefunden werden, die Hibernate von Haus aus mit bringt. Das Definieren eigener Strategien ist jedoch ebenso möglich. [1]

Actions von Controllern

Treten bei Controllern Fehler auf, dass Annotationen nicht erlaubt seien, liegt das daran, dass Actions seit Grails 2 als Methoden implementiert werden sollen. Bei Applikationen der Version 1.x war es üblich diese als Closures zu programmieren. [2]

Transaktionen in Services

Transaktionen ist der wesentliche Punkt, der sich für Services verändert hat. Bis zur Version 2.3 von Grails wurden die Spring Transaktionen verwendet, die durch das statische Attribut transactional aktiviert oder deaktiviert wurden. Empfohlen wird, diese komplett durch die seither neuen, Grails Transaktionen zu ersetzen, die über Annotationen verwendet werden. Alle Services ohne jegliche Annotationen benutzen standardmäßig Transaktionen.

Vorsicht ist geboten, wenn der Modus einzelner Methoden vom default Wert read-write beispielsweise zu readOnly geändert werden soll, denn dann muss die Service Klasse ebenfalls als @Transactional gekennzeichnet werden. Sind nur einzelne Methoden und nicht die Klasse gekennzeichnet, werden nur für diese Methoden Transaktionen verwendet, alle anderen jedoch nicht. [3]

Resource- Plugin durch Asset- Pipeline ersetzen

Seit der Version 2.0 von Grails wurde das bisher verwendete resource Plugin durch asset-pipeline ersetzt. Dadurch muss das Einbinden der JavaScript und CSS Dateien angepasst werden. In der Datei UIResources.groovy wurden bisher sogenannte Module definiert. Mit dem Tag <r:require modules=“common”/> wurden diese Dateien in dem entsprechenden View eingebettet. Um ein ähnliches Verhalten nachzubilden, habe ich für jedes Modul eine neue JavaScript und eine CSS Datei erstellt, die die zugehörigen Dateien definiert. In den folgenden Ausschnitten ist dies beispielhaft dargestellt.

//= require jquery.js
//= require common/common.js
//= require_self
/*
*= require jquery.css
*= require common/common.css
*= require_self
*/

Die verwendeten Pfade der Ressourcen sind relativ, ausgehend von der Dateistruktur grails-app/assets/javascript/ oder grails-app/assets/stylesheets/. Wichtig ist hierbei auf die Reihenfolge zu achten, denn ansonsten könnte es vorkommen, dass JavaScript eine Funktion verwenden will, die noch nicht definiert wurde, was zu Fehlern führt. Um die neu erstellten "Module" zu nutzen, werden nun zwei Aufrufe benötigt: <asset:javascript src=“commonModule.js”/> und <asset:stylesheet src=“commonModule.css”/>.

Deploying

Abschließend gehe ich noch auf ein Problem ein, das schwer zu entdecken ist. Ein, mit Hilfe des Gradle Task bootRepackage erstelltes Standalone War-File, hat zufällig, entweder unter Windows oder, was öfter auftrat, unter Linux, anstelle der erwarteten Login Seite eine Liste aller Controller angezeigt. Verursacht wurde dieses Problem dadurch, dass in unserer Applikation das URL- Mapping, speziell der Root- Endpunkt (“/“), angepasst wurde. Dadurch kann es auftreten, dass ein Plugin, das auch eine UrlMapping.groovy Datei besitzt, die eigene Änderung überschreibt. Hier war noch der Standardwert enthalten, der auf eine fehlerhafte View verwiesen hat. Mit einem Workaround lässt sich dies umgehen. Dazu wird ein Interceptor implementiert, der aktiv werden soll, wenn eine Anfrage an “/“ gesendet wird und dann manuell auf den gewollten Controller und dessen action umleitet. Hier ist ein Beispiel, das eine mögliche Implementierung des Interceptors darstellt.

class RootUriInterceptor {
  RootUriInterceptor(){
    match(uri: "/")
  }
  
  boolean before(){
    redirect(controller: "login", action: "auth")
    return false
  }
}

Fazit

Es wurden einige Probleme und deren Möglichkeiten zur Behebung bei einer Grails-Migration vorgestellt. Eine Migration von älteren Versionen ist also nicht unmöglich, aber auch nicht an allen Stellen einfach. Oft sind es nur Kleinigkeiten, die sich geändert haben und meist durch eine Anpassung der Konfigurationen behoben werden können. Problematisch ist eher das Finden der Ursache zu den auftretenden Fehlern.

Kategorien: GrailsGroovy

Zurück