CS11 Erlang - Lab 6

One of the more powerful capabilities of Erlang is the ability to take server processes and factor them into generic features and server-specific features. In the Open Telecom Platform (OTP), we put these generic features into a behavior module, and the server-specific features are stored into a callback module. This division of features allows us to build generic server frameworks with very useful capabilities, and it also reduces the effort of writing standalone servers into the simple task of implementing a handful of purely sequential functions.

This week we will update our RSS feed aggregator to use the Erlang/OTP gen_server behavior.

RSS Queues and gen_server

We will actually only update the rss_queue process to use the gen_server, since the only other process we have right now is the rss_reader, and it's a dedicated tool managed directly by the rss_queue. Make a copy of your work into a new ~/cs11/erlang/lab6 directory, and then you can begin updating your rss_queue process to work with gen_server.

Here is a genserver_template.erl template file, which contains all callbacks for the gen_server behavior. You will definitely want to use this template next week, but this week you can just cut pieces from it and put them into your rss_queue.erl file. Note that you can use the stubbed implementations for several of the callbacks. Although you won't provide a specialized implementation for every single one, you still need to provide some implementation of each one, so that the gen_server behavior doesn't try to call a nonexistent function and then crash on you. The implementations provided should be sufficient to keep gen_server happy.

So far your RSS queues handle a certain number of messages. Some of these messages require a synchronous response, and others do not. Here are the messages you should already handle so far, and whether they are synchronous or asynchronous:

You will need to update your queue to use handle_call/3 or handle_cast/2 to handle these incoming messages, depending on whether they are synchronous or not. However, you will have to make some changes:

Of course, your queue also receives process-exit notifications from other processes; if it is linked to an rss_reader process, as well as linking to (or monitoring) all subscribers. These messages are processed via the handle_info callback, so you will need to update this callback to properly handle process-exit signals. Don't forget that you need to set up process-exit signal trapping in your init/1 callback; it does not happen automatically!

Startup Functions and the init/1 Callback

Probably the biggest amount of work will be implementing a new init function. This function takes a single argument, which is a list of values to use in initializing server instances. Your queues have several ways of being initialized, but they must all work through this single init/1 function, with different sets of arguments.

You should already have an rss_queue:start/0 function that starts a queue in standalone mode, and an rss_queue:start/1 function that starts a queue with a URL as an argument. You will definitely keep these helper functions going forward, but you also need to update them since the gen_server module provides its own startup functions. So, you will have two main tasks for queue-startup this week:

Once you have made these updates, you can see that the process will be slightly different for starting new RSS queues. First of all, the arguments are slightly different, and second, the return-value will be a little different as well. You might have an interaction like this:

    1> {ok, QPid1} = rss_queue:start(cnn_top_stories,
        "http://rss.cnn.com/rss/cnn_topstories.rss").
    {ok,<0.30.0>}
    2> {ok, QPid2} = rss_queue:start(bbc_top_stories,
        "http://newsrss.bbc.co.uk/rss/newsonline_world_edition/front_page/rss.xml").
    {ok,<0.32.0>}
    3> {ok, QPid3} = rss_queue:start(top_stories).
    {ok,<0.34.0>}
    4> rss_queue:subscribe(top_stories, cnn_top_stories).
    ok
    5> rss_queue:subscribe(QPid3, QPid2).  % another way of subscribing
    ok

Of course, your rss_queue module doesn't have a subscribe function yet, so that will be the next task, implementing some helper functions.

Helper Functions

Most gen_server callback modules provide a variety of helper functions to facilitate interacting with the server via simple callbacks. Although it is a little annoying to have to write a bunch of wrappers, it's also very helpful because it encapsulates the details of the message protocol so that callers don't need to care how the protocol is specified. This is actually a big benefit, and definitely outweighs the annoyance of having to write these functions the first time.

Besides that, gen_server provides several helper functions to make this much easier. There is gen_server:call for synchronous calls, and gen_server:cast for asynchronous calls. You will also notice from the API docs that these functions can handle several different ways of specifying a server. A PID can be specified, or a name (atom) corresponding to the server, or even a name corresponding to another node in an Erlang cluster. This gives us even more capability in writing our servers.

You should already have these helper functions:

Of all these helpers, only add_feed/2 doesn't correspond to a specific message. Update add_item/2 and get_all/1 to use the gen_server:call or gen_server:cast functions (remember, the {add_item, RSSItem} message is asynchronous, so you don't use call). Also, update all of these functions to take a PID or a name. This shouldn't be hard, since call and cast already know how to handle a PID or a name.

Once you have done this, you should also add these helper functions:

That's it for this time!

Once you have completed all of the above tasks, you should again try to construct a set of RSS queues like the configuration shown last week:

Write up the sequence of operations into a text file testing.txt, and submit this file along with your completed sources on csman.


Copyright (C) 2009, California Institute of Technology. All rights reserved.
Last updated February 23, 2009.