There are a few methods for creating envelopes.
Send to a microservice
The following method can be used to send a message to a microservice. The system looks for a microservice with just this ID and then enters the fully qualified target address.
@NotNull public static CEnvelope forMicroService(@NotNull final IId aMicroServiceId);
Send to a nano service in the same node
To send a message to a nano service in the same node, the following method can be used. The namespace in which this nano service was registered is required:
@NotNull public static CEnvelope forLocalNanoService(@NotNull final IId aNID);
The generated record class can also be used. But the record must be a nano service and must contain the namespace where the nano service was registered.
@NotNull public static CEnvelope forLocalNanoService(@NotNull final Class extends IGeneratedRecord> aClass) throws CException;
Send to a nano service on another node
These methods are used to create an envelope for a message to another node.
@NotNull public static CEnvelope forRemoteNanoService(@NotNull final IId aNID, @NotNull final CNodeId aNodeId); @NotNull public static CEnvelope forRemoteNanoService(@NotNull final IId aNID, @NotNull final CNodeAddress aNodeAddress); @NotNull public static CEnvelope forRemoteNanoService(@NotNull final Class extends IGeneratedRecord> aClass, @NotNull final CNodeAddress aNodeAddress) throws CException;
Send directly to a target
If the address of a target is known, you can also use these methods:
@NotNull public static CEnvelope forSingleTarget(@Nullable final IId aTID, @Nullable final IId aNID, @Nullable final CNodeId aNodeId); @NotNull public static CEnvelope forSingleTarget(@Nullable final IId aTID, @Nullable final IId aNID, @Nullable final CNodeAddress aNodeAddress); @NotNull public static CEnvelope forSingleTarget(final CTargetAddress aReceiver);
There is also a static copy method:
@NotNull public static CEnvelope copy(@NotNull final CEnvelope aEnvelope);
Messages can of course also be read from a DataInput stream and written to a DataOutput stream:
@NotNull public static CEnvelope fromStream(@NotNull final DataInput aStream) throws IOException; public void toStream(@NotNull final DataOutput aStream) throws IOException;
An envelope contains exactly one sender. Normally it is set by CTarget when sending the message. But it can also be set manually.
public void setSender(final CTargetAddress aSender);
The getter always returns an address, but it can be invalid (INVALID), if it has not been set before.
@NotNull public CTargetAddress getSender();
Normally, an envelope contains exactly one recipient address. This is sufficient to deliver a message nyssr.net-wide.
public void setReceiver(@Nullable final CTargetAddress aReceiver); @NotNull public CTargetAddress getReceiver();
In contrast to the sender, however, receivers are stored in a stack list, which means that several receivers can exist. However, it is not the case that this message is delivered to multiple receivers. Rather, this list is used to forward messages to additional receivers, after it has reached the first receiver.
If the envelope already has a valid receiver address,
and another valid address is passed to
push() is performed.
The new address is then the valid address.
If zero is passed, a
pop() is executed and the first address is discarded.
Accordingly, there is a method to fetch all the recipients.
@NotNull public Stack<CTargetAddress> getReceivers();
The envelope of a response always carries a result code and sometimes a result text.
If the message has not been processed by the receiver, the reply carries the result code
In case of technical errors in the delivery of the message (e.g. receiver unknown),
the system enters a corresponding ResultCode.
The user is required to provide messages that have been processed by him with a result code.
CResult is a small auxiliary class that contains code and text.
There are many predefined codes in
public void setResult(final int aCode, final String aText); public void setResultSuccess(); public int getResultCode(); public String getResultText(); public CResult getResult();
Each envelope contains a
Boolean in which it is noted whether a reply is desired.
In this case the message is sent back to the sender by the system itself.
This happens after leaving the handler.
In case of errors during the delivery of the message, it will be sent back regardless of the user's request.
public void setIsAnswer(final boolean aIsAnswer); public boolean isAnswer(); public void setWantAnswer(final boolean aWantAnswer); public boolean wantAnswer();
The automatic handling of responses is an essential feature of
A message contains a flag that indicates whether this message has been processed by the recipient. It corresponds to the return code of a message handler.
public void setHandled(final boolean aHandled); public boolean hasBeenHandled();
There are some methods that are called by the system to be able to send a response. For this purpose the addresses must be swapped: the receiver address becomes the sender address and vice versa.
Messages cannot always be sent back immediately. Often, for example, the recipient itself must send messages to other destinations in order to process data. The message should not be returned as a response until it has been fully processed.
The recipient must therefore block the message to prevent it from being sent back automatically. Likewise, a method is needed to manually return a postponed message to the sender.
public void setBlocked(final boolean aBlocked); public boolean isBlocked(); // send back manually final IMessageSender messageSender = CServiceRegistry.getInstance(). getService(IMessageSender.class); // or with a target final IMessageSender messageSender = target.getMessageSender(); messageSender.sendBack(msg);
A message can be forwarded to another address in a message handler. This interrupts the normal process of sending back a response.
public void forwardMessage(final CTargetAddress aReceiver); public boolean shallForward();
We have seen above that envelopes can be created specifically for sending to network microservices. In this case, a microservice ID is passed. There are also methods for accessing this ID.
Nano service flag
When a message is sent from a nano service to Observer, it contains a special flag.
public void setIsNanoServiceMessage(final boolean aIsNanoMessage); public boolean isNanoServiceMessage();
Sometimes it is important to store data until a response to a message comes back. This is where a transaction ID comes into play, which is transported in the envelope. This ID is entered manually. In a Hashtable data can be saved so simply to this ID.
public void setTransactionId(@Nullable final UUID aId); public UUID getTransactionId();
It can be forbidden that messages leave the own node. This is a security feature.
public void setOnlyLocal(final boolean aOnlyLocal); public boolean isOnlyLocal();
Messages can be sent from node to node. At each hop, messages are stored in a queue. Messages with high priority are transported to the next node first.
While log messages or transport of file snippets are given low priority by the system, replies are given the highest priority by the system. The programmer can set a priority for each message himself.
public void setPriority(@NotNull final EPriority aPriority); public EPriority getPriority();
Packet builder sequence
Messages can be compressed and encrypted by the system. This can be configured globally for nodes or connections, but also for each message directly. Currently, there are only three methods for processing streamed messages. They are identified by a letter. The order is important here:
- M: stands for the message itself (always present, always at the beginning)
- Z: stands for ZIP compression
- A: stands for AES encryption
By default, only one M is entered for the message. Conceivable are also the combinations "MZ" for compressed messages and MZA for first compressed and then encrypted messages. The combination MAZ makes no sense, because encrypted messages are hardly compressible. By the way: Before nodes send encrypted messages to each other, they each create an RSA pair of keys and exchange the public keys with each other. This applies to each individual node pair.
public void setPacketBuilderSequence(final String aSequence); public String getPacketBuilderSequence();
Another method checks if the message carries only the minimal "M" as PacketBuilderSequence.
public void setRawTransport(); public boolean isRawTransport();
The use of messages can always lead to errors that are strange at first sight. Therefore, there are a number of methods to help the programmer at work.
Messages contain multiple timestamps that are populated by the system.
public Instant getTimeSend(); public Instant getTimeDeliver(); public Instant getTimeSendBack(); public Instant getTimeDeliverAnswer(); public void setTimeSend(@Nullable final Instant aTimeSend); public void setTimeDeliver(@Nullable final Instant aTimeDeliver); public void setTimeSendBack(@Nullable final Instant aTimeSendBack); public void setTimeDeliverAnswer(@Nullable final Instant aTimeDeliverAnswer);
Each envelope has a sequential number, that will NOT be copied or transported.
public void setInstanceId(final int aInstanceId); public int getInstanceId();
Each envelope has a sequential number, that will be copied and transported. It is suitable for tracking message sequences. To track messages across node boundaries, the transaction ID can be used.
void setEnvId(final short aEnvId); short getEnvId();
The debug mode can be switched on manually. It provides for the transport of debug data (timestamps) across node boundaries. By default, it is switched off.
public static void setDebug(final boolean aDebug); public static boolean isDebug();
Message logging can be suppressed for each message.
public void setLogEnabled(final boolean aEnabled);