Envelopes

Der Umschlag beinhaltet alle Datenfelder, die für die Zustellung der Messages erforderlich sind.

Konstruktoren

Erst wollen wir hier die Konstruktoren vorstellen:

// Ein leerer Umschlag.
CEnvelope();

// Hier wird eine Kopie eines Umschlags erstellt.
CEnvelope(final CEnvelope aTemplate);

// In diesem Umschlag wird die Empfänger-Adresse auf ein
// lokales Namespace gesetzt. Dieser Konstruktor wird für
// Messages an lokale Services genutzt. Die Record-ID
// ist dann die Service-ID.
CEnvelope(final IId aNID);

// Hier wird die Empfänger-Adresse auf ein remote Namespace
// gesetzt. Dieser Konstruktor wird für
// Messages an remote Services genutzt. Die Record-ID
// ist dann die Service-ID.
CEnvelope(final IId aNID,
                 final IHid aHID);

// Die Empfänger-Adresse wird auf die Adresse eines Targets gesetzt.
// Die Message geht dann direkt an das Target.
CEnvelope(final IId aTID,
                 final IId aNID,
                 final IHid aHID);

// Die Empfänger-Adresse wird auf die Adresse eines Targets gesetzt.
// Die Message geht dann direkt an das Target.
CEnvelope(final ITargetAddress aReceiver);

Für das Lesen eines Envelopes aus einem Stream gibt es eine Factory-Methode:

static CEnvelope createFromStream(final DataInputStream aStream) throws IOException;

Die Methode berücksichtigt dabei die verschiedenen Versionsstände des Umschlags. Zur Zeit gibt es nur die Version 0.

Empfänger- und Absender-Adresse

Die Empfänger-Adresse ist die Adresse des Targets, welches die Message erhalten soll. Ist nur der Namespace-Teil der Adresse ausgefüllt (NID+HID), wird damit ein Service adressiert, welcher in genau diesem Namespace registriert ist. Die Service-ID (SID) ist dabei gleich der Record-ID der Message:

// Hole den Empfänger
ITargetAddress getReceiver();

// Setze den Empfänger
void setReceiver(final ITargetAddress aReceiver);

Da die Target-Adressen immutable ist, kann sie nicht einfach geändert werden. Es muss mit der Änderung also eine neue Adresse erzeugt werden. Dafür gibt es ein paar Convenience-Methoden:

// Erzeugt und setzt eine neue Empfänger-Adresse mit geänderter TID
void setReceiverTID(final IId aTID);

// Erzeugt und setzt eine neue Empfänger-Adresse mit geänderter NID
void setReceiverNID(final IId aNID);

// Erzeugt und setzt eine neue Empfänger-Adresse mit geänderter HID
void setReceiverHID(final IHid aHID);

Wird die Empfänger-NID weggelassen, nimmt der Kernel an, es handelt sich um dieselbe NID wie die des Absenders. Das gilt ebenso für die HID.

Analog gibt es ähnliche Methoden für den Sender:

void setSender(final ITargetAddress aSender);

ITargetAddress getSender();

void setSenderTID(final IId aTID);
void setSenderNID(final IId aNID);
void setSenderHID(final IHid aHID);

Wenn die Sende-Methoden des Targets verwendet werden, wird der Absender vom System gesetzt.

Antworten

Wenn das System eine Message zurück sendet, werden die Adressen getauscht (Receiver <—> Sender). Dazu gibt es Support-Methoden:

// Gibt SUCCESS zurück, wenn der Absender gesetzt und die Message keine Antwort ist
CResult canSwap();

// Tauscht Sender und Empfänger.
CResult swap();

Gleichzeitig wird beim Tausch der Adressen ein Bit gesetzt, das anzeigt, das die Message jetzt eine Antwort ist:

void setIsAnswer(final boolean aIsAnswer);

boolean isAnswer();

Da Antworten nur im Fehlerfall automatisch gesendet werden, fehlt noch ein Bit, das der Sender eine Antwort wünscht:

boolean wantAnswer();

void setWantAnswer(final boolean aWantAnswer);

Dieses Bit wird bei den entsprechenden Sende-Methoden (request(...)) gesetzt. Bei den notification(...)- und send(...)-Methoden wird das Bit nicht gesetzt.

Result

Der Result-Code bei Antworten wird auch im Envelope gespeichert und nicht etwa im Record. Ein Grund dafür ist der Umstand, das Records auch außerhalb von Messages verwendet werden können. Zudem wird immer nur ein Record zur Zeit an einen Message-Handler gereicht, auch wenn die Message mehrere Records besitzt. Also kommt auch nur ein Result-Code in die Antwort.

Der Zugriff auf die Ergebnisse einer Message erfolgt so:

void setResult(final CResult aResult);

void setResult(final int aCode,
               final String aText);

CResult getResult();

int getResultCode();

String getResultText();

Eine Message hat intern ein boolean mHasBeenHandled. was beim Senden der Nachricht auf false gesetzt wird. Ein Message-Handler gibt in der handleMessage()-Methode ein boolean zurück: True heißt hier, das die Message behandelt wurde. Wird hier false zurückgegeben, wird mHasBeenHandled auf false gesetzt, und der ResultCode auf CResult.NOT_HANDLED gesetzt.

Wenn die Antwort zurückkommt, und dieser ResultCode ist gesetzt, soll der Absender davon ausgehen, dass die Nachricht aus irgendeinem Grund nicht behandelt wurde. Das kann z.B. auch daher rühren, dass die StateMachine des Empfänger-Targets im aktuellen State diese Message nicht behandelt.

Die folgenden Methoden werden intern dafür genutzt:

boolean hasBeenHandled();

void setHandled();

Blocked Messages

Da Nachrichten, wenn sie den Message-Handler des Empfängers verlassen, vom Framework als Antworten zurückgeschickt werden, muss es eine Möglichkeit geben, das zu unterbinden. Denn wenn z.B. ein Request nicht sofort erledigt werden kann, muss die Nachricht gespeichert und nach getaner Arbeit manuell zurückgeschickt werden. Es könnte z.B. sein, dass der Empfänger seinerseits erst Nachrichten verschicken und empfangen muss, um den Request abzuarbeiten.

Dazu blockt er die Message:

mag.setBlocked(true);

Analog gibt es dazu natürlich:

boolean isBlocked();

Die Nachricht wird vom Empfänger (!) gespeichert. Nach getaner Arbeit sendet er sie mit einer der sendBack(...)-Methoden von Target, Namespace oder Kernel zurück, z.B.:

msg.setBlocked(false);
sendBack(msg);

Geblockte Messages dürfen (und sollen) gespeichert werden. Nicht geblockte Messages sollten auf keinen Fall gespeichert werden, da sie schon weiter geschickt und verändert wurden!

Services

Damit der Empfänger (falls das mal notwendig sein sollte) prüfen muss, ob die angekommene Message über einen Service oder aber direkt gekommen ist, gibt es eine Hilfsmethode:

boolean isServiceMessage();

Transport

Es gibt eine Reihe von Methoden, die dem Message-Transport zwischen den D1-Instanzen erleichtern.

Folgende Methode verhindert, dass eine Message das System verlässt (z.B. im LOG):

void setOnlyLocal();

boolean isOnlyLocal();

In einer neuen Message ist das Bit auf false gesetzt.

Ein weiteres Bit verhindert, das Messages komprimiert oder verschlüsselt werden (Default: false):

void setRawTransport();

boolean isRawTransport();

Die Priorität von Messages wird nur beachtet, wenn sie von Instanz zu Instanz transportiert werden. Es wird immer eine Message aus einer Queue mit höherer Priorität zuerst transportiert, falls mehrere Messages zum Transport anstehen.

void setPriority(final EPriority aPrio);

EPriority getPriority();

Das Framework kennt in dieser Version die Prioritäten LOW, STREAM, NORMAL und HIGH. Default ist NORMAL. Log-Messages werden auf LOW gesetzt, Antworten immer auf HIGH.

Weitere Felder

Um LOG-Nachrichten davor zu schützen, geloggt zu werden, gibt es weitere Methoden. Ohne sie würde das System kaskadenartig geflutet werden:

boolean isLogEnabled();

void disableLog();

Eine Nummer, die mit jeder Instanz einer Message inkrementiert wird, mag beim Debugging hilfreich sein:

int getInstanceId();

Für das Debuggen sind auch folgende Methoden gedacht:

long getTimeSend(); long getTimeDeliver(); long getTimeSendBack(); long getTimeDeliverAnswer();

void setTimeSend(final long aTimeSend); void setTimeDeliver(final long aTimeDeliver); void setTimeSendBack(final long aTimeSendBack); void setTimeDeliverAnswer(final long aTimeDeliverAnswer);

Diese Zeiten werden vom Framework mit System.currentTimeMillis() belegt.

Für die Version einer Message Klasse gibt es die Methode:

byte getVersion();

Zur Zeit ist die Version 0 aktuell.

Envelopes kann man kopieren, entweder über den Copy-Konstruktor, oder über diese Methode:

CEnvelope getCopy();

Transaktions-IDs in Form von UUIDs kann man über diese Methoden setzen und holen:

UUID getTransactionId();

void setTransactionId(final UUID aId);