AWS - version 2.7.0w
Support for SOAP 1.1 - version 1.5.0
SMTP, POP, LDAP and Jabber protocols.
Document revision level $Revision: 134864 $
Date: 15 March 2010
AdaCore
Copyright © 2000, Pascal Obry
Copyright © 2001, Pascal Obry, Dmitriy Anisimkov
Copyright © 2002-2007, AdaCore
This document may be copied, in whole or in part, in any form or by any means, as is or with alterations, provided that (1) alterations are clearly marked as alterations and (2) this copyright notice is included unmodified in any copy.
AWS
stand for Ada Web Server. It is an Ada implementation of the
HTTP/1.1
protocol as defined in the RFC 2616 from June 1999.
The goal is not to build a full Web server but more to make it possible
to use a Web browser (like Internet Explorer, or Netscape Navigator) to
control an Ada application. As we'll see later it is also possible to
have two Ada programs exchange informations via the HTTP
protocol. This
is possible as AWS
also implement the client side of the HTTP protocol.
Moreover with this library it is possible to have more than one server
in a single application. It is then possible to export different kind
of services by using different HTTP
ports, or to have different ports
for different services priority. Client which must be served with a
very high priority can be assigned a specific port for example.
As designed, AWS
big difference with a standard CGI
server
is that there is only one executable. A CGI
server has one
executable for each request or so, this becomes a pain to build and
to distribute when the project gets bigger. We will also see that it is
easier with AWS
to deal with session data.
AWS
support also HTTPS
(secure HTTP
) using
SSL
. This is based on OpenSSL
a very good and Open Source SSL
implementation.
Major supported features are:
AWS
has been mainly developed with GNAT
on Windows.
It is built and tested regularly on GNU/Linux
and
Solaris
, it should be fairly portable across platforms. To
build AWS
you need:
GNAT GPL 2007 Edition
or GNAT Pro 6.0
as some Ada 2005 fearures
(Ada.Containers
, interfaces, overriding keyword) are used. The
code should be fairly portable but has never been tested on another
compiler than GNAT
.
AWS/LDAP
API
on UNIX based systems, you need to install properly the OpenLDAP
package. On Windows you don't need to install it as the
libldap.a library will be built by AWS
and will use the
standard Windows LDAP DLL
wldap32.dll.
You can download OpenLDAP from http://www.openldap.org.
This package is the standard (non-SSL) socket implementation. It exists different implementations of this package:
GNAT.Sockets
. This is the default implementation
used.
$ make IPv6=true
Before building be sure to edit makefile.conf, this file
contains many settings important for the build. Note that it is
important to run make setup
each time you edit this file.
When you have built and configured all external libraries you must set the
ADA_PROJECT_PATH variable to point to the GNAT Project files for
the different packages. For XML/Ada
support, you also need to set
XMLADA to true in makefile.conf.
At this point you can build AWS
with:
$ make setup build
Note that by default AWS
demos will be built without SSL
support except on Windows. If you want to build the demos with
SSL
on UNIX (in this case you must have libssl.a and
libcrypto.a available on your platform), open
makefile.conf and set the SOCKET variable to
openssl
. Then rebuild with:
$ make setup build
It is is possible to build AWS
in debug mode by setting
DEBUG make's variable in makefile.conf, or just:
$ make DEBUG=true setup build
Note that by default AWS
is configured to use the GNAT
compiler. So, if you use GNAT
you can build AWS
just with:
$ make setup build
If you want to build only the AWS
libraries and tools and do not want
to build the demos you can set DEMOS
to "false" as in:
$ make DEMOS=false setup
During the build, the AWS
library and demos will be compiled. The
demos are a good way to learn how to use AWS
. Yet to learn all
features in AWS
you'll need to read the documentation.
Here are a short description of the demos:
PNG
image and an HTML
page. The executable is self contained.
AWS
features.
AWS.Services.Page_Server
.
see Static Page server.
If XML/Ada
is installed it is possible to build the SOAP
binding
and the SOAP
demos, for this just type:
$ make build_soap
There is four demos:
SOAP
protocol.
SOAP
procedures for testing purpose.
SOAP
program that test all seven SOAP
procedure of the above server.
In each case you'll certainly have to edit the makefile to correctly set
the include path for the libraries OpenSSL
, Socket
and
XML/Ada
. For more information, look at the makefiles.
When the build is done you must install AWS
at a specific
location. The target directory is defined with the prefix
makefile.conf variable. The default value is set to the
compiler root directory. To install:
$ make install
To install AWS
into another directory you can either edit
makefile.conf and set prefix to the directory you like
to install AWS
or just force the make prefix variable:
$ make prefix=/opt install
Alternatively, with GNAT
5.03 and above it is possible to
install AWS
into the GNAT Standard Library location. In this
case AWS
is ready-to-use as there is no need to set
ADA_PROJECT_PATH, just set prefix to point to GNAT
root
directory:
$ make prefix=/opt/gnatpro/6.1.1 install
Now you are ready to use AWS
!
After installing AWS
you must set the build environment to
point the compiler to the right libraries. First let's say that
AWS
has been installed in awsroot directory.
Following are the instructions to set the environment yourself. Note that the preferred solution is to use project files. In this case there is no manual configuration.
-aI<awsroot>/include/aws
.
AWS
uses some components they are installed in
<awsroot>/include/aws/components. Add this path into ADA_INCLUDE_PATH
or put it on the command line -I<awsroot>/include/aws/components
.
AWS
libraries
(libaws.a) are installed into <awsroot>/lib/aws/static. Add this
path into ADA_OBJECTS_PATH or put it on the command line
-aO<awsroot>/lib/aws/static
. Furthermore for gnatlink
to find the
libraries you must add the following library path option on the
gnatmake
command line -largs -L<awsroot>/lib/aws/static -laws
.
Note that to build SSL applications you need to add -lssl -lcrypto
on
gnatmake's -largs
section.
The best solution is to use the installed GNAT Project File
aws.gpr. This is supported only for GNAT 5.01
or
above. You must have installed XML/Ada
with project file
support too.
If this is the case just set the ADA_PROJECT_PATH variable to
point to the AWS
and XML/Ada
install directories. From
there you just have to with the AWS
project file in your GNAT
Project file, nothing else to set.
with "aws"; project Simple is for Main use ("prog.adb"); for Source_Dirs use ("src"); for Object_Dir use "obj"; end Simple;
See the GNAT User's Guide for more information about GNAT Project Files.
AWS
is not a Web Server like IIS or Apache, it is a component
to embedded HTTP protocol in an application. It means that it is possible
to build an application which can also answer to a standard browser like
Internet Explorer or Netscape Navigator. Since AWS
provides
support client and server HTTP protocol, applications can communicate
through the HTTP channel. This give a way to build distributed
applications, See AWS.Client.
An application using AWS
can open many HTTP
channels. Each
channel will use a specific port. For example, it is possible to
embedded many HTTP
and/or many HTTPS
channels in the
same application.
To build a server you must:
WS : AWS.Server.HTTP;
AWS.Server.Start
(See AWS.Server.)
procedure Start (Web_Server : in out HTTP; Name : in String; Callback : in Response.Callback; Max_Connection : in Positive := Def_Max_Connect; Admin_URI : in String := Def_Admin_URI; Port : in Positive := Def_Port; Security : in Boolean := False; Session : in Boolean := False; Case_Sensitive_Parameters : in Boolean := True; Upload_Directory : in String := Def_Upload_Dir); -- Start the Web server. It initialize the Max_Connection connections -- lines. Name is just a string used to identify the server. This is used -- for example in the administrative page. Admin_URI must be set to enable -- the administrative status page. Callback is the procedure to call for -- each resource requested. Port is the Web server port. If Security is -- set to True the server will use an HTTPS/SSL connection. If Session is -- set to True the server will be able to get a status for each client -- connected. A session ID is used for that, on the client side it is a -- cookie. Case_Sensitive_Parameters if set to False it means that the CGI -- parameters name will be handled without case sensitivity. Upload -- directory point to a directory where uploaded files will be stored.
Start
takes many parameters:
The administrative page returns many information about the server. It is
possible to configure the server via two configuration files
See Configuration options.
OpenSSL
library.
Note that there is other Start
routines which support other features.
For example there is a Start
routine which use a dispatcher routine
instead of the simple callback procedure. see AWS.Server. And
there is also the version using a Config.Object
which is the most
generic one.
The callback procedure has the following prototype:
function Service (Request : in AWS.Status.Data) return AWS.Response.Data;
This procedure receive the request status. It is possible to retrieve
information about the request through the AWS.Status
API
(See AWS.Status.).
For example, to know what URI has been asked:
URI : constant String := AWS.Status.URI (Request); if URI = "/whatever" then ... end if;
Then this function should return an answer using one of the constructors
in AWS.Response
(See AWS.Response.). For example, to return an
HTML
message:
AWS.Response.Build (Content_Type => "text/html", Message_Body => "<p>just a demo");
It is also possible to return a file. For example, here is the way to return a PNG image:
AWS.Response.File (Content_Type => "image/png", Filename => "adains.png");
Note that the main procedure should exit only when the server is terminated.
For this you can use the AWS.Server.Wait
service.
A better solution is to use a template engine like Templates_Parser to
build the HTML
Web Server answer. Templates_Parser module is
distributed with this version of AWS.
The callback procedure is the user's code that will be called by the AWS
component to get the right answer for the requested resource. In fact
AWS just open the HTTP message, parsing the HTTP header and it builds
an object of type AWS.Status.Data
. At this point it calls the
user's callback procedure, passing the object. The callback procedure
must returns the right response for the requested resources. Now AWS
will just build up the HTTP response message and send it back to
user's browser.
But what is the resource ?
Indeed in a standard Web development a resource is either a static
object - an HTML
page, an XML
or XSL
document -
or a CGI
script. With AWS
a resource is just a
string to identify the resource, it does not represent the name of a
static object or CGI
script.
So this string is just an internal representation for the resource. The callback procedure must be implemented to handle each internal resource and return the right response.
Let's have a small example. For example we want to build a Web server that will answer “Hello World” if we ask for the internal resource /hello, and must answer “Hum...” otherwise.
with AWS.Response; with AWS.Server; with AWS.Status; procedure Hello_World is WS : AWS.Server.HTTP; function HW_CB (Request : in AWS.Status.Data) return AWS.Response.Data is URI : constant String := AWS.Status.URI (Request); begin if URI = "/hello" then return AWS.Response.Build ("text/html", "<p>Hello world !"); else return AWS.Response.Build ("text/html", "<p>Hum..."); end if; end HW_CB; begin AWS.Server.Start (WS, "Hello World", Callback => HW_CB'Unrestricted_Access); delay 30.0; end Hello_World;
Now of course the resource internal name can represent a file on
disk. It is not mandatory but it is possible. For example it is
perfectly possible to build with AWS
a simple page server.
As an example, let's build a simple page server. This server will
returns files in the current directory. Resources internal name
represent an HTML
page or a GIF
or PNG
image for
example. This server will return a 404 message (Web Page Not Found) if
the file does not exist. Here is the callback procedure that implements
such simple page server:
function Get (Request : in AWS.Status.Data) return AWS.Response.Data is URI : constant String := AWS.Status.URI (Request); Filename : constant String := URI (2 .. URI'Last); begin if Utils.Is_Regular_File (Filename) then return AWS.Response.File (Content_Type => AWS.MIME.Content_Type (Filename), Filename => Filename); else return AWS.Response.Acknowledge (Messages.S404, "<p>Page '" & URI & "' Not found."); end if; end Get;
Form parameters are stored into a table of key/value pair. The key is the form input tag name and the value is the content of the input field as filled by the user.
Enter your name <FORM METHOD=GET ACTION=/get-form>" <INPUT TYPE=TEXT NAME=name VALUE="<default>" size=15> <INPUT TYPE=SUBMIT NAME=go VALUE="Ok"> </FORM>
Note that as explained above see Callback procedure, the resource
described in ACTION
is just an internal string representation
for the resource.
In this example there is two form parameters:
There is many functions (in AWS.Parameters
) to retrieve the tag name
or value and the number of parameters. Here are some examples:
function Service (Request : in AWS.Status.Data) return AWS.Response.Data is P : constant AWS.Parameters.List := AWS.Status.Parameters (Request); ...
AWS.Parameters.Get (P, "name")
AWS.Parameters.Get_Name (P, 1)
AWS.Parameters.Get (P, 1)
AWS.Parameters.Get (P, "go")
AWS.Parameters.Get_Name (P, 2)
AWS.Parameters.Get (P, 2)
Request
is the AWS
current connection status passed to the
callback procedure. And P
is the parameters list retrieved from the
connection status data. For a discussion about the callback procedure
See Building an AWS server.
The directory containing the server program must contain the following files if you plan to use a status page See Status page.
HTML
file for the AWS
status page.
AWS
logo displayed on the status page.
AWS
hotplug table up arrow.
AWS
hotplug table down arrow.
Note that these filenames are the current AWS
default. But it is
possible to change those defaults using the configuration files
see Configuration options.
We have already seen, in simple examples, how to build basic answers using
AWS.Response
API. In this section we present all ways to build
answers from basic support to the more advanced support like the
compressed memory stream response.
A redirection is a way to redirect the client's browser to another URL. Client's won't notice that a redirection has occurs. As soon as the browser has received the response from the server it will retrieve the page as pointed by the redirection.
return Response.URL (Location => "/use-this-one");
User will receive a Web page saying that this page has moved and eventually pointing to the new location.
return Response.Moved (Location => "/use-this-one"; Message => "This page has moved, please update your reference");
For protected pages you need to ask user to enter a password. see Authentication.
Acknowledge
can be used to send back error messages. There is
many kind of status code, see Message.Status_Code
definition. Together with the status code it is possible to pass
textual error message in Message_Body
parameter.
return Response.Acknowledge (Status_Code => Messages.S503, Message_Body => "Can't connect to the database, please retry later.", Content_Type => MIME.Text_Plain);
This is the simplest way to build a response object. There is two
constructors in AWS.Response
, one based on a standard string
and one for Unbounded_String.
return Response.Build (MIME.Text_HTML, "My answer");
The Build routine takes also a status code parameter to handle
errors. By default this code is Messages.S200
which is the
standard HTTP status (no error encountered). The other parameter can
be used to control caches. see AWS.Response.
This is exactly as above but the Build routine takes a
Stream_Element_Array
instead of a string.
To build a File
response there is a single constructor named
File
. This routine is very similar to the one above except that
we specify a filename as the response.
return Response.File (MIME.Text_HTML, "index.html");
Again there parameters to control the status code and cache. No check on the filename is done at this point, so if index.html does not exit no exception is raised. The server is responsible to check for the file and to properly send back the 404 message if necessary.
Note that this routine takes an optional parameter named Once
that is to be used for temporary files created on the server side for
the client. With Once
set to True
the file will be
deleted by the server after sending it (this includes the case where
the download is suspended).
Sometimes it is not possible (or convenient) to build the response in
memory as a string object for example. Streams can be used to
workaround this. The constructor for such response is again very
similar to the ones above except that instead of the data we pass an
handle to a Resources.Streams.Stream_Type
object.
The first step is to build the stream object. This is done by deriving
a new type from Resources.Streams.Stream_Type
and implementing
three abstract procedures.
Read
End_Of_File
Close
The second step is to build the response object:
type SQL_Stream is new Resources.Streams.Stream_Type; Stream_Object : SQL_Stream; procedure Read (...) is ... function End_Of_File (...) return Boolean is ... procedure Close (...) is ... return Response.Stream (MIME.Text_HTML, Stream_Object);
Note that in some cases it is needed to create a file containing the
data for the client (for example a tar.gz or a zip archive). But there
is no way to properly remove this file from the file system as we
really don't know when the upload is terminated when using the
AWS.Response.File
constructor. To solve this problem it is
possible to use a stream as the procedure Close
is called by
the server when all data have been read. In this procedure it is
trivial to do the necessary clean-up.
An ready-to-use implementation of the stream API described above where the stream content is read from an on-disk file.
An ready-to-use implementation of the stream API described above where the stream content is read from an on-disk file. When the transfer is completed the file is removed from the file system.
This is an implementation of the standard stream support described above. In this case the stream is in memory and built by adding data to it.
To create a memory stream just declare an object of type
AWS.Resources.Streams.Memory.Stream_Type
. When created, this
memory stream is empty, using the Streams.Memory.Append
routines it is possible to add chunk of data to it. It is of course
possible to call Append
as many times as needed. When done just
return this object to the server.
Data : AWS.Resources.Streams.Memory.Stream_Type; Append (Data, Translator.To_Stream_Element_Array ("First chunk")); Append (Data, Translator.To_Stream_Element_Array ("Second chunk...")); ... return Response.Stream (MIME.Text_HTML, Data);
Note that you do not have to take care of releasing the allocated
memory, the default Close
routine will do just that.
This is a slight variant of the standard memory stream described
above. In this case the stream object must be declared as a
AWS.Resources.Streams.Memory.ZLib.Stream_Type
.
The ZLib stream object must be initialized to enable the
compression and select the right parameters. This is done using the
AWS.Resources.Streams.Memory.ZLib.Deflate_Initialize
routine which
takes many parameters to select the right options for the compression
algorithm, all of them have good default values. When initialized the
compressed stream object is used exactly as a standard stream.
Data : AWS.Resources.Streams.Memory.ZLib.Stream_Type; Deflate_Initialize (Data); Append (Data, Translator.To_Stream_Element_Array ("First chunk")); Append (Data, Translator.To_Stream_Element_Array ("Second chunk...")); ... return Response.Stream (MIME.Text_HTML, Data);
Note that there is the reverse implementation to decompress a stream. see AWS.Resources.Streams.Memory.ZLib. It's usage is identical.
AWS
has a specific high level service to split a large response
into a set of pages. For more information see Split pages.
The response sent to the server is read from the output of an external
application. This kind of stream can be used to avoid writing a temporary
file into the hard disk. For example it is possible to return an archive
created with the tar
tool without writting the intermediate tar
achive on the disk.
To configure an AWS
server it is possible to use a
configuration object. This object can be set using the AWS.Config.Set
API or initialized using a configuration file.
Configuration files are a way to configure the server without recompiling it. Each application can be configured using two configurations files:
Furthermore, it is possible to read a specific configuration file
using the AWS.Config.Ini.Read
routine. see AWS.Config.Ini.
Current supported options are:
Accept_Queue_Size (positive)
Admin_Password (string)
$ aws_password admin <password>
Admin_URI (string)
AWS.Server.Start
. The default is .
Certificate (string)
PEM
format
and the chain must be sorted starting with the subject's certificate, followed
by intermediate CA certificates if applicable and ending at the highest
level (root) CA certificate. If the file contains only a single
certificate, it can be followed by a private key. In this case the Key
parameter (see below) must empty.
Case_Sensitive_Parameters (boolean)
True
the HTTP
parameters are case
sensitive. The default value is TRUE.
Check_URL_Validity (boolean)
Cleaner_Wait_For_Client_Timeout (duration)
Cleaner_Client_Header_Timeout (duration)
Cleaner_Client_Data_Timeout (duration)
Cleaner_Server_Response_Timeout (duration)
Directory_Browser_Page (string)
Down_Image (string).
Error_Log_Filename_Prefix (string)
Error_Log_Split_Mode [None/Each_Run/Daily/Monthly]
Exchange_Certificate (boolean)
Force_Wait_For_Client_Timeout (duration)
Force_Client_Header_Timeout (duration)
Force_Client_Data_Timeout (duration)
Force_Server_Response_Timeout (duration)
Free_Slots_Keep_Alive_Limit (positive)
Hotplug_Port (positive)
Key (string)
Line_Stack_Size (positive)
Log_Extended_Fields (string list)
Log_File_Directory (string)
AWS.Log
if logging
facility is enabled. By default log files are written in the current
directory. The default is ./.
Log_Filename_Prefix (string)
Log_Split_Mode [None/Each_Run/Daily/Monthly]
Logo (string).
Max_Connection (positive)
AWS.Server.Start
. The
default is 5.
Note that the total number of threads used by a server is:
N = <main server thread> + <max connections> [+ <session>] Note: [...] means optional value Add 1 only if the session feature is activated. This is due to the session cleaner task.
Receive_Timeout (duration)
Reuse_Address (boolean)
Security_Mode (string)
Send_Timeout (duration)
Server_Host (string)
Server_Name (string)
AWS.Server.Start
. The default is "AWS Module".
Server_Port (integer)
AWS.Server.Start
. The default is
8080.
Session (boolean)
Session_Name (string)
Session_Lifetime (duration)
Session_Cleanup_Interval (duration)
Status_Page (string)
Transient_Cleanup_Interval (positive)
Transient_Lifetime
Up_Image (string)
Upload_Directory (string)
WWW_Root (string)
Each option value can be retrieved using the AWS.Config
unit or
set using AWS.Config.Set
.
For example to build a server where the port and the maximum number of connection can be changed via a configuration file (either aws.ini or <program_name>.ini):
WS : AWS.Server.HTTP; Conf : constant AWS.Config.Object := AWS.Config.Get_Current; Server.Start (WS, Service'Access, Conf);
AWS
provides a way to keep session data while users are
browsing. There is no need to mess with the Cookies, AWS
will do
that for you. The idea is simple. A session ID will be transparently
created and then you'll be able to insert and retrieve session data
using a standard Ada API (See AWS.Session.). Session data
are key/value pair each of them being strings. How to deal with that ?
WS : AWS.Server.HTTP; Server.Start (WS, Port => 1234, Callback => Service'Access, Session => True);
Here we have built an HTTP channel with a maximum of 3 simultaneous connections using the port 1234. A session ID will be created and sent inside a cookie to the client's browser at the first request. This session ID will be sent back to the server each time the client will ask for a resource to the server.
function Service (Request : in AWS.Status.Data) return AWS.Response.Data;
The Session ID is kept in the Request object and can be retrieved using:
Session_ID : constant AWS.Session.ID := AWS.Status.Session (Request);
declare C : Integer; begin C := AWS.Session.Get (Session_ID, "counter"); C := C + 1; AWS.Session.Set (Session_ID, "counter", C); end;
This example first get the value (as an Integer) for session data whose
key is "counter
", increment this counter and then set it back to
the new value.
It is also possible to save and restore all session data. It means that the server can be shutdown and launched some time after and all client data are restored as they were at shutdown time. Client will just see nothing. With this feature it is possible to shutdown a server to update its look or because a bug has been fixed for example. It is then possible to restart it keeping the complete Web server context.
AWS
supports Basic and Digest authentication. The
authentication request can be sent at any time from the callback
procedure. For this the AWS.Response.Authenticate
message must
be returned.
The authentication process is as follow:
From the callback routine return an authentication request when needed.
function Service (Request : in Status.Data) return Response.Data is URI : constant String := Status.URI (Request); User : constant String := Status.Authorization_Name (Request); begin -- URI starting with "/prot/" are protected if URI (URI'First .. URI'First + 5) = "/prot/" and then User = "" then return Response.Authenticate ("AWS", Response.Basic);
The first parameter is the Realm, it is just a string that will be displayed (on the authentication dialog box) by the browser to indicate for which resource the authentication is needed.
When an authentication as been done the callback's request data contain the user and password. Checks the values against an ACL for each protected resources.
function Protected_Service (Request : in AWS.Status.Data) return AWS.Response.Data is User : constant String := Status.Authorization_Name (Request); Pwd : constant String := Status.Authorization_Password (Request); begin if User = "xyz" and then Pwd = "azerty" then return ...;
Note that the Basic authentication is not secure at all. The password is sent unencoded by the browser to the server. If security is an issue it is better to use the Digest authentication and/or an SSL server.
File upload is the way to send a file from the client to the server. To enable file upload on the client side the Web page must contain a FORM with an INPUT tag of type FILE. The FORM must also contain the enctype attribute set to multipart/form-data.
<FORM enctype="multipart/form-data" ACTION=/whatever METHOD=POST> File to process: <INPUT NAME=filename TYPE=FILE> <INPUT TYPE=SUBMIT NAME=go VALUE="Send File"> </FORM>
On the server side, AWS
will retrieve the file and put it into the
upload directory. AWS
add a prefix to the file to ensure that the
filename will be unique on the server side. The upload directory can be
changed using the configuration options. See Configuration options.
The uploaded files are removed after the user's callback. This is done for security reasons, if files were not removed it would be possible to fill the server hard disk by uploading large files to the server. This means that uploaded files must be specifically handled by the users by either copying or renaming them.
AWS
will also setup the form parameters as usual. In the above example
there is two parameters (See Form parameters.)
This API is used to do communication between programs using the HTTP
GET protocol. It is a very simple API not to be compared with GLADE
or SOAP
. This communication facility is to be used for simple
request or when a light communication support is needed. For more
complex communications or to achieve inter-operability with other
modules it is certainly a good idea to have a look at the
AWS/SOAP
support, see SOAP.
In a communication there is a Client and a Server. Here is what is to be done on both sides to have programs talking together.
On the client side it is quite simple. You just have to send a message
using AWS.Communication.Client.Send_Message
.
function Send_Message (Server : in String; Port : in Positive; Name : in String; Parameters : in Parameter_Set := Null_Parameter_Set) return Response.Data;
The message is sent to the specified server using the given port. A
message is composed of a name which is a string and a set of
parameters. There is a parameter set constructor in
AWS.Communication
. This function return a response as for any
callback procedure.
On the server side things are a bit more complex but not that
difficult. You must instantiate the AWS.Communication.Server
generic package by providing a callback procedure. This callback
procedure will must handle all kind of message that a client will send.
During instantiation you must also pass a context for the communication server. This context will be passed back to the callback procedure.
generic type T (<>) is limited private; type T_Access is access T; with function Callback (Server : in String; Name : in String; Context : in T_Access; Parameters : in Parameter_Set := Null_Parameter_Set) return Response.Data; package AWS.Communication.Server is ...
A complete example can be found in the demos directory. Look for com_1.adb and com_2.adb.
Note that this communication API is used by the Hotplug module facility See Hotplug module.
An Hotplug module is a module that can by dynamically binded to a running server. It is a Web server and the development process is very similar to what we have seen until now See Building an AWS server. The Hotplug module will register itself into a Web server by sending a message using the communication API. The Hotplug module send to the server a regular expression and an URL. The main server will redirect all URL matching the regular expression to the Hotplug module.
Note that the main server will redirect the URL to the first matching regular expression.
The first step is to properly create the main server hotplug module registration file. This file must list all hotplug modules that can register into the main server. Each line have the following format:
hotplug_module_name:password:server:port
server
.
You must create a password for each hotplug modules. The generated
password depends on the hotplug module name. A tool named
aws_password
is provided with AWS
to generate such
password. Usage is simple:
$ aws_password <hotplug_module_name> <password>
Then, after starting the main server you must activate the Hotplug feature:
AWS.Server.Hotplug.Activate (WS'Unchecked_Access, 2222, "hotplug_conf.ini");
hotplug_conf.ini is the hotplug module registration file described above.
Here is how to create an Hotplug module:
WS : AWS.Server.HTTP (3, 1235, False, Hotplug_CB.Hotplug'Access, False);
Here we have a server listening to the port 1235. This server can be used alone if needed as any Server developed with AWS.
Response := AWS.Client.Hotplug.Register (Name => "Hotplug_Module_Demo", Password => "my_password", Server => "http://dieppe:2222", Regexp => ".*AWS.*", URL => "http://omsk:1235/");
The hotplug module Hotplug_Module_Demo
must have been declared
on the main server, the password and redirection must have been
properly recorded too for security reasons
see Hotplug module - server activation.
This command register Hotplug_Module_Demo
into the server running
on the machine dieppe
and ask it to redirect all URL
containing AWS
to the server running on machine omsk
on
port 1235
.
Response := AWS.Client.Hotplug.Unregister (Name => "Hotplug_Module_Demo", Password => "my_password", Server => "http://dieppe:2222", Regexp => ".*AWS.*");
Here we ask to unregister Hotplug_Module_Demo
from server
dieppe
. As for the registration process a proper password must
be specified see Hotplug module - server activation.
A complete example can be found in the demos directory. Look for main.adb and hotplug.adb.
Server Push is a feature that let the Web Server send continuously data to client's Web Browser or client applications. The client does not have to reload at periodic time (which is what is called client pull) to have the data updated, each time the server send a piece of data it gets displayed on the client.
To build a push server you need to build an instance of the
AWS.Server.Push
package. This package takes a set of formal
parameters. Here are the step-by-step instructions to build a Push
Server:
First you must create a type that will contains the data to be sent to
client's browser except if it is a standard Ada type. See
Client_Output_Type
formal parameter.
This is the representation of the data that will be sent to client's
browser. This will be either a String
for Web pages or
Stream_Element_Array
for binary data like pictures. See
Stream_Output_Type
formal parameter.
It is often nice to be able to configure each client with different
parameters if needed. This can be achieved with the Context data type
that will be passed as parameter of the conversion function described
below. See Client_Environment
formal parameter.
This is a function that will transform the data described on point 1
above to the form described on point 2 above. See
To_Stream_Output
formal parameter.
To do so you just need to instantiate AWS.Server.Push
with the
above declarations.
In the standard AWS
procedure callback it is possible to register a
client if requested. This is done by calling
AWS.Server.Push.Register
. It is possible to unregister a
client using AWS.Server.Push.Unregister
. Each client must be
identified with a unique client ID. After registering a new client
from the callback procedure you must return the
AWS.Response.Socket_Taken
message. This is very important, it
tells the server to not close this socket.
At this point it is possible to send data to clients. To do so two routines are available.
Very large Internet applications should use this feature carefully. A push server keeps a socket reserved for each registered clients and the number of available sockets per process is limited by the OS.
With AWS
is is possible to take out a socket from the server and give
it back later. This feature must be used carefully but it gives a lot
of flexibility. As the socket is taken away, the connection line (or slot)
is released, AWS
can then use it to handle other requests.
This can be used to better support heavy loaded servers when some requests need a long time to complete. Long time here means longer than most of the other requests which should be mostly interractives for a Web server. Of course in such a case a keep-alive connection is kept open.
The usage in such a case is to take out the socket and put it in a waiting line. This releases the connection for the server. When the data to prepare the answer is ready you give back the socket to the server.
This first step is done form the callback function. A user instead of
replying immediatly decides to take away the socket from the
server. The first step is to record the connection socket socket by
calling AWS.Status.Socket
. The second step is to tell the
server to not release this socket by returning AWS.Response.Socket_Taken
from the callback function. At this point the server will continue to
serve other clients.
Note that this feature is used by the server push implementation see Server Push.
Calling AWS.Sever.Give_Back_Socket
will register the socket for
reuse. This socket will be placed into a spool, next time the server
will check for incoming requests it will be picked up.
It is possible to have the server activity logged into the file
<progname>-Y-M-D.log. To activate the logging you must call the
AWS.Server.Log.Start
, and it is possible to stop logging by calling
AWS.Server.Log.Stop
. Note that AWS.Server.Log.Start
have
a parameter named Auto_Flush
to control output buffering. This
parameter is False by default. If set to True, the log file will be
automatically flushed after each data. If the server logging is not
buffered, i.e. Auto_Flush is False, the log can still be flushed by
calling the AWS.Server.Log.Flush
routine. See AWS.Log, for
more information especially about the way rotating logs can be
setup. Using this feature it is possible to have automatic split of
the log file each day, each month or at every run. See AWS.Log
spec. This is very useful to avoid having very big log files.
The log format depend on Log_Extended_Fields configuration parameter. If this parameter is empty, the HTTP log would have fixed apache compartible format:
<client IP> - <auth name> - [<date and time>] "<request>" <status code> <size> For example: 100.99.12.1 - - [22/Nov/2000:11:44:14] "GET /whatever HTTP/1.1" 200 1789
If the extended fields list is not empty, the log file format would have user defined fields set:
#Version: 1.0 #Date: 2006-01-09 00:00:01 #Fields: date time c-ip cs-method cs-uri cs-version sc-status sc-bytes 2006-01-09 00:34:23 100.99.12.1 GET /foo/bar.html HTTP/1.1 200 30
Fields in the comma separated Log_Extended_Fields list could be:
AWS
also support error log files. If activated every internal error
detected by AWS
will gets logged into this special file.
Log file for errors would be in simple apache compartible format.
See AWS.Server.Log.Start_Error
and AWS.Server.Log.Stop_Error
.
For the full set of routines supporting the log facility see AWS.Server.Log .
It is not much difficult to use a secure server (HTTPS
) than a
standard one. Here we describe only what is specific to an HTTPS
server.
Before going further you must check that AWS
has been
configured with SSL
support. see Building. You must also
have installed the OpenSSL
library on your system. If this is
done, you can continue reading this section.
A server is configured as using the HTTPS protocol at the time it is
started. The only thing to do is to set the Start's Security parameter
to True. This will start a server and activate the SSL
layer by
default. A secure server must use a valid certificate, the default one
is cert.pem. This certificate has been
created by the openssl tool and is valid until year 2008. Yet, this
certificate has not been signed. To build a secure server user's can
rely on, you must have a valid certificate signed by one of the
Certificate Authorities.
The certificate to be used must be specified before starting the
secure server with AWS.Server.Set_Security
.
AWS.Server.Set_Security (WS, Certificate_Filename => "/xyz/aws.pem");
The goal here is not to replace the OpenSSL
documentation but
just to present one way to create a certificate for an HTTPS
test server.
$ openssl genrsa -rand <filename> -out ca-key.pem
Filename must be point to any file, this is used to initialized the
random seed.
$ openssl req -new -x509 -days 730 -key ca-key.pem -out ca-cert.pem
$ cat ca-key.pem ca-cert.pem > aws.pem
A this point you can use aws.pem with your server.
There are different security options, either SSLv2
, SSLv3
or
TLSv1
. SSLv2
and SSLv3
are supported by most if
not all Web browsers. These are the default protocol used by
AWS
.
TLSv1
is not supported at this point.
When AWS
detects an internal problem, it calls a specific
handler. This handler can be used to log the error, send an alert
message or build the answer to be sent back to the client's browser.
Here is the spec for this handler:
type Unexpected_Exception_Handler is access procedure (E : in Ada.Exceptions.Exception_Occurrence; Log : in out AWS.Log.Object; Error : in Data; Answer : in out Response.Data);
The handler can be called in two modes:
AWS
will continue working without problem. A
bug has been detected but it was not fatal to the thread (slot in
AWS
's terminology) handling. In this case it is possible to
send back an application level message to the client's browser. For
that you just have to fill the unexpected handler's Answer parameter
with the right response message. The Error parameter receive
information about the problem, see AWS.Exceptions.
AWS
will continue working but a thread (slot number
Error.Slot in AWS
's terminology) will be killed. It means
that AWS
will have lost one the simultaneous connection
handler. The server will continue working unless it was the last slot handler
available. Note that a Fatal error means an AWS
internal bug
and it should be reported if possible. In this mode there is no way to
send back an answer to the client's browser and Error value must
be ignored.
The default handler for unexpected exceptions send a message to standard error for fatal errors. For non fatal errors it log a message (if the error log is activated for the server) and send back a message back to the client. The message is either a built-in one or, if present in the server's directory, the content of the 500.tmplt file. This templates can used the following tags:
XML
payload for SOAP
request.
SOAP
request.
For more information see AWS.Server and see AWS.Exceptions.
To ease AWS
applications debugging it is possible to log all data
sent/received to/from the sockets. For this you need to call the
AWS.Net.Log.Start
routine by passing a write procedure
callback. You have to create such procedure or use one read-to-use
provided in AWS.Net.Log.Callbacks
package.
For more information see AWS.Net.Log and see AWS.Net.Log.Callbacks.
AWS
is not only a server it also implement the HTTP and HTTPS
protocol from the client side. For example with AWS
it is
possible to get a Web page content using the AWS.Client
API,
See AWS.Client.
It also support client Keep-Alive connections. It is then possible to request many URI from the same server using the same connection (i.e. the same sockets).
AWS
client API also support proxy, proxy authentication and Web server
authentication. Only basic (and not digest) authentication is
supported at this time.
Let's say that you want to retrieve the contrib.html
Web page from
Pascal Obry's homepage which is http://perso.wanadoo.fr/pascal.obry. The
code to do so is:
Data := Client.Get (URL => "http://perso.wanadoo.fr/pascal.obry/contrib.html");
From there you can ask for the result's content type:
if Response.Content_Type (Data) = "text/html" then ... end if;
Or using the MIME types defined in AWS.MIME
unit:
if Response.Content_Type (Data) = MIME.Text_HTML then ... end if;
And display the content if it is some kind of text data:
Text_IO.Put_Line (Response.Message_Body (Data));
If the content is some kind of binary data (executable, PNG image, Zip
archive...), then it is possible to write the result to a file for
example. Look at the agent
program in the demos
directory.
If the Web page is protected and you must pass the request through an authenticating proxy, the call will becomes:
Data := Client.Get (URL => "http://www.mydomain.net/protected/index.html" User => "me", Pwd => "mypwd", Proxy => "192.168.67.1", Proxy_User => "puser", Proxy_Pwd => "ppwd");
The client upload protocol is implemented. Using AWS.Client.Upload
it
is possible to send a file to a server which support the file upload protocol.
Here you will find a description of high level services. These services are ready to use with AWS and can be used together with user's callbacks.
Refer to the Ada spec for a complete API and usage description.
This service will help building a Web directory browser. It has a lot of options to sort directory entries and is based on the templates interface see AWS.Templates. This means that you can use the default directory template or provide your own.
see AWS.Services.Directory for complete spec and services descriptions.
In many AWS applications it is needed to check the URI to give the right answer. This means that part of the application is a big if/elsif procedure. Also, in standard callback it is not possible to have user data. Both of these restrictions are addressed with the Dispatchers facilities.
Working with a dispatcher is quite easy:
This is a wrapper around the standard callback procedure. It is needed
to mix dispatcher based callback and access to procedure
callback. Note that it is not in the AWS.Services.Dispatchers
hierarchy but in AWS.Dispatchers.Callback
because this is a
basic service needed for the server itself. It is referenced here for
documentation purpose but an AWS server can be built with using it.
see AWS.Dispatchers.Callback for complete spec description.
This is a dispatcher based on the request method. A different callback procedure can be registered for the supported request methods: GET, POST, PUT, HEAD.
see AWS.Services.Dispatchers.Method for complete spec description.
This is a dispatcher based on the request resource. A different callback procedure can be registered for specific resources. The resource is described either by its full name (string) or a regular expression.
see AWS.Services.Dispatchers.URI for complete spec description.
This is a dispatcher based on the host name. A different callback procedure can be registered for specific host. This is also known as virtual hosting.
The same computer can be registered into the DNS with different names. So all names point to the same machine. But in fact you want each name to be seen as a different Web server. This is called virtual hosting. This service will just do that, call different callback procedures or redirect to some machine/port based on the host name in the client's request.
see AWS.Services.Dispatchers.Virtual_Host for complete spec description.
This is a dispatcher that calls a user's callback and if the resource requested is not found (i.e. the user's callback returns status code 404) it checks if this resource is known as a transient page. see Transient Pages.
A timer dispatcher can be used to call different callback routines
depending on the current date and time. Such dispatcher is composed of
a set of Period
activated. When the current date and time is
inside a Period
the corresponding callback is called. A
Period
can eventually be repeated. Here are the different kind
of Period
supported by AWS
:
A dispatcher that can be used to chain two dispatchers. The response of the first dispatcher is returned except if it is a 404 (Not Found) error. In this case, the response of the second dispatcher is returned.
AWS
provides also a SOAP
specific dispatcher. This is a way to
automatically route HTTP requests or SOAP
requests to different
callback routines.
see SOAP helpers for more information.
see SOAP.Dispatchers.Callback for complete spec description.
This service is a ready to use static page server callback. Using it is possible to build a simple static page server, as simple as:
with AWS.Server; with AWS.Services.Page_Server; procedure WPS is WS : AWS.Server.HTTP; begin AWS.Server.Start (WS, "Simple Page Server demo", Port => 8080, Callback => AWS.Services.Page_Server.Callback'Access, Max_Connection => 5); AWS.Server.Wait (AWS.Server.Q_Key_Pressed); AWS.Server.Shutdown (WS); end WPS;
Build this program and launch it, it will server HTML
pages and images
in the current directory.
It is possible to activate the directory browsing facility of this simple page server. This is not activated by default. This feature is based on the directory browsing service see Directory browser.
Note that this service uses two template files:
Note that on Microsoft IE this page will be displayed only if the total page size is bigger than 512 bytes or it includes at least one image.
see AWS.Services.Page_Server for a complete spec description.
A transient page is a resource that has a certain life time on the server. After this time the resource will be released and will not be accessible anymore.
Sometimes you want to reference, in a Web page, a resource that is built in memory by the server. This resource can be requested by the client (by clicking on the corresponding link) or not, in both cases the page must be released after a certain amount of time to free the associated memory.
This is exactly what the transient pages high level service do
automatically. Each transient page must be registered into the
service, a specific routine named Get_URI
can be used to create
a unique URI
on this server. see AWS.Services.Transient_Pages.
A transient pages dispatcher can be used to build a transient pages aware server. see Transient pages dispatcher.
It not not very convenient to send back a Web page with a large table. In such a case it is better to split the table in chunks (20 lines or so) and to send only the first page. This page reference the next pages and can also contains an index of the pages.
The AWS
's split page feature can automatically do that for
you. Given template Translate_Table
or Translate_Set
and the
max line per page it returns the first page and create a set of
transient pages for all other pages. A set of template tags are used
to reference the previous and next page and also to build the page index.
There is different ways to split a set of pages and ready-to-use splitters are available:
Using the spliter abstract interface it is possible to build a customized splitter algorithm. see AWS.Services.Split_Pages.
A server that need to handle lot of large downloads can run out of connection to answer the standard Web pages. A solution is to increase the number of simultaneous connections, but this is not really efficient as a task is created for each connection and does not ensure that all the connections will be used for the downloads anyway.
The download manager can be used for that, and provides the following feature:
The server must be configured to use dispatchers (standard callbacks are not supported, note that it is possible to create a dispatcher for standard callbacks. see AWS.Dispatchers.Callback).
To start the download manager you need to pass the main server dispatcher object. The start routine will return a new dispatcher, linked with the download server specific dispatcher, that must be used to start the standard Web server. See comment in see AWS.Services.Download.
To queue a download request in the download manager you just need to
create a stream object (can be any kind of stream, see
AWS.Resources.Streams.*
) for the resource to download.
The download manager needs two templates files:
aws_download_manager_waiting.thtml
aws_download_manager_start.thtml
It is important to note that those templates must be reloaded
periodically. The best way to do that in the context of an HTML
document is to use a meta-tag. For example to refresh the page every
two seconds:
<meta http-equiv="refresh" content="2">
The templates could look like:
aws_download_manager_waiting.thtml
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <meta http-equiv="refresh" content="2"> <title>Download Manager - waiting</title> </head> <body> <p>Waiting for downloading @_NAME_@ <p>Position in the waiting line @_POSITION_@ </body> </html>
aws_download_manager_start.thtml
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <meta http-equiv="refresh" content="2"> <title>Download Manager - waiting</title> </head> <body> <p>Waiting for downloading @_NAME_@ <p>The download will start in a moment </body> </html>
AWS
provides some components to help creating nice looking Web
interfaces. It is possible to browse those Web Elements using the
web_elements
demo. Just launch this Web application from the
demos directory and turn your Web browser to
http://localhost:2400.
Currently AWS
provides:
All of them are based on templates to be easily reused in other
applications. The three first are best described by the Web Elements
demos as they are 100% design. The Ajax
one is a bit more complex, we
will present its use in the following section.
To ease integration we have used the following design:
AWS
's web_elements directory
are self contained. The content must be copied into the project. Note
that the icons and javascripts directories contain the
icons and javascripts code shared by all web elements and must also be
copied, see below.
/we_icons/<icon_name>
. So users must provide the right alias
("/we_icons/
") in the Web server.
/we_js/<script>
. So users must provide the right alias
("/we_js/"
) in the Web server.
First of all, Ajax
stand for
Asynchronous JavaScript language and XML, and is not well defined
at the moment. Ajax
is on one side able to send HTTP requests
to the Web server and on the other side able to manipulate directly the Web
browser's DOM
tree. On the DOM
it can add, remove or
replace XML
nodes. So, it is possible to change the content of
a Web page without reloading it from the server.
Most importantly, Ajax
changes the way Web applications are
thought from page based to event based.
As implemented into AWS
, Ajax
support comes as a set of
JavaScript
templates. Using those templates there is no need to
know JavaScript
(except for the JavaScript
event names) and it
makes Ajax
programming lot easier. Two actions are provided,
one for replacing another for clearing part of the web page content.
What are the steps to do Ajax
?
Remember, do not think about the Web page but about a specific widget
(HTML
fragments) with the associated event and action.
This is the AWS/Ajax
runtime, it contains JavaScript
code needed for the AWS/Ajax
support.
There is nothing special here, use your favorite Web designer tool.
Using some HTML
<div> tags we create areas where we will place
HTML
fragments later. For example when clicking on a button
(described above) in our Web interface we want to display a new form
in this area.
Give a different name to the widgets using id="name". This name will
be later used to identify the widgets on which the envent and
corresponding action must be placed. We do not want to clutter the Web
design with JavaScript
code like onclick="dothis()"
or
onchange="dothat()"
.
This is the interresting part. At this point we link events/actions to the widgets and specify in which area the results sent by the server will be placed.
This is not the only way to do Ajax
, we just presented here a simple
approach that works well with the AWS/Ajax
templates.
This section describes the AWS/Ajax
support where the answer from the
server is an HTML
fragment. This basic support is designed to
be used for migration of a Web server to Ajax
. For new
applications, it is worth considering using the XML based Ajax support,
see XML based Ajax.
Let's have a very simple example:
@@INCLUDE@@ aws.tjs
<input id="clickme" type="button" value="Clik Me">
<div id="placeholder">... result here ...</div>
@@INCLUDE@@ aws_action_replace.tjs onclick clickme placeholder
Basically it places an onclick attribute (the event) in the HTML
<input>
identified as clickme (the action) above. Here is
what happen when the button is clicked:
<div>
placeholder.
On the server side the code would look like this:
function Callback (Request : in Status.Data) return Response.Data is URI : constant String := Status.URI (Request); begin if URI = "/clickme" then return Response.Build (MIME.Text_HTML, "you click me!"); ...
So when the button is clicked the string "you click me!" will replace the "... result here ..." string of the place holder div above.
This is a simple and very limited example as there is no parameter
passed to the HTTP
request. In real Web applications it is necessary
to send a context with the request. This can be either the value of
other widgets or all values of widgets' form.
References to widgets or forms can be passed to the aws_action_replace.tjs template starting with the 5th parameter.
<input id="field" type="text" value="default value"> ... @@INCLUDE@@ aws_action_replace.tjs (onclick clickme placeholder 5=>field)
or
<form id="small_form" name="small_form"> ... </form> @@INCLUDE@@ aws_action_replace.tjs (onclick clickme placeholder 5=>small_form)
Note that the onclick
event is only one of the possible
JavaScript
event on a button
. It is possible to used
any supported event, for example on an HTML
<select>
widget
it is common to map the action to the onchange
event.
AWS
also provides support for clearing an area or a widget
content (like an input).
@@INCLUDE@@ aws_action_clear.tjs (onclick, clear, field)
This simple action adds the onclick event to the clear button to erase the content of the field widget.
In many cases you'll like to update and/or clear multiple areas in your
Web interface. With the templates above only a single action is
possible. AWS
provides support for XML
based answers. In
this XML
documents it is possible to:
<replace id="item_id">new text</replace>
<clear id="item_id"/>
<select action="add" id="item_id" option_value="value" option_content="content"/>
<select action="delete" id="item_id" option_value="value"/>
<select action="select" id="item_id" option_value="value"/>
<select action="clear" id="item_id"/>
<radio action="select" id="item_id"/>
<check action="select" id="item_id"/>
<check action="clear" id="item_id"/>
<get url="http://thishost/action"> <parameters value="name=Ajax"/> <field id="input1"/> </get>
This will send the following request:
http://thishost/action?name=Ajax&input1=<val_input1>
Where val_input1 is the current value of the input1 input
widget. The result must be an XML/Ajax
document that will be parsed.
<make_sortable> <list id="firstlist"/> <list id="secondlist"/> </make_sortable>
Here firstlist and secondlist are id of UL
elements. It is
possible to specified as many list id as needed. A drag and drop is
then possible for all elements in those lists. It is then possible to
reference such list by passing the list id as a field to the
template. Items on those list will be serialized and passed to the AWS
callback. Note that for the serialization to work properly, each
LI
elements must be given the id of the list and then the value
we want to pass.
<ul id="firstlist"> <li id="firstlist_red">Red</li> <li id="firstlist_green">Green</li> <li id="firstlist_blue">Blue</li> </ul>
The serialization will send each value on this list using a multi-valued parameter named firstlist[].
http://server?firstlist[]=red&firstlist[]=green&firstlist[]=blue
<destroy_sortable> <list id="firstlist"/> <list id="secondlist"/> </destroy_sortable>
Remove the sortable properly from the specified lists.
<location url="http://thishost/go_there"/>
Redirect the browser to the specified URL.
<refresh/>
Refresh the current page as if the Web Browser refresh button was pressed.
<apply_style id="node_id"> <attribute id="display" value="none"/> </apply_style>
Add the CSS style display:none
to the node_id element. It
is possible to specify multiple attributes if needed.
Here is an example of such XML document:
<response> <replace id="xml_status_bar">Fill Widgets...</replace> <replace id="text1">Response from XML</replace> <replace id="text2">Another response for text2</replace> <replace id="input1">tag is input1</replace> <replace id="input2">tag is input2</replace> <select action="add" id="xmlsel" option_value="one" option_content="1"/> <select action="add" id="xmlsel" option_value="two" option_content="2"/> <select action="add" id="xmlsel" option_value="three" option_content="3"/> <select action="select" id="xmlsel" option_value="two"/> <radio action="select" id="radio1"/> <check action="select" id="check1"/> <check action="select" id="check3"/> <check action="clear" id="check2"/> </response>
The template to use this feature is aws_action_xml.tjs. The usage is similar to what is described in the previous section (see Basic Ajax support) expect that in this case we do not have to pass the placeholder.
Let's revisit the first example above to use the XML
Ajax
support.
@@INCLUDE@@ aws.tjs
<input id="clickme" type="button" value="Clik Me">
<div id="placeholder">... result here ...</div>
@@INCLUDE@@ aws_action_xml.tjs onclick clickme
Basically it places an onclick attribute (the event) in the HTML
<input>
identified as clickme (the action) above. Here is
what happen when the button is clicked:
XML
content.
To set the placeholder with "new text", the XML
document
returned by the server must be:
<response> <replace id="placeholder">new text</replace> </response>
If we want also to clear the input field named field and to select the radio button named radio1 we must return:
<response> <replace id="placeholder">new text</replace> <clear id="field"/> <radio action="select" id="radio1"/> </response>
This is by far the most flexible solution as it is possible to return, from the server, a structured answer.
A final comment, if the text returned by the server to replace a
specific area is an HTML
fragment, the content must be placed into a
CDATA
tag:
<response> <replace id="item_id"> <![CDATA[ HTML CODE HERE ]]> </replace> </response>
Finally, if this is not enough because you need to use some specific
JavaScript
code, AWS
provides a template to add an event
to a specific widget, the action being the name of a JavaScript
routine, see aws_action_js.tjs.
This template together with aws_func_replace.tjs, aws_func_clear.tjs and aws_func_xml.tjs can be used to chain multiple actions. Those templates are the function body used by the corresponding templates aws_action_replace.tjs, aws_action_clear.tjs and aws_action_xml.tjs.
Let say you want to clear a widget, change the content of another one
and calling one of your specific JavaScript
routine when clicking on
a button. It is not possible to have mutiple onclick
events on
the same widget, the solution is the following:
For this in the the body of the clear_replace()
JavaScript
routine we place:
function clear_replace()
{
@@INCLUDE@@ aws_func_replace.tjs (clickme placeholder 4=>field) @@INCLUDE@@ aws_func_clear.tjs (area) call_this_routine();
}
Then to add the event on the widget:
@@INCLUDE@@ aws_action_js.tjs (onclick clickme clear_replace)
Furthermore, it is possible to pass (as the parameter number 20) a routine to call after a specific action to all templates. This is another way to chain multiple actions for a single event.
Note that all AWS/Ajax
templates have a set of comments at the start
explaining in details the usage of each parameter.
The AWS.Services.Web_Block
hierarchy contains an API useful for
keeping context on Web pages. It has been designed to be able to split
a Web application into a set of independent blocks that can be put
together in the same Web page. The context is then useful as it is
passed and known by each individual block. Note that this is different
than the session as a session is global to the current Web browser
whereas the context can be different for each individual web pages
opened.
Instead of parsing a whole page using AWS.Templates
API the web blocks
are registered independently using AWS.Services.Web_Block.Registry
.
The block is registered together with its templates and a callback to use
to get user's data for this specific block with the given context.
So using this API instead of having a set of callbacks returning an
AWS.Response.Data
and where the final rendering is to be done
by the client code we have a set of callbacks that returns a
Translate_Set
. The client just have to fill the set with the
data corresponding to the actual request and possibly using the
context. The final rendering is done by the provided services in
Web_Block.Registry
.
It is possible to use the Templates_Parser's templates2ada
tool for
generating the callbacks register calls. This ensures that all tags on the
application Web Pages have a corresponding callback.
Let's have a simple example, a block with a single tag (@_LAZY_COUNTER_@) that is incremented by one each time it is used.
First create the following simple HTML fragment and place it into
templates/counter.thtml. Note that by convention the lazy tags
(see Templates_Parser documentation) start with the prefix LAZY_
.
<p>@_LAZY_COUNTER_@</p>
The Web_Callbacks
package contains the application callbacks.
with AWS.Status; with AWS.Templates; with AWS.Services.Web_Block.Context; package Web_Callbacks is use AWS; use AWS.Services; procedure Counter (Request : in Status.Data; Context : access Web_Block.Context.Object; Translations : in out AWS.Templates.Translate_Set); end Web_Callbacks;
We need also modify the standard templates.tads as distributed
with the Templates_Parser
to add the following template code:
with AWS.Services.Web_Block.Registry; with Web_Callbacks; package body @_PACKAGE_@ is package body Lazy is procedure Register; -- Register lazy tags into the AWS's Web Block framework -------------- -- Register -- -------------- procedure Register is use AWS.Services; begin @@TABLE@@ @@IF@@ @_
UPPER:SLICE(1..5):VARIABLE_LIST_@ = "LAZY_" Web_Block.Registry.Register ("@_VARIABLE_LIST_@", "templates/@_
LOWER:REPLACE_ALL(LAZY_/):VARIABLE_LIST_ @.thtml", Web_Callbacks.@_
CAPITALIZE:REPLACE_ALL(LAZY_/):VARIABLE_LIST_@'Access); @@END_IF@@ @@END_TABLE@@ end Register; begin Lazy.Register; end Lazy; end @_PACKAGE_@;
Basically this is to write a register call for every template's tag. The registration is done during elaboration. The templates can be tailored as needed, for example it is also possible to generate all callback's specs.
Now let's parse the template HTML fragment and create the corresponding Ada spec:
$ templates2ada -d templates -o code.ada -r templates.tads $ gnatchop code.ada
Look at the generated code, it properly register the Counter
callback to be used for rendering LAZY_COUNTER
using the
templates/counter.thtml. So we have a tight coupling between
the code and the template file. If the tag is renamed in the template
file the application will not compile anymore. This greatly helps
keeping the application code synchronized.
procedure Register is use AWS.Services; begin Web_Block.Registry.Register ("LAZY_COUNTER", "templates/counter.thtml", Web_Callbacks.Counter'Access); end Register;
Last part is to actually implement the Counter
callback. Here
is a possible implementation making use of the context to keep the
counter state.
with AWS.Utils; with Templates; package body Web_Callbacks is procedure Counter (Request : in Status.Data; Context : access Web_Block.Context.Object; Translations : in out AWS.Templates.Translate_Set) is N : Natural := 0; begin if Context.Exist ("N") then N := Natural'Value (Context.Get_Value ("N")); end if; N := N + 1; Context.Set_Value ("N", Utils.Image (N)); AWS.Templates.Insert (Translations, AWS.Templates.Assoc (Templates.Lazy.Lazy_Counter, N)); end Counter; end Web_Callbacks;
When building an Ajax Web applications it is required to give ids to web elements to be able to reference them. It is also quite common to use CSS to give such and such item a specific style. After some time it is quite difficult to keep track of all those ids. Are they all used ? Don't we reference an id that does not exist anymore ?
webxref
has been designed to help finding such problems.
The files kinds handled are:
The features are:
webxref
output all the references to ids and classes.
Note that all references are in a format recognized by tools like GPS
and Emacs
. It is then possible to navigate inside them easily.
All webxref
options are listed using the -h
option.
SOAP
can be used to implements Web Services. The SOAP
implementation uses AWS HTTP
as the transport layer. SOAP
is
platforms and languages independent, to ensure a good
inter-operability, AWS/SOAP
implementation has been validated through
http://validator.soapware.org/, the version number listed on
this server corresponds to the AWS version string
(AWS.Version
) catenated with the SOAP
version string
(SOAP.Version
).
This SOAP
implementation is certainly one with the higher level
of abstraction. No need to mess with a serializer, to know what is a
payload or be an XML
expert. All the low level stuffs are
completely hidden as the SOAP
type system has been binded as
much as possible to the Ada type system.
The SOAP
type system has been relaxed to be compatible with
WSDL
based SOAP
implementation. In these implementations, types
are generally (as in the Microsoft implementation) not part of the
payload and should be taken from the WSDL
(Web Services Description
Language). AWS/SOAP
is not WSDL
compliant at this stage, all
such types are binded into the Ada type system as strings. It is up to
the programer to convert such strings to the desired type.
The SOAP
client interface is quite simple. Here are the step-by-step
instructions to call a SOAP
Web Service:
SOAP
parameters
As for the SOAP
servers, the SOAP
parameters are built using a
SOAP.Parameters.List
object.
Params : constant Parameters.List := +I (10, "v1") & I (32, "v2");
SOAP
Payload
The Payload object is the procedure name and the associated parameters.
declare Payload : Message.Payload.Object; begin Payload := Message.Payload.Build ("Add", Params);
SOAP
Web Service
Here we send the above Payload to the Web Server which handles the Web
Service. Let's say that this server is named myserver
, it is
listening on port 8082
and the SOAPAction
is soapdemo
.
Resp : constant Message.Response.Object'Class := SOAP.Client.Call ("http://myserver:8082/soapdemo", Payload);
Let's say that the answer is sent back into the parameter named "myres", to get it:
My_Res : constant Integer := SOAP.Parameters.Get (Params, "myres");
In the above example we have called a Web Service whose spec could be described in Ada as follow:
function Add (V1, V2 : in Integer) return Integer; -- Add V1 and V2 and returns the result. In SOAP the result is named "myres"
A SOAP
server implementation must provides a callback procedure as for
standard Web server see Callback procedure. This callback must
checks for the SOAP
Action URI to handle both standard Web requests
and SOAP
ones. The SOAPAction
is sent with the HTTP headers and
can be retrieved using AWS.Status.SOAPAction
.
Here are the step-by-step instructions to be followed in the SOAP
callback procedure:
SOAP
Payload
The SOAP
Payload is the XML
message, it contains the
procedure name to be called and the associated parameters.
function SOAP_CB (Request : in AWS.Status.Data) return AWS.Response.Data is use SOAP.Types; use SOAP.Parameters; Payload : constant SOAP.Message.Payload.Object := SOAP.Message.XML.Load_Payload (AWS.Status.Payload (Request));
AWS.Status.Payload
returns the XML
Payload as sent by
the SOAP
Client. This XML
Payload is then parsed using
SOAP.Message.XML.Load_Payload
which returns a
SOAP.Message.Payload.Object
object.
SOAP
Parameters
The SOAP
procedure's parameters.
Params : constant SOAP.Parameters.List := SOAP.Message.Parameters (Payload);
SOAP.Parameters.List
is a structure which holds the SOAP
parameters. Each parameter can be retrieved using a
SOAP.Parameters
API, see SOAP.Parameters. For example to
get the parameter named myStruc
which is a SOAP
struct:
My_Struct : constant SOAP_Record := SOAP.Parameters.Get (Params, "myStruct");
Another example, to get the parameter named myInt
which is a
SOAP
integer:
My_Int : constant Integer := SOAP.Parameters.Get (Params, "myInt");
This is the real job, as for any procedure you can do whatever is needed to compute the result.
SOAP
answer
This is the procedure answer. A SOAP
answer is built from the
SOAP
Payload and by setting the returned parameters.
declare Resp : SOAP.Message.Response.Object; Resp_Params : SOAP.Parameters.List; begin Resp := SOAP.Message.Response.From (Payload); Resp_Params := +I (My_Int * 2, "answer"); SOAP.Message.Set_Parameters (Resp, Resp_Params);
This build a response which is a single integer value named
answer
with the value My_Int * 2
.
This last step will encode the response object in XML
and will
returns it as the body of an HTTP
message.
return SOAP.Message.Response.Build (Resp);
There is two ways to help building the SOAP
callbacks. AWS
provides a SOAP
specific callback, the spec is :
function SOAP_Callback (SOAPAction : in String; Payload : in Message.Payload.Object; Request : in AWS.Status.Data) return AWS.Response.Data;
With both solutions exposed below, AWS
retrieve the
SOAPAction
and the Payload from the SOAP
request. This
is transparent to the user.
SOAP.Utils.SOAP_Wrapper
generic routine.
generic with function SOAP_CB (SOAPAction : in String; Payload : in Message.Payload.Object; Request : in AWS.Status.Data) return AWS.Response.Data; function SOAP_Wrapper (Request : in AWS.Status.Data) return AWS.Response.Data; -- From a standard HTTP callback call the SOAP callback passed as generic -- formal procedure. Raise Constraint_Error if Request is not a SOAP -- request.
For example, from the standard HTTP callback CB
we want to call
SOAP_CB
for all SOAP
requests:
function SOAP_CB (SOAPAction : in String; Payload : in Message.Payload.Object; Request : in AWS.Status.Data) return AWS.Response.Data is begin -- Code here end SOAP_CB; procedure SOAP_Wrapper is new SOAP.Utils.SOAP_Wrapper (SOAP_CB); function CB (Request : in AWS.Status.Data) return AWS.Response.Data is SOAPAction : constant String := Status.SOAPAction (Request); begin if SOAPAction /= "" then SOAP_Wrapper (Request); else ...
AWS
provides also a SOAP
specific dispatcher. This
dispatcher will automatically calls a standard HTTP
or
SOAP
callback depending on the request. If SOAPAction
is
specified (i.e. it is a SOAP
request), the dispatcher will call
the SOAP
callback otherwise it will call the standard HTTP
callback. This is by far the easiest integration procedure. Using
dispatcher the above code will be written:
function SOAP_CB (SOAPAction : in String; Payload : in Message.Payload.Object; Request : in AWS.Status.Data) return AWS.Response.Data is begin -- Code here end SOAP_CB; function CB (Request : in AWS.Status.Data) return AWS.Response.Data is SOAPAction : constant String := Status.SOAPAction (Request); begin -- Code here end CB; -- In the main procedure begin AWS.Server.Start (WS, Dispatcher => SOAP.Dispatchers.Callback.Create (CB'Access, SOAP_CB'Access), Config => AWS.Config.Default_Config);
The dispacther is created using SOAP.Dispatchers.Callback.Create
.
This routine takes two parameters, one is the standard HTTP
callback procedure and the other is the SOAP
callback procedure.
WSDL
(Web Service Definition Language) is an XML
based document
which described a set of Web Services either based on SOAP
or
XML/RPC
.
By using a WSDL
document it is possible to describe, in a formal way,
the interface to any Web Services. The WSDL
document contains the
end-point (URL to the server offering the service), the SOAPAction
(needed to call the right routine), the procedure names and a
description of the input and output parameters.
AWS
provides two tools to work with WSDL
documents:
WSDL
document from an Ada package spec.
SOAP
interface is completely abstracted out, users will deal only
with Ada
API. All the SOAP
marshaling will be created
automatically.
Note that this tool is based on ASIS
.
ada2wsdl
can be used on any Ada spec file to generated a
WSDL
document. The Ada spec is parsed using ASIS
.
The simplest way to use it is:
$ ada2wsdl simple.ads
Given the following Ada spec file:
package Simple is function Plus (Value : in Natural) return Natural; end Simple;
It will generate the following WSDL
document:
<?xml version="1.0" encoding="UTF-8"?> <definitions name="Simple" targetNamespace="urn:aws:Simple" xmlns:tns="urn:aws:Simple" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soap-enc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <message name="Plus_Request"> <part name="Value" type="xsd:int"/> </message> <message name="Plus_Response"> <part name="Result" type="xsd:int"/> </message> <portType name="Simple_PortType"> <operation name="Plus"> <input message="tns:Plus_Request"/> <output message="tns:Plus_Response"/> </operation> </portType> <binding name="Simple_Binding" type="tns:Simple_PortType"> <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/> <operation name="Plus"> <soap:operation soapAction="Plus"/> <input> <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:aws:Simple" use="encoded"/> </input> <output> <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:aws:Simple" use="encoded"/> </output> </operation> </binding> <service name="Simple_Service"> <port name="Simple_Port" binding="tns:Simple_Binding"> <soap:address location="http://.../"/> </port> </service> </definitions>
In bold are marked the important parts from a spec point of view. The
first item is the name of the WSDL
document (the name of the
Ada spec package). On the portType
section we have the
description of the Ada Plus function. Something important to note
is that in Ada a function does not have a named return parameter,
ada2wsdl use Result for the response. Both the input and
output parameter are mapped to SOAP
xsd:int
type.
Note that the SOAP
address generated by default (http://.../)
must be edited manually or specified using ada2wsdl's -a
option.
This is of course a very simple example. ada2wsdl
does support lot
more complex specs and will map Ada records, arrays, enumerations,
derived types to a corresponding XML
schema definition. See
section below for a description of the mapping.
ada2wsdl
parse Ada records, arrays, derived types, enumerations,
procedures and functions and generate the corresponding WSDL
document. In this section we describe the mapping between Ada and
WSDL
.
<simpleType name="Character"> <restriction base="xsd:string"> <length value="1"/> </restriction> </simpleType>
SOAP.Utils.SOAP_Base64
is a
subtype of string which is is recognized by ada2wsdl
to
generate the proper SOAP type.
SOAP.Types.Byte
is a type which is
recognized by ada2wsdl
to generate the proper SOAP type.
SOAP.Types.Short
is a type which is
recognized by ada2wsdl
to generate the proper SOAP type.
SOAP.Types.Long
is a type which is
recognized by ada2wsdl
to generate the proper SOAP type.
SOAP.Types.Unsigned_Byte
is a
type which is recognized by ada2wsdl
to generate the proper SOAP type.
SOAP.Types.Unsigned_Short
is a
type which is recognized by ada2wsdl
to generate the proper SOAP type.
SOAP.Types.Unsigned_Int
is a
type which is recognized by ada2wsdl
to generate the proper SOAP type.
SOAP.Types.Unsigned_Long
is a
type which is recognized by ada2wsdl
to generate the proper SOAP type.
type Number is new Integer;
is defined as:
<simpleType name="Number" targetNamespace="http://soapaws/WSDL_C_pkg/"> <restriction base="xsd:int"/> </simpleType>
type Small is range 1 .. 10;
is defined as:
<simpleType name="Small" targetNamespace="http://soapaws/WSDL_C_pkg/"> <restriction base="xsd:byte"/> </simpleType>
type Color is (Red, Green, Blue);
is defined as:
<simpleType name="Color"> <restriction base="xsd:string"> <enumeration value="Red"/> <enumeration value="Green"/> <enumeration value="Blue"/> </restriction> </simpleType>
type Rec is record A : Integer; B : Float; C : Long_Float; D : Character; E : Unbounded_String; F : Boolean; end record;
is defined as:
<complexType name="Rec"> <all> <element name="A" type="xsd:int"/> <element name="B" type="xsd:float"/> <element name="C" type="xsd:double"/> <element name="D" type="tns:Character"/> <element name="E" type="xsd:string"/> <element name="F" type="xsd:boolean"/> </all> </complexType>
type Set_Of_Rec is array (Positive range <>) of Rec;
is defined as:
<complexType name="Set_Of_Rec"> <complexContent> <restriction base="soap-enc:Array"> <attribute ref="soap-enc:arrayType" wsdl:arrayType="tns:Rec[]"/> </restriction> </complexContent> </complexType>
SOAP
arrays is most of the time not constrained at all. To
support this AWS
use a safe access array component. Such a type
is built using a generic runtime support package named
SOAP.Utils.Safe_Pointers
. This package implements a reference
counter for the array access and will release automatically the memory
when no more reference exists for a given object.
For example, let's say that we have an array of integer that we want to put inside a record:
type Set_Of_Int is array (Positive range <>) of Integer;
The first step is to create the safe array access support:
type Set_Of_Int_Access is access Set_Of_Int; package Set_Of_Int_Safe_Pointer is new SOAP.Utils.Safe_Pointers (Set_Of_Int, Set_Of_Int_Access);
Note that the name Set_Of_Int_Safe_Pointer
(<type>_Safe_Pointer)
is mandatory (and checked by ada2wsdl) to achieve
interoperability with wsdl2aws. see Working with WSDL documents.
From there the safe array access can be placed into the record:
type Complex_Rec is record SI : Set_Of_Int_Safe_Pointer.Safe_Pointer; end record;
To create a Safe_Pointer given a Set_Of_Int
one must use
Set_Of_Int_Safe_Pointer.To_Safe_Pointer
routine. Accessing
individual items is done with SI.Item (K)
.
These Ada definitions are fully recognized by ada2wsdl and will
generate standard array and record WSDL
definitions as seen above.
<complexType name="Set_Of_Int"> <complexContent> <restriction base="soap-enc:Array"> <attribute ref="soap-enc:arrayType" wsdl:arrayType="xsd:int[]"/> </restriction> </complexContent> </complexType> <complexType name="Complex_Rec"> <all> <element name="SI" type="tns:Set_Of_Int"/> </all> </complexType>
Usage: ada2wsdl [options] ada_spec
ada2wsdl
options are:
URL
for the Web Server address. Web Services will be
available at this address. A port can be specified on the URL
,
http://server[:port]
. The default value is http://.../
.
WSDL
file. Overwrite exiting file
with the same name.
ASIS
compilation step. This option can
appear any number of time on the command line.
WSDL
representation for Ada enumerations, map
them to standard string. see Ada mapping to WSDL.
WSDL
document into file.
WSDL
document, by default
the spec package's name is used.
This section describe how to use a Web Service. Let's say that we want
to use the Barnes & Noble Price Quote service. The WSDL document for
this service can be found at
http://www.xmethods.net/sd/2001/BNQuoteService.wsdl. In summary
this document says that there is a service named getPrice
taking as input a string representing the ISBN number and returning
the price as floating point.
The first step is to generate the client interface (stub):
$ wsdl2aws -noskel http://www.xmethods.net/sd/2001/BNQuoteService.wsdl
This will create many files, the interesting one at this point is bnquoteservice-client.ads, inside we have:
function getPrice (isbn : in String) return Float; -- Raises SOAP.SOAP_Error if the procedure fails
Let's call this service to find out the price for The Sword of Shannara Trilogy book.
with Ada.Text_IO; with BNQuoteService.Client; procedure Price is use Ada; ISBN : constant String := "0345453751"; -- The Sword of Shannara Trilogy ISBN package LFIO is new Text_IO.Float_IO (Float); begin Text_IO.Put_Line ("B&N Price for The Sword of Shannara Trilogy"); LFIO.Put (BNQuoteService.Client.getPrice (ISBN), Aft => 2, Exp => 0); end Price;
That's all is needed to use this Web Service. This program is fully functional, It is possible to build it and to run it to get the answer.
Building a Web Service can also be done from a WSDL
document. Let's
say that you are Barnes & Noble and that you want to build Web Service
getPrice
as described in the previous section.
You have created the WSDL
document to specify the service spec.
From there you can create the skeleton:
$ wsdl2aws -nostub http://www.xmethods.net/sd/2001/BNQuoteService.wsdl
This will create many files, the interesting one here is bnquoteservice-server.ads, inside we have:
Port : constant := 80; generic with function getPrice (isbn : in String) return Float; function getPrice_CB (SOAPAction : in String; Payload : in SOAP.Message.Payload.Object; Request : in AWS.Status.Data) return AWS.Response.Data;
This is a SOAP AWS
's callback routine that can be instantiated
with the right routine to retrieve the price of a book given its ISBN
number. A possible implementation of such routine could be:
function getPrice (isbn : in String) return Float is begin if isbn = "0987654321" then return 45.0; elsif ... end getPrice; function SOAP_getPrice is new BNQuoteService.Server.getPrice_CB (getPrice);
SOAP_getPrice
is a SOAP AWS
's callback routine (i.e. it is not
a standard callback). To use it there is different solutions:
AWS.Status.Data
into a SOAP
callback routine.
function getPrice_Wrapper is new SOAP.Utils.SOAP_Wrapper (SOAP_getPrice);
The routine getPrice_Wrapper
can be used as any other AWS's
callback routines. Note that inside this wrapper the XML
payload is
parsed to check the routine name and to retrieve the SOAP
parameters. To call this routine the payload needs to be parsed (we
need to know which routine has be invoked). In this case we have
parsed the XML
payload twice, this is not efficient.
SOAP
procedures as the payload is parsed only once.
function CB (Request : in Status.Data) return Response.Data is SOAPAction : constant String := Status.SOAPAction (Request); Payload : constant SOAP.Message.Payload.Object := SOAP.Message.XML.Load_Payload (AWS.Status.Payload (Request)); Proc : constant String := SOAP.Message.Payload.Procedure_Name (Payload); begin if SOAPAction = "..." then if Proc = "getPrice" then return SOAP_getPrice (SOAPAction, Payload, Request); elsif ... ... end if; else ... end if;
Note that the port to be used by the AWS server is described into the server spec.
Usage: wsdl2aws [options] <file|URL>
It is possible to pass a WSDL
file or direct wsdl2aws to
a WSDL
document on the Web by passing it's URL
.
wsdl2aws
options are:
getPrice
will be converted
to Get_Price
. This formatting is done for packages, routines and formal
parameters.
SOAP
routines. If -s
is not used,
wsdl2aws
will exit with an error when a problem is found while
parsing the WSDL
document. This option is useful to skip
routines using non supported types and still be able to compile the
generated files.
WSDL
document. This option can be used
only when using a Web WSDL
document (i.e. passing an URL to
wsdl2aws
).
WSDL
document specify a document style binding even
though it is really an RPC one.
WSDL
document tree.
WSDL
document as comment into the generated root unit.
SOAP
dispatcher callback routine for the
server. This dispatcher routine contains the code to handle all the
operations as described in the WSDL
document. You need also to
specify the -spec
and/or -types
options, see below.
operation
to the list of SOAP
operations to skip during the
code generation. It is possible to specify multiple -x
options on the
command line.
SOAP
routines. This is used for example by the -cb
option above
to instantiate all the server side SOAP
callbacks used by the main
SOAP
dispatcher routine. If -types
is not specified, the
type definitions are also used from this spec.
SOAP
routines specified with option -spec
. If -spec
is
not specified, the spec definitions are also used from this spec.
WSDL
document. This tag can be used to include the right units
with @_SOAP_SERVICE_@.Client; with @_SOAP_SERVICE_@.CB;
procedure @_UNIT_NAME_@ is begin ...
WSDL
document and generate code to access
to these Web Services via this proxy. The proxy can be specified by
its DNS name or IP address.
The wsdl2aws
tool read a WSDL
document and creates a root
package and a set of child packages as described below:
WSDL
in
comment and the description of the services as read from the WSDL
document.
SOAP
base types. We find here the definitions of the SOAP
structs
and arrays with routines to convert them between the Ada and SOAP
type
model. A subtype definition is also created for every routine's returned type.
In fact, all definitions here are only alias or renaming of types
and/or routines generated in other packages. The real definitions for
structs, arrays, enumerations and derived types are generated into a
package whose name depends on the name space used for these
entities. This package act as a container for all definitions and it
is the only one used in the other generated packages.
SOAP
dispatcher callback routine.
It is hard to know all current limitations as the WSDL
and
SOAP
world is quite complex. We list there all known limitations:
SOAP
base types are not supported : date, time, xsd:hexBinary, decimal. All these are easy to add (except decimal), it is just not
supported with the current version.
Using both tools together is an effective way to build rapidely a SOAP
server. It can be said that doing so is quite trivial in fact. Let's
take the following spec:
package Graphics is type Point is record X, Y : Float; end record; function Distance (P1, P2 : in Point) return Float; -- Returns the distance between points P1 and P2 end Graphics;
We do not show the body here but we suppose it is implemented. To build a server for this service it is as easy as:
$ ada2wsdl -a http://localhost:8787 -o graphics.wsdl graphics.ads
The server will be available on localhost at port 8787.
$ wsdl2aws -cb -main server -types graphics graphics.wsdl $ gnatmake server -largs ...
Options
-cb
SOAP
dispatcher callback routine,
-main server
-types graphics
Graphics.Point
for example).
AWS
provides a complete API to send e-mail using SMTP
protocol. You need to have access to an SMTP server to use this
feature. The API covers sending simple mail with text message and/or
with MIME
attachments (base64 encoded). Here are the steps to
send a simple e-mail:
SMTP_Server : SMTP.Receiver := SMTP.Client.Initialize ("smtp.hostname");
Here AWS
uses the default SMTP port to create an SMTP mail server but
it is possible to specify a different one. The hostname specified
must be a valid SMTP server.
To send an e-mail there is many different API. Let's send a simple text mail.
Status : SMTP.Status; SMTP.Client.Send (SMTP_Server, From => SMTP.E_Mail ("Pascal Obry", "p.obry@wanadoo.fr"), To => SMTP.E_Mail ("John Doe", "john.doe@here.com"), Subject => "About AWS SMTP protocol", Message => "AWS can now send mails", Status => Status);
Here Status will contain the SMTP returned status.
Using above status data it is possible to check that the message was
sent or not by the server. The status contain a code and an error
message, both of them can be retrieved using specific routines,
See AWS.SMTP. It is also possible to check that the call was
successful with SMTP.Is_Ok
routine.
if not SMTP.Is_Ok (Status) then Put_Line ("Can't send message: " & SMTP.Status_Message (Status)); end if;
In the above example, the message content was given as a string but it
is possible to specify a disk file. AWS
can also send MIME messages
either from disk files or with in memory base64 encoded binary
data. The API provides also a way to send messages to multiple
recipients at the same time and to send messages with alternative
contents (text and HTML for example). These features are not described here,
complete documentation can be found on the spec see AWS.SMTP and
see AWS.SMTP.Client.
AWS
provides an API to retrieve e-mails from a POP
mailbox. POP
stands for Post Office Protocol and is the main
protocol used by Internet Service Providers around the
world. IMAP
is another well known protocol in this area but it
is not supported by AWS
.
We describes here the POP
API. For a complete description see
see AWS.POP.
The first step is to authenticate using a user name and
password. AWS
supports two methods one called Clear_Text
which is the most used and another one APOP
which is more secure but
almost not supported by ISP
for the moment (and will probably
never be supported as a more secure protocol named SPA
-Secure
Password Authentication- could be used instead).
Mailbox : POP.Mailbox := POP.Initialize ("pop.hostname", "john.does", "mysuperpwd");
The default Authentication method is Clear_Text
.
When the connection is opened it is possible to get information about the mailbox like the number of messages or the total number of bytes in the mailbox.
N : constant Natural := POP.Message_Count (Mailbox); Bytes : constant Natural := POP.Size (Mailbox);
Each message is numbered starting from 1. A function named Get
will return a message given its mailbox's number.
Message : constant POP.Message := POP.Get (Mailbox, 2, Remove => True);
Remove can be set to False
for the message to stay on the
mailbox. The default value is False
.
Another way to retreive message is by using an iterator.
procedure Print_Subject (Message : in POP.Message Index : in Positive; Quit : in out Boolean) is begin Text_IO.Put_Line (POP.Subject (Message)); end Print_Message; procedure Print_All_Subjects is new POP.For_Every_Message (Print_Subject); ... Print_All_Subjects (Mailbox, Remove => True);
It exists a set of routines on a POP.Message
object to get the subject
the content, the date or any headers. It is also possible to work with
attachments. See point below.
A message can have a set of MIME
attachments. The number of
attachments can be retrieved using Attachment_Count
.
Message : constant POP.Message := ...; A_Count : constant Natural := POP.Attachment_Count (Message);
As for messages it is possible to get a single attachment using its index in the message or by using an iterator.
First_Attachment : constant POP.Attachment := POP.Get (Message, 1); procedure Write_Attachment (Attachment : in POP.Attachment Index : in Positive; Quit : in out Boolean) is begin POP.Write (Attachment, Directory => "."); end Print_Message; procedure Write_All_Attachments is new POP.For_Every_Attachment (Write_Attachment); ... Write_All_Attachments (Message);
It is also possible to retrieve the attachment's filename, the content as a memory stream. see AWS.POP.
POP.Close (POP_Server);
AWS
provides a complete API to retrieve information from LDAP servers.
Note that there is no support for updating, modifying or deleting
information only to read information from the server.
The AWS/LDAP
implementation is based on OpenLDAP
. To
build an LDAP application you need to link with the libldap.a
library. This library is built by AWS
on Windows based system
and will use the wldap32.dll as provided with Windows
NT/2000/XP. On UNIX based systems, you must install properly the
OpenLDAP
package.
The steps required to read information from an LDAP server are:
declare Directory : LDAP.Client.Directory; begin Directory := LDAP.Client.Init (Host);
Host is the hostname where the LDAP directory is running. It is
possible to specify the port if the LDAP server does not use the
default one.
LDAP.Client.Bind (Directory, "", "");
Response_Set := LDAP.Client.Search (Directory, Base_DN, Filter, LDAP.Client.LDAP_Scope_Subtree, LDAP.Client.Attributes ("cn", "sn", "telephonenumber"));
For more information see AWS.LDAP.Client.
First_Entry
/Next_Entry
or the
generic high level iterator For_Every_Entry
.
declare Message : LDAP.Client.LDAP_Message; begin Message := LDAP.Client.First_Entry (Directory, Response_Set); while Message /= LDAP.Client.Null_LDAP_Message loop Do_Job (Message); Message := LDAP.Client.Next_Entry (Directory, Message); end loop; end;
First_Attribute
/ Next_Attribute
or the generic high level iterator For_Every_Attribute
.
declare BER : aliased LDAP.Client.BER_Element; Attr : constant String := LDAP.Client.First_Attribute (Directory, Message, BER'Unchecked_Access); begin Do_Job (Attr); loop declare Attr : constant String := LDAP.Client.Next_Attribute (Directory, Message, BER); begin exit when Attr = ""; Do_Job (Attr); end; end loop; end;
LDAP.Client.Free (Message); LDAP.Client.Unbind (Directory);
see AWS.LDAP.Client for all high level supported API and documentation.
Note that for complete information about AWS/LDAP
you you should read
an LDAP API description. AWS/LDAP
is only a binding and follow the
LDAP API closely.
AWS
support part of the Jabber protocol. At this stage only two kind
of messages are supported:
To check the presence status of a specific JID (Jabber ID)
To send messages to a specific JID (Jabber ID)
Note that if you want an application to check the presence or send message to users it is recommended to create a specific Jabber ID on the server for this application and ask users to accept this specific user to check their presence status.
To check for the presence of another JID you must first have the right to do so. The jabber server won't let you see presence of another JID unless the JID have permitted you to see its presence.
Server : AWS.Jabber.Server; Status : AWS.Jabber.Presence_Status;
AWS.Jabber.Connect (Server, "jabber.domain.org", "joe", "mysuperpwd");
AWS.Jabber.Check_Presence (Server, "john@jabber.domain.org", Status);
AWS.Jabber.Close (Server);
To send a message to a specific JID, you must connect to the server as above and close the server when you don't need to communicate with it anymore. The only different part is to send the message, here is an example:
Send_Message (Server, JID => "john@jabber.domain.org", Subject => "Hello there!", Content => "Are you using AWS ?");
It is as simple as that !
AWS
support embedded resources. It means that it is possible to build
a fully self dependent executable. This is useful when distributing a
server. The server program contains the code but also the images (PNG
,
JPEG
, GIF
), the templates, the HTML
pages... more
generally any file the Web Server must serve to clients.
To embbed the files into the executable you must build a resource tree. This task is greatly simplified using AWSRes tool. For example let's say that you want to build a simple server with a single page containing some text and one PNG image. The text is handled directly in the callback procedure and contain a reference to the image logo.png. To build the resource tree:
$ awsres logo.png
This will create a set of packages whose root is the unit res
by
default. The resource tree is created. see awsres tool for the complete
AWS's usage description.
awsres
can also compress the resource files. This can be done
by using awsres
's -z
option. Compressed resources are
handled transparently. If the Web client supports compression the
resource is sent as-is otherwise a decompression stream will be
created for the resource to be decompressed on-the-fly while sending it.
This is really the simplest step. The resource tree must be linked
with your executable, to do so you just have to “with” the
resource tree root into one of your program unit. This will ensure
that the resource tree will be compiled and linked into the
executable. AWS
and Templates_Parser know
about resource
files and will pick them up if available.
Note that this is transparent to users. It is possible to build the very same server based on standard files or resources files. The only change in the code is to “with” or not the resource tree.
Note that AWS
supports only a single resource tree. If more
than one resource tree is included into a program only one will be
seen.
Users can build a response directly from a stream. In this case the
callback answer is built using AWS.Response.Stream
. It creates a
resource object whose operations have been inherited from
AWS.Resource.Stream.Stream_Type
and redefined by the user. So
the Read
operation can dynamically create the result stream
data, the End_Of_File
operation must returns True
when the
stream data is out and so on. This feature is useful to let users completely
create and control dynamically AWS
's response content.
AWSRes
is a tool to build resource files. It creates a root package
named res by default and a child package for each resource
file.
Usage: awsres [-hrquz] file1 [-uz] [file2...]
-h
-q
-r name
res
.
-u
-z
The status page gives information about the AWS
internal status. For
example it returns the server socket ID, the number of simultaneous
connection, the number of time a connection has been used...
To display the information AWS
use a template file. The
template file (default is aws_status.thtml) is an HTML
file with
some specific tags recognized by the parser. For more information
about how the template parser works, please look for the template
parser documentation distributed with AWS
.
Here are the tag variables recognized by AWS
status page:
HTML
"<td>" tag. This is to be able to display the key/value in column.
HTML
tag. This is the name of
the AWS
logo image.
NONE
, DAILY
,
MONTHLY
or EACH_RUN
.
HTML
"<td>" tag. This is to be able to display the key/value in column.
AWS
version string.
There is also all Templates_Parser
specific tags. This is not listed
here please have a look at the Templates_Parser
documentation
distributed with AWS
.
Here is a list of documents used to implement AWS
, the
SOAP
support and associated services:
SIMPLE MAIL TRANSFER PROTOCOL Jonathan B. Postel August 1982 Information Sciences Institute University of Southern California 4676 Admiralty Way Marina del Rey, California 90291
Network Working Group E. Nebel Request For Comments: 1867 L. Masinter Category: Experimental Xerox Corporation November 1995 Form-based File Upload in HTML
Network Working Group J. Myers Request for Comments: 1939 Carnegie Mellon STD: 53 M. Rose Obsoletes: 1725 Dover Beach Consulting, Inc. Category: Standards Track May 1996 Post Office Protocol - Version 3
Network Working Group T. Berners-Lee Request for Comments: 1945 MIT/LCS Category: Informational R. Fielding UC Irvine H. Frystyk MIT/LCS May 1996 Hypertext Transfer Protocol -- HTTP/1.0
Network Working Group N. Freed Request for Comments: 2049 Innosoft Obsoletes: 1521, 1522, 1590 N. Borenstein Category: Standards Track First Virtual November 1996 Multipurpose Internet Mail Extensions (MIME) Part Five: Conformance Criteria and Examples
Network Working Group D. Kristol Request for Comments: 2109 Bell Laboratories, Lucent Technologies Category: Standards Track L. Montulli Netscape Communications February 1997 HTTP State Management Mechanism
Network Working Group J. Klensin Request for Comments: 2195 R. Catoe Category: Standards Track P. Krumviede Obsoletes: 2095 MCI September 1997 IMAP/POP AUTHorize Extension for Simple Challenge/Response
Network Working Group J. Myers Request for Comments: 2554 Netscape Communications Category: Standards Track March 1999 SMTP Service Extension for Authentication
Network Working Group R. Fielding Request for Comments: 2616 UC Irvine Obsoletes: 2068 J. Gettys Category: Standards Track Compaq/W3C J. Mogul Compaq H. Frystyk W3C/MIT L. Masinter Xerox P. Leach Microsoft T. Berners-Lee W3C/MIT June 1999 Hypertext Transfer Protocol -- HTTP/1.1
Network Working Group J. Franks Request for Comments: 2617 Northwestern University Obsoletes: 2069 P. Hallam-Baker Category: Standards Track Verisign, Inc. J. Hostetler AbiSource, Inc. S. Lawrence Agranat Systems, Inc. P. Leach Microsoft Corporation A. Luotonen Netscape Communications Corporation L. Stewart Open Market, Inc. June 1999 HTTP Authentication: Basic and Digest Access Authentication
Transport Layer Security Working Group Alan O. Freier INTERNET-DRAFT Netscape Communications Expire in six months Philip Karlton Netscape Communications Paul C. Kocher Independent Consultant November 18, 1996 The SSL Protocol Version 3.0
Simple Object Access Protocol (SOAP) 1.1 W3C Note 08 May 2000 This version: http://www.w3.org/TR/2000/NOTE-SOAP-20000508 Latest version: http://www.w3.org/TR/SOAP Authors (alphabetically): Don Box, DevelopMentor David Ehnebuske, IBM Gopal Kakivaya, Microsoft Andrew Layman, Microsoft Noah Mendelsohn, Lotus Development Corp. Henrik Frystyk Nielsen, Microsoft Satish Thatte, Microsoft Dave Winer, UserLand Software, Inc. Copyright© 2000 DevelopMentor, International Business Machines Corporation, Lotus Development Corporation, Microsoft, UserLand Software
By Dave Winer, Jake Savin, UserLand Software, 4/2/01.
AWS User's API:
SOAP User's API: