CTarget

A target

A target is any class that implements the ITarget interface. However, it offers considerable advantages to derive the class from CTarget, since many methods are implemented there that make the work easier. The only thing missing is the creation of one or more message handlers, which catch and process messages directed to this target.

A typical target looks like this:

class CTest extends CTarget
{
    CTest()
    {
        addMessageHandler(CRecordStartTarget.ID,
                          this::asyncStartTarget)
    }

    private boolean asyncStartTarget(@NotNull final CEnvelope aEnvelope,
                                     @NotNull final CRecord aRecord) throws CException
    {
        if (aEnvelope.isAnswer())
        {
            return false;
        }
        else
        {
            // do something
            aEnvelope.setResultSuccess();
            return true;
        }
    }
}

In the example, a class is derived from CTarget. A message handler for the message ID CRecordStartTarget.ID is registered in the constructor. If this message is sent to the target, the method asyncStartTarget is called. The envelope (CEnvelope) and the payload (CRecord) of the message are passed as arguments. The message is marked with a result (success), and the method ends with a Boolean, which indicates whether the message was handled successfully (true).

Any number of message handlers can be registered in a target.

The message ID

The message ID is assigned to the record. It identifies the message and is specified as the first parameter when a message handler is registered.

A special message handler is the null handler. It receives all messages that are directed to the target, but for which no message handler has been registered.

addMessageHandler(null,
                  this::asyncNullHandler);

The message handler method

An interface of type IMessageHandler is expected as message handler method:

public interface IMessageHandler
{
    boolean handleMessage(@NotNull CEnvelope aEnvelope,
                          @NotNull CRecord aRecord) throws Exception;
}

The use of the interface would look like this:

addMessageHandler(CRecordStartTarget.ID,
                  new IMessageHandler()
                  {
                      @Override
                      public boolean handleMessage(@NotNull final CEnvelope aEnvelope,
                                                   @NotNull final CRecord aRecord)
                                                   throws Exception
                      {
                          return false;
                      }
                  });

As of Java 8, a lambda can be used as well:

addMessageHandler(CRecordStartTarget.ID,
                  (envelope, record) -> asyncStartTarget(envelope,
                                                         record));

However, it is easier to use method references:

addMessageHandler(CRecordStartTarget.ID,
                  this::asyncStartTarget)

The Result Code

When a fresh message is sent, it carries the result code CResultCode.NOT_HANDLED. If a message returns as a response and still carries this code, then either the message has not been handled or this result code has been forgotten to be set. In any case this message will be logged. If the message could not be delivered due to an error, the message contains other result codes, depending on the error.

The return value of the message handler

If a message has been handled finally, true is returned, otherwise false. The latter causes this message to be passed to the null handler. However, there is also the possibility of using Hierarchical State Machines (HSM) in targets. In this case, the message handlers are assigned in hierarchically arranged states. The message is forwarded to the top, i.e. to the parent state, until the message is consumed or the top state is reached.

Registering a target

Targets are registered in the target registry of a namespace:

ITarget tgt = new CTestTarget();
namespace.getTargetRegistry()
         .registerTarget(tgt);

The target receives an ID, the target ID (TID for short). Alternatively, you can enter a preferred TID during registration:

ITarget tgt = new CTestTarget();
IId tid = CIdFactory.create("TEST1");
namespace.getTargetRegistry()
         .registerTarget(tgt,
                         tid);

Usually, however, a random ID generated by the target registry is sufficient.

For namespaces with more than one thread, you can optionally specify the queue ID of the thread:

ITarget tgt = new CTestTarget();
IId tid = CIdFactory.create("TEST1");
IId qid = CIdFactory.create("QUEUE1");
namespace.getTargetRegistry()
         .registerTarget(tgt,
                         tid,
                         qid);

The target address

Each target receives a nyssr.net -wide unique address through registration. These addresses can be used in messages as recipient and sender addresses. The target address is accessible via getAddress(). It is immutable and can therefore be passed on.

Notification after registration

This method is called after a target is registered. However, it is called in the thread of the registering code. A better possibility is to handle the message CRecordStartTarget, since message are processed in the thread of the target. The method can be overridden, of course, but the super method should be called first.

void notifyTargetRegistered(@NotNull CTargetAddress aAddress,
                            @NotNull IId aQID,
                            @NotNull ITargetRegistry aTargetRegistry,
                            @NotNull IMessageSender aMessageSender,
                            @NotNull IMessageLogger aMessageLogger) throws Exception;

The address is the new target address. The queue ID identifies the thread of the namespace in which the messages for the target are delivered. The target registry is the one where the target was registered. The message sender is the service used to send all messages. The message logger is the logger that is used to log the message.

Notification before de-registration

The method is called before the target is deregistered. Messages can still be sent. The call is made in the thread that also calls deregisterTarget(). This method can be overridden by any target, but the base method should be called.

void notifyTargetWillBeRemoved() throws Exception;

Notification after de-registration

The method is called after the target has been deregistered. The call is made in the thread that also calls deregisterTarget(). This method can be overridden by any target, but the base method should be called.

void notifyTargetDeRegistered() throws Exception;

Ping support

The class CTarget processes the message CRecordPing. It also provides support for sending ping messages.

ping(address);

This makes it easy to check if another target exists.

Convenience methods

// send a message
public void send(@NotNull final CMessage aMessage) throws CException;

// send a message (with envelope and record)
public void send(@NotNull final CEnvelope aEnvelope,
                 @NotNull final CRecord aRecord) throws CException;


// send a notification message (reply not needed)
public void sendNotification(@NotNull final CMessage aMsg) throws CException;
public void sendNotification(@NotNull final CEnvelope aEnvelope,
                             @NotNull final CRecord aRecord) throws CException;

// send a request message (reply wanted)
public void sendRequest(@NotNull final CMessage aMsg) throws CException;
public void sendRequest(@NotNull final CEnvelope aEnvelope,
                        @NotNull final CRecord aRecord) throws CException;

// send a message to itself
public final void triggerThisTarget(@NotNull final IId aMID) throws CException;

nyssr.net - Innovative Distributed System