Artikel

 
Mai 2009 | Artikel

Enterprise Eclipse RCP - Teil 2 Fortsetzung, Teil 3

Teil 1   Teil 2   Teil 3   

Objektidentität und Datenvolumen
Wie bereits beschrieben, führt die Tatsache, dass beim Aufruf eines entfernten Service das Caching des O/R Mappers nicht greift, zu technischen Dubletten eines fachlichen Objekts. Da diese Dubletten potenziell bei jedem Aufruf entstehen, sollte hierauf in bestimmten Fällen besonderes Augenmerk gelegt werden. Berechtigterweise kann man sagen, dass beim Laden von Einzelsatzdaten der Effekt zu vernachlässigen ist. Einzelsatzdaten sind oft mit einem Editor verbunden und werden nach dem Schließen des Editors von der Garbage Collection entfernt. Interessant wird es jedoch beim Laden von konkreten Objektlisten. Eclipse RCP als Plattform bietet viele Möglichkeiten, die Eingabe von Daten zu vereinfachen. Autocompletion und Adhoc-Validierung bieten dem Anwender einen sehr guten Bedienkomfort. Da es häufig nicht empfehlenswert ist, für jede Autovervollständigung bzw. Validierungsaufruf die Runde über den Server zu drehen, erfordern beide Funktionen zumeist eine entsprechende Menge an Daten. Die Autovervollständigung muss zumindest eine Teilmenge der potenziell gültigen Eingaben kennen, um einen gültigen Wert vorschlagen zu können – eine Validierung sollte dagegen, wenn möglich, die gesamte Menge kennen. Um nun zu vermeiden, dass diese Daten weder in dem einen noch in dem anderen Fall mehrfach geladen werden müssen, ist es nur sinnvoll, diese Daten an zentraler Stelle im Client vorzuhalten, in anderen Worten, clientseitig zu cachen.

Clientseitiger Cache
Zugegebenermaßen muss die Entscheidung, Daten clientseitig zu cachen, situativ getroffen werden. In jedem Fall muss der erhöhte Zeitbedarf für den Aufruf einer entfernten Servicemethode gegen den erhöhten Speicherbedarf im Client gegenübergestellt und bewertet werden. Die Implementierung des Cache ist also im Idealfall transparent und bei Bedarf aktivierbar.

Wir stellen als Lösung ein Konzept für eine clientseitige Datenzugriffsschicht vor, das genau diese Funktionalität bietet. Diese clientseitigen DAOs, die so genannten Dataprovider arbeiten grundsätzlich ähnlich wie der Cache des O/R Mappers. Sie stellen eine generische Schicht im Client dar, die sowohl Zugriff auf Einzelsatzdaten als auch Objektlisten bietet. Durch den generischen Ansatz ist es möglich, dynamisch eine Cache-Implementierung transparent einzuhängen.

Dataprovider
Zentrales Objekt der Zugriffschicht ist das IDataProvider-Interface. Listing 7 zeigt, dass es die Standardzugriffsmethoden auf Daten bereitstellt:

getData(IKey): Diese Methode wird zum Laden von Einzelsatzdaten verwendet. Als Identifikation dient hier ein Objekt, das das Markerinterface IKey implementiert. Dieses Objekt dient als Schlüssel, unter dem wieder auf den Einzeldatensatz zugegriffen werden kann.

getCollectionData(ICollectionKey): Um auf eine Liste von Daten zuzugreifen, wird ebenfalls ein Markerinterface ICollectionKey verwendet. Dieses Objekt dient ebenfalls als Schlüssel, unter dem wieder auf die Collection zugegriffen werden kann.

Listing 7: IDataProvider.java
  1. /**
  2. * Interface for a provider of a specific kind of data.
  3. */
  4. public interface IDataProvider {
  5. /**
  6. * @return the type of this <code>IDataProvider</code>
  7. */
  8. String getType();
  9. /**
  10. * @return the group this <code>IDataProvider</code> belongs to.
  11. */
  12. String getProviderGroup();
  13. /**
  14. * @return the data with the given <code>IKey</code>
  15. */
  16. Object getData(IKey key);
  17. /**
  18. * @return the <code>Collection</code> of data with the given
  19. * <code>ICollectionKey</code>
  20. */
  21. @SuppressWarnings("unchecked")
  22. Collection getDataCollection(ICollectionKey collectionKey);
  23. /**
  24. * @return the <code>IKey</code> for a given piece of data
  25. */
  26. IKey getKey(Object data);
  27. }


Um das Handling der Dataprovider so flexibel wie möglich zu halten, werden diese über eine Extension registriert. Über den von der Methode getType() jeweils zurückgegebenen Typ kann die konkrete registrierte Implementierung über der Registry angefragt werden.

Transparentes Caching
Der zentrale Zugriff auf einen Dataprovider über die Registry bietet die Möglichkeit, ein Caching einzuhängen. Ist in der Extension für den Dataprovider eine Caching Strategy hinterlegt, reagiert die Registry entsprechend und umhüllt den geladenen Dataprovider mit einem Wrapper. Durch die generische Zugriffsmethode mit den Markerinterfaces IKey und ICollectionKey, kann dieser Wrapper vorher an eine entsprechende Caching-Strategie delegieren (Listing 8).

Listing 8: ICachingStrategy.java
  1. /**
  2. * Interface for a caching strategy.
  3. */
  4. public interface ICachingStrategy {
  5. /**
  6. * Evaluates whether the data with the given <code>IKey</code> expired
  7. * yet. This method should ensure, that the given entry exists by calling
  8. * <code>exists(IKey)</code> beore evaluating the given
  9. * <code>ICacheEntryInformation</code>.
  10. *
  11. * @param key
  12. * The <code>IKey</code> of the investigated data
  13. * @param cacheEntryInformation
  14. * The <code>ICacheEntryInformation</code> of the investigated
  15. * data
  16. * @return whether the data has expired
  17. */
  18. boolean expired(IKey key, ICacheEntryInformation cacheEntryInformation);
  19. /**
  20. * Evaluates whether the data with the given <code>IKey</code> exists. If
  21. * <code>evictCachedData(IKey)</code> was called for this <code>Key</code>
  22. * before, this method should return <code>false</code>.
  23. *
  24. * @param key
  25. * The <code>IKey</code> of the investigated data
  26. * @see #expired(IKey, ICacheEntryInformation)
  27. */
  28. boolean exists(IKey key);
  29. /**
  30. * @return the data with the given <code>IKey</code>
  31. */
  32. Object getCachedData(IKey key);
  33. /**
  34. * Evict the entry for the data with the given <code>IKey</code>.
  35. *
  36. * @param data
  37. * The updated data
  38. */
  39. void evictCachedData(IKey key);
  40. /**
  41. * Registers an entry for the data with the given <code>IKey</code>.
  42. *
  43. * @param key
  44. * The <code>IKey</code> of the data to register
  45. * @param data
  46. * The data to rigister for the given <code>IKey</code>
  47. * @param cacheEntryInformation
  48. * The corresponding <code>ICacheEntryInformation</code>
  49. */
  50. void registerCachedData(IKey key, Object data,
  51. ICacheEntryInformation cacheEntryInformation);
  52. /**
  53. * Clears the whole cache.
  54. */
  55. void clearCache();
  56. }


Die Caching-Strategie sorgt für das eigentliche Caching. Da auch diese über ein Interface ICachingStrategy definiert wird, kann hier entweder auf eine einfache Map-basierte Implementierung zurückgegriffen, bei Bedarf aber auch eine spezifischere Lösung transparent eingebunden werden. Der Wrapper für den Dataprovider prüft beim Aufruf der getData(IKey)-Methode den Zustand des Caches und reagiert entsprechend (Listing 9).

Listing 9: DataproviderWrapper
  1. public Object getData(IKey key) {
  2. CacheEntryInformation cacheEntryInformation = cacheInformationMap
  3. .get(key);
  4. if (cacheEntryInformation != null
  5. && !cachingStrategy.expired(key, cacheEntryInformation)) {
  6. cacheEntryInformation.registerAccess();
  7. return (Object) cachingStrategy.getCachedData(key);
  8. }
  9. else {
  10. Object data = adaptedDataProvider.getData(key);
  11. flushCacheForUpdate(key, data);
  12. return (Object) data;
  13. }
  14. }


Da der Wrapper selbst das IDataProvider-Interface implementiert, ist das Caching für den aufrufenden Codeteil transparent. Dieser arbeitet auf den vom Interface angebotenen Methoden. Die Verwendung von Extensions macht die Konfiguration zusätzlich flexibel. Um das Caching zu aktivieren, muss lediglich die Extension um die Definition einer ICachingStrategy erweitert werden.

Caching auf der Clientseite stellt wie gesagt nur einen Lösungsansatz für einen Teil der Herausforderungen einer auf Remoting basierenden Architektur dar. Im nächsten Teil von Eclipse Enterprise RCP werden wir näher auf die Themen Security und Fehlerbehandlung eingehen.

Peter Friese arbeitet als Softwarearchitekt für die itemis AG in Kiel (http://www.itemis.de, http://www.peterfriese.de). Er ist Committer für die Open-Source-Projekte openArchitectureWare und Eclipse Modeling. Peter ist Autor verschiedener Artikel zu den Themenkomplexen Eclipse, Spring und modellgetriebene Softwareentwicklung und hält zu diesen Themen regelmäßig Vorträge auf Softwarekonferenzen.

Stefan Reichert arbeitet als Softwarearchitekt und Berater bei Lufthansa Systems in Hamburg. Er beschäftigt sich seit mehreren Jahren mit serviceorientierten Enterprise-Anwendungen, Eclipse und Eclipse RCP. Stefan ist Autor mehrerer Artikel zum Thema Architektur und Eclipse RCP und Committer mehrerer Open-Source Projekte.
  1. Gamma, Helm, Johnson, Vlissides: Design Patterns: Elements of Reusable Object-oriented Software, Addison-Wesley.
  2. Stephan Anft, Peter Friese: Der Prokurist – praktische Anwendungsszenarien des Dynamic Proxy API, Java Magazin 10/2003.
  3. Martin Lippert: Spring Extension Factory

Teil 1   Teil 2   Teil 3   

andere Artikel dieser Serie


Anzeige

Kommentare


Anzeige

zurück zum Seitenanfang