1 Themenübersicht
Caches erlauben, es die Ergebnisse komplexer Berechnungen mehrfach zu verwenden ohne diese jedes Mal neu zu berechnen. Wenn sich die der Berechnung zu Grunde liegenden Daten ändern, so muss der Cache die veralteten Inhalte verwerfen.
Semiramis stellt eine Schnittstelle bereit, so dass Caches einfach entwickelt werden können. Diese Schnittstelle verwirft automatisch die veralteten Inhalte der Caches
2 Zielgruppe
- Entwickler
- Technische Berater
3 Beschreibung
Die System-Engine von Semiramis stellt eine Schnittstelle für Caches in PGM bereit. Die Cache-Instanzen der über diese Schnittstelle registrierten Caches können vom CisSystemManager abgefragt werden. Die Cache-Instanzen werden von der System-Engine über die CisFactory erzeugt.
Für jeden Cache müssen daher alle Business Objects angegeben werden, aus denen der Inhalt des Caches berechnet wird. Wenn ein Business Objects auf einer OLTP-Datenbank geändert wird, so werden alle Caches auf dieser OLTP, die dieses Business Object verwenden, vollständig invalidiert. Beim Invalidieren wird die Referenz auf die veraltete Cache-Instanz verworfen und beim nächsten Abfragen der Cache-Instanz wird eine neue Instanz erzeugt. Die veraltete Cache-Instanz wird also nicht benachrichtigt, dass diese veraltet ist, sondern es wird neue Cache-Instanz erstellt, die auf den aktuellen Daten arbeitet.
Um die aktuelle Cache-Instanz zu erhalten, muss bei jeder Verwendung die Cache-Instanz neu vom System-Manager abgefragt werden.
Hinweis:
Cache-Instanzen dürfen nicht in Instanzenvariablen von Klassen gespeichert werden. Wenn eine Cache-Instanz langfristig gebunden ist, ist die Invalidierung bei Business Objects unwirksam und die Objekte arbeiten mit veralteten Daten. Weiterhin werden unter Umständen mehrere Cache-Instanzen mit gleichem Inhalt im Speicher gehalten. Dies kann den Speicherverbrauch deutlich erhöhen.
3.1 Implementierung des Caches
Die Schnittstelle stellt an die den Cache implementierende Klasse nur die Anforderung, dass diese sich mit CisFactory.createInstance erzeugen lässt. D.h. dass diese eine Konstruktor ohne Parameter haben muss. Es gibt zwar keine gemeinsame Basisklasse oder ein Interface für die Caches, dennoch sind die folgenden Programmierkonventionen einzuhalten.
3.1.1 Session spezifische Variablen
Der Cache darf keine Session spezifischen Variablen und Manager speichern. Der Cache wird Session übergreifend verwendet. Damit ist es insbesondere nicht erlaubt, dass der Cache Instanzenvariablen der folgenden Klassen hat:
- CisEnvironment
- CisObjectManager
- CisTransactionManager
- CisMessageManager
- …
Alle diese Manager müssen in jeder Methode erneut abgefragt werden. Wenn die diese Manager in einer anderen Session als der in der diese erzeugt wurden, verwendet werden, kann es zu Fehlern kommen.
3.1.2 Session spezifische Eigenschaften
Der Cache muss berücksichtigen, dass in jeder Session eine andere Inhaltsprache oder ein anderer Organisationskontext gilt.
Wenn beispielsweise die Inhaltsprache im Cache nicht berücksichtigt wird, so bestimmt die Inhaltsprache der ersten Session, die einen sprachspezifischen Wert von einem Cache abfragt, in welcher Übersetzung dieser Wert an alle anderen Sessions herausgegeben wird.
Es kann hilfreich sein, nur den Primärschlüssel (die GUID) eine Business Objects im Cache zu speichern und beim Abfragen, dieses Business Object mit getObject zu laden. Dies hat die folgenden Vorteile:
- Die Inhaltsprache des Business Objects durch die aktuelle Session bestimmt.
- Falls das Business Object zeitabhängig ist, wird die aktuell gültige Version geliefert.
- Der Speicherbedarf des Caches ist gering, wenn nur GUIDs im Cache gespeichert werden.
- Der Zugriff auf Business Objects über den Primärschlüssel ist sehr schnell.
3.1.3 Speicherbedarf
Jeder Cache darf nur eine begrenzte Menge an Hauptspeicher belegen. Es ist nicht sinnvoll, wenn ein Cache abhängig von der Datenkonstellation unbegrenzt viel Speicher belegt, da so der sichere Betrieb des Application-Servers gefährdet ist.
Verwenden Sie daher zum Speichern der Daten eines Caches immer Container mit begrenzter Größe. Beispielsweise die Klassen CisLRUMap und LRUMap sind Maps begrenzter Größe bei denen, falls die maximale Größe erreicht ist, beim Einfügen eines neuen Elements jeweils das am längsten nicht verwendete Element entfernt wird.
3.1.4 Synchronisierung
Beliebig viele Sessions können gleichzeitig auf die gleiche Cache-Instanz zugreifen. Sie müssen daher die Zugriffmethoden insbesondere bei Verwendung der LRU-Maps synchronisieren.
3.2 Registrierung
Caches für die OLTP-Datenbank werden mit dem Hook com.cisag.pgm.appserver.hook.CacheRegistryHook von der Hook-Contract-Definition com.cisag.pgm.appserver.Server registriert. Für jeden Cache muss am der CacheRegistry die Methode registerOLTPCache mit der Klasse des Caches, optional mit einer zusätzlichen Id und den Klassen, der vom Cache verwendeten Business Objects, aufgerufen werden. Die Id bietet neben der Klasse eine zusätzliche Identifikation an. Mit unterschiedlichen Ids können mehrere Caches mit der gleichen Klasse des Caches registriert werden.
Beispiel:
Registrierung des Caches CalendarLogic für die Business Objects Calendar, CalendarDayTypes und DayType:
registry.registerOLTPCache(
com.cisag.app.general.log.CalendarLogic.class,
com.cisag.app.general.obj.Calendar.class,
com.cisag.app.general.obj.CalendarDayTypes.class,
com.cisag.app.general.obj.DayType.class);
Registrierung des Caches TaxExemptions mit der Id “1” für das Business Object TaxExemption:
registry.registerOLTPCache(
com.cisag.app.financials.log.TaxExemptions.Cache.class,
“1”,
com.cisag.app.financials.obj.TaxExemption.class);
3.3 Abfragen einer Cache-Instanz
Eine Cache-Instanz kann vom System-Manager mit den folgenden Methoden abgefragt werden:
<T> T getCacheInstance(Class<T> cacheClass);
<T> T getCacheInstance(Class<T> cacheClass, String cacheId);
Diese Methoden geben entweder die existierende und gültige Cache-Instanz eines Caches zurück oder aber es wird eine neue Instanz erzeugt.
Eine Cache-Instanz kann am System-Manager mit den folgenden Methoden verworfen werden:
<T> void invalidateCacheInstance(Class<T> cacheClass);
<T> void invalidateCacheInstance(Class<T> cacheClass,
String cacheId);
Es ist sinnvoll, eine Cache-Instanz explizit zu invalidieren, wenn in der Cache-Instanz Daten verwaltet werden, die nicht aus Business Objects stammen, und so nicht durch den Persistenzdienst überwacht werden können.
3.4 Beispiel
public class InfoLogic {
private LRUMap<CisInfo> cache;
protected InfoLogic() {
cache = new LRUMap<CisInfo>(CACHE_SIZE);
}
public static InfoLogic getInstance() {
return CisEnvironment.getInstance().getSystemManager().
getCacheInstance(InfoLogic.class);
}
public CisInfo getInfo(String name) {
CisEnvironment env = CisEnvironment.getInstance();
// look up in the cache
CisKey key = CisKey.create(name,
env.getDisplayLanguage(),
env.getContentLanguage());
CisInfo result;
synchronized (cache) {
result = cache.get(key);
}
if (result!=null) {
return result;
}
// create new info
…
// put info int cache
synchronized (cache) {
cache.put(key,result);
}
return result;
}
}