Leonardo: the Jolie web server

Leonardo is a web server developed in pure Jolie.

It is very flexible and can scale from service simple static HTML content to supporting powerful dynamic web applications.


Launching Leonardo and serving static content

The latest version of Leonardo is available from its GitHub page, at URL: https://github.com/jolie/leonardo.

After having downloaded and unpacked the archive, we can launch Leonardo from the leonardo directory with the command jolie leonardo.ol.

By default Leonardo looks for static content to serve in the leonardo/www subdirectory. For example, we can store an index.html file in www subdirectory containing a simple HTML page.

Then, pointing the browser at URL http://localhost:8000/index we can see the web page we created. In the same way other files (of any format) and subdirectories can be stored inside the www directory: Leonardo makes them available to web browsers as expected.


Configuration

Leonardo comes with a config.iol file, where are stored some constants for basic configuration. The content of the default config.iol file is shown below:

constants {
      // The location for reaching the Leonardo web server
      Location_Leonardo = "socket://localhost:8000/",

      // Root content directory
      RootContentDirectory = "www/",

      // Default page to serve in case clients do not specify one
      DefaultPage = "index.html",

      // Print debug messages for all exchanged HTTP messages
      DebugHttp = false,

      // Add the content of HTTP messages to their debug messages
      DebugHttpContent = false
}

As aforementioned, RootContentDirectory points to the www folder, which is the default container of static pages, but it can also be overridden by declaring the new path as the first parameter in Leonardo execution command, e.g.,

jolie leonardo.ol /path/to/my/content/directory


Serving dynamic content

Leonardo supports dynamic web application through the Jolie HTTP protocol. There are many ways this can be achieved, hereby we overview some of these:

  • HTML querystring and HTML forms;
  • via web development libraries like JQuery and Google Web Toolkit (GWT).

In the following examples we show how to interface a web application with some Jolie code through Leonardo. Specifically, we expose an operation - length - which accepts a list of strings, computes their total length and, finally, returns the computed value.

We do this by editing the code inside Leonardo, while in real-world projects, it is recommended to separated the application logic and the web server one: this can be achieved with ease by creating a separate service and aggregate it from the HTTP input port of Leonardo.


Creating our web application: the application logic

We start by creating the Jolie code that serves the requests from the web interface.

Let us open leonardo.ol and add the following interface:

type LengthRequest: void{
    .item[ 1, * ]: string
}

interface ExampleInterface {
    RequestResponse:
        length( LengthRequest )( int )
}

Then we edit the main HTTP input port, HTTPInput, and add ExampleInterface to the published interfaces:

inputPort HTTPInput {
    // other deployment code
    Intefaces: HTTPInterface, ExampleInterface
}

Finally, we write the operation length by adding the code below to the input choice inside the main procedure in Leonardo:

main
{
    // existing code in Leonardo
    [ length( request )( response ){
        response = 0;
        for( i = 0, i < #request.item, i++ ){
            length@StringUtils( request.item[ i ] )( l );
            response += l
        }
    }
    ]{ nullProcess }
}

The code above iterates over all the received items and sums their lengths.


HTML querystrings

Once the server-side part is in place, we can start experimenting by invoking it from the browser, by pointing it to the address:

http://localhost:8000/length?item=Hello&item=World

which will reply with an XML response like the following:

<lengthResponse>10</lengthResponse>

Leonardo replies with XML responses by default, but the response can be formatted in fully-fledged HTML code by adding it in the code of operation length and setting the parameter .format inside input port HTTPInput as html.

Querystrings and other common message formats used in web applications, such as HTML form encodings, present the problem of not carrying type information. Instead, they simply carry string representations of values that were potentially typed on the invoker's side. However, type information is necessary for supporting services. To cope with such cases, Jolie introduces the notion of automatic type casting.

Automatic type casting reads incoming messages that do not carry type information and tries to cast their content values to the types expected by the service interface for the message operation.


HTML forms

Operations can be invoked via HTML forms too.

Let us consider a html page with a form which submits a request to the operation length.

After it is stored in our www directory, we can navigate to: http://localhost:8000/form.html where we can find the form containing both a text input and a file input fields. If we write something in the text field and choose a file to upload for the file input one, we can submit the request to the operation length that will reply with the sum of the length of both the text and the content of the file.


JQuery

Jolie fully supports asynchronous JavaScript and XML (AJAX) calls via XMLHttpRequest, which subsequently assures the support of most part of web application development libraries.

For the sake of brevity, we are not showing the boilerplate for building the HTML interface here, but it can be downloaded entirely from the link below:

Once downloaded and unpacked, we can launch Leonardo and navigate to address http://localhost:8000/. Inside the www directory there are a index.html with a form containing three text fields - text1, text2, and text3. Submitting the request, by pressing the submit button, the event is intercepted by the JavaScript code shown below:

$( document ).ready( function() {
  $( "#lengthButton" ).click( function() {
    Jolie.call(
       'length',
       { item: [
          $("#text1").val(),
          $("#text2").val(),
          $("#text3").val()
        ] 
      },
      function( response ) {
         $( "#result" ).html( response );
       }
     );
  })
});

The code is contained in library jolie-jquery.js stored inside the lib directory.


Google Web Toolkit (GWT)

Jolie supports Google Web Toolkit too by means of the jolie-gwt.jar library stored inside the lib subdirectory of the standard trunk Jolie installation. Inside the library there is a standard GWT module, called JolieGWT, which must be imported into the GWT module we are using.

The module comes with support classes for invoking operations published by the service of Leonardo which is serving the GWT application. In our case, we can easily call the length operation with the following code:

Value request = new Value();
request.getNewChild( "item" ).setValue( "Hello" );
request.getNewChild( "item" ).setValue( "World!" );
JolieService.Util.getInstance().call( 
    "length", 
    request,
    new AsyncCallback<Value> () {
        public void onFailure( Throwable t ) {}
        public void onSuccess( Value response )
        {
            Window.alert( response.strValue() );
        }
    });

Jolie HTTP protocol is able to handle cookies both when processing outbound and inbound HTTP messages. Jolie allows to define specific cookies handling at port level without the need of writing further processing code.

Inbound Cookies Handling

In a classic client/server application where Leornardo.ol is acting as WebServer the programmer is able to define specific binding between inbound cookies and message subnodes. To do so it is sufficient to define in the input Port configuration parameters .osc.operationName.cookies.cookieName = "subNodeName". A working example has been provided to exemplify the handling inbound cookies

In this specific example you will find the code for both the client side (HTML/JQuery) and server side (Jolie) able to handle two separated cookies. In order to understand the example specific attention should be paid to the following files:

  • /doc/TestingInstruction.txt
  • /doc/ExampleAbstract.txt
  • /www/js/CookiesHandler.js
  • /leornardo.ol

It is not necessary to define precisely the cookie binding for each operation exposed by the HTTP input port, one can use the global cookies configuration expressed in the following form .cookies.cookieName = "subNodeName", Particular attention must be paid on the presence subNodeName in the type of all operation exposed by the port otherwise a TypeMismatch will occur.

results matching ""

    No results matching ""