Michael Erdmann
November 7, 1999 19:26
This article describes a small scale server framework which is based upon Posix-Bindings for Ada 95. This work is part of the ACDE project.
The server is based upon two data types. The Server Object and the Agent Object. The Server Object represents the server process listening on a certain port for socket connections to be accepted. Every time a socket is accepted a new Agent thread is started which processes incoming data until it terminates or the client releases the connection. The Agent process operates on an instance of the Agent data type.
This package contains only the data type Object for the agent, the interface of abstract procedures needed for the processing of incoming data and a simple output queue. Besides of the Initialize/Finalize procedures the implementation of an agent has to provide a Process procedure. This procedure is called every time a package is received from the socket. This procedure may put one or more reply records in an output queue which is associated with the agent.
This class provides the following Interface:
This package implements the server framework. Upon initializing an Server.Object instance, the main dispatcher process is started, which listens on the specified port for incoming connection requests. Upon reception of an connection request a new instance of an agent process is created which listens on the client connection and a new instance of the agent data is created by calling the Create procedure of the Agent class.
The following Methods are provided by this package:
The server framework may be used by simply defining a single package. The following basic packages are needed for the implementation of a server.
with ASCL.SV.Agent; with ASCL.SV.Server; use ASCL.SV;
The function of the server will be to reply to every record received by simply sending it back. First of all the data required for the agents function has to be derived from the abstract agent type. In our very simple case no data is required:
type Reply_Agent is new Agent.Object with record ...... end record;
procedure Initialize( O : in out Reply_Agent ); procedure Finalize( O : in out Reply_Agent );
The method Process has to be supplied in order to process the packages received from the client socket. The procedure Initialize and Finalize have to be provided as well. They are called by the server frame work it self.
function Process( O : in Reply_Agent; Data : in String)
Additional a function has to be implemented which allows the server to create a new instance of the Agent when an incoming connection is received by the server.
type Object is new Server.Object with null record; function Create( O : in Object ) return Agent.Reference;
with ASCL.SV.Agent; with ASCL.SV.Server; with Ada.Streams; use Ada.Streams;
package Test.Server is type Object is new ASCL.SV.Server.Object with null record; function Create( O : in Object ) return ASCL.SV.Agent.Reference;
type Session is new ASCL.SV.Agent.Object with null record;
procedure Initialize( O : in out Session ); procedure Finalize( O : in out Session );
procedure Process( O : in out Session; Data : in String );
end Test.Server;
The implementation of the agent requires the same basic packages as the declaration. The Initialize/Finalize is more or less a dummy procedure because nothing has to be initialized.
with Ada.Text_IO; use Ada.Text_IO; with ASCL.SV.Agent; use ASCL.SV.Agent;
package body Test.Server is
procedure Initialize( O : in out Session ) is begin Put_Line( "Agent Initialize"); end Initialize;
procedure Finalize( O : in out Session ) is begin Put_Line("Finalize"); end Finalize;
The procedure Create is used to create an instance of the agent data. It is not intended for initialization of the agents data. This is always a task to the Initialize Procedure.
function Create( O : in Object ) return ASCL.SV.Agent.Reference is Result : ASCL.SV.Agent.Reference := new Session; begin return Result; end Create;
The working function Process every time called, when the agent receives a data package. Currently this package is assumed to be an array of Character (String). Our working function simply places the input String in the output queue of the agent process.
procedure Process( O : in out Session; Data : in String ) is begin Put( O, Data ); end Process;
This is only a simplified example, i.e. the working function could output as well multiple packages to the client using the Put procedure.
In the current implementation, the Agent is not able to change the agents instance data.
The functionality of the framework depends on the quality of the Posix-Bindings used. In the Forrest Library 270399 contains several errors in the Posix.Sockets package which have to be fixed.
Please be aware, that this packge has been tested within a very limited test scope. Further enhancements will be for sure.
Component |
Decription |
Version |
Comment |
---|---|---|---|
Linux |
SuSe Distribution |
6.0 |
|
Forrest |
Posix Bindings for Ada |
270399 |
The example below implements the above described server. It consists of the following files:
test-server.ads - Agent Implementation
test-server.adb
mainserver.adb - Main program of the server.
It can be tested on a Linux System by:
$ mainserver &
$ telnet localhost 3000
***** test-server.ads ******
with ASCL.SV.Agent; with ASCL.SV.Server;
package Test.Server is
type Object is new ASCL.SV.Server.Object with null record;
function Create( O : in Object ) return ASCL.SV.Agent.Reference;
type Session is new ASCL.SV.Agent.Object with null record;
procedure Initialize( O : in out Session ); procedure Finalize( O : in out Session );
procedure Process( O : in out Session; Data : in String );
end Test.Server;
***** test-server.adb ****
with Ada.Text_IO; use Ada.Text_IO; with ASCL.SV.Agent; use ASCL.SV.Agent;
package body Test.Server is
procedure Initialize( O : in out Session ) is begin Put_Line( "Agent Initialize"); end Initialize;
procedure Finalize( O : in out Session ) is begin Put_Line("Finalize"); end Finalize;
function Create( O : in Object ) return ASCL.SV.Agent.Reference is Result : ASCL.SV.Agent.Reference := new Session; begin return Result; end Create;
procedure Process( O : in out Session; Data : in String ) is begin Put( O, Data ); end Process;
end Test.Server;
***** mainserver.adb *****
with Test.Server; with ASCL.SV.Server; use ASCL.SV.Server; with ASCL.Debugging_Support; use ASCL.Debugging_Support;
use ASCL.SV; use ASCL;
with Test.Debug; use Test.Debug; with Test.Persistant; use Test.Persistant; use Test;
procedure MainServer is P : ASCL.SV.Server.Reference := new Test.Server.Object( 3000 ); D : Debugging_Support.Handle := new Debug.Object; begin
Initialize( D.all ); Set( D.all, Verbose );
Initialize( P, 0, D );
loop delay 20.0; end loop;
Finalize( P );
end MainServer;
type Object( Port : Positive ) is abstract tagged private; type Reference is access all Object'Class;
procedure Initialize( O : in Reference; Port : in Integer := 0 ); procedure Finalize( O : in Reference );
function Create( O : in Object ) return Agent.Reference is abstract; procedure Delete( A : Agent.Object'Class ) is abstract;
private
.....
end ASCL.SV.Server;
with Ada.Streams; use Ada.Streams;
package ASCL.SV.Agent is
type Object is abstract tagged null record; type Reference is access all Object'Class;
procedure Put( This : in out Object'Class; data : in String ); function Get( This : in out Object'Class ) return String; function Data_Available( This : in Object'Class ) return Boolean;
procedure Initialize( O : in out Object) is abstract; procedure Finalize( O : in out Object ) is abstract;
procedure Process( O : in Object; Data : in String ) is abstract
End_Of_Processing : exception;
end ASCL.SV.Agent;