Das Target

Die Klasse, mit der Programmierer am häufigsten arbeiten, ist die Target-Klasse. Targets sind die Klassen, die Nachrichten empfangen können. Um das zu ermöglichen, werden sie in der TargetRegistry eines Namespaces registriert. Ein Target erhält dabei eine ID, die Target-ID (kurz: TID). Man kann den Targets eigene TIDs geben; gewöhnlich reicht jedoch eine von der TargetRegistry generierte zufällige ID.

Targets werden bei der Registrierung an einen Thread gebunden. Alle Messages, welche sie im Laufe ihres Lebens erhalten, werden sie immer in demselben Thread erhalten. Es werden niemals Messages zur selben Zeit an das Target gereicht, sondern sie kommen immer nacheinander in die Verarbeitung. Daher braucht man sich hier keine Gedanken um Probleme der Nebenläufigkeit zu machen.

Der Aufbau eines Targets

Ein Target ist irgendeine Klasse, die das ITarget-Interface implementiert.
Besser ist es jedoch, eine Klasse von CTarget abzuleiten, denn hier ist schon alles Nötige implementiert. Es fehlt lediglich das Anlegen von einem oder mehreren MessageHandlern, welche Nachrichten, die an dieses Target gerichtet sind, auffangen und bearbeiten.

Ein typisches Target sieht so aus:

class CTestApp extends CTarget
{
    CTestApp()
    {
        addMessageHandler(CRecordStartTarget.ID, new CMessageHandler(this, "StartTarget")
        {
            @Override
            public boolean handleMessage(final CEnvelope aEnvelope,
                                         final CRecord aRecord) throws Exception
            {
                aEnvelope.setResult(null);
                return true;
            }
        });
    }
}

Hier wird im Konstruktor des Objektes ein Message-Handler implementiert. Der Konstruktor des Message-Handlers hat zwei Argumente:

  • Das Target, zu dem er gehört (this)
  • Einen Text, welcher das Loggen von Nachrichten erleichtert ("StartTarget)

Es ist eine Methode zu implementieren: die handleMessage-Methode, welche die Nachricht erhält. Diese hat auch zwei Argumente:

  • Den Envelope, welcher den Message-Kopf der Nachricht enthält. Hier sind insbesondere die Absender- und Empfänger-Adresse notiert. Zudem enthält er etliche Einstellungen zum Verschicken von Nachrichten.
  • Den Record, welcher die Nutzlast der Nachricht darstellt. Hier ist vor allem die Message-ID zu erwähnen.

Beim Hinzufügen eines Message-Handlers zum Target (addMessageHandler(...)) ist noch ein wichtiges Argument anzugeben, nämlich die Message-ID. Die Message-ID ist vom Typ IId, und in diesem Fall ist es die StartTarget-ID. Diese Nachricht wird als erste vom System an ein neu registriertes Target verschickt, gleichsam als Willkommensgruß. Sie kommt asynchron, d.h. sie wird in dem Thread geliefert, welcher beim Registrieren des Targets angegeben wurde. Alle Nachrichten des Targets werden in diesem Thread abgeliefert, um Probleme mit dem Zugriff auf eigene Daten zu verhindern. Denn wenn alle Nachrichten in demselben Thread geliefert werden, heißt das, dass alle Nachrichten schön hintereinander das Target erreichen.

Das Registrieren eines Targets

Targets werden in der TargetRegistry eines Namespaces registriert:

ITarget myTarget = new CTestTarget();
getNamespace().getTargetRegistry().registerTarget(myTarget, CWellKnownTID.KERNEL);

Das Target erhält dabei eine ID, die Target-ID (kurz: TID). Alternativ kann man bei der Registrierung eine Wunsch-TID mitgeben:

ITarget myTarget = new CTestTarget();
IId myTID = CIdFactory.create(EIdType.STRING, "TEST1");
getNamespace().getTargetRegistry().registerTarget(myTarget, myTID);

Gewöhnlich reicht jedoch eine von der TargetRegistry generierte zufällige ID. Wenn der Namespace mehr als einen Thread hat, kann man auch die Queue-ID des Threads angeben:

ITarget myTarget = new CTestTarget();
IId myTID = CIdFactory.create(EIdType.STRING, "TEST1");
getNamespace().getTargetRegistry().registerTarget(myTarget, myTID, qid);

Die Target Adresse

Jedes Target erhält durch die Registrierung eine eindeutige Adresse. Diese Adressen benutzt man in Nachrichten als Empfänger- und Absenderadresse.

Über den Aufbau der Adresse lies hier weiter.

Die Adresse kann man nach der Registrierung des Targets mit getAddress() holen.

Sie ist unveränderlich, kann also weitergegeben werden, ohne dass man befürchten muss, dass sie verändert wird.

De-Registrierung

Das Target kann jederzeit abgemeldet werden:

addMessageHandler(CRecordRequestStopApp.ID, new CMessageHandler(this, "RequestStopApp")
{
    @Override
    public boolean handleMessage(final CEnvelope aEnvelope,
                                 final CRecord aRecord) throws Exception
    {
        deregisterTarget();

        aEnvelope.setResult(null);
        return true;
    }
});

Zugriff auf die Umgebung

Innerhalb des Targets kann man leicht auf seine Umgebung zugreifen:

// holt den Kernel
getKernel();

// Holt den eigenen Namespace
getNamespace();

// Holt die ID des Threads, in dem das Target registriert ist
getQueueId();

Convenience-Methoden

Einige Methoden des Targets sind nur der Bequemlichkeit halber vorhanden. Sie rufen dann Methoden des Namespaces bzw, des Kernels auf:

// holt die Slot-Factory; wird mitunter beim Allozieren von Messages benötigt
getSlotFactory();

// TimerManager zum Aufziehen von Timern
getTimerManager();

// sendet eine Nachricht
send(envelope, record);

// ebenso
send(message);

// sendet eine Nachricht zurück, die vorher zurückgestellt wurde
sendBack(envelope, record);

// ebenso
sendBack(message);

// sendet eine Notification; eine Antwort wird nicht gewünscht.
sendNotification(envelope, record);

// ebenso
sendNotification(message);

// sendet einen Request mit Wunsch auf Antwort
sendRequest(envelope, record);

// ebenso
sendRequest(message);

Debugging Tools

Einige Methoden erleichtern das Debugging:

// gibt den Simple-Name des Targets zurück (ohne Package)
getCurrentName();

// gibt einen eigenen Namen zurück
getName();

// prüft, ob Nachrichten, die dieses Target erhält, geloggt werden
isVerbose();

// Sollen Nachrichten geloggt werden?
setVerbose(boolean);