Archiv nach Schlagworten: Data Dictionary

Eigene Klassifikationen verwalten und nutzen

Mit Klassifikationen ermöglicht Alfresco die Auszeichnung und Einordnung von Inhalten in hierarchische Bäume von Kategorien. Das Standardprodukt liefert hier out-of-the-box einen generischen Baum mit Einordnungen bzgl. Sprache, Region und Klassifizierung von Software Dokumentationen. Mit Lucene können auf Basis dieser Bäume Abfragen formuliert werden, die Inhalte entsprechend der Zuordnung zu bestimmten Ebenen oder gar Teilbäumen auffinden können, z.B. alle Dokumente in der englischen Sprache, unabhängig vom konkreten Dialekt, d.h. zusammenfassend US-, Britisch- oder sonstiges Englisch.

Das Alfresco Wiki liefert auf der entsprechenden Seite eine sehr gute Dokumentation bezüglich Klassifikationen und Kategorien. Leider entspricht die Dokumentation aber nicht unbedingt der Masse an Projekten, die Klassifikationen einsetzen. Statt wie im Wiki beschrieben eine eigene Klassifikation anzulegen, habe ich schon viele Beispiele gesehen, bei denen lediglich der Standardbaum erweitert wurde. Aufgrund der vorhandenen Funktionalität bzgl. des Standardbaums und der damit verbundenen Einsparung von Entwicklungsaufwand ist dies grundsätzlich nachvollziehbar. Allerdings verlieren Kategorien dadurch ihre eigentlich angedachte, semantisch unterschiedliche Bedeutung, da für das gleiche Metadatum (cm:categories) beliebige Werte genutzt werden können statt aus sauber getrennten Wertebereichen mit ggf. einigen, fachlich motivierten Querverbindungen zu wählen.

Meine Kollegen und ich haben schon mehrfach Projekte realisiert, die eigene Klassifikationen nutzen, um Objekte eindeutig einzuordnen und in einzelnen Fällen sogar die Basis virtueller Navigationsstrukturen zu bilden. Neben der technischen Architektur für Klassifikationen liefert Alfresco leider nur wenig Unterstützung für die Arbeit mit eigenen Klassifikationen. Der Kategoriemanager des Web Clients kann nur mit dem Standardbaum arbeiten und selbst das in Alfresco 4.0 enthaltene Share-Äquivalent von Jan Pfitzner ist auf diesen eingeschränkt.  Damit wir – und die Entwickler der Community – nicht immer wieder das Rad neu erfinden müssen, habe ich mich zuletzt mit einer Erweiterung von Jans Komponente beschäftigt und diese als Contribution bei Alfresco eingestellt.

Grob betrachtet habe ich vier Aspekte auf Basis von Alfresco 4.0c angepasst:

  • Möglichkeit, mehrere Klassifikationen verwalten zu können
  • Möglichkeit, spezielle Subtypen von Kategorien über Forms erstellen / modifizieren zu können
  • Patch der Forms API um neue Objekte unter einer anderen Kindassoziation als cm:contains anlegen zu können
  • Patch der Object-Finder Form-Komponente um spezielle Subtypen von Kategorien nutzen zu können

Kategoriemanager - Mehrere Klassifikationen

Der Share Kategoriemanager hat – genau wie sein Vorgänger im Web Client – lediglich den Standardbaum von cm:generalclassifiable zur Administration angeboten. Um die Nutzung von eigenen Klassifikationen einfacher zu gestalten, müssen auch diese ohne zusätzlichen Entwicklungsbedarf verwaltet werden können. Mit minimalen Änderungen in der Logik des Baumaufbaus und einem zusätzlichen Daten Webscript auf Seite des Repository können nun beliebige Klassifikationen verwaltet werden. Zur Ausblendung von technischen Klassifikationen, die andersweitig genutzt und verwaltet werden, habe ich eine Konfiguration eingeführt, mit der bestimmte Klassifikationen ignoriert werden können (z.B. cm:taggable und cm:classification).

Kategoriemanager - Form-basierte Verwaltung

Kategorien sind wie nahezu alles in Alfresco ganz normale Knoten vom Typ cm:category. Für viele Einsatzzwecke mag dieser Typ ausreichen, aber gelegentlich gibt es die Notwendigkeit, fachliche Metadaten mit Kategorien zu verknüpfen. Alfresco ermöglicht sowohl die Modellierung von Subtypen von cm:category als auch die Verwendung von Aspekten an Kategorien, um diese zusätzlichen Daten verwalten zu können. Der Share Kategoriemanager jedoch unterstützte bisher nur einfache Kategorien mit einem Namen als einzige Eigenschaft. Hier habe ich auf Forms zurückgegriffen, um die notwendige Flexibilität in der Kategoretypisierung zu erreichen. Neue Kategorien werden immer per Form angelegt, während bestehende auf Basis einer ebenfalls neu eingeführten Konfiguration wahlweise durch den Insitu-Editor oder Forms modifiziert werden können. Ersterer ist der Standard für einfache Kategorien von cm:category.

Für die Form-basierte Verwaltung von Klassifikationen war es notwendig, die Forms API zu erweitern. Zum Einen bedarf es eines FormFilters, der neue Wurzelkategorien an der korrekten Position anlegt, da heirfür nur die virtuelle Referenz alfresco://category/root bekannt ist und im Standard vom TypeFormProcessor nicht aufgelöst werden kann. Zum Andern war es notwendig, Unterkategorien unter cm:subcategories in der übergeordneten Kategorie einordnen zu können – die entsprechende Möglichkeit, von cm:contains abweichende Assoziationen angeben zu können war und ist seit (mindestens) Alfresco 3.2 mit einem entsprechenden TODO im Code vorgesehen aber bisher nicht implementiert worden.

Werte aus einer eigenen Klassifikation zuordnen

Die Verwendung einer eigenen Klassifikation beim Bearbeiten von Metadaten funktionierte bisher nur dann erfolgreich, wenn cm:category für alle Kategorien genutzt wurde. Andernfalls ist es nicht möglich, tiefer als auf die erste Ebene des Klassifikationsbaums zu navigieren. Die Unterstützung von speziellen Subtypen erforderte die Anpassungen des Object-Finders, welcher den Pickerdialog für die Forms bereitstellt, und des entsprechenden Daten Webscripts im Repository. Harte Typprüfungen wurden hier gegen saubere Typhierarchieprüfungen ausgetauscht. Mit einer kleinen Spring Surf Extension kann man dann zusätzlich noch die notwendige Verknüpfung von Kategorietyp mit einem eigenen oder dem allg. Icon herstellen.

cm:name – Die erzwungene Eigenschaft

Im letzten Post habe ich ein Performanceproblem beschrieben, welches auf die Nutzung von cm:name (bzw. cm:cmobject als Vatertyp) bei der Modellierung / Instanziierung von 500.000+ Datensätzen im Standard ContentStore zurückgeführt werden konnte. Entsprechend eines der aufgeführten Ansätze wollte ich diese Tage eine kleine Migration umsetzen, bei welcher die redundante Eigenschaft cm:name durch Wechsel auf den Vatertyp sys:base und anschließender Löschung der persistenten Werte von den 500.000+ Datensätzen eliminiert werden sollte. Dabei wurde mir bewusst, dass cm:name unabhängig von der Definition meiner Inhaltstypen im Data Dictionary durch das System immer gespeichert und indiziert wird – lediglich die Prüfung auf Pflicht und Constraint basiert auf der tatsächlichen Typdefinition.

Damit wird natürlich der Ansatz zur Bekämpfung des Performanceproblems wertlos – wenn es unmöglich ist, ein Node ohne cm:name in Datenbank und Index anzulegen, dann lassen sich Seiteneffekte auf die Standardnavigation von Share durch große Datensätze nicht vermeiden.

Wie äußert sich dieses Verhalten?

  • Wird ein Node ohne cm:name angelegt, so wird kein Wert gespeichert, aber beim Auslesen die UUID der NodeRef als vorgetäuschte cm:name zurück gegeben (z.B. DBNodeServiceImpl.getProperty(NodeRef, QName) oder ReferenceablePropertiesEntity.addReferenceableProperties(Node, Map<QName,Serializable>)).
  • Nur die lt. Typ definierten Eigenschaften werden bei der Anlage / einer Aktualisierung validiert. Da cm:name nur für cm:cmobject und davon abgeleitete Typen definiert ist, wird der Constraint nur für diese Typen geprüft. Die Prüfung auf Pflicht ist zwecklos, da Alfresco die Eigenschaft transparent auf die UUID setzt, wenn sie nicht explizit angegeben wurde.
  • Beim Indizieren gilt nichtmehr die Typdefinition, sondern die vorhandenen Eigenschaften und deren Definition. D.h. für Knoten, die nicht von cm:cmobject ableiten wird cm:name trotzdem indiziert, weil es a) automatisch den Wert der UUID hat, wenn es nicht gesetzt wurde und b) eine Definition der Eigenschaft gibt, welche die Indizierung vorgibt (nämlich an cm:cmobject).

Lt. SVN ist dieses Verhalten soweit von mir geprüft seit 3.2 so anzufinden und auch in der aktuellen 4.0 unverändert. Für welche Funktionalität muss diese Eigenschaft erzwungen werden, auch wenn ich sie nicht in meinem Datenmodell (bzw. den definierenden Typ) verwendet habe? Bei der in solchen Fällen üblichen Frage “Bug oder Feature” stimme ich aktuell für “Bug”. Als Partner im Auftrag eines Enterprise Kunden habe ich diesen Punkt entsprechend dem Alfresco Support übermittelt. Wenn das Verhalten bewusst so sein soll, dann wäre es zumindest sauberer, cm:name – ähnlich wie die Eigenschaften von sys:referencable – an sys:base zu hängen.