2.4. Step 4 - Implement server side customer list

We want to store the customer data in a database. As language to describe this transaction we use TDL (Transaction Definition Language) and as database we use Sqlite. So we have to tell the server to load the TDL module and the database module for Sqlite:

LoadModules {
    Module mod_protocol_wolframe
    Module mod_command_tdl
    Module mod_db_sqlite3
}
		

Now we also have to create a database and populate it with the following schema:

CREATE TABLE Customer (
    id              INTEGER PRIMARY KEY AUTOINCREMENT,
    name            TEXT    NOT NULL,
    address         TEXT
);
		

Store this into schema.sql. Then execute:

sqlite3 tutorial.db < schema.sql
		

Now we have to tell server to use this sqlite database file:

Database {
    SQLite {
        Identifier db
        File tutorial.db
        ForeignKeys yes
    }
}
		

When we restart the server we see:

DEBUG: SQLite database unit 'db' created with 4 connections to file 'tutorial.db'
		

Now we want to use some XML filters to send/receive XML over the protocol, so we have to add the following modules to tutorial.conf:

LoadModules {
    Module mod_protocol_wolframe
    Module mod_command_tdl
    Module mod_db_sqlite3
    Module mod_doctype_xml
    Module mod_filter_libxml2
}
		

The module mod_doctype_xml is there to detect documents of type XML. The module mod_filter_libxml2 is there to process XML documents. In order to see which modules are currently loaded in the wolframed we can use:

/usr/sbin/wolframed -p -c tutorial.conf
		

We see:

..
Module files to load:
   /usr/lib/wolframe/modules/mod_protocol_wolframe.so
   /usr/lib/wolframe/modules/mod_command_tdl.so
   /usr/lib/wolframe/modules/mod_db_sqlite3.so
   /usr/lib/wolframe/modules/mod_doctype_xml.so
   /usr/lib/wolframe/modules/mod_filter_libxml2.so
..
		

which looks ok.

For mapping the requests to programs in the business layer we need the directmap module. First add to tutorial.conf:

LoadModules {
    Module mod_protocol_wolframe
    Module mod_command_tdl
    Module mod_db_sqlite3
    Module mod_doctype_xml
    Module mod_filter_libxml2
    Module mod_command_directmap
}
		

and a new section 'Processor' on the same level as 'LoadModules'

Processor {
    Database db
    CmdHandler {
        Directmap {
            Program tutorial.dmap
            Filter XML=libxml2
	}
    }
}
		

The "filter" directive denotes that the command handler should use the "libxml2" module for parsing XML requests.

Now we have to create a file tutorial.dmap. This file maps the requests to the corresponding transaction definitions:

COMMAND CustomerListRequest
    CALL SelectCustomerList
    RETURN SKIP {standalone="yes", root="list"};
		

We map the 'CustomerListRequest' request to a function 'SelectCustomerList' that is executed to perform the request. It will return the document without validation (SKIP). The attributes in curly brackets after the RETURN SKIP define the meta data of the document. Depending of the output filters used we have to define a set of document meta data. Because we do not declare the meta data as part of the document type description as we will do later, we have to declare them here in the command. The meta data attribute standalone='yes' tells the XML filter not to include any document type declaration. The declaration root='list' declares the root element for the XML output. In our example we need to define the root element because we use XML for output and XML needs a root element to be defined. The returned content will be a list of customers. The function will be implemented in a TDL program in the server. We first add the TDL program declaration to the 'Processor' configuration section:

Processor {
    Database db
    CmdHandler {
        Directmap {
            Program tutorial.dmap
            Filter XML=libxml2
	}
    }
    Program Customer.tdl
}
		

The Customer.tdl file contains the database transaction 'SelectCustomerList' we want to execute. We only have to specify the enclosing tag 'customer' for each customer record we are going to retrieve with 'INTO customer'.

TRANSACTION SelectCustomerList
BEGIN
    INTO customer DO SELECT * from Customer;
END
		

We also need a validator for the input when the client sends a 'CustomerListRequest'. We load the simple form DDL compiler in the 'LoadModules' section of the tutorial.conf and register the simpleform program Customer.sfrm to the list of programs in the same file:

LoadModules {
    Module mod_protocol_wolframe
    Module mod_command_tdl
    Module mod_db_sqlite3
    Module mod_doctype_xml
    Module mod_filter_libxml2
    Module mod_command_directmap
    Module mod_ddlcompiler_simpleform
}

Processor {
    Database db
    CmdHandler {
        Directmap {
            Program tutorial.dmap
            Filter XML=libxml2
	}
    }
    Program Customer.tdl
    Program Customer.sfrm	
}
		

and we add a simple form file Customer.sfrm. We add a form called 'CustomerListRequest' which is empty for now but for the root element 'customer':

FORM CustomerListRequest
    -root customer
{
}
		

We have to restart the Wolframe daemon now and restart it with:

pkill wolframed
/usr/sbin/wolframed -f -c tutorial.conf			
		

This we have to do every time we change the configuration file tutorial.conf or one of the files referenced there as tutorial.dmap, Customer.tdl or Customer.sfrm.

The server shows now a message about the transaction function it loaded:

DEBUG: Loaded transaction function 'SelectCustomerList'
		

Now we create a telnet request which contains pseudo authentication credentials and a request for the list of customers, called ~/tutorial/client/CustomerListRequest.netcat:

AUTH
MECH NONE
REQUEST
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE customer SYSTEM 'CustomerListRequest'>
<customer/>
.
QUIT

		

This we can execute with:

netcat -v localhost 7661 < CustomerListRequest.netcat
		

and we get:

Wolframe
OK
MECHS NONE
OK authorization
ANSWER
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<list/>
	
.
OK REQUEST CustomerListRequest
BYE

		

We got an empty list of customers. So we have successfully configured the server for our first command.

Let's add some customer data now:

cat > data.sql
INSERT INTO Customer(name,address) values('Dr Who','Blue Police Box');
INSERT INTO Customer(name,address) values('John Smith','The Wheel in Space');
Ctrl-D

sqlite3 tutorial.db < data.sql
		

When we reexecute the netcat command we see that the answer contains now the list of customers:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<list>
    <customer>
        <id>1</id>
        <name>Dr Who</name>
        <address>Blue Police Box</address>
    </customer>
    <customer>
        <id>2</id>
        <name>John Smith</name>
        <address>The Wheel in Space</address>
    </customer>
</list>

		

So, the data is now correctly retrieved from the database.

Instead of using netcat you can also use the wolframec for testing the requests.

First create an XML file containing just the request and name it CustomerListRequest.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE customer SYSTEM 'CustomerListRequest'>
<customer/>

		

The request can be executed by calling:

wolframec -d CustomerListRequest.xml
		

We can also test the command without even starting the wolframed daemon process. We use the wolfilter program for that in the following way:

cat ~/tutorial/client/CustomerListRequest.xml | \
    wolfilter -c ~/tutorial/server/tutorial.conf CustomerListRequest
		

We can move now to the wolfclient to make our first request visible.