Spring, EHCache und Wicket

Hallo zusammen,

ich habe mich kürzlich in einer schlaflosen Nacht mit dem äusserst interessanten Thema „EhCache“ auseinandergesetzt.

Ehcache ist ein transparenter Cache, der ideal mit Spring zusammenarbeitet. Ehcache lässt sich hierbei in verschiedenen Modi betreiben (DiskStore basiert, InMemory etc.).

Um einen QuickWin zu erzielen empfiehlt es sich, hier verschiedene Modi auszuprobieren.

Eine sehr gute und brauchbare Dokumentation dazu findet sich hier.

Da ich persönlich sehr interessiert an Spring und Wicket bin, habe ich natürlich die Möglichkeiten ausgelotet, ehcache hier mit einzubinden.

Offensichtlich gibt es zu der Thematik aber nicht allzu viel Onlinedokumentation, und falls man welche findet ist diese meist veraltet, denn auf dem Sprung zu Spring 3 hat sich hier einiges getan, was die meisten beschriebenen Ansätze nicht mehr verwendbar macht. Ich habe mir als die Mühe gemacht und habe das für mich zum Laufen gebracht, dieser Artikel soll die notwendigen Schritte (nicht zuletzt für mich selbst) dokumentieren.

Zum Thema Wicket und Spring gibt es schon ausreichend gute Onlinedokumentation, beispielsweise hier, hier und hier.

Ich setze also voraus, dass das mehr oder minder funktioniert. Die Frage ist jetzt, was muss getan werden, um das Ganze mit ehcache zum laufen zu bekommen.

Zunächst mal benötigt ehcache eine Konfigurationsdatei, in der die einzelnen Caches konfiguriert werden können, typischerweise wird diese einfach ehcache.xml genannt.


<ehcache>
<defaultCache maxElementsInMemory="500" eternal="true"
overflowToDisk="false" memoryStoreEvictionPolicy="LFU" />

<cache name="shopCache" maxElementsInMemory="5000" eternal="false"
overflowToDisk="false"
diskPersistent="false"
statistics="true"/>
</ehcache>

In dieser Datei haben wir zwei Caches konfiguriert, einen defaultCache der laut ehcache dokumentation immer definiert sein muss und immer dann greift, wenn kein anderer Cache zuständig ist, und viel interessanter, ein „shopCache“, den wir in der Applikation verwenden möchten.

Die Attribute sollten eigentlich selbsterklärend sein, ansonsten gibt die Doku hier wirklich viel her, was evtl interessant ist, ist das Attribut „statistics“, damit stellt ehcache eine Cachestatistik zur Verfügung, die beispielsweise mit einer MBean ausgewertet werden kann, aber dazu später mehr.

Wie bereits erwähnt arbeitet ehcache perfekt mit Spring zusammen, hierzu ist folgende Konfiguration im spring-context notwendig.

Folgende Deklaration von Namespaces kann hilfreich sein:


<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:ehcache="http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring
http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring/ehcache-spring-1.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">

<!-- Konfiguration von Beans -->

</beans>


<ehcache:config cache-manager="cacheManager">
<ehcache:evict-expired-elements
interval="60" />
</ehcache:config>

<bean id="cacheManager"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:de/pentasys/wicket/ehcache.xml"></property>
</bean>

Zunächst muss die Bean „cacheManager“ vom Typ org.springframework.cache.ehcache.EhCacheManagerFactoryBean definiert werden.

Diese wird anschliessend mit dem element <ehcache:config cache-manager=“cacheManager“> aktiviert.

Und prinzipiell wars das schon mit der Konfiguration!!

Leider war das bisher nur der gemütliche Teil, jetzt gehts darum, die richtigen Dependencies zu deklarieren, damit das Ganze auch baut, und DAS ist wirklich hart, denn die EhCacheManagerFactoryBean wurde beim Umstieg auf Spring 3 umgezogen, und anscheinend nicht richtig dokumentiert.

Folgende Dependencies müssen unbedingt deklariert werden:

Spring Context Support – hier ist die EhCacheManagerFactoryBean verortet und alles was mit ehcache zu tun hat.


<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>

Ehcache-Spring-Annotations – 80% aller online-tutorials verwenden ein modul namens „spring-modules“, diese Modul ist jedoch veraltet, wird nicht mehr gewartet und funktioniert obendrein nicht mit Sping 3. Stattdessen gibt es ein neues Projekt, das noch einfacher in der Handhabung ist und perfekt funktioniert (ich glaube, es ist sogar die einzige aktuell funktionierende Implementierung)


<dependency>
<groupId>com.googlecode.ehcache-spring-annotations</groupId>
<artifactId>ehcache-spring-annotations</artifactId>
<version>1.1.3</version>
</dependency>

Zusätzlich müsst ihr natürlich die entsprechenden Spring Dependencies deklarieren, aber auf diese verzichte ich an dieser Stelle einfach mal.

Damit lässt sich das Projekt schonmal bauen, aber wir haben noch nichts gewonnen, denn wir müssen ehcache noch genau sagen, welcher Methodenaufruf in welcher Bean wohin gecached werden soll, nichts einfacher als das. Hierzu Annotieren wir einfach in einem beliebigen Spring-Service die entsprechenden Methoden wie folgt:


@Cacheable(cacheName = "shopCache")
public List<ProductBundle> products() {
timeOut();
return new ArrayList<ProductBundle>(products.values());
}

Die Annotation @Cacheable kann man Methoden deklarieren, deren Rückgabewert im Cache gespeichert werden sollen, welcher Cache das sein soll gibt man mit dem Attribut „shopCache“ an. Dies ist der Cache, den wir zuvor in der ehcache.xml definiert haben.

Ein weiterer typischer Usecase ist, dass ein Cache beim Aufruf einer bestimmten Methode geleert werden soll. Nichts leichter als das:


@TriggersRemove(cacheName="shopCache",removeAll=true)
public void storeOrder(Order order){
if(order.getBundleList().isEmpty()){
throw new RuntimeException("error, no bundles in order");
}
orders.add(order);
}

Dies veranlasst ehcache, nach dem Aufruf der Methode den Cache zu invalidieren.

Statistiken

Wie können wir jetzt aber auswerten, wie gut oder schlecht unser Caching funktioniert? Dies ist ein idealer Fall für Mbeans! Hierzu kann testweise einfach mal folgendes im Spring-Context deklariert werden:


<bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean">
<property name="locateExistingServerIfPossible" value="true" />
</bean>

<bean class="net.sf.ehcache.management.ManagementService"class="net.sf.ehcache.management.ManagementService"
init-method="init">
<constructor-arg ref="cacheManager" />
<constructor-arg ref="mbeanServer" />
<constructor-arg value="true" />
<constructor-arg value="true" />
<constructor-arg value="true" />
<constructor-arg value="true" />
</bean>

Dies ist die einfachste Art und Weise, in Spring einen MBean Server zu starten und in diesem eine MBean zu deklarieren, die uns Auskunft über den Zustand des Caches gibt.

Anschliessend kann man sich einfach mal mit JConsole auf den MBeanserver verbinden. Was man dann sieht ist ungefähr folgendes:

Also ich persönlich bin begeistert, wie einfach und transparent das ging, hätte ich vorher eine Beschreibung wie diesen Artikel ( — 🙂 –) gefunden, wäre das Caching eine Sache von 30min gewesen.

Das Ganze ist in einem Demoprojekt verfügbar und kann hier ausgecheckt werden:

git@github.com:dilgerma/Wicket-Workshop-Sources.git

und hier unter advanced-workshop/advanced.

Die einzige Frage die sich mir noch stellt, wie kriege ich den Cache Session-Scoped. SOweit ich das beurteilen kann, ist der Cache, wie er aktuell konfiguriert ist im Applicationscope, das heisst, gilt für alle. Bisher habe ich es nicht hinbekommen, das Ganze für jede Session zu konfigurieren, für alle Tipps bin ich dankbar.

Ansonsten freue ich mich natürlich über Kommentare.

Advertisements

Ein Gedanke zu „Spring, EHCache und Wicket

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s