Herzlich willkommen auf dem Blog der exensio GmbH

exensio ist ein unabhängiges Software-Unternehmen von erfahrenen IT Beratern und Architekten. Zu unseren Kunden zählen namhafte deutsche Großunternehmen.

exensio verbessert das Kommunikations- und Informationsmanagement von Unternehmen. Auf Basis sinnvoller und zweckmäßiger Technologien erarbeiten wir flexible und übergreifende Lösungen für webbasierte Informationssysteme. Abhängig von den Kundenanforderungen realisieren wir Web Applikationen sowie Lösungen auf Basis von Web Content Management- und Portalsystemen. Integrativ, wirtschaftlich und anwenderfreundlich!

Hier gelangen Sie zur exensio GmbH.

Montag, 13. Februar 2012

Teil 1 - Grails in Produktion – mit Apache, Tomcat und MySQL

Mit diesem Blog-Post möchte ich die Schritte, die nötig sind, um eine Grails Applikation produktiv zu stellen, beschreiben. Dieses Thema werde ich in aufeinanderfolgende Blog-Posts aufteilen. Die Aufteilung sieht folgendermaßen aus:
Systemarchitektur
Das Bild rechts beschreibt die gewählte Systemarchitektur. Diese besteht aus folgenden Komponenten:
  • Load-Balancer – dieser kann sowohl auf Soft-, wie auch auf Hardware basieren. Meistens wird hier Hardware favorisiert. Wichtig ist, dass der Load-Balancer „Sticky Sessions“ unterstützt, sprich einen User-Request immer wieder an den ursprünglichen Apache WS weiterleitet.
  • Apache Web Server – hier benutzen wir mod_jk zur Kommunikation zwischen Apache WS und Tomcat. Gründe hierfür sind: sehr ausgereift und wir benutzen meistens keine SSL Verschlüsselung zwischen Apache WS und Tomcat. Falls SSL notwendig ist, sollte eher mod_proxy_http benutzt werden [1]. 
  • Tomcat – hier ist wichtig zu erwähnen, dass wir nur ein Load-Balancing für die Requests durchführen, also kein Cluster mit Session Replication. Dies reicht in den meisten Fällen vollkommen aus. Nachteilig hierbei ist, dass ein User sich erneut anmelden muss, falls eine Tomcat-Instanz abstürzt. Als Nachteile für ein Cluster können folgende Punkte aufgeführt werden:
    • Performanz – jede Session wird auf einen Backup-Server (in-memory) über das Netzwerk repliziert. Bei sehr vielen Sessions (und vielen Tomcat-Instanzen im Cluster) kann dies eine sehr hohe Netzwerklast verursachen.  Die Sessions können auch in einer Datenbank persistiert werden. Dies ist jedoch nicht performanter.
    • Bei der Entwicklung muss beispielsweise beachtet werden, dass alle Objekte in der HTTP-Session serialisierbar sind. Auch darf die http-Session nicht zu groß sein (max. 50 – 70 KByte). Des Weiteren darf beispielsweise keine Hibernate-Session in einem HTTP-Objekt gespeichert werden. Obwohl wir wissen, dass dies Anfängerfehler sind, treten diese Fälle trotzdem sehr oft auf. Insbesondere in hektischen Projektphasen. Dies bedeutet schließlich, dass ein Cluster höhere Aufwände verursacht.
  • MySQL – wir verwenden auch sehr gerne PostgreSQL. Jedoch ist MySQL bei den Hosting Providern unserer Kunden öfters vertreten. Deshalb wird hier auf MySQL eingegangen. Wichtig ist hier, sich über einen Fail-Over-Szenario im klaren zu sein. Es gibt mehrere Möglichkeiten, beispielsweise Linux-Cluster, VM-Ware-Cluster, Sequoia, etc. Die Hosting-Provider unserer Kunden setzen hier meistens ein Linux-Cluster ein.
Ergänzungen in Grails
Folgende Ergänzungen sollten meines Erachtens in Grails hinzugefügt werden:
  • GSP-Seite mit folgenden Informationen:
    • Information über die für den Build des WAR-Files benutzte Revision-Nummer des Sourcecode-Systems (Subversion in meinem Fall). 
    • Ausgabe von allen Cookies. Hier ist insbesondere die JSESSIONID von Interesse, da mod_jk den in der workers.properties gewählten Servernamen an die JSESSIONID anhängt.
  • Konfiguration des Connection-Pools im Tomcat. Dieser wird dann als JNDI Connection-Pool in der Grails-Applikation benutzt. Dies hat den Vorteil, dass eine neue Datenbank mit anderen Settings, ohne ein erneutes Kompilieren der Grails-Applikation, benutzt werden kann.
  • Ausgabe der Logfiles in ${CATALINA_HOME}/logs. Nun könnte jemand auf die Idee kommen, dies über einen JNDI Eintrag steuern zu wollen, wie in meinem Blog [3] beschrieben. Hierbei ist jedoch das Problem, dass die Config.groovy vor der resources.groovy (die den Zugriff auf JNDI durchführt) aufgerufen wird.
Revision aus Subversion während Grails War-File Build ermitteln
Der Code unten basiert auf einem Code-Snippet von StackOverflow [2]. Ich habe diesen modifiziert (Zeile 32), so dass die Revision-Nummer angezeigt wird. Dieser Code muss der Datei _Events.groovy hinzugefügt werden.
import org.tmatesoft.svn.core.SVNException
import org.tmatesoft.svn.core.wc.SVNRevision
import org.tmatesoft.svn.core.wc.SVNInfo
import org.tmatesoft.svn.core.wc.SVNWCClient
import org.tmatesoft.svn.core.wc.SVNClientManager
import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory
import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory

// Create revision info in case of a WAR build
eventWarStart = { type ->

    println "******************* eventWarStart *****************"
    try {
        // initialise SVNKit
        DAVRepositoryFactory.setup();
        SVNRepositoryFactoryImpl.setup();
        FSRepositoryFactory.setup();

        SVNClientManager clientManager = SVNClientManager.newInstance();
        println "clientManager = " + clientManager.toString();
        SVNWCClient wcClient = clientManager.getWCClient();
        println "wcClient = " + wcClient.toString();

        // the svnkit equivalent of "svn info"
        File baseFile = new File(basedir);

        println "baseFile = " + baseFile.toString();
        SVNInfo svninfo = wcClient.doInfo(baseFile, SVNRevision.WORKING);
        println "svninfo = " + svninfo.toString();

        def version = svninfo.revision.number as String
        println "Setting Version to: ${version}"
        metadata.'app.version' = "${version}".toString()
        metadata.persist()

    }
    catch (SVNException ex) {
        //something went wrong
        println "**************** SVN exception **************"
        println ex.getMessage();
    }

} // End eventWarStart()  

Während des Builds wird die Revisions-Nummer in der Datei application.properties (siehe unten Zeile 6) geändert.
#Grails Metadata file
#Sat Jan 21 16:17:44 CET 2012
app.grails.version=1.3.7
app.name=MyGrailsProject
app.servlet.version=2.4
app.version=455
plugins.aop-reloading-fix=0.1
plugins.batch-launcher=0.5.6
plugins.csv=0.3.1
plugins.fixtures=1.1
plugins.hibernate=1.3.7
plugins.jquery=1.7.1
plugins.jquery-ui=1.8.15
plugins.mail=1.0-SNAPSHOT
plugins.resources=1.0.2
plugins.spring-security-core=1.2.4
plugins.tomcat=1.3.7

In der BuildConfig.groovy muss dann noch überprüft werden, ob das War-File nicht die Revisionsnummer angehängt bekommen hat. Sonst hätte der Name des War-Files folgenden Aufbau : MyProject-455.war, mit dem Nachteil, dass sich jedes Mal die Url der Grails-Applikation ändern würde.
grails.project.class.dir = "target/classes"
grails.project.test.class.dir = "target/test-classes"
grails.project.test.reports.dir = "target/test-reports"
grails.project.war.file = "target/${appName}.war"
grails.project.dependency.resolution = {
...

Das Ganze kann in einer GSP-Seite (inkl. Ausgabe der Cookies) folgendermaßen dargestellt werden:
<%--
  Created by IntelliJ IDEA.
  User: Peter Soth
  Date: 11.01.12
  Time: 11:49
  To change this template use File | Settings | File Templates.
--%>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <meta name="layout" content="main"/>
    <title>Info:</title>
</head>

<body>

The following revision has been used for the build of this WAR.

REV: ${grailsApplication.metadata.'app.version'}

Show all cookies

NAME: ${cookie.name} VALUE: ${cookie.value} </body> </html>
Tomcat JDBC Connection-Pool mittels JNDI einbinden
Hierzu wird in DataSource.groovy folgendes eingetragen:
// environment specific settings
environments {
    production {
        dataSource {
            jndiName        = "java:comp/env/jdbc/production"
            dialect         = org.hibernate.dialect.MySQL5InnoDBDialect
            dbCreate        = 'update'
        }
    }
...
Im Blog-Post zum Tomcat zeige ich dann, wie dies im Tomcat konfiguriert wird.


Logging in CATALINA_HOME festlegen
Mit folgendem Code-Snippet werden die Logs-Files der Grails-Applikation in das Log-Verzeichnis des Tomcat-Servers geschrieben. Hierzu muss die Variable logDirectory in der log4j-Konfiguration in Config.groovy benutzt werden.
// request parameters to mask when logging exceptions
grails.exceptionresolver.params.exclude = ['password']

// set per-environment serverURL stem for creating absolute links
environments {
 ...
    production {
        grails.serverURL = "http://www.changeme.com"
        //tomcat based logging
        def catalinaHome = System.getenv('CATALINA_HOME')
        if (!catalinaHome) catalinaHome = '.'   // falls nicht gesetzt
        def String grailsLogDir = "${catalinaHome}/logs/"
        ...
    }
}

[1] Tomcat Expert Blog
[2] StackOverflow: SVN Revisionsnummer in Grails einbinden
[3] Grails : JNDI Werte im Embedded Tomcat setzen

Montag, 6. Februar 2012

Grails 2.0 Upgrade Guide


Auf Google+ [1] hat das Grails-Team die Grails Community über Ihre Erfahrungen bei der Migration auf Grails 2.0 befragt. Um es kurz zu machen: Bei kleinen Applikationen ist eine Migration sehr einfach, bei großen Applikationen gibt es jedoch Probleme. Vor allem bei der Migration von Tests gibt es Probleme. Laut Grails Team soll man auf Grails 2.0.1 [2] warten, sofern man die Tests auch migrieren möchte. Dies dürfte bei den meisten Projekten – wie auch bei uns - der Fall sein. Und hier geht es zum Upgrade Guide von Peter Ledbrook [3].

[1] https://plus.google.com/117411438136918964913/posts/axyCZk1P5L5
[2] https://plus.google.com/117411438136918964913/posts/TFw4YvNfQXX
[3] http://pledbrook.github.com/grails-howtos/en/upgradeToGrails2.html

Sonntag, 5. Februar 2012

Recruiting 2.0 – exensio auf Youtube - IT Berater (w/m) mit hohem *PZU-Faktor gesucht


Unter Recruiting 2.0 versteht  man die Benutzung von Web 2.0 Diensten, wie Xing, Facebook, Twitter, Google+ und Youtube zur Mitarbeitergewinnung. Bisher sind wir bei exensio auch den klassischen Weg mit Anzeigen in Jobbörsen gegangen. Vor allem für kleinere Unternehmen hat dies jedoch den Nachteil, dass Stellen- ausschreibungen leider in der Masse untergehen.

Warum neue Kollegen über einen Youtube Kanal suchen?
Uns wurde schnell klar, dass wir einen anderen Weg versuchen mussten und als Software-Ingenieure auch professionelle Unterstützung hierfür benötigen würden. Mit dem Kommunikationsdesigner Michael Starzmann von gadget Strategie + Design [1][2] hatten wir endlich den Richtigen gefunden, der sich voll und ganz auf unserer Wellenlänge befindet. gadget zeichnet meiner Meinung nach vor allem durch tragfähige sowie angemessene Lösungen aus, denn wir hatten kein Budget für ein ganzes Filmteam etc. Weitere Infos finden sich auf dem gadget Blog [3]

Fazit
Um es gleich vorweg zu nehmen: Mit dem Hochladen eines Videos auf Youtube steigen nicht über Nacht die Zahl der Bewerbungen an. Unsere Intension war hierbei eine andere: Wir sehen das Youtube Video mehr als Möglichkeit, uns unseren potentiellen neuen Mitarbeitern in 30 Sekunden vorzustellen und vielleicht etwas mehr von der exensio „Idee“ rüberzubringen, als dies mit einer statischen Web-Seite oder einer PDF-Datei möglich ist. Das sehr positive Feedback, das wir bisher erhalten haben, zeigt uns, dass es der richtige Weg ist - wir würden es auf jeden Fall wieder machen. Des Weiteren ist zu erwähnen, dass das Video länger für uns arbeitet, als eine Anzeige auf einem Jobportal.

[1] http://www.gadget.de
[2] http://blog.gadget.de
[3] http://blog.gadget.de/?p=163

Donnerstag, 2. Februar 2012

Vermeiden von ClassCastExceptions beim Reloading in Grails-Applikationen

Problem

Einer der Hauptvorteile von Grails gegenüber anderen Frameworks bzw. Sprachen ist die schnelle Entwicklung. Bei laufendem Server können Änderungen vorgenommen und diese sind ohne einen lang dauernden Publizierungsvorgang verfügbar.
Unter bestimmten Umständen funktioniert allerdings der Reload von Änderungen in Controllern oder Services nicht bei laufendem Server. Nimmt man eine Änderung vor und lädt anschließend über den Browser die Seite neu, dann resultiert dies in eine ClassCast-Exception, die nur durch einen Server-Neustart zu beheben ist. Das Problem tritt unseren Beobachtungen zufolge insbesondere im Zusammenhang mit dem Spring-Cache-Plugin auf, das Annotation verwendet. Auch bei anderen Annotationstypen ist das Problem schon aufgetaucht und bekannt [1]

Lösung

Ab der Grails-Version 2.0 ist dieses Problem behoben. Für andere Grails-Versionen lässt sich das Problem durch Installation des „AOP Reloading Fix“ Plugins [2] beheben. Dies funktioniert standardmäßig allerdings nur für die Umgebung (Environment) Development.
Wird eine eigene Umgebung für die Entwicklung eingerichtet, z.B. zum Umsetzen von Batches, dann werden die Erweiterungen des Plugins nicht angezogen. Durch Setzen der Variable grails.reload.enabled beim Starten der Anwendung kann dies umgangen werden und das Plugin wird angezogen. Die folgende Abbildung zeigt in IntelliJ den Befehl zum Starten der Umgebung devbatch. Zusätzlich werden die beiden Variablen grails.reload.enabled=true und grails.reload.location=. übergeben.



Links

[1] http://jira.grails.org/browse/GRAILS-6370
[2] AOP Reloading Fix Plugin