Einen Service nutzen



Eine Message an einen Service schicken

Wir wollen jetzt einmal eine Nachricht an einen Service schicken. Zuerst werden zwei Records definiert, die sich sehr ähneln. Während der erste Record ein ganz normaler Record für eine direkte Message von Target zu Target darstellt, ist der zweite Record eine Service-Message. Dieses ist an dem Attribut "isService" im Element Record zu erkennen. Dieser Service wird vom System automatisch im Namespace "EXAMPLES" angemeldet. Das kann man am Namespace-Element innerhalb des Record-Elementes erkennen.

<?xml version="1.0" encoding="ISO-8859-1"?>
<records>

    <record id="5cfd0c48-5ef6-40e8-89ce-53606270c67b" name="INCREASE_INT_1" access="PACKAGE">
    
        <description>
            Target021 will increase an integer with this message.
        </description>
        
        <slot keyid="" keytype="1" name="NUMBER" type="INT" direction="REQUEST">
            <description>A single integer. Will be increased by 1.</description>
        </slot>

    </record>

    <record id="78aa831b-a458-4a2a-add4-0f9fe7f219d8" name="INCREASE_INT_2" isService="true" access="PACKAGE">
    
        <description>
            Target021 will increase an integer with this service.
        </description>
        
        <namespace>EXAMPLES</namespace>
        
        <slot keyid="" keytype="1" name="NUMBER" type="INT" direction="REQUEST">
            <description>A single integer. Will be increased by 1.</description>
        </slot>
        
    </record>

</records>

Die Records werden als Beispiel für einen Request von einem Target zu einem anderen Target verwendet. Sie transportieren beide einen Integer, welcher vom Receiver inkrementiert werden soll.

Wir definieren erst das Target, welches den Service (2x) anbietet. Erstens für direkte Messages, und zweitens für einen Service, zum direkten Vergleich.

final class CTarget021 extends CTarget
{

    CTarget021()
    {
        addMessageHandler(CRecordStartTarget.ID, new IMessageHandler()
        {
            @Override
            public boolean handleMessage(final CEnvelope aEnvelope,
                                         final CRecord aRecord) throws Exception
            {
                // add an observer to Service CRecordIncreaseInt2
                CRecordIncreaseInt2.addObserver(CTarget021.this);
                
                aEnvelope.setResult(null);
                return true;
            }
        });
        
        addMessageHandler(CRecordIncreaseInt1.ID, new IMessageHandler()
        {
            @Override
            public boolean handleMessage(final CEnvelope aEnvelope,
                                         final CRecord aRecord) throws Exception
            {
                if (aEnvelope.isAnswer())
                {
                    return false;
                }
                else
                {
                    int number = CRecordIncreaseInt1.getParamNumber(aRecord, 0);
                    number++;
                    CRecordIncreaseInt1.setParamNumber(aRecord, number);
                    aEnvelope.setResult(null);
                    return true;
                }
            }
        });

        addMessageHandler(CRecordIncreaseInt2.ID, new IMessageHandler()
        {
            @Override
            public boolean handleMessage(final CEnvelope aEnvelope,
                                         final CRecord aRecord) throws Exception
            {
                if (aEnvelope.isAnswer())
                {
                    return false;
                }
                else
                {
                    int number = CRecordIncreaseInt2.getParamNumber(aRecord, 0);
                    number++;
                    CRecordIncreaseInt2.setParamNumber(aRecord, number);
                    aEnvelope.setResult(null);
                    return true;
                }
            }
        });
    }
}

Wie wir erkennen können, ist der MessageHandler für direkte Messages (CRecordIncreaseInt1) identisch zu dem der Service-Message (CRecordIncreaseInt2). Für das anbietende Target ändert sich also fast gar nichts bei der Nutzung von Services. Es muss lediglich ein Observer für den Service registriert werden, was die Zugriffsklasse des Records für uns erledigt. Diese kennt ja schließlich den Namespace, in dem der Service registriert wurde, siehe die Record-Definition oben.

Es folgt das sendende Target:

final class CTarget022 extends CTarget
{
    private static final int NUMBER1 = 100;
    private static final int NUMBER2 = 200;

    CTarget022()
    {
        addMessageHandler(CRecordStartTarget.ID, new IMessageHandler()
        {
            @Override
            public boolean handleMessage(final CEnvelope aEnvelope,
                                         final CRecord aRecord) throws Exception
            {
                sendDirectMessage(NUMBER1);
                sendServiceMessage(NUMBER2);
                aEnvelope.setResult(null);
                return true;
            }

            private void sendDirectMessage(final int aNumber) throws CException
            {
                final IId tid = CIdFactory.create("Target021");
                final IId nid = getAddress().getNID();
                final INodeID nodeid = getAddress().getNodeID();

                final CEnvelope env = new CEnvelope(tid, nid, nodeid);
                final CRecord rec = CRecordIncreaseInt1.create();
                CRecordIncreaseInt1.setParamNumber(rec, aNumber);
                sendRequest(env, rec);
            }

            private void sendServiceMessage(final int aNumber) throws CException
            {
                final IId nid = CRecordIncreaseInt2.getNID();

                final CEnvelope env = new CEnvelope(nid);
                final CRecord rec = CRecordIncreaseInt2.create();
                CRecordIncreaseInt2.setParamNumber(rec, aNumber);
                sendRequest(env, rec);
            }
        });

        addMessageHandler(CRecordIncreaseInt1.ID, new IMessageHandler()
        {
            @Override
            public boolean handleMessage(final CEnvelope aEnvelope,
                                         final CRecord aRecord) throws Exception
            {
                if (aEnvelope.isAnswer())
                {
                    final int number = CRecordIncreaseInt1.getParamNumber(aRecord, 0);
                    CConstants.LOG.debug("Direct Message: Number is now {}", number);
                    return true;
                }
                else
                {
                    return false;
                }
            }
        });

        addMessageHandler(CRecordIncreaseInt2.ID, new IMessageHandler()
        {
            @Override
            public boolean handleMessage(final CEnvelope aEnvelope,
                                         final CRecord aRecord) throws Exception
            {
                if (aEnvelope.isAnswer())
                {
                    final int number = CRecordIncreaseInt2.getParamNumber(aRecord, 0);
                    CConstants.LOG.debug("Service Message: Number is now {}", number);
                    return true;
                }
                else
                {
                    return false;
                }
            }
        });
    }
}

Die Antwort-MessageHandler sind wieder identisch. Der Unterschied liegt bei der Erstellung des Envelopes. Während für die direkte Message eine Target-Adresse bzw. deren Bestandteile (TID, NID, NODEID) bereitzustellen sind, genügt für den Service eine Namespace-ID. Und diese NID bekommen wir auch noch von der generierten Zugriffsklasse: CRecordIncreaseInt2.getNID().

Würde das Empfänger-Target in einem anderen NODE registriert sein, so müssten wir (zusätzlich zur NID)noch die NODEID des NODES angeben.

Der Vorteil liegt auf der Hand: Einmal benötigen wir eine Target-Adresse (hart verdrahten, oder per Konfiguration), und einmal benötigen wir: nichts. Lediglich wenn der Service von einem anderen NODE angeboten wird, bräuchten wir diese NODEID. Für die Lösung dieses Problems gibt es es jedoch die Netzwerkservices, die mit einem weiteren PlugIn angeboten werden. Dann benötigen wir nicht mal mehr die Kenntnis des NODES, welcher den Service anbietet. Doch dazu später.

Services bestehen aus Record-IDs, die in der ServiceRegistry eines Namespaces angemeldet werden. Beim Einlesen der Record-XML-Files werden die Services vom System automatisch angelegt. In der ServiceRegistry können aber auch Services einfach angemeldet werden:

getNamespace().getServiceRegistry().registerService(CRecordIncreaseInt2.ID, "IncreaseInt");

Näheres dazu bietet die Dokumentation: Services und ServiceRegistry

LOG Monitor

Hier ein Output des LOG Monitors.

Diese Beispiele findest du im Projekt D1ExamplesNode001 im package de.softdevel.d1.examples.node001.example003.