Dynamische Inhalte mit UX-Bridge und FirstSpirit am Bsp. Apache Solr

Das FirstSpirit Enterprise CMS von eSpirit stellt in erster Linie eine Plattform für die Vereinheitlichung, Verwaltung und Veröffentlichung von vorhandenen Daten, Applikationen und Prozessen dar. Ein Hauptaugenmerk fällt dabei auf die Vorgenerierung und Publizierung von Inhalten. Durch die statische Auslieferung ergibt sich zunächst auch eine hohe Performanz der Webseite, jedoch sind dynamische Inhalte wie z.B. ein Live-News-Feed oder sortierte Blogeinträge (z.B. nach Kommentaren) schwer integrierbar. Vorallem diese Anforderungen tauchen bei unseren Kunden immer wieder auf und spiegeln das Gesamtbild bei vielen realen Projekten wieder.

Hier setzt das UX-Bridge Modul an, welches laut eSpirit ein „Infrastruktur-Modul für hochdynamische Webseiten zur Verbesserung der User Experience“ ist. Es fügt sich nahtlos in die hybride Architektur von FirstSpirit – „So viel Dynamik wie nötig, so viel Inhalte vorgenerieren wie möglich“ ein und ergänzt diese um eine volldynamische Auslieferung von Inhalten. Ein gefragter Business-Case und prädestiniert für den Einsatz des Moduls ist die Indizierung und Suche von CMS-Inhalten mittels Apache Solr. Zunächst einmal widmen wir uns einer technischen Betrachtung der UX-Bridge um ein tieferes Verständnis aufzubauen und eine Grundlage für einen später folgenden Anwendungsfall zu bilden.

 Unter der Haube

Wie bereits erwähnt sieht die klassische Architektur vor, Inhalte und Medien auf dem FirstSpirit-Server vorzugenerieren und mittels Synchronisationsmethoden (z.B. rSync) auf den Live-Web/Applikationserver zu übertragen (siehe Abbildung 1). Das Ergebnis können statische Html-Seiten mit finalem Layout sein.

Klassische Architektur

Abbildung 1, Quelle: eSpirit AG

Die UX-Bridge erweitert diese Architektur. Die zugrunde liegenden Technologien sind Apache ActiveMQ (Messaging & EIP Server), Apache Camel (Routing der Nachrichten) und KahaDb (Persistenzspeicher für Nachrichten). eSpirit bietet bereits einen vorkonfigurierten UX-Bus (basierend auf ActiveMQ) an. Es ist aber auch möglich die aktuellen ActiveMQ Binaries zu verwenden und die (Camel-Routing) Konfigurationen nachzuziehen. Bisher haben wir uns in unseren Projekten für letztere Variante entschieden.

erweiterte Architektur mit UX-Bridge

Abbildung 2, Quelle: eSpirit AG

Es kann durchaus vorkommen, wie auch in unseren Projekten, dass CMS und  Live-Web/Applikationserver durch eine Firewall voneinander getrennt sind. Dadurch gestaltet es sich schwierig auf die internen Repositories und Daten des CMS vom Live-Server aus zuzugreifen und Inhalte dynamisch auszuliefern. Deshalb wird dem Live-Server ein Repository zur Seite gestellt, in welches die Daten vom CMS übertragen werden.

An diesem Punkt setzt die UX-Bridge, respektive der UX-Bus ein. Aufgabe des UX-Bus ist es, beliebig viele Nachrichten zu empfangen und an die Repository-Adapter weiterzuleiten, welche das Einfügen/Update/Löschen der Daten in die Live-Repositories übernehmen. Web-Applikationen können dann auf diese zugreifen und dynamische Abfragen stellen (siehe Abbildung 2). Nachrichten werden vorzugsweise bei Generierung von Inhalten und Freigabe/Löschen von Datensätzen vom CMS via UX-Bridge-Modul an den UX-Bus geschickt. Mit Hilfe dieser Architektur besteht nun auch die Möglichkeit weitere Drittsysteme neben dem CMS anzubinden. Entsprechende Repository-Adapter vorausgesetzt, die Nachrichten entgegennehmen und verarbeiten.

Anwendungsfall Apache Solr

Wie bekommt man Inhalte aus dem FirstSpirit-CMS in den Solr Index? Diese Frage haben wir uns zu Beginn der technischen Konzeptionsphase gestellt. Dabei kristallisierte sich zunächst der Ansatz einen Webcrawler wie Apache Nutch einzusetzen heraus, um die generierten Html-Seiten aufzusammeln und in den Suchindex zu pushen. Der Ansatz wurde aber verworfen und sich der UX-Bridge gewidmet. Grund hierfür war u.a. die Anforderung auch dynamische Inhaltsbereiche mit strukturierten Daten wie einen News-Feed, Eventkalendar, Mediathek bereitzustellen, die teilweise auch durchsuchbar sein müssen. Ebenso sollte ein nahtloser Übergang zwischen Publizierung von Inhalten und Aufnahme in den Index geschaffen werden.

Routing

In erster Linie sollte man sich Gedanken über das Routing der Nachrichten via Apache Camel machen. Je nach Projektsetup/-infrastruktur ergibt es Sinn die Nachrichten an verschiedene Endpunkte zu schicken. Bsp. Ein CMS-Server (mit Projekten für Produktion, PreProduktion etc.) und ein UX-Bus-Server der mehrere Umgebungen (Produktion, PreProduktion, etc.) bedient:

<filter>
   <xpath>//uxb_entity[@stage = 'preprod']</xpath>
   <to uri="activemq:topic:VirtualTopic.BUS_OUT_PREPROD" />
</filter>

 

In unserem vorliegenden Anwendungsszenario gibt es pro Umgebung ein CMS-Server und zwei UX-Bus-Server im Clusterverbund. Anpassungen der von eSpirit definierten Standardrouten sind nicht nötig gewesen. Am UX-Bus haben wir jeweils einen Adapter registriert und in der Spring DSL einen Endpoint im Camel-Context definiert:

<from uri="activemq:Consumer.solr.VirtualTopic.BUS_OUT />

 

Adapter

Jetzt können im Camel-Context über verschiedene Enterprise Integration Patterns (EIP) die XML-Nachrichten gefiltert, gesplittet etc. und anschließend mittels TypeConverter und JAXB-Annotationen auf Java-Objekte gemappt werden. Als Ausgangspunkt für die Implementierung eines Adapters können die Beispiele von eSpirit genutzt werden. In den Beispielen werden pro Destination und ObjectType ein „Handler“ für C(R)UD Operationen verwendet:

<filter>
    <xpath>//uxb_entity[contains(@destinations, 'postgres')]</xpath>
    <filter>
        <xpath>//uxb_entity[@objectType = 'news']</xpath>
        <filter>
            <xpath>//uxb_entity[@command = 'add']</xpath>
            <convertBodyTo type="com.espirit.moddev.examples.uxbridge.newswidget.entity.UXBEntity"/>
            <bean ref="articleHandler" method="add"/>
        </filter>
    </filter>
</filter>

 

Aufgrund der Vielzahl von ObjectTypes und Commands die wir unterstützen, haben wir uns aber bewusst dagegen entschieden für eben diese jeweils neue <filter> Konfigurationen zu definieren. Der Spring-XML-Konfigurationsaufwand wäre zu hoch gewesen und mit der Zeit auch unübersichtlich geworden. Stattdessen nutzen wir einen zentralen Handler, der alles an die entsprechenden Interfaces delegiert (siehe Abbildung 3):

<filter>
	<xpath>//uxb_entity</xpath>
	<convertBodyTo type="de.sew.core.adapter.entity.UXBEntity" />
	<bean ref="messageEntityHandler" method="onMessage" />
</filter>

 

Sequenzdiagramm

Abbildung 3 – Inhalt zum Index hinzufügen

In der onMessage Methode wird das Command ermittelt, welches als Attribut im UXBEntity mitgeliefert wird und anschließend darauf ausgeführt.

public interface Command {
   Status applyOn(UXBEntity entity);
}

 

Als nächstes wird die Destination ermittelt und der Inhalt dieser hinzugefügt, gelöscht etc. Mit Hilfe dieser Architektur gestaltet es sich einfach, auf neue Commands, Destinations und ObjectTypes zu reagieren.

public interface Destination {
   void add(UXBEntity entity);
   void update(UXBEntity entity);
   ...
}

 

Fazit

Vor allem ein konsistenter und aktueller Index ist mit dem Einsatz der UX-Bridge sehr gut umsetzbar. Durch die nahtlose Integration in den Generierungs- und Publizierungprozess sind Inhalte immer in der neuesten Version im Index vorhanden. Ebenso ist ein Löschen von Inhalten aus dem Index mittels eines separat programmierten Workflow-Scripts einfach umsetzbar. Index-Inhaltsleichen gehören somit der Vergangenheit an.
Zeit sollte man auch im Bereich ActiveMQ-Tuning (z.B. Message Prefetch) investieren, ebenso in die Ausarbeitung einer Solr Commit-Strategie (Soft-/AutoCommit) bei sehr vielen parallel eingehenden Nachrichten. Letzteres kann die Performance der Solr-Server deutlich erhöhen, indem ein Hard-Commit für jede konsumierte Nachricht und deren Inhalt vermieden wird.

Hinterlasse eine Antwort

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *

*

*

Du kannst folgende HTML-Tags benutzen: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>