This section is maintained by the qooxdoo community. Here is how you can contribute.

Perl Backend for Qooxdoo with Mojolicious

by Tobi Oetiker, OETIKER+PARTNER AG

Write the JsonRPC backend for your qooxdoo in perl, using the MojoX::Dispatcher::Qooxdoo module.

Out of the Box you get …

  • The ability to run both source and build version of your qooxdoo application with the integrated webserver. No need to put everything onto an actual webserver or running from the filesystem.
  • Once your are ready to deploy, your application runs as CGI, FastCGI, PSGI or via reverse proxy.
  • … and you get all the other Mojolicious features like a complete testing framework on top of it

Installation

In all likelihood your local perl setup will not yet contain a copy of Mojolicious. Since all the required software is hosted on CPAN, the easiest way to install it is to use App::cpanminus. If you run cpanminus as root, the extra modules will be integrated into your local perl setup. If you run it as a normal user, the installation will go to ~/perl5.

You do not even have to install App::cpanminus in order to run it. Just pipe it into perl.

$ curl -L cpanmin.us | perl - MojoX::Dispatcher::Qooxdoo::Jsonrpc Mojo::Server::FastCGI

or if you want to install to a particular directory:

$ curl -L cpanmin.us | perl - --local-lib ~/scratch/mojo \
          MojoX::Dispatcher::Qooxdoo::Jsonrpc Mojo::Server::FastCGI

all dependencies will be downloaded and installed automatically.

The Mojo::Server::FastCGI bit is only necessary if you intend to run your rpc service as a FastCGI. Mojolicious supports other deployment options too.

Using the Dispatcher with Mojolicious

Mojolicious applications are written as perl modules which get executed by a pretty generic starter application: Our application will consist of the following files:

qserv/backend/bin/qserv.pl
qserv/backend/lib/QxServ/MojoApp.pm
qserv/backend/lib/QxServ/JsonRpcService.pm
qserv/backend/public/<the qooxdoo build>
qserv/backend/t/simple.t
qserv/frontend/<the qooxdoo application directory>

Lets start with the initial perl script:

#!/usr/bin/perl
# if mojo is installed in a non-standard location
# tell perl where to find it.
use lib $ENV{HOME}.'/scratch/mojo/lib/perl5';
 
# to find our own application we use the FindBin module
# to locate the library relative to the location of the perl
# script
use FindBin;
use lib "$FindBin::Bin/../lib";
 
# then load our application
use QxServ::MojoApp;
# and the Mojolicious application starter command
use Mojolicious::Commands;
 
# Mojo expects to find an instance of the application in the
# MOJO_APP environment variable.
$ENV{MOJO_APP} = QxServ::MojoApp->new;
 
# finally launch
Mojolicious::Commands->start;

Now for the actual Mojolicious application. It is built upon routes which get loaded at startup time and tell Mojolicious where to send web requests as they come in. To make things simple for Qooxdoo, the Mojolicious::Plugin::QooxdooJsonrpc plugin takes care of installing the necessary routes.

package QxServ::MojoApp;
# Inherit from the Mojolicious package
use Mojo::Base 'Mojolicious';
# Load our JsonRpcService module (see below)
use QxServ::JsonRpcService;
 
sub startup {
    my $self = shift;
 
    # load the Mojolicious::Plugin::QooxdooJsonrpc module
    $self->plugin('qooxdoo_jsonrpc', {
        services => {
             rpc => QxServ::JsonRpcService->new,
        }
    });
}
 
1;

Writing a JsonRPC service

With "MojoApp.pm" in place, all that is left todo, is to write the actual service module: "JsonRpcService.pm":

package QxServ::JsonRpcService;
use Mojo::Base -base;
 
# whenever a service method is about to be executed, the dispatcher calls the
# "allow_rpc_access" method to determine if the incoming request should be satisfied.
# You can use this facility to enforce access control to the methods of your service.
# Make sure to only allow access to the things you want to be public. Also the answers
# of the "allow_rpc_access" method could depend on the session status.
 
our %allow_access =  (
    echo => 1
);
 
sub allow_rpc_access {
    my $self = shift;
    my $method = shift;
    return $allow_access{$method};
}
 
# the echo method returns what it hears except when it hears 'die' then
# it dies. Here we use a hash pointer with a code and a message property
# with this we can control what gets sent back to the qooxdoo application.
 
sub echo {
    my $self = shift;
    my $arg = shift;
    if ($arg eq 'die'){
       die {code=>666,message=>"Aaaargh!"};
    }
    return "I hear: $arg";
}

Creating Tests

Before throwing a real qooxdoo application at the new service, we can easily write a test application to check that everything is working as expected:

#!/usr/bin/perl
use lib $ENV{HOME}.'/scratch/mojo/lib/perl5';
use FindBin;
use lib $FindBin::Bin.'/../lib';
use Test::More tests => 5;
use Test::Mojo;
 
# check if MojoApp loads ok
use_ok 'QxServ::MojoApp';
# create a test harness for the application
my $t = Test::Mojo->new(app =>QxServ::MojoApp->new());
 
# check if normal operations work
$t->post_ok('/jsonrpc','{"id":1,"service":"rpc", "method":"echo","params":["hello"]}')
  ->json_content_is({id=>1,result=>'I hear: hello'});
 
# check if the exception works
$t->post_ok('/jsonrpc','{"id":2,"service":"rpc","method":"echo","params":["die"]}')
  ->json_content_is({error=>{origin=>2,code=>666,
                               message=>"Aaaargh!"},id=>2});

Now you can run the test script to see if your new service performs as expected.

$ perl simple.t
1..5
ok 1 - use MojoApp;
ok 2 - post /jsonrpc
ok 3 - exact match for JSON structure
ok 4 - post /jsonrpc
ok 5 - exact match for JSON structure

The Qooxdoo Application

I guess if you are reading this guide you have already setup the qooxdoo SDK and managed to get the qooxdoo hello world application up and running. Lets create the frontend of our application in an appropriately named directory.

$ cd ~/scratch/qserv
$ create-application.py -n frontend -s qserv -t gui 

Modify "./frontend/source/class/qserv/Application.js" a bit to include a JsonRPC call:

qx.Class.define("qserv.Application", {
    extend : qx.application.Standalone,
    members : {
        main: function() {
             this.base(arguments);
             if (qx.core.Variant.isSet("qx.debug", "on")) {
                 qx.log.appender.Native;
                 qx.log.appender.Console;
             }
             var doc = this.getRoot();
             var input = new qx.ui.form.TextField("Hello World");
             doc.add(input,{left: 10, top: 50});
             var send = new qx.ui.form.Button("Get Echo", "qserv/test.png");
             doc.add(send, {left: 200, top: 50});
             send.addListener("execute", function(e) {
                   var rpc=new qx.io.remote.Rpc().set({
                         url: 'jsonrpc/',
                         serviceName : 'rpc'
                   });
                   rpc.callAsync(function(ret,exc){
                         if (exc){
                            alert("ERROR " + exc.code + ": " + exc.message);
                         }
                         else {
                            alert("ECHO: " + ret); 
                         }
                   },'echo',input.getValue());
 
             });
        }
    }
});

Now compile and run the backend server on the source version:

$ cd frontend
$ ./generate.py source-hybrid
$ env QX_SRC_MODE=1 ../backend/bin/qserv.pl daemon

Now surf to http://localhost:3000 and enjoy. Try to get an echo when typing 'die' into the textfield.

Into Production

Once you are done with developing your application, it is time to wrap things up for production.

./generate.py build
cp -rp build ../backend/public

Again you can run

backend/bin/qserv.pl daemon

and access the server on http://localhost:3000

You probably want to run your application from your webserver. You can run "qserv.pl" in CGI or fastCGI mode. If you want good performance use the fastCGI interface.

Configure your apache webserver with fastcgi support and place a ".htaccess" into the webtree:

RewriteEngine On
RewriteBase /qserver-web
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule (.*) qserv.fcgi/$1  

By setting the MOJO_MODE to 'production' in the "qserv.fcgi" your code will produce less debug output.

#!/bin/sh
export MOJO_MODE=production
exec "$HOME/scratch/qserv/backend/bin/qserv.pl" fastcgi