Main  /  Edit  /  History  /   /  Users Area

Applications Hierarchy

General

The basic approach of mapping an URL to a member function of our class works well for small applications, but can become problematic for complex systems.

CppCMS allows you to create a hierarchy of applications that work as a single unit. You connect them by using the url_mapper and url_dispatcher classes.

Before you read this tutorial, please read URL Dispatching and Mapping tutorial.

Example

Hierarchy

We will create a very simple example of an "educational" application, that consists of two sub-applications:

We will create 3 classes where myapp would be the topmost class in the hierarchy, and numbers and letters would be sub-applications.

Sub-Applications

Let's define our sub-applications:

class numbers : public cppcms::application {
public:

The constructor:

numbers(cppcms::service &srv) : cppcms::application(srv)
{
    dispatcher().assign("",&numbers::all,this);
    mapper().assign("");

    dispatcher().assign("/odd",&numbers::odd,this);
    mapper().assign("odd","/odd");

    dispatcher().assign("/even",&numbers::even,this);
    mapper().assign("even","/even");

    dispatcher().assign("/prime",&numbers::prime,this);
    mapper().assign("prime","/prime");
}

Create default application URL - all, and connect 4 member functions odd, even, prime and all to it, such that:

void prime()
{
    response().out() << "2,3,5,7,...";
}
void odd()
{
    response().out() << "1,3,5,7,9,...";
}
void even()
{
    response().out() << "2,4,6,8,10,...";
}

Now let's create our all member function that would include links to different modules:

void all()
{
    response().out()
        << "<a href='" << url("/")       << "'>Top</a><br>"
        << "<a href='" << url("/letters")<< "'>Letters</a><br>"
        << "<a href='" << url(".")       << "'>All Numbers</a><br>"
        << "<a href='" << url("odd")     << "'>Odd Numbers</a><br>"
        << "<a href='" << url("even")    << "'>Even Numbers</a><br>"
        << "<a href='" << url("prime")   << "'>Prime Numbers</a><br>"
        << "1,2,3,4,5,6,7,8,9,10,...";
}

Note:

Now we create a similar class for letters class letters : public cppcms::application { public:

Constructor:

letters(cppcms::service &srv) : cppcms::application(srv)
{
    dispatcher().assign("",&letters::all,this);
    mapper().assign("");

    dispatcher().assign("/capital",&letters::capital,this);
    mapper().assign("capital","/capital");

    dispatcher().assign("/small",&letters::small,this);
    mapper().assign("small","/small");

}

Member functions:

void all()
{
    response().out()
        << "<a href='" << url("/")       << "'>Top</a><br>"
        << "<a href='" << url("/numbers")<< "'>Numbers</a><br>"
        << "<a href='" << url(".")       << "'>All Letters</a><br>"
        << "<a href='" << url("capital") << "'>Capital Letters</a><br>"
        << "<a href='" << url("small")   << "'>Small Letters</a><br>"
        << "Aa, Bb, Cc, Dd,...";
}

void capital()
{
    response().out() << "A,B,C,D,...";
}
void small()
{
    response().out() << "a,b,c,d,...";
}

Main Application

Now lets create our topmost application:

class myapp: public cppcms::application {
public:

Constructor:

myapp(cppcms::service &srv) :
    cppcms::application(srv)
{
    attach( new numbers(srv),
            "numbers", "/numbers{1}", // mapping
            "/numbers(/(.*))?", 1);   // dispatching
    attach( new letters(srv),
            "letters", "/letters{1}", // mapping
            "/letters(/(.*))?", 1);   // dispatching

    dispatcher().assign("",&myapp::describe,this);
    mapper().assign(""); // default URL

    mapper().root("/myapp");
}

Note: we call attach member function with following parameters:

new numbers(srv)

New sub-application - we pass the ownership to the parent so we don't need to handle its lifetime on our own.

"numbers", "/numbers{1}"

Mapping - application named "numbers" is mapped according the the pattern "/numbers{1}" - such that each sub application's URL would replace the {1} placeholder.

"/numbers(/(.*))?", 1)

The regular expression pattern for matching the sub-application. Note, unlike with member functions, exactly one sub-expression is expected - this captured subexpression is passed to the child's main function for further dispatching.

The same is done for the letters and myapp member functions.

Now lets analyze how an HTTP request GET /myapp/numbers/prime is parsed.

  1. The web server splits /myapp/numbers/prime to /myapp as SCRIPT_NAME and /numbers/prime as PATH_INFO
  2. myapp matches /numbers/prime against the /numbers(/(.*))? expression, and extracts /prime as the subexpression that is passed to numbers's main function.
  3. /prime is matched against /prime expression and numbers::prime function is called.

Now lets show the final touch - the describe function of myapp:

void describe()
{
    response().out()
        << "<a href='" << url("/numbers")<< "'>Numbers</a><br>"
        << "<a href='" << url("/letters")<< "'>Letters</a><br>"
        << "<a href='" << url("/numbers/odd")<< "'>Odd Numbers</a><br>";
}

URL("/numbers") - relates to the default URL of the "numbers" sub-application, and similar for letters. The URL("/numbers/odd`) refers to "odd" mapping of the "numbers" sub-application. Let's see how the mapping is done in detail.

  1. A path /numbers/odd is split into "numbers" and "odd" tokens.
  2. We search for "numbers" sub-application and find it.
  3. In numbers's sub-application mapper we search for "odd" mapping and find it.
  4. We construct all backward:

    • "odd" -> "/odd"
    • "/numbers{1}" + "/odd" -> "/numbers/odd"
    • Root "/myapp" + "/numbers/odd" -> "/myapp/numbers/odd"

So we fully connect our main applications and sub-applications using some generic names.

Running

Finally let's add the main function

int main(int argc,char ** argv)
{
    try {
        cppcms::service app(argc,argv);
        app.applications_pool().mount(cppcms::applications_factory<myapp>());
        app.run();
    }
    catch(std::exception const &e) {
        std::cerr<<e.what()<<std::endl;
    }
}

Create the configuration file:

{   
  "service" : { 
    "api" : "http",
    "port" : 8080
  },  
  "http" : { 
    "script" : "/myapp"
  }   
}   

Build and run as:

./myapp -c config.js

And hit the URL: http://localhost:8080/myapp


URL Dispatching and Mapping | Top | Views Inheritance

<h1>include <iostream></h1> <h1>include <string></h1> <h1>include <cstring></h1>

include <sys/socket.h>

include <netinet/in.h>

include <unistd.h>

int main() { int server_fd, client_fd; struct sockaddr_in address; int addrlen = sizeof(address);

// HTML content to be served
std::string html = 
    "HTTP/1.1 200 OK\r\n"
    "Content-Type: text/html\r\n\r\n"
    "<!DOCTYPE html>"
    "<html>"
    "<head><title>My C++ Web</title></head>"
    "<body><h1>Welcome to a Simple Web Page!</h1><p>This is served from C++.</p></body>"
    "</html>";

// Create socket
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd == 0) {
    perror("socket failed");
    exit(EXIT_FAILURE);
}

// Bind
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(8080);

if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
    perror("bind failed");
    exit(EXIT_FAILURE);
}

// Listen
if (listen(server_fd, 3) < 0) {
    perror("listen");
    exit(EXIT_FAILURE);
}

std::cout << "Server is running on http://localhost:8080" << std::endl;

while (true) {
    // Accept client
    client_fd = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen);
    if (client_fd < 0) {
        perror("accept");
        exit(EXIT_FAILURE);
    }

    char buffer[3000] = {0};
    read(client_fd, buffer, 3000);  // Read client request

    std::cout << "Request:\n" << buffer << std::endl;

    // Send response
    send(client_fd, html.c_str(), html.size(), 0);
    close(client_fd); // Close connection
}

return 0;

}

<h1>include <iostream></h1> <h1>include <string></h1> <h1>include <cstring></h1>

include <sys/socket.h>

include <netinet/in.h>

include <unistd.h>

int main() { int server_fd, client_fd; struct sockaddr_in address; int addrlen = sizeof(address);

// HTML content to be served
std::string html = 
    "HTTP/1.1 200 OK\r\n"
    "Content-Type: text/html\r\n\r\n"
    "<!DOCTYPE html>"
    "<html>"
    "<head><title>My C++ Web</title></head>"
    "<body><h1>Welcome to a Simple Web Page!</h1><p>This is served from C++.</p></body>"
    "</html>";

// Create socket
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd == 0) {
    perror("socket failed");
    exit(EXIT_FAILURE);
}

// Bind
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(8080);

if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
    perror("bind failed");
    exit(EXIT_FAILURE);
}

// Listen
if (listen(server_fd, 3) < 0) {
    perror("listen");
    exit(EXIT_FAILURE);
}

std::cout << "Server is running on http://localhost:8080" << std::endl;

while (true) {
    // Accept client
    client_fd = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen);
    if (client_fd < 0) {
        perror("accept");
        exit(EXIT_FAILURE);
    }

    char buffer[3000] = {0};
    read(client_fd, buffer, 3000);  // Read client request

    std::cout << "Request:\n" << buffer << std::endl;

    // Send response
    send(client_fd, html.c_str(), html.size(), 0);
    close(client_fd); // Close connection
}

return 0;

}


Navigation

Main Page



Valid CSS | Valid XHTML 1.0