Eine vierte Message



Ein Target, welches sich selbst eine Message zuschickt (Version 4).

Wir haben vorhin gesehen, dass eine Message manuell nur umständlich mit Daten zu füllen ist. Das wollen wir hier einfacher machen.

Wir definieren ein Record per XML:

<records>

    <record id="92d3c047-ecfb-445c-bda6-c2cebe498481" name="TRIGGER_1" access="PACKAGE">
    
        <description>
            A Message for Target003 with data
        </description>
        
        <slot keyid="" keytype="1" name="NAME" type="STRING" direction="REQUEST">
            <description>A String.</description>
        </slot>

        <slot keyid="" keytype="2" name="SINGLE_NUMBER" type="INT" direction="REQUEST">
            <description>A single integer.</description>
        </slot>

        <slot keyid="" keytype="3" name="NUMBERS" type="SHORTARRAY" direction="REQUEST">
            <description>A short array.</description>
        </slot>
        
    </record>

</records>

Die Message von Target004 im letzten Beispiel kann man hier gut wiedererkennen. Das Erstellen einer solchen Beschreibung geht schnell von der Hand, insbesondere wenn man schon viele Records geschrieben hat und mit Copy & Paste arbeiten kann.

softdevel liefert mit der devel.one Distribution ein Tool namens RecordGenerator mit. Dieses generiert statische Zugriffsklassen, mit denen auf die Daten von Records sehr viel leichter zugegriffen werden kann. Aus dem Namen des oben definierten Records "TRIGGER_1" erstellt es eine Klasse CRecordTrigger1, welche im Target CTarget005 verwendet wird.

final class CTarget005 extends CTarget
{
    private static final short SHORT23 = (short) 23;
    private static final short SHORT99 = (short) 99;
    private static final short SHORT04 = (short) 04;
    private static final short SHORT31000 = (short) 31000;
    private static final int INT67000 = 67000;
    private static final String MARY_ANN = "Peter P.";

    CTarget005()
    {
        addMessageHandler(CRecordStartTarget.ID, new IMessageHandler()
        {
            @Override
            public boolean handleMessage(final CEnvelope aEnvelope,
                                         final CRecord aRecord) throws Exception
            {

                triggerThisTargetWithData(MARY_ANN, INT67000, new short[] { SHORT04, SHORT23, SHORT99, SHORT31000 });

                aEnvelope.setResult(null);
                return true;
            }

            private void triggerThisTargetWithData(final String aName,
                                                   final int aSingleNumber,
                                                   final short[] aNumbers) throws CException
            {
                final CEnvelope env = new CEnvelope(getAddress());
                
                final CRecord rec = CRecordTrigger1.create();
                CRecordTrigger1.setParamName(rec, aName);
                CRecordTrigger1.setParamSingleNumber(rec, aSingleNumber);
                CRecordTrigger1.setParamNumbers(rec, aNumbers);
                
                sendNotification(env, rec);
            }
        });

        addMessageHandler(CRecordTrigger1.ID, new IMessageHandler()
        {
            @Override
            public boolean handleMessage(final CEnvelope aEnvelope,
                                         final CRecord aRecord) throws Exception
            {
                // get data from the slots via generated helper class (type safe, no cast, easy access via methods, no slot keys to
                // memorize)
                final String name = CRecordTrigger1.getParamName(aRecord, null);
                final int number = CRecordTrigger1.getParamSingleNumber(aRecord, 0);
                final short[] numbers = CRecordTrigger1.getParamNumbers(aRecord, null);

                CConstants.LOG.debug("String is {}", name);
                CConstants.LOG.debug("Integer is {}", number);
                CConstants.LOG.debug("ShortArray is {}", Arrays.toString(numbers));

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

Hier sieht man schön, wie der Zugriff auf die Daten durch Getter (getParamXXX()) und Setter (setParamXXX()) erleichtert wird. Die Verwendung der Methoden erfolgt typsicher. Es muss nicht gecastet werden. Der Programmierer benötigt nicht einmal die SlotKeys.

Das Schöne an den XML-Definitionen ist, dass sie als API-Bestandteil weitergegeben werden können. Jede an der Entwicklung beteiligte Gruppe kann sich ihre Zugriffsklassen selbst generieren, d.h. es muss kein Code in Form von JAVA™-Interfaces weitergegeben werden. Die Verwendung der API ist zudem unabhängig von der Programmiersprache, denn für jede Sprache kann es ein Generator geben. Der Generator kann übrigens gestartet bleiben, so dass nach einer Änderung im XML File ein Knopfdruck genügt, um die Zugriffsklassen zu updaten.

Außer den Settern und Gettern werden noch ein paar andere hilfreiche Methoden generiert. Außerdem ist die ID des Records in der Zugriffsklasse abgelegt. Sie wird hier bei der Anmeldung des MessageHandlers verwendet: CRecordTrigger1.ID. Bei den Gettern wird übrigens außer dem Record noch ein weiteres Argument erwartet: Der Default-Wert, der zurück geliefert werden soll, wenn der Slot nicht gefunden wird.

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