{"id":393,"date":"2020-05-25T09:55:31","date_gmt":"2020-05-25T09:55:31","guid":{"rendered":"http:\/\/spiderwiz.org\/project\/?page_id=393"},"modified":"2025-04-09T06:15:57","modified_gmt":"2025-04-09T06:15:57","slug":"lesson-2","status":"publish","type":"page","link":"https:\/\/spiderwiz.org\/project\/tutorial\/lesson-2\/","title":{"rendered":"Lesson 2: Communication &#8211; Producers and Consumers"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">In <a href=\"http:\/\/spiderwiz.org\/project\/tutorial\/lesson-1\/\">the previous lesson<\/a> we set a property in a <code>HelloWorld<\/code> data object and <em>committed<\/em> it, then, when handling the <em>event<\/em>, we retrieved the object\u2019s property and displayed it. That means that we acted as both a <em>Producer<\/em> and a <em>Consumer<\/em> of the object. Since the application implemented both roles we did not have to worry about communication.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Obviously, this is not a common scenario. Normally you would have a service-mesh in which some microservices act as <em>Producers<\/em> and others act as <em>Consumers<\/em>, communicating over some kind of a communication channel and protocol. Frequently a microservice acts as a producer of some types of data and a consumer of others, but the idea is the same.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In this lesson we keep with the <code>HelloWorld<\/code> application, but we will have two of them \u2013 <code>HelloWorldProducer<\/code> and <code>HelloWorldConsumer<\/code>. The first produces and commits the <code>HelloWorld<\/code> data object, while the second acts upon receiving an object <em>event<\/em> and displays the greeting set by the producer, as in the previous lesson. We will also learn how to connect the two applications over the network (spoiler: this will not require even a single line of code).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We will have two projects \u2013 <code>hello-world-producer<\/code> and <code>hello-world-consumer<\/code>. Needless to say, both shall include the Spiderwiz dependency as in <a href=\"http:\/\/spiderwiz.org\/project\/tutorial\/lesson-1\/\">lesson 1<\/a> (we will not mention it again in this tutorial). Each of the applications has its own <code>Main<\/code> class. Let\u2019s see first <code>HelloWorldProducerMain.java<\/code>:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre>package org.spiderwiz.tutorial.lesson2.producer;\n\nimport java.util.List;\nimport org.spiderwiz.core.DataObject;\nimport org.spiderwiz.core.Main;\nimport org.spiderwiz.tutorial.objectLib.HelloWorld;\n\n\/**\n * Provides the entry point of the application. Initializes and executes the Spiderwiz framework.\n *\/\npublic class HelloWorldProducerMain extends Main{\n    private static final String ROOT_DIRECTORY = &quot;&quot;;\n    private static final String CONF_FILENAME = &quot;hello-world-producer.conf&quot;;\n    private static final String APP_NAME = &quot;Hello World Producer&quot;;\n    private static final String APP_VERSION = &quot;Z1.01&quot;;  \/\/ Version Z1.01: Initial version\n\n    \/**\n     * Class constructor with constant parameters.\n     *\/\n    public HelloWorldProducerMain() {\n        super(ROOT_DIRECTORY, CONF_FILENAME, APP_NAME, APP_VERSION);\n    }\n\n    \/**\n     * Application entry point. Instantiate the class, initialize the instance, then call a command-line hook that would shut down\n     * the application when &quot;exit&quot; is typed.\n     * \n     * @param args the command line arguments. Not used in this application.\n     *\/\n    public static void main(String[] args) {\n        HelloWorldProducerMain main = new HelloWorldProducerMain();\n        if (main.init())\n            main.commandLineHook();\n    }\n    \n    \/**\n     * @return the list of produced objects, in this case HelloWorld is the only one.\n     *\/\n    @Override\n    protected String[] getProducedObjects() {\n        return new String[]{HelloWorld.ObjectCode};\n    }\n\n    \/**\n     * @return the list of consumed objects, in this case we do not consume any so we return an empty list.\n     *\/\n    @Override\n    protected String[] getConsumedObjects() {\n        return new String[]{};\n    }\n\n    \/**\n     * Add HelloWorld class to the object factory list of this application.\n     * @param factoryList\n     *\/\n    @Override\n    protected void populateObjectFactory(List&lt;Class&lt;? extends DataObject&gt;&gt; factoryList) {\n        super.populateObjectFactory(factoryList);\n        factoryList.add(HelloWorld.class);\n    }\n\n    \/**\n     * Create a HelloWorld object, set its field and commit it.\n     *\/\n    @Override\n    protected void postStart() {\n        try {\n            HelloWorld helloWorld = createTopLevelObject(HelloWorld.class, null);\n            helloWorld.setSayHello(&quot;Hello World&quot;);\n            helloWorld.commit();\n            return;\n        } catch (NoSuchFieldException | IllegalAccessException ex) {\n            ex.printStackTrace();\n        }\n    }\n}<\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Beyond the obvious\nchanges in <code>APP_NAME<\/code> and <code>CONF_FILENAME<\/code>, you can see (line 40)\nthat the application is still a producer of <code>HelloWorld<\/code>\nobjects. However in <code>getConsumedObjects()<\/code>\n(line 48) we return an empty list because the application consumes nothing. The\nrest of the code is identical to the code of the previous lesson.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The sharp-eyed among you\nmay notice another small change. While in the previous lesson <code>HelloWorld<\/code> class was defined in the same\npackage as <code>HelloWorldMain<\/code>, here we\nimport it from <code>org.spiderwiz.tutorial.objectLib<\/code>\n(line 6). The reason is that from now on we will have multiple projects sharing\nthe same <em>data object<\/em> classes\ntherefore we have created a class library that can be shared by other projects\n\u2013 producers and consumers alike. We store <code>HeloWorld<\/code>\nin that library so that it can be used also by the other project that we are\ngoing to build in this lesson \u2013 <code>hello-world-consumer<\/code>.\nBefore we start with it let\u2019s first look at the new <code>HelloWorld<\/code> class, as it differs a bit from the previous lesson.<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre>package org.spiderwiz.tutorial.objectLib;\n\nimport org.spiderwiz.annotation.WizField;\nimport org.spiderwiz.core.DataObject;\n\n\/**\n * Implements HelloWorld data object.\n *\/\npublic class HelloWorld extends DataObject{\n\n    \/**\n     * Mandatory public static field for all data objects.\n     *\/\n    public final static String ObjectCode = &quot;HLWRLD&quot;;\n    \n    @WizField private String sayHello;\n\n    public String getSayHello() {\n        return sayHello;\n    }\n\n    public void setSayHello(String sayHello) {\n        this.sayHello = sayHello;\n    }\n\n    \/**\n     * @return null as this is a root object.\n     *\/\n    @Override\n    protected String getParentCode() {\n        return null;\n    }\n\n    \/**\n     * @return true as this object is disposable.\n     *\/\n    @Override\n    protected boolean isDisposable() {\n        return false;\n    }\n}<\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">There are two changes\nfrom the <code>HelloWorld<\/code> class of the\nprevious lesson. First, we do not override <code>onEvent()<\/code>.\nThe reason is that only the consumer needs to implement this method, while\nclasses defined in <code>objectLib<\/code> library\nare used by the producer and the consume alike.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The other change is the value returned by <a href=\"http:\/\/spiderwiz.org\/apidocs\/org\/spiderwiz\/core\/DataObject.html#isDisposable()\">isDisposable()<\/a> (38). This was modified from <code>true<\/code> to <code>false<\/code>. This is because we are going to have two applications that communicate, and obviously we cannot guarantee that they will start simultaneously. We also do not want to mess up with synchronization of the \u201chello world\u201d greeting until the two applications connect. The solution is the Spiderwiz way. Right after the producer starts and initializes, it creates a <code>HelloWorld<\/code> object and commits it. From now on, since the object is not disposable, it is conceptually \u201cin the space\u201d. Once the consumer starts and connects to the producer, it will encounter the event that is fired by the object.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In order to handle <code>HelloWorld<\/code> events, <code>hello-world-consumer<\/code> defines (in its own package) a new class \u2013 <code>HelloWorldConsumer<\/code> that extends <code>HelloWorld<\/code>. Here it is: <\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre>package org.spiderwiz.tutorial.lesson2.consumer;\n\nimport org.spiderwiz.tutorial.objectLib.HelloWorld;\n\n\/**\n * Extends HelloWorld and implement consumer code in onEvent().\n *\/\npublic class HelloWorldConsumer extends HelloWorld {\n\n    \/**\n     * Do the consumer work.\n     * @return true to indicate that the event has been handled.\n     *\/\n    @Override\n    protected boolean onEvent() {\n        System.out.println(getSayHello());\n        return true;\n    }\n}<\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">The extension adds <a href=\"http:\/\/spiderwiz.org\/apidocs\/org\/spiderwiz\/core\/DataObject.html#onEvent()\">onEvent()<\/a> to the base <code>HelloWorld<\/code> class. The method does exactly what it did in lesson 1.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We still need to see how the <code>hello-world-consumer<\/code> project implements <code>HelloWorldConsumerMain<\/code>. Here it goes:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre>package org.spiderwiz.tutorial.lesson2.consumer;\n\nimport java.util.List;\nimport org.spiderwiz.core.DataObject;\nimport org.spiderwiz.core.Main;\nimport org.spiderwiz.tutorial.objectLib.HelloWorld;\n\n\/**\n * Provides the entry point of the application. Initializes and executes the Spiderwiz framework.\n *\/\npublic class HelloWorldConsumerMain extends Main{\n    private static final String ROOT_DIRECTORY = &quot;&quot;;\n    private static final String CONF_FILENAME = &quot;hello-world-consumer.conf&quot;;\n    private static final String APP_NAME = &quot;Hello World Consumer&quot;;\n    private static final String APP_VERSION = &quot;Z1.01&quot;;  \/\/ Version Z1.01: Initial version\n\n    \/**\n     * Class constructor with constant parameters.\n     *\/\n    public HelloWorldConsumerMain() {\n        super(ROOT_DIRECTORY, CONF_FILENAME, APP_NAME, APP_VERSION);\n    }\n\n    \/**\n     * Application entry point. Instantiate the class, initialize the instance, then call a command-line hook that would shut down\n     * the application when &quot;exit&quot; is typed.\n     * \n     * @param args the command line arguments. Not used in this application.\n     *\/\n    public static void main(String[] args) {\n        HelloWorldConsumerMain main = new HelloWorldConsumerMain();\n        if (main.init())\n            main.commandLineHook();\n    }\n    \n    \/**\n     * @return an empty list as we produce nothing.\n     *\/\n    @Override\n    protected String[] getProducedObjects() {\n        return new String[]{};\n    }\n\n    \/**\n     * @return the list of consumed objects, in this case HelloWorld is the only one.\n     *\/\n    @Override\n    protected String[] getConsumedObjects() {\n        return new String[]{HelloWorld.ObjectCode};\n    }\n\n    \/**\n     * Add HelloWorldConsumer class to the object factory list of this application.\n     * @param factoryList\n     *\/\n    @Override\n    protected void populateObjectFactory(List&lt;Class&lt;? extends DataObject&gt;&gt; factoryList) {\n        super.populateObjectFactory(factoryList);\n        factoryList.add(HelloWorldConsumer.class);\n    }\n}<\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">As expected, here <code>getProducedObjects()<\/code> (line 40) and <code>getConsumedObjects()<\/code> (line 48) reverse their roles compared to <code>HelloWorldProducerMain<\/code> &#8211; we produce nothing and consume <code>HelloWorld<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">There is one more crucial\ndiscrepancy. In <code>populateObjectFactory()<\/code>\n(line 57) we register the <code>HelloWorldConsumer<\/code>\nextension class rather than <code>HelloWorld<\/code>.\nThis ensures that whenever a <code>HelloWorld<\/code>\nobject is received it will be handled by an instance of <code>HelloWorldConsumer<\/code> and its <code>onEvent()<\/code>\nmethod will be activated.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We are done with the code. Now the big question is how do the two applications communicate. The answer is: through the <em>magic of Spiderwiz<\/em>  \u2013 two lines that we add to the configuration files \u2013 one per application. Let\u2019s see that.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">With the current Spiderwiz\nversion we have two ways to connect the applications \u2013 TCP\/IP sockets and\nWebSockets. Other methods can be implemented as plugins, as we will see later\nin this tutorial. In this lesson we will use TCP\/IP (WebSockets are used in the\nnext lesson). In Java, an implementation of TCP\/IP connection involves two\nclasses &#8211; <a href=\"https:\/\/docs.oracle.com\/en\/java\/javase\/13\/docs\/api\/java.base\/java\/net\/Socket.html\">Socket<\/a> and <a href=\"https:\/\/docs.oracle.com\/en\/java\/javase\/13\/docs\/api\/java.base\/java\/net\/ServerSocket.html\">ServerSocket<\/a>. We do not need to use them here\nsince they are already built into the Spiderwiz framework, but we need to\nconfigure their use.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Let\u2019s assume the <code>hello-world-producer<\/code> project is the\nserver. A TCP\/IP server requires the allocation of a port number \u2013 ours will be\n31415. Here is <code>hello-world-producer.conf<\/code>:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre>[log folder]\/tests\/HelloWorldProducer\/Logs\n[producer server-1]port=31415<\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The first line is a\ndefinition of a log folder as in lesson 1. The second line is what interests us\nnow. We set up a communication server by using the <code>producer server-<\/code><em>n<\/em> property, when <em>n<\/em> is any number between 1 and 99 that is unique across all <em>producer servers<\/em> defined in the\nconfiguration file. We can define as many as 99 <em>producer servers<\/em> and 99 <em>consumer\nservers<\/em> for a single application. There is no material difference between <em>producer servers<\/em> and <em>consumer servers<\/em>. Both can be used for\nboth <em>produced objects<\/em> and <em>consumed objects<\/em>. The choice is mainly\ncosmetic for the clarity of the network topology \u2013 applications that mostly\nproduce objects will be normally set as producers, while applications that\nmostly consume objects will be normally set as consumers. It is very important,\nhowever, that a consumer client always connects to a producer server and vice\nversa because the hand shaking mechanism depends on this relationship.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Our application defines <code>producer server-1<\/code> property. A TCP\/IP\nserver is the default for Spiderwiz servers, so all we need to do is to specify\nthe port number served by this server, 31415 in our case.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It is left for us to set <code>hello-world-consumer.conf<\/code> and then we\nwill be ready to run:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre>[log folder]\/tests\/HelloWorldConsumer\/Logs\n[consumer-1]ip=localhost;port=31415<\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Here we configure a TCP\/IP client. Similarly to servers, we can have up to 99 <code>producer<\/code> clients and 99 <code>consumer<\/code> clients for a single application, using the properties <code>producer-<\/code><em>n<\/em> and <code>consumer-<\/code><em>n<\/em>. Note that the value of <em>n<\/em> has no meaning except the unique identification of the property across the configuration file. Specifically <strong>there is no need to match<\/strong> the number of the client to the number of the server. They find each other by the IP address and the port number. However, as mentioned above, <code>consumer<\/code> clients connect to <code>producer<\/code> servers and vice versa.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Our consumer application\ndefines <code>consumer-1<\/code> property. Again,\nTCP\/IP is the default for Spiderwiz clients, so we just need to specify a\nserver address and a port number. We use <code>localhost<\/code>\nand 31415.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Everything is ready. The\nfollowing table shows what happens when we run and stop the two applications\nfew times in some occasional order:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">[table id=1 \/]<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">These logs are quite\nstraightforward but there are few points to notice.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">First, you see that <code>hello-world-consumer<\/code> prints <code>Hello World<\/code> regardless of whether the\nproducer or the consumer was launched first. You can also see that this is\nprompted every time a connection between them is established, even if this\nhappens more than once during application lifetime.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You can also see the client-server mechanism applied by <em>Spiderwiz<\/em>. If a server is available when the client is launched, connection is established immediately. If a server is not available when a client is trying to connect to the designated port, connection fails and the client repeats the attempt every set time interval until success. By default this time interval is 1 minute, but it can be configured using the <code>reconnection seconds<\/code> property in the <a href=\"http:\/\/spiderwiz.org\/apidocs\/org\/spiderwiz\/core\/doc-files\/config.html\">application configuration file<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Before concluding this lesson let\u2019s revisit the log files that were mentioned in the previous lesson. Besides the main log that was described there, classified by date and hour of the day, you will now see a new subfolder named <code>Consumers<\/code> under <code>\/tests\/HelloWorldProducer\/Logs<\/code> and a new subfolder named <code>Producers<\/code> under <code>\/tests\/HelloWorldConsumer\/Logs<\/code>. The former will contain a subfolder named \u201c<code>Hello World Consumer.127.0.0.1\u201d<\/code> and the latter will contain a subfolder named \u201c<code>Hello World Producer.localhost\u201d<\/code>. In each of these there will be a folder by the date and a file by the hour of the day. The following table compares the contents of these two files: <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">[table id=2 \/]<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">These log lines are produced when the two applications connect and the <code>HelloWorld<\/code> object is sent from the Producer to the Consumer. The log of <code>HelloWorldProducer<\/code> shows that it received a request from a <em>consumer<\/em> called <code>Hello World Consumer<\/code> running on IP <code>127.0.0.1<\/code> to reset the objects whose <code>ObjectCode<\/code> was <code>HLWRLD<\/code>, and the log of <code>HelloWorldConsumer<\/code> shows that it received the first object whose <code>ObjectCode<\/code> was <code>HLWRLD<\/code> from a <em>producer<\/em> called <code>Hello World Producer<\/code> running on <code>localhost<\/code>. This is indeed not much information, but this logging sub-system plays a major role when it comes to full-fledged data logging. Spiderwiz can be configured to log every message that passes between applications and then these log files can become pretty fat.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In the <a href=\"http:\/\/spiderwiz.org\/project\/tutorial\/lesson-3\/\">next lesson<\/a> we will learn more\nabout communication and the Spiderwiz network topology with a WebSocket based\napplication hub.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Learn about weaving a service mesh. We will get acquainted with Producers and Consumers, and will see how easy it is to link them with Spiderwiz.<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":145,"menu_order":2,"comment_status":"closed","ping_status":"closed","template":"tutorial-child.php","meta":{"footnotes":""},"class_list":["post-393","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/spiderwiz.org\/project\/wp-json\/wp\/v2\/pages\/393","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/spiderwiz.org\/project\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/spiderwiz.org\/project\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/spiderwiz.org\/project\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/spiderwiz.org\/project\/wp-json\/wp\/v2\/comments?post=393"}],"version-history":[{"count":75,"href":"https:\/\/spiderwiz.org\/project\/wp-json\/wp\/v2\/pages\/393\/revisions"}],"predecessor-version":[{"id":1419,"href":"https:\/\/spiderwiz.org\/project\/wp-json\/wp\/v2\/pages\/393\/revisions\/1419"}],"up":[{"embeddable":true,"href":"https:\/\/spiderwiz.org\/project\/wp-json\/wp\/v2\/pages\/145"}],"wp:attachment":[{"href":"https:\/\/spiderwiz.org\/project\/wp-json\/wp\/v2\/media?parent=393"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}