5. Using the generated API in applications

The DDS API implementation will allow the use of GPB types for DDS transparently, and the generated underlying DDS type will be invisible to the application.

5.1. Protobuf data model

For the comming example the following proto file is used:

import "omg/dds/descriptor.proto";

package address;

message Organisation {
    required string name = 1 [(.omg.dds.member).key = true];
    required string address = 2 [(.omg.dds.member).filterable = true];
    optional Person.PhoneNumber phone = 3;
}

message Person {
  option (.omg.dds.type) = {name: "dds.Person"};
  required string name = 1 [(.omg.dds.member).key = true]; 
  required int32 age = 2 [(.omg.dds.member) = {filterable: true}];
  optional string email = 3;
  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    required string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }
  repeated PhoneNumber phone = 4;
  required Organisation worksFor = 5;
}

5.2. Java

java

In this example we will publish a person Jane Doe with one friend, John Doe.

The Subscriber example will read this data and print it to the stdout. This example is delivered with OpenSplice, and is located in examples/protobuf/java5.

5.2.1. Publisher

java

Example Publisher for the generated Person data:

import java.util.concurrent.TimeoutException;

import org.omg.dds.core.InstanceHandle;
import org.omg.dds.core.ServiceEnvironment;
import org.omg.dds.core.policy.PolicyFactory;
import org.omg.dds.core.status.PublicationMatchedStatus;
import org.omg.dds.domain.DomainParticipant;
import org.omg.dds.domain.DomainParticipantFactory;
import org.omg.dds.pub.DataWriter;
import org.omg.dds.pub.Publisher;
import org.omg.dds.topic.Topic;

import address.Address.Organisation;
import address.Address.Person;
import address.Address.Person.PhoneNumber;
import address.Address.Person.PhoneType;

public class ProtobufPublisher {
    public static void main(String[] args) {
        ServiceEnvironment env;
        DomainParticipantFactory domainParticipantFactory;
        DomainParticipant participant;
        Topic<Person> topic;
        Publisher publisher;
        DataWriter<Person> writer;
        Person.Builder janeDoeBuilder;
        PhoneNumber phone;
        Person janeDoe;
        PolicyFactory policyFactory;

        System.setProperty(
                ServiceEnvironment.IMPLEMENTATION_CLASS_NAME_PROPERTY,
                "org.opensplice.dds.core.OsplServiceEnvironment");

        env = ServiceEnvironment.createInstance(ProtobufPublisher.class
                .getClassLoader());

        policyFactory = PolicyFactory.getPolicyFactory(env);
        participant = null;
        domainParticipantFactory = DomainParticipantFactory.getInstance(env);

        try {
            participant = domainParticipantFactory.createParticipant();
            // Creating a Topic for a Protobuf class
            topic = participant.createTopic("Person", Person.class);

            // Creating a Publisher and DataWriter for the Protobuf Topic
            publisher = participant.createPublisher();
            writer = publisher.createDataWriter(
                    topic,
                    publisher.getDefaultDataWriterQos().withPolicy(
                            policyFactory.Reliability().withReliable()));

            waitForSubscriber(writer);
            // Creating a builder the Protobuf data structure
            janeDoeBuilder = Person.newBuilder();

            // Creating Jane Doe
            janeDoeBuilder.setName("Jane Doe")
                    .setEmail("jane.doe@somedomain.com").setAge(23);
            phone = PhoneNumber.newBuilder().setNumber("0123456789").build();
            janeDoeBuilder.addPhone(phone);
            Organisation.Builder orgBuilder = Organisation.newBuilder();
            orgBuilder.setName("Acme Corporation");
            orgBuilder.setAddress("Wayne Manor, Gotham City");
            orgBuilder.setPhone(PhoneNumber.newBuilder()
                    .setNumber("9876543210").setType(PhoneType.WORK));
            janeDoeBuilder.setWorksFor(orgBuilder);

            janeDoe = janeDoeBuilder.build();

            System.out.println("Publisher: publishing Person: "
                    + janeDoe.getName());
            writer.write(janeDoe);

            System.out.println("Publisher: sleeping for 5 seconds...");

            Thread.sleep(5000);

            System.out.println("Publisher: disposing Jane Doe...");

            // Disposing the DDS instance associated with the name field of the
            // Protobuf data structure which is the key in DDS
            writer.dispose(InstanceHandle.nilHandle(env), Person.newBuilder()
                    .setName("Jane Doe").setWorksFor(
                            Organisation.newBuilder().setName("Acme Corporation.").buildPartial()).buildPartial());

        } catch (TimeoutException e) {
            System.err.println(e.getMessage());
            e.printStackTrace();
        } catch (InterruptedException e) {
            System.err.println(e.getMessage());
            e.printStackTrace();
        } finally {
            System.out.println("Publisher: terminating...");

            if (participant != null) {
                participant.close();
            }
        }
    }

    public static void waitForSubscriber(DataWriter<Person> writer)
            throws InterruptedException {
        PublicationMatchedStatus matched;
        long millis = System.currentTimeMillis();
        long timeout = millis + (30 * 1000);
        boolean stop = false;

        System.out.println("Publisher: waiting for subscriber... ");

        do {
            matched = writer.getPublicationMatchedStatus();

            if (System.currentTimeMillis() > timeout) {
                stop = true;
            }
            if ((matched.getCurrentCount() == 0) && (!stop)) {
                Thread.sleep(500);
            }
        } while ((matched.getCurrentCount() == 0) && (!stop));

        if (matched.getCurrentCount() != 0) {
            System.out.println("Publisher: Subscriber found");
        } else {
            System.out.println("Publisher: Subscriber NOT found");
            throw new InterruptedException(
                    "Publisher: subscriber not detected within 30 seconds.");
        }
    }
}

5.2.2. Subscriber

java

Example Subscriber for the generated Person data:

import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import org.omg.dds.core.ServiceEnvironment;
import org.omg.dds.core.WaitSet;
import org.omg.dds.core.policy.PolicyFactory;
import org.omg.dds.domain.DomainParticipant;
import org.omg.dds.domain.DomainParticipantFactory;
import org.omg.dds.sub.DataReader;
import org.omg.dds.sub.Sample;
import org.omg.dds.sub.Sample.Iterator;
import org.omg.dds.sub.Subscriber;
import org.omg.dds.topic.Topic;

import address.Address.Person;
import address.Address.Person.PhoneNumber;

public class ProtobufSubscriber {
    public static void main(String[] args) {
        ServiceEnvironment env;
        DomainParticipantFactory domainParticipantFactory;
        DomainParticipant participant;
        Topic<Person> topic;
        DataReader<Person> reader;
        Subscriber subscriber;
        WaitSet ws;
        PolicyFactory policyFactory;
        int expectedUpdates = 2;

        System.setProperty(
                ServiceEnvironment.IMPLEMENTATION_CLASS_NAME_PROPERTY,
                "org.opensplice.dds.core.OsplServiceEnvironment");

        env = ServiceEnvironment.createInstance(ProtobufSubscriber.class
                .getClassLoader());

        policyFactory = PolicyFactory.getPolicyFactory(env);
        participant = null;
        domainParticipantFactory = DomainParticipantFactory.getInstance(env);

        try {
            participant = domainParticipantFactory.createParticipant();
            // Creating a Topic for a Protobuf class
            topic = participant.createTopic("Person", Person.class);

            // Creating a Subscriber and DataReader for the Protobuf Topic
            subscriber = participant.createSubscriber();
            reader = subscriber.createDataReader(
                    topic,
                    subscriber.getDefaultDataReaderQos().withPolicy(
                            policyFactory.Reliability().withReliable()));

            // Creating a WaitSet to block for incoming samples

            ws = env.getSPI().newWaitSet();
            ws.attachCondition(reader.createReadCondition(subscriber
                    .createDataState().withAnyViewState()
                    .withAnyInstanceState().withAnySampleState()));

            System.out.println("Subscriber: waiting for incoming samples...");

            do {
                // Waiting for data to become available
                ws.waitForConditions(30, TimeUnit.SECONDS);

                // Take all data and print it to the screen
                expectedUpdates -= printAllData(reader);
            } while (expectedUpdates > 0);

        } catch (RuntimeException e) {
            System.err.println(e.getMessage());
            e.printStackTrace();
        } catch (TimeoutException e) {
            System.out
                    .println("Subscriber: time-out while waiting for updates.");
        } finally {
            System.out.println("Subscriber: terminating...");

            if (participant != null) {
                participant.close();
            }
        }
    }

    public static int printAllData(DataReader<Person> reader) {
        Iterator<Person> iter;
        Sample<Person> sample;
        Person data;
        String states;
        int sampleCount = 0;

        iter = reader.take();

        while (iter.hasNext()) {
            sample = iter.next();
            sampleCount++;

            states = "(" + sample.getSampleState() + ", "
                    + sample.getViewState() + ", " + sample.getInstanceState()
                    + ")";

            if (sample.getData() != null) {
                System.out
                        .println("Subscriber: reading sample " + states + ":");
                printPerson(sample.getData(), "");
            } else {
                data = ((org.opensplice.dds.sub.Sample<Person>) sample)
                        .getKeyValue();
                System.out.println("Subscriber: reading invalid sample "
                        + states + ":");
                System.out.println("- Name  = " + data.getName());
                System.out.println("   - Company    =");
                System.out.println("      - Name    = "
                        + data.getWorksFor().getName());
            }
        }
        return sampleCount;
    }

    public static void printPerson(Person person, String tabs) {

        System.out.println(tabs + "- Name       = " + person.getName());
        System.out.println(tabs + "- Age        = " + person.getAge());
        System.out.println(tabs + "- Email      = " + person.getEmail());

        for (PhoneNumber phone : person.getPhoneList()) {
            System.out.println(tabs + "- Phone      = " + phone.getNumber()
                    + " ("
                    + phone.getType() + ")");

        }
        System.out.println(tabs + "- Company    =");
        System.out.println(tabs + "   - Name    = "
                + person.getWorksFor().getName());
        System.out.println(tabs + "   - Address = "
                + person.getWorksFor().getAddress());

        if (person.getWorksFor().hasPhone()) {
            System.out.println(tabs + "   - Phone   = "
                    + person.getWorksFor().getPhone().getNumber() + " ("
                    + person.getWorksFor().getPhone().getType() + ")");
        } else {
            System.out.println(tabs + "   - Phone   = ");
        }
    }
}

5.3. ISO-C++

cpp

In this example the publisher and subscriber are embedded into one file.

The publisher part will publish a person Jane Doe with one friend, John Doe.

The Subscriber part in this example will read this data and print it to the stdout.

This example is delivered with Vortex OpenSplice, and is located in examples/protobuf/isocpp2.

/*
 *                         OpenSplice DDS
 *
 *   This software and documentation are Copyright 2006 to TO_YEAR PrismTech
 *   Limited, its affiliated companies and licensors. All rights reserved.
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 */

#include "implementation.hpp"
#include "common/example_utilities.h"

#include <iostream>

#include "address.pbdds.hpp"

namespace examples { namespace protobuf { namespace isocpp  {

int publisher(int argc, char *argv[])
{
    int result = 0;
    (void) argc;
    (void) argv;
    try
    {
        /** A dds::domain::DomainParticipant is created for the default domain. */
        dds::domain::DomainParticipant dp(org::opensplice::domain::default_id());

        /** A dds::topic::Topic is created for our protobuf type on the domain participant. */
        dds::topic::Topic<address::Person> topic(dp, "Person");

        /** A dds::pub::Publisher is created on the domain participant. */
        dds::pub::Publisher pub(dp);

        /** The dds::pub::qos::DataWriterQos is derived from the topic qos */
        dds::pub::qos::DataWriterQos dwqos;

        dwqos << dds::core::policy::Reliability::Reliable();
        /** A dds::pub::DataWriter is created on the Publisher & Topic with the modififed Qos. */
        dds::pub::DataWriter<address::Person> dw(pub, topic);

        /** Synchronize on subscriber availability. */
        std::cout << "Publisher: waiting for subscriber... " <<std::endl;
        unsigned long current = exampleTimevalToMicroseconds(exampleGetTime());
        unsigned long timeout = current + (30 * 1000 * 1000);
        bool stop = false;
        ::dds::core::status::PublicationMatchedStatus matched;

        do {
            matched = dw.publication_matched_status();

            if (exampleTimevalToMicroseconds(exampleGetTime()) > timeout) {
                stop = true;
            }
            if ((matched.current_count() == 0) && (!stop)) {
                exampleSleepMilliseconds(500);
            }
        } while ((matched.current_count() == 0) && (!stop));

        if (matched.current_count() != 0) {
            std::cout << "Publisher: Subscriber found" << std::endl;

            /** A sample is created and then written. */
            address::Person msgInstance;
            msgInstance.set_name("Jane Doe");
            msgInstance.set_email("jane.doe@somedomain.com");
            msgInstance.set_age(23);

            address::Person::PhoneNumber* phone = msgInstance.add_phone();
            phone->set_number("0123456789");
            address::Organisation* worksFor = msgInstance.mutable_worksfor();
            worksFor->set_name("Acme Corporation");
            worksFor->set_address("Wayne Manor, Gotham City");
            worksFor->mutable_phone()->set_number("9876543210");
            worksFor->mutable_phone()->set_type( ::address::Person_PhoneType_WORK);
            std::cout << "Publisher: publishing Person: " << msgInstance.name() << std::endl;

            ::dds::core::InstanceHandle handle = dw.register_instance(msgInstance);

            dw << msgInstance;

            std::cout << "Publisher: sleeping for 5 seconds..." << std::endl;

            exampleSleepMilliseconds(5000);

            std::cout << "Publisher: disposing Jane Doe..." << std::endl;

            /** Disposing the DDS instance associated with the name field of the
              * Protobuf data structure which is the key in DDS*/

            dw.dispose_instance(handle);
            exampleSleepMilliseconds(1000);
        } else {
            throw ::dds::core::PreconditionNotMetError("Subscriber NOT found, terminating...");
        }
    }
    catch (const dds::core::Exception& e)
    {
        std::cerr << "Publisher: ERROR: " << e.what() << std::endl;
        result = 1;
    }
    std::cout << "Publisher: terminating..." << std::endl;
    return result;
}

class ReadCondHandler
{
public:
    /**
     * @param dataState The dataState on which to filter the samples
     */
    ReadCondHandler(): updateCount(0) {}

    void operator() (dds::sub::cond::ReadCondition c)
    {
        std::string states, sampleState, viewState, instanceState;
        dds::sub::DataReader<address::Person> dr = c.data_reader();
        dds::sub::LoanedSamples<address::Person> samples = dr.select().state(c.state_filter()).take();

        for (dds::sub::LoanedSamples<address::Person>::const_iterator sample = samples.begin();
            sample < samples.end(); ++sample)
        {
            updateCount++;

            if(sample->info().state().sample_state() == dds::sub::status::SampleState::read()){
                sampleState = "READ";
            } else {
                sampleState = "NOT_READ";
            }
            if(sample->info().state().view_state() == dds::sub::status::ViewState::new_view()){
                viewState = "NEW";
            } else {
                viewState = "NOT_NEW";
            }
            if(sample->info().state().instance_state() == dds::sub::status::InstanceState::alive()){
                instanceState = "ALIVE";
            } else if(sample->info().state().instance_state() == dds::sub::status::InstanceState::not_alive_disposed()){
                instanceState = "NOT_ALIVE_DISPOSED";
            } else {
                instanceState = "NOT_ALIVE_NO_WRITERS";
            }
            states = "(" + sampleState + ", " + viewState + ", " + instanceState + ")";

            if(sample->info().valid())
            {
                std::cout << "Subscriber: reading sample " <<  states << ":" << std::endl;
                printPerson(sample->data(), "");
            } else {
                std::cout << "Subscriber: reading invalid sample " << states << ":" << std::endl;
                std::cout << "- Name     = " << sample->data().name() << std::endl;
                std::cout << "- Company  = " << std::endl;
                std::cout << "   - Name  = " << sample->data().worksfor().name() << std::endl;
            }
        }
    }

    int getUpdateCount(){
        return updateCount;
    }

private:
    int updateCount;

    void printPhone(const ::address::Person_PhoneNumber phone, std::string tabs){
        std::string type;

        switch(phone.type()){
        case ::address::Person_PhoneType_MOBILE:
            type = "MOBILE";
            break;
        case ::address::Person_PhoneType_HOME:
            type = "HOME";
            break;
        case ::address::Person_PhoneType_WORK:
            type = "WORK";
            break;
        default:
            type = "UNKNOWN";
            break;
        }
        std::cout << tabs << "- Phone      = " << phone.number() <<
                " (" + type << ")" << std::endl;

    }

    void printPerson(address::Person person, std::string tabs){
        std::cout << tabs << "- Name       = " << person.name() << std::endl;
        std::cout << tabs << "- Age        = " << person.age() << std::endl;
        std::cout << tabs << "- Email      = " << person.email() << std::endl;

        for (int i=0; i< person.phone_size(); i++) {
            printPhone(person.phone(i), tabs);
        }
        std::cout << tabs << "- Company    = " << std::endl;
        std::cout << tabs << "   - Name       = " << person.worksfor().name() << std::endl;
        std::cout << tabs << "   - Address    = " << person.worksfor().address() << std::endl;

        if(person.worksfor().has_phone()){
            printPhone(person.worksfor().phone(), tabs + "   ");
        } else {
            std::cout << tabs << "   - Phone   = NONE" << std::endl;
        }
    }
};


/**
 * Runs the subscriber role of this example.
 * @return 0 if a sample is successfully read, 1 otherwise.
 */
int subscriber(int argc, char *argv[])
{
    int result = 0;
    (void) argc;
    (void) argv;
    try
    {
        int expectedUpdates = 2;
        /** A dds::domain::DomainParticipant is created for the default domain. */
        dds::domain::DomainParticipant dp(org::opensplice::domain::default_id());

        /** A dds::topic::Topic is created for our protobuf type on the domain participant. */
        dds::topic::Topic<address::Person> topic(dp, "Person");

        /** A dds::pub::Subscriber is created on the domain participant. */
        dds::sub::Subscriber sub(dp);

        /** The dds::pub::qos::DataWriterQos is derived from the topic qos */
        dds::sub::qos::DataReaderQos drqos;

        drqos << dds::core::policy::Reliability::Reliable();
        /** A dds::pub::Reader is created on the Subscriber & Topic with the modififed Qos. */
        dds::sub::DataReader<address::Person> dr = dds::sub::DataReader<address::Person>(sub, topic, drqos);

        /** any sample, view and instance state */
        dds::sub::cond::ReadCondition readCond(dr, dds::sub::status::DataState::any());
        ReadCondHandler personHandler;
        readCond.handler(personHandler);

        /** A WaitSet is created and the four conditions created above are attached to it */
        dds::core::cond::WaitSet waitSet;
        waitSet += readCond;

        dds::core::Duration waitTimeout(30, 0);

        /** Wait until the condition in the WaitSet triggers and dispatch the corresponding functor*/
        do {
            waitSet.dispatch(waitTimeout);
        } while(personHandler.getUpdateCount() < expectedUpdates);
    }
    catch (const dds::core::Exception& e)
    {
        std::cerr << "Subscriber: ERROR: " << e.what() << std::endl;
        result = 1;
    }
    std::cout << "Subscriber: terminating..." << std::endl;

    return result;
}

}
}
}

EXAMPLE_ENTRYPOINT(DCPS_ISOCPP_Protobuf_publisher, examples::protobuf::isocpp::publisher)
EXAMPLE_ENTRYPOINT(DCPS_ISOCPP_Protobuf_subscriber, examples::protobuf::isocpp::subscriber)