Java 2 Ada - Tag Tutorial2023-10-01T16:33:41+00:00Stephane Carrezurn:md5:d12e23c53b2436d6becce3d51ddbdf38AWAUnlocking the Power of OpenAI in Ada programsurn:md5:689bff7708f38b8dc2fdd5cf1d282af22023-10-01T16:33:41+00:002023-10-01T16:33:41+00:00Stephane CarrezAdaOpenAITutorial
<div class="post-text"><h5>[Ada/openai-img.jpg](Ada/openai-img.jpg)</h5><p>The <a href="OpenAI">OpenAI</a>(https://openai.com/) provides a service that can be queried using REST requests. The service API can be classified as follows:</p><ul><li>OpenAI's GPT (generative pre-trained transformer) is the well-known text generation based on text inputs.</li><pre><code> It gives access to several AI models that have been trained to understand natural language and code.
</code></pre><li>Image generation gives access to the <a href="DALL.E">DALL.E</a>(https://platform.openai.com/docs/models/dall-e) for both</li><pre><code> generating images based on a text description, or, modify an existing image based on a text description.
</code></pre><li>Speech to text gives access to the <a href="Whisper model">Whisper model</a>(https://openai.com/research/whisper) that converts</li><pre><code> audio records into text (several languages are supported).
</code></pre><li>OpenAIâs text embeddings measure the relatedness of text strings.</li></ul><p>For a detailed description of these API have a look at the <a href="OpenAI API Introduction">OpenAI API Introduction</a>(https://platform.openai.com/docs/introduction) document. To use these APIs, you'll first need to register on their service and get an API key that will grant access to the operations.</p><p>The library was generated by the <a href="OpenAPI">OpenAPI</a>(https://github.com/OpenAPITools/openapi-generator) code generator from the OpenAPI description of the OpenAI service and it uses the <a href="OpenAPI Ada">OpenAPI Ada</a>(https://github.com/stcarrez/swagger-ada) library to make HTTP requests and perform JSON serialization and de-serialization. Each OpenAI operation is made available through an Ada procedure which takes care of building the request that was filled within some Ada record, submitting it to the OpenAI server and extract the response in another Ada record. Every request submitted to the OpenAI server is therefore strongly typed!</p><ol><li><ol><li>Setup</li></ol></li></ol><p>To use the library, you should use Alire to setup your project and use:</p><p>``` alr index <del>add git+https://gitlab.com/stcarrez/awa-alire-index.git </del>name awa alr with openai ```</p><p>For the HTTP connection, you can either use <a href="AWS">AWS</a>(https://github.com/AdaCore/aws) or <a href="curl">curl</a>(https://curl.se/) and run one of the following commands:</p><p>``` alr with utilada_curl alr with utilada_aws ```</p><ol><li><ol><li>Initialization</li></ol></li></ol><p>First, make sure you import at least the following Ada packages:</p><p>``` with Util.Http.Clients.Curl; -- replace Curl with AWS if needed with OpenAPI.Credentials.OAuth; with OpenAI.Clients; with OpenAI.Models; ```</p><p>If you want to use <a href="curl">curl</a>(https://curl.se/), the initialization should use the following:</p><p>``` Util.Http.Clients.Curl.Register; ```</p><p>But if you want to use <a href="AWS">AWS</a>(https://github.com/AdaCore/aws), you will initialize with:</p><p>``` Util.Http.Clients.AWS.Register; ```</p><p>After the initialization is done, you will declare the `OpenAI` client instance to access the API operations. The <a href="OpenAI">OpenAI</a>(https://openai.com/) service uses an OAuth bearer API key to authenticate requests made on the server. We will need an `OAuth2_Credential_Type` instance represented by `Cred` below.</p><p>``` Cred : aliased OpenAPI.Credentials.OAuth.OAuth2_Credential_Type; Client : OpenAI.Clients.Client_Type; ```</p><ol><li><ol><li>Credential setup</li></ol></li></ol><p>For the credential setup you will first need to get your access key from your account. Once you have your key as a `String`, you can configure the `Cred` object and tell the client connection entry point which credentials to use:</p><p>``` Api_Key : constant String := ...;</p><pre><code> Cred.Bearer_Token (Api_Key);
Client.Set_Credentials (Cred'Unchecked_Access);
</code></pre><p>```</p><ol><li><ol><li>OpenAPI client setup</li></ol></li></ol><p>The last step necessary before you can make requests, is to setup the server base URL to connect for the REST requests:</p><p>```</p><pre><code> Client.Set_Server ("https://api.openai.com/v1");
</code></pre><p>```</p><ol><li><ol><li>API for chat</li></ol></li></ol><p>The `Create_Chat` is the main operation for the conversation chat generation. The request is represented by the `ChatRequest_Type` type which describes the OpenAI chat model that must be used and the query parameters. The request can be filled with a complete conversation chat which means it is possible to call it several times with previous queries and responses to proceed in the chat conversation.</p><p>``` C : OpenAI.Clients.Client_Type; Req : OpenAI.Models.ChatRequest_Type; Reply : OpenAI.Models.ChatResponse_Type; ...</p><pre><code> Req.Model := OpenAPI.To_UString ("gpt-3.5-turbo");
Req.Messages.Append ((Role => Openapi.To_Ustring ("user"),
Content => Prompt,
others => <>));
Req.Temperature := 0.3;
Req.Max_Tokens := (Is_Null => False, Value => 1000);
Req.Top_P := 1.0;
Req.Frequency_Penalty := 0.0;
Req.Presence_Penalty := 0.0;
C.Create_Chat (Req, Reply);
</code></pre><p>```</p><p>Upon successful completion, we get a list of choices in the reply that contains the text of the conversation. You can iterate over the list with the following code extract. Beware that the returned string is using UTF-8 encoding and it may need a conversion to a `Wide_Wide_String` if necessary.</p><p>```</p><pre><code> for Choice of Reply.Choices loop
declare
Utf8 : constant String := OpenAPI.To_String (Choice.Message.Content);
begin
Put_Line (Ada.Strings.UTF_Encoding.Wide_Wide_Strings.Decode (Utf8));
end;
end loop;
</code></pre><p>```</p><p>The complete chat example is available at: <a href="OpenAI Chat">OpenAI Chat</a>(https://gitlab.com/stcarrez/openai-chat) .</p><ol><li><ol><li>Generating an image</li></ol></li></ol><p>The image generation is based on the <a href="DALL.E">DALL.E</a>(https://platform.openai.com/docs/models/dall-e) model generator. The creation is made by populating a request object, making the call (which is an HTTP POST) and getting the result in a response. Both request and response are represented by full Ada types and are strongly typed:</p><p>The request contains a prompt string which must be provided and is the textual description of the image to create. An optional parameter allows to control the number of images which are created (from 1 to 10). Another optional parameter controls the dimension of the final image. The OpenAI API limits the possible values to: `256x256`, `512x512` and `1024x1024`. The image creation is represented by the `Create_Image` procedure and we can call it with our request instance:</p><p>``` Req : OpenAI.Models.CreateImagesRequest_Type; Reply : OpenAI.Models.ImagesResponse_Type; ...</p><pre><code> Req.Prompt := Prompt;
Req.N := (Is_Null => False, Value => 3);
Req.Size := (Is_Null => False, Value => "512x512");
C.Create_Image (Req, Reply);
</code></pre><p>```</p><p>Once it has finished, it produces a response which basically contains a list of URLs for each generated image.</p><p>```</p><pre><code> for Url of Reply.Data loop
if not Url.Url.Is_Null then
Ada.Text_IO.Put_Line (OpenAPI.To_String (Url.Url.Value));
end if;
end loop;
</code></pre><p>```</p><p>The complete image generation example is available at: <a href="OpenAI Image Generation">OpenAI Image Generation</a>(https://gitlab.com/stcarrez/openai-image) .</p><p>For more information about the image creation, have a look at the <a href="OpenAI Images API reference">OpenAI Images API reference</a>(https://platform.openai.com/docs/api-reference/images).</p><ol><li><ol><li>Conclusion</li></ol></li></ol><p>The <a href="Ada OpenAI">Ada OpenAI</a>(https://gitlab.com/stcarrez/ada-openai) library is eagerly awaiting your Ada programming creativity. You can try using the chat generation example to ask the AI to generate some Ada code, but you'll likely be disappointed by the poor quality of Ada programs that <a href="OpenAI">OpenAI</a>(https://openai.com/) generates. However, you may still find some small benefits in using it across different domains. I encourage you to give it a try for your enjoyment and pleasure.</p></div> IO stream composition and serialization with Ada Utility Libraryurn:md5:94a130493002050aa31349cb63f025f32022-03-05T22:48:00+00:002022-03-05T22:48:00+00:00Stephane CarrezTutorialAdaXMLJSONStreamlzmapipe
<div class="post-text"><p>To be able to provide this IO stream combination, the <a href="Ada Utility Library">Ada Utility Library</a>(https://github.com/stcarrez/ada-util) defines two Ada types: the `Input_Stream` and the `Output_Stream` limited interfaces. The `Input_Stream` interface only defines a `Read` procedure and the `Output_Stream` interface defines the `Write`, `Flush` and `Close` procedures. By implementing these interfaces, it is possible to provide stream objects that can be combined together.</p><h5>[IO Stream Composition and Serialization](Ada/ada-util-streams.png)</h5><p>The <a href="Ada Utility Library">Ada Utility Library</a>(https://github.com/stcarrez/ada-util) provides stream types that implement these interfaces so that it is possible to read or write files, sockets and system pipes. In many cases, the concrete type implements both interfaces so that reading or writing is possible. This is the case for `File_Stream` which allows to read or write on a file, the `Socket_Stream` which handles sockets by using GNAT sockets. The `Pipe_Stream` on its side allows to launch an external program and either read its output, available through the `Input_Stream`, or feed the external program with some input by using the `Output_Stream`.</p><p>The <a href="Ada Utility Library">Ada Utility Library</a>(https://github.com/stcarrez/ada-util) also provides stream objects that make transformation on the data through various data encoders. The Ada library supports the following encoders:</p><ul><li>Base 16, Base 64,</li><li>AES encryption or decryption,</li><li>LZMA compression or decompression</li></ul><p>Other encoders could be added and it is always possible to provide custom transformations by implementing the `Input_Stream` and `Output_Stream` interfaces.</p><p>The last part that completes the IO stream framework is the serialization framework. That framework defines and provides interface and types to read or write a CSV, XML, JSON or HTTP form stream. The serialization framework uses either the `Input_Stream` or the `Output_Stream` interfaces to either read or write the content. The serialization framework defines operations in a way that allows to read or write these streams independently of their representation format.</p><ol><li><ol><li>LZMA Compression</li></ol></li></ol><p>Let's have a look at compressing a file by using the `Util.Streams` framework. First we need a `File_Stream` that is configured to read the file to compress and we need another `File_Stream` configured for writing to save in another file. The first file is opened by using the `Open` procedure and the `In_File` mode while the second one is using `Create` and the `Out_File` mode. The `File_Stream` is using the Ada `Stream_IO` standard package to access files.</p><p>```Ada with Util.Streams.Files;</p><pre><code> In_Stream : aliased Util.Streams.Files.File_Stream;
Out_Stream : aliased Util.Streams.Files.File_Stream;
</code></pre><pre><code> In_Stream.Open (Mode => Ada.Streams.Stream_IO.In_File, Name => Source);
Out_Stream.Create (Mode => Ada.Streams.Stream_IO.Out_File, Name => Destination);
</code></pre><p>```</p><p>In the middle of these two streams, we are going to use a `Compress_Stream` whose job is to compress the data and write the compressed result to a target stream. The compression stream is configured by using the `Initialize` procedure and it is configured to write on the `Out_Stream` file stream. The compression stream needs a buffer and its size is configured with the `Size` parameter.</p><p>```Ada with Util.Streams.Lzma;</p><pre><code> Compressor : aliased Util.Streams.Lzma.Compress_Stream;
</code></pre><pre><code> Compressor.Initialize (Output => Out_Stream'Unchecked_Access, Size => 32768);
</code></pre><p>```</p><p>To feed the compressor stream with the input file, we are going to use the `Copy` procedure. This procedure reads the content from the `In_Stream` and writes what is read to the `Compressor` stream.</p><p>```Ada</p><pre><code> Util.Streams.Copy (From => In_Stream, Into => Compressor);
</code></pre><p>```</p><p>Flushing and closing the files is automatically handled by a `Finalize` procedure on the `File_Stream` type.</p><p>Complete source example: <a href="compress.adb">compress.adb</a>(https://github.com/stcarrez/ada-util/tree/master/samples/compress.adb)</p><ol><li><ol><li>LZMA Decompression</li></ol></li></ol><p>The LZMA decompression is very close to the LZMA compression but instead it uses the `Decompress_Stream`. The complete decompression method is the following:</p><p>```Ada procedure Decompress_File (Source : in String;</p><pre><code> Destination : in String) is
In_Stream : aliased Util.Streams.Files.File_Stream;
Out_Stream : aliased Util.Streams.Files.File_Stream;
Decompressor : aliased Util.Streams.Lzma.Decompress_Stream;
</code></pre><p>begin</p><pre><code> In_Stream.Open (Mode => Ada.Streams.Stream_IO.In_File, Name => Source);
Out_Stream.Create (Mode => Ada.Streams.Stream_IO.Out_File, Name => Destination);
Decompressor.Initialize (Input => In_Stream'Unchecked_Access, Size => 32768);
Util.Streams.Copy (From => Decompressor, Into => Out_Stream);
</code></pre><p>end Decompress_File; ```</p><p>Complete source example: <a href="decompress.adb">decompress.adb</a>(https://github.com/stcarrez/ada-util/tree/master/samples/decompress.adb)</p><ol><li><ol><li>AES Encryption</li></ol></li></ol><p>Encryption is a little bit more complex due to the encryption key that must be configured. The encryption is provided by the `Encoding_Stream` and it uses a `Secret_Key` to configure the encryption key. The `Secret_Key` is a limited type and it cannot be copied. To build the encryption key, one method consists in using the PBKDF2 algorithm described in <a href="RFC 8018">RFC 8018</a>(https://tools.ietf.org/html/rfc8018). The user password is passed to the PBKDF2 algorithm configured to use the HMAC-256 hashing. The hash method is called on itself 20000 times in this example to produce the final encryption key.</p><p>```Ada with Util.Streams.AES; with Util.Encoders.AES; with Util.Encoders.KDF.PBKDF2_HMAC_SHA256;</p><pre><code> Cipher : aliased Util.Streams.AES.Encoding_Stream;
Password_Key : constant Util.Encoders.Secret_Key := Util.Encoders.Create (Password);
Salt : constant Util.Encoders.Secret_Key := Util.Encoders.Create ("fake-salt");
Key : Util.Encoders.Secret_Key (Length => Util.Encoders.AES.AES_256_Length);
...
PBKDF2_HMAC_SHA256 (Password => Password_Key,
Salt => Salt,
Counter => 20000,
Result => Key);
</code></pre><p>```</p><p>The encoding stream is able to produce or consume another stream. For the encryption, we are going to use the first mode and use the `Produces` procedure to configure the encryption to write on the `Out_Stream` file. Once configured, the `Set_Key` procedure must be called with the encryption key and the encryption method. The initial encryption `IV` vector can be configured by using the `Set_IV` procedure (not used by the example). As soon as the encryption key is configured, the encryption can start and the `Cipher` encoding stream can be used as an `Output_Stream`: we can write on it and it will encrypt the content before passing the result to the next stream. This means that we can use the same `Copy` procedure to read the input file and pass it through the encryption encoder.</p><p>```Ada</p><pre><code> Cipher.Produces (Output => Out_Stream'Unchecked_Access, Size => 32768);
Cipher.Set_Key (Secret => Key, Mode => Util.Encoders.AES.ECB);
Util.Streams.Copy (From => In_Stream, Into => Cipher);
</code></pre><p>```</p><p>Complete source example: <a href="encrypt.adb">encrypt.adb</a>(https://github.com/stcarrez/ada-util/tree/master/samples/encrypt.adb)</p><ol><li><ol><li>AES Decryption</li></ol></li></ol><p>Decryption is similar but it uses the `Decoding_Stream` type. Below is the complete example to decrypt the file:</p><p>```Ada procedure Decrypt_File (Source : in String;</p><pre><code> Destination : in String;
Password : in String) is
In_Stream : aliased Util.Streams.Files.File_Stream;
Out_Stream : aliased Util.Streams.Files.File_Stream;
Decipher : aliased Util.Streams.AES.Decoding_Stream;
Password_Key : constant Util.Encoders.Secret_Key := Util.Encoders.Create (Password);
Salt : constant Util.Encoders.Secret_Key := Util.Encoders.Create ("fake-salt");
Key : Util.Encoders.Secret_Key (Length => Util.Encoders.AES.AES_256_Length);
</code></pre><p>begin</p><pre><code> -- Generate a derived key from the password.
PBKDF2_HMAC_SHA256 (Password => Password_Key,
Salt => Salt,
Counter => 20000,
Result => Key);
</code></pre><pre><code> -- Setup file -> input and cipher -> output file streams.
In_Stream.Open (Ada.Streams.Stream_IO.In_File, Source);
Out_Stream.Create (Mode => Ada.Streams.Stream_IO.Out_File, Name => Destination);
Decipher.Produces (Output => Out_Stream'Access, Size => 32768);
Decipher.Set_Key (Secret => Key, Mode => Util.Encoders.AES.ECB);
</code></pre><pre><code> -- Copy input to output through the cipher.
Util.Streams.Copy (From => In_Stream, Into => Decipher);
</code></pre><p>end Decrypt_File; ```</p><p>Complete source example: <a href="decrypt.adb">decrypt.adb</a>(https://github.com/stcarrez/ada-util/tree/master/samples/decrypt.adb)</p><ol><li><ol><li>Stream composition: LZMA > AES</li></ol></li></ol><p>Now, if we want to compress the stream before encryption, we can do this by connecting the `Compressor` to the `Cipher` stream and we only have to use the `Compressor` instead of the `Cipher` in the call to `Copy`.</p><p>```Ada</p><pre><code> In_Stream.Open (Ada.Streams.Stream_IO.In_File, Source);
Out_Stream.Create (Mode => Ada.Streams.Stream_IO.Out_File, Name => Destination);
Cipher.Produces (Output => Out_Stream'Unchecked_Access, Size => 32768);
Cipher.Set_Key (Secret => Key, Mode => Util.Encoders.AES.ECB);
Compressor.Initialize (Output => Cipher'Unchecked_Access, Size => 4096);
</code></pre><pre><code> Util.Streams.Copy (From => In_Stream, Into => Compressor);
</code></pre><p>```</p><p>When `Copy` is called, the following will happen:</p><ul><li>first, it reads the `In_Stream` source file,</li><li>the data is written to the `Compress` stream,</li><li>the `Compressor` stream runs the LZMA compression and writes on the `Cipher` stream,</li><li>the `Cipher` stream encrypts the data and writes on the `Out_Stream`,</li><li>the `Out_Stream` writes on the destination file.</li></ul><p>Complete source example: <a href="lzma_encrypt.adb">lzma_encrypt.adb</a>(https://github.com/stcarrez/ada-util/tree/master/samples/lzma_encrypt.adb)</p><ol><li><ol><li>More stream composition: LZMA > AES > Base64</li></ol></li></ol><p>We can easily change the stream composition to encode in Base64 after the encryption. We only have to declare an instance of the Base64 `Encoding_Stream` and configure the encryption stream to write on the Base64 stream instead of the output file. The Base64 stream is configured to write on the output stream.</p><p>```Ada In_Stream : aliased Util.Streams.Files.File_Stream; Out_Stream : aliased Util.Streams.Files.File_Stream; Base64 : aliased Util.Streams.Base64.Encoding_Stream; Cipher : aliased Util.Streams.AES.Encoding_Stream; Compressor : aliased Util.Streams.Lzma.Compress_Stream;</p><pre><code> In_Stream.Open (Ada.Streams.Stream_IO.In_File, Source);
Out_Stream.Create (Mode => Ada.Streams.Stream_IO.Out_File, Name => Destination);
Base64.Produces (Output => Out_Stream'Unchecked_Access, Size => 32768);
Cipher.Produces (Output => Base64'Unchecked_Access, Size => 32768);
Cipher.Set_Key (Secret => Key, Mode => Util.Encoders.AES.ECB);
Compressor.Initialize (Output => Cipher'Unchecked_Access, Size => 4096);
</code></pre><p>```</p><p>Complete source example: <a href="lzma_encrypt_b64.adb">lzma_encrypt_b64.adb</a>(https://github.com/stcarrez/ada-util/tree/master/samples/lzma_encrypt_b64.adb)</p><ol><li><ol><li>Serialization</li></ol></li></ol><p>Serialization is achieved by using the `Util.Serialize.IO` packages and child packages and their specific types. The parent package defines the limited `Output_Stream` interface which inherit from the `Util.Streams.Output_Stream` interface. This allows to define specific operations to write various Ada types but also it provides common set of abstractions that allow to write either a JSON, XML, CSV and FORM (`x-www-form-urlencoded`) formats.</p><p>The target format is supported by a child package so that you only have to use the `Output_Stream` type declared in one of the `JSON`, `XML`, `CSV` or `Form` child package and use it transparently. There are some constraint if you want to switch from one output format to another while keeping the same code. These constraints comes from the nature of the different formats: `XML` has a notion of entity and attribute but other formats don't differentiate entities from attributes.</p><ul><li>A `Start_Document` procedure must be called first. Not all serialization method need it but it is required for JSON to produce a correct output.</li><li>A `Write_Entity` procedure writes an XML entity of the given name. When used in JSON, it writes a JSON attribute.</li><li>A `Start_Entity` procedure prepares the start of an XML entity or a JSON structure with a given name.</li><li>A `Write_Attribute` procedure writes an XML attribute after a `Start_Entity`. When used in JSON, it writes a JSON attribute.</li><li>A `End_Entity` procedure terminates an XML entity or a JSON structure that was opened by `Start_Entity`.</li><li>At the end, the `End_Document` procedure must be called to finish correctly the output and terminate the JSON or XML content.</li></ul><p>```Ada procedure Write (Stream : in out Util.Serialize.IO.Output_Stream'Class) is begin</p><pre><code> Stream.Start_Document;
Stream.Start_Entity ("person");
Stream.Write_Entity ("name", "Harry Potter");
Stream.Write_Entity ("gender", "male");
Stream.Write_Entity ("age", 17);
Stream.End_Entity ("person");
Stream.End_Document;
</code></pre><p>end Write; ```</p><ol><li><ol><li><ol><li>JSON Serialization</li></ol></li></ol></li></ol><p>With the above `Write` procedure, if we want to produce a JSON stream, we only have to setup a JSON serializer. The JSON serializer is connected to a `Print_Stream` which provides a buffer and helper operations to write some text content. An instance of the `Print_Stream` is declared in `Output` and configured with a buffer size. The JSON serializer is then connected to it by calling the `Initialize` procedure and giving the `Output` parameter.</p><p>After writing the content, the JSON is stored in the `Output` print stream and it can be retrieved by using the `To_String` function.</p><pre><code>
</code></pre><p>```Ada with Ada.Text_IO; with Util.Serialize.IO.JSON; with Util.Streams.Texts; procedure Serialize is</p><pre><code> Output : aliased Util.Streams.Texts.Print_Stream;
Stream : Util.Serialize.IO.JSON.Output_Stream;
</code></pre><p>begin</p><pre><code> Output.Initialize (Size => 10000);
Stream.Initialize (Output => Output'Unchecked_Access);
Write (Stream);
Ada.Text_IO.Put_Line (Util.Streams.Texts.To_String (Output));
</code></pre><p>end Serialize; ```</p><p>The `Write` procedure described above produces the following JSON content:</p><p>```C {"person":{"name":"Harry Potter","gender":"male","age": 17}} ```</p><p>Complete source example: <a href="serialize.adb">serialize.adb</a>(https://github.com/stcarrez/ada-util/tree/master/samples/serialize.adb)</p><ol><li><ol><li><ol><li>XML Serialization</li></ol></li></ol></li></ol><p>Switching to an XML serialization is easy: replace `JSON` by `XML` in the package to use the XML serializer instead.</p><p>```Ada with Ada.Text_IO; with Util.Serialize.IO.XML; with Util.Streams.Texts; procedure Serialize is</p><pre><code> Output : aliased Util.Streams.Texts.Print_Stream;
Stream : Util.Serialize.IO.XML.Output_Stream;
</code></pre><p>begin</p><pre><code> Output.Initialize (Size => 10000);
Stream.Initialize (Output => Output'Unchecked_Access);
Write (Stream);
Ada.Text_IO.Put_Line (Util.Streams.Texts.To_String (Output));
</code></pre><p>end Serialize; ```</p><p>This time, the same `Write` procedure produces the following XML content:</p><p>```C <person><name>Harry Potter</name><gender>male</gender><age>17</age></person> ```</p><p>Complete source example: <a href="serialize_xml.adb">serialize_xml.adb</a>(https://github.com/stcarrez/ada-util/tree/master/samples/serialize_xml.adb)</p></div> Easy reading and writing files with Ada Utility Libraryurn:md5:3b8780a649dd29ae069d836ebd6c052e2020-08-09T20:49:00+00:002020-08-09T20:49:00+00:00Stephane CarrezAdaTutorialfiles
<div class="post-text"><p>The <a href="https://github.com/stcarrez/ada-util">Ada Utility Library</a> provides several simple operations that simplify the reading and writing of files through a single procedure call. These operations are not new since I've implemented most of them 10 years ago!!!</p><p>Reading a file line by line by using <code>Ada.Text_IO</code> is quite easy but annoying due to the fact that you have to open the file, iterate over the content getting one line at a time and then closing the file. To simplify this process, the <a href="https://github.com/stcarrez/ada-util/blob/master/src/base/files/util-files.ads">Util.Files</a> package of <a href="https://github.com/stcarrez/ada-util">Ada Utility Library</a> provides a simple <code>Read_File</code> procedure that uses a procedure as parameter and that procedure will be called for each line that is read.</p><pre><code class="lang-ada">with Util.Files;
procedure Read_Line (Line : in String) is ...;
Util.Files.Read_File (Path => "file.txt",
Process => Read_Line'Access);
</code></pre><p>Another <code>Read_File</code> procedure allows to read the file and get its content in a vector of strings so that once the file is read the vector contains each line. Yet, another <code>Read_File</code> procedure reads the file content in an <code>Unbounded_String</code>. You will use that later form as follows:</p><pre><code class="lang-ada">with Util.Files;
with Ada.Strings.Unbounded;
Content : Ada.Strings.Unbounded.Unbounded_String;
Util.Files.Read_File (Path => "file.txt",
Into => Content);
</code></pre><p>Very often it is also necessary to write some content in a file. Again, this is easy to do but a simple <code>Write_File</code> procedure that takes the file path and the content to write is easier to use in several cases:</p><pre><code class="lang-ada">with Util.Files;
Util.Files.Write_File (Path => "file.txt",
Content => "something");
</code></pre><p>The <a href="https://github.com/stcarrez/ada-util">Ada Utility Library</a> contains other useful operations and features that have helped me in implementing various projects such as <a href="https://github.com/stcarrez/ada-awa">Ada Web Application</a> and <a href="https://github.com/stcarrez/ada-keystore">Ada Keystore</a>. Have a look at the <a href="https://ada-util.readthedocs.io/en/latest/">Ada Utility Library Programmer's Guide</a>!</p></div> Ada Stemmer Libraryurn:md5:2609b15398e6015330ddfd83cb1d292a2020-05-16T07:55:00+00:002020-05-16T07:55:00+00:00Stephane CarrezAdaTutorialStemmerAnalysis
<div class="post-text"><p>Stemming is not new as it was first introduced in 1968 by <a href="https://en.wikipedia.org/wiki/Julie_Beth_Lovins">Julie Beth Lovis</a> who was a computational linguist that created the first algorithm known today as the <a href="http://snowball.tartarus.org/algorithms/lovins/stemmer.html">Lovins Stemming</a> algorithm. Her algorithm has significantly influenced other algorithms such as the <a href="https://tartarus.org/martin/PorterStemmer/">Porter Stemmer</a> algorithm which is now a common stemming algorithm for English words. These algorithms are specific to the English language and will not work for French, Greek or Russian.</p><p>To support several natural languages, it is necessary to have several algorithms. The <a href="https://snowballstem.org/">Snowball stemming algorithms</a> project provides such support through a specific string processing language, a compiler and a set of algorithms for various natural languages. The <a href="https://snowballstem.org/">Snowball compiler</a> has been adapted to generate Ada code (See <a href="https://github.com/stcarrez/snowball/tree/ada-support">Snowball Ada</a> on GitHub).</p><p>The <a href="https://github.com/stcarrez/ada-stemmer">Ada Stemmer Library</a> integrates stemming algorithms for: English, Danish, Dutch, French, German, Greek, Italian, Serbian, Spanish, Swedish, Russian. The Snowball compiler provides several other algorithms but they are not integrated yet: their integration is left as an exercise to the reader.</p><h3>Stemmer Overview</h3><p>Snowball is a small string processing language designed for creating stemming algorithms for use in Information Retrieval. A Snowball script describes a set of rules which are applied and checked on an input word or some portion of it in order to eliminate or replace some terms. The stemmer will usually transform a plural into a singular form, it will reduce the multiple forms of a verb, find the noun from an adverb and so on. <a href="https://snowballstem.org/algorithms/romance.html">Romance languages</a>, <a href="https://snowballstem.org/algorithms/germanic.html">Germanic languages</a>, <a href="https://snowballstem.org/algorithms/scandinavian.html">Scandinavian languages</a> share some common rules but each language will need its own snowball algorithm. The Snowball compiler provides a detailed list of several stemming algorithms for various natural languages. This list is available on: <a href="https://snowballstem.org/algorithms/">https://snowballstem.org/algorithms/</a></p><p><img src="/vacs/blogs/images/2601/901/default/ada-stemmer-overview.png" width="801" height="491" alt="C"></img></p><p>The Snowball compiler reads the Snowball script and generates the stemmer implementation for a given target programming language such as Ada, C, C#, Java, JavaScript, Go, Python, Rust. The <a href="https://github.com/stcarrez/ada-stemmer">Ada Stemmer Library</a> contains the generated algorithms for several natural languages. The generated stemmers are not able to recognize the natural language and it is necessary to tell the stemmer library which natural language you wish to use.</p><p>The <a href="https://github.com/stcarrez/ada-stemmer">Ada Stemmer Library</a> supports only UTF-8 strings which simplifies both the implementation and the API. The library only uses the Ada <code>String</code> type to handle strings.</p><h3>Setup</h3><p>To use the library, you should run the following commands:</p><pre><code class="lang-shell"> git clone https://github.com/stcarrez/ada-stemmer.git
cd ada-stemmer
make build install
</code></pre><p>This will fetch, compile and install the library. You can then add the following line in your GNAT project file:</p><pre><code class="lang-ada"> with "stemmer";
</code></pre><h3>Stemming examples</h3><p>Each stemmer algorithm works on a single word at a time. The <a href="https://github.com/stcarrez/ada-stemmer">Ada Stemmer Library</a> does not split words. You have to give it one word at a time to stem and it returns either the word itself or its stem. The <code>Stemmer.Factory</code> is the multi-language entry point. The stemmer algorithm is created for each call. The following simple code:</p><pre><code class="lang-ada"> with Stemmer.Factory; use Stemmer.Factory;
with Ada.Text_IO; use Ada.Text_IO;
...
Put_Line (Stem (L_FRENCH, "chienne"));
</code></pre><p>will print the string:</p><pre><code class="lang-ada"> chien
</code></pre><p>When multiple words must be stemmed, it may be better to declare the instance of the stemmer and use the same instance to stem several words. The <code>Stem_Word</code> procedure can be called with each word and it returns a boolean that indicates whether the word was stemmed or not. The result is obtained by calling the <code>Get_Result</code> function. For exemple,</p><pre><code class="lang-ada"> with Stemmer.English;
with Ada.Text_IO; use Ada.Text_IO;
..
Ctx : Stemmer.English.Context_Type;
Stemmed : Boolean;
..
Ctx.Stem_Word ("zealously", Stemmed);
if Stemmed then
Put_Line (Ctx.Get_Result);
end if;
</code></pre><h3>Integrating a new Stemming algorithm</h3><p>Integration of a new stemming algorithm is quite easy but requires to install the <a href="https://github.com/stcarrez/snowball/tree/ada-support">Snowball Ada</a> compiler.</p><pre><code class="lang-shell"> git clone --branch ada-support https://github.com/stcarrez/snowball
cd snowball
make
</code></pre><p>The Snowball compiler needs the path of the stemming algorithm, the target programming language, the name of the Ada child package that will contain the generated algorithm and the target path. For example, to generate the Lithuanian stemmer, the following command can be used:</p><pre><code class="lang-shell"> ./snowball algorithms/lithuanian.sbl -ada -P Lithuanian -o stemmer-lithuanian
</code></pre><p>You will then get two files: <code>stemmer-lithuanian.ads</code> and <code>stemmer-lithuanian.adb</code>. After integration of the generated files in your project, you can access the generated stemmer with:</p><pre><code class="lang-ada"> with Stemmer.Lithuanian;
..
Ctx : Stemmer.Lithuanian.Context_Type;
</code></pre><h3>Conclusion</h3><p>Thanks to the <a href="https://snowballstem.org/">Snowball compiler</a> and its algorithms, it is possible to do some natural language analysis. Version 1.0 of the <a href="https://github.com/stcarrez/ada-stemmer">Ada Stemmer Library</a> being available on GitHub, it is now possible to start doing some natural language analysis in Ada!</p></div> Using the Gnome and KDE Secret Service API in Adaurn:md5:f5a51944f79fb0287d4bfbd009419e282017-06-25T17:00:00+00:002017-06-25T17:00:00+00:00Stephane CarrezAdaTutorialSecurity
<div class="post-text"><p>The <a href="https://people.gnome.org/~stefw/libsecret-docs/">libsecret</a> is the C library that gives access to the <a href="https://standards.freedesktop.org/secret-service/">Secret Service API</a>. The <a href="https://github.com/stcarrez/ada-libsecret">Ada Libsecret</a> is an Ada binding for the C library. The Ada binding does not allow to access and use all of the functionalities implemented by the C library but it implements the most useful operations allowing to store, retrieve and delete some application secret data.</p><h3>Understanding the Secret Service API</h3><p>At first glance, the <a href="https://standards.freedesktop.org/secret-service/">Secret Service API</a> is not easy to use. Each secret is stored together with lookup attributes and a label. Lookup attributes are formed of key/value pairs. The label is the user friendly name that desktop key manager will use to display some information to the end user.</p><p><div class="wiki-img-center"><div class="wiki-img-inner"><img src="/images/Ada/ada-libsecret-dbus.png" longdesc="Secret Service Overview" alt="ada-libsecret-dbus.png"></img></div></div></p><p>The <a href="https://standards.freedesktop.org/secret-service/">Secret Service API</a> is implemented by a keyring manager such as <a href="https://wiki.gnome.org/Projects/GnomeKeyring/RunningDaemon">gnome-keyring-daemon</a> or kwalletd. This is a daemon that is started when a user opens a desktop session. It manages the application secrets and protects their access. The secret database can be locked in which case the access to secrets is forbidden. Unlocking is possible but requires authentication by the user (in most cases a dialog popup window opens and asks to unlock the keyring).</p><p>When a client application wishes to retrieve one of its secret, it builds the lookup attributes that correspond to the secret to retrieve. The lookup attributes are not encrypted and they are not part of the secret. The client application uses the <a href="https://en.wikipedia.org/wiki/D-Bus">D-Bus</a> IPC mechanism to ask the keyring manager for the secret. The keyring manager will manage for unlocking the database by asking the user to confirm the access. The keyring manager will then look in its database for the secret associated with the lookup attributes.</p><p>Note that the label cannot be used as a key to retrieve the secret since the same label can be associated with different lookup attributes.</p><h3>Using the Ada Secret Service API</h3><h4>Setting up the project</h4><p>After building and installing the <a href="https://github.com/stcarrez/ada-libsecret">Ada Libsecret</a> library you will add the following line to your GNAT project file:</p><pre><code class="lang-ada">with "secret";
</code></pre><p>This definition will give you access to the <code>Secret</code> package and will handle the build and link support to use the libsecret C library.</p><h4>Setting the lookup attributes</h4><p>Attributes are defined by the <code>Secret.Attributes</code> package which provides the <code>Map</code> type that represents the lookup attributes. First, you will add the following <code>with</code> clause:</p><pre><code class="lang-ada">with Secret.Attributes;
</code></pre><p>to make available the operations and types provided by the package. Then, you will declare the attributes instance by using:</p><pre><code class="lang-ada"> List : Secret.Attributes.Map;
</code></pre><p>At this stage, the lookup attributes are empty and you can check that by using the <code>Is_Null</code> function that will return <code>True</code> in that case. You must now add at least one key/value pair in the attributes by using the <code>Insert</code> procedure:</p><pre><code class="lang-ada"> List.Insert ("secret-tool", "key-password");
List.Insert ("user", "joe");
</code></pre><p>Applications are free about what attributes they use. The attributes have to be unique so that the application can identify and retrieve them. For example, the <code>svn</code> command uses two attributes to store the password to authenticate to svn remote repositories: <code>domain</code> and <code>user</code>. The <code>domain</code> represents the server URL and the <code>user</code> represents the user name to use for the connection. By using these two attributes, it is possible to store several passwords for different svn accounts.</p><h4>Storing a secret</h4><p>To store a secret, we will use the operations and types from the <code>Secret.Services</code> and <code>Secret.Values</code> packages. The following definitions:</p><pre><code class="lang-ada">with Secret.Services;
with Secret.Values;
</code></pre><p>will bring such definitions to the program. The secret service is represented by the <code>Service_Type</code> type and we will declare an instance of it as follows:</p><pre><code class="lang-ada"> Service : Secret.Services.Service_Type;
</code></pre><p>This service instance is a proxy to the <a href="https://standards.freedesktop.org/secret-service/">Secret Service API</a> and it communicates to the <code>gnome-keyring-daemon</code> by using the D-Bus protocol.</p><p>The secret value itself is represented by the <code>Secret_Type</code> and we can define and create such secret by using the <code>Create</code> function as follows:</p><pre><code class="lang-ada"> Value : Secret.Values.Secret_Type := Secret.Values.Create ("my-secret");
</code></pre><p>Storing the secret is done by the <code>Store</code> operation which associates the secret value to the lookup attributes and a label. As explained before, the lookup attributes represent the unique key to identify the secret. The label is used to give a user friendly name to the association. This label is used by the desktop password and key manager to give information to the user.</p><pre><code class="lang-ada"> Service.Store (List, "Secret tool password", Value);
</code></pre><h4>Retreiving a secret</h4><p>Retreiving a secret follows the same steps but involves using the <code>Lookup</code> function that returns the secret value from the lookup attributes. Care must be made to provide the same lookup attributes that were used during the store phase.</p><pre><code class="lang-ada"> Value : Secret.Values.Secret_Type := Service.Lookup (List);
</code></pre><p>The secret value should be checked by using the <code>Is_Null</code> function to verify that the value was found. The secret value itself is accessed by using the <code>Get_Value</code> function.</p><pre><code class="lang-ada"> if not Value.Is_Null then
Ada.Text_IO.Put_Line (Value.Get_Value);
end if;
</code></pre><h3>Conclusion</h3><p>By using the <a href="https://github.com/stcarrez/ada-libsecret">Ada Secret Service API</a>, Ada applications can now securely store private information and protect resources for their users. The API is fairly simple and can be used to store OAuth access tokens, database passwords, and more...</p><p>Read the <a href="https://github.com/stcarrez/ada-libsecret/wiki/Secret">Ada Libsecret Documentation</a> to learn more about the API.</p></div> Using the Ada Wiki Engineurn:md5:d7865ea9f436d824248bb116b420dd732016-04-30T16:07:00+00:002016-04-30T16:07:00+00:00Stephane CarrezAdaTutorialWiki
<div class="post-text"><p>The <a href="https://github.com/stcarrez/ada-wiki">Ada Wiki Engine</a> is used in two steps:</p><ol><li>The Wiki text is parsed according to its syntax to produce a Wiki Document instance.</li><li>The Wiki document is then rendered by a renderer to produce the final HTML or text.</li></ol><p>The <a href="https://github.com/stcarrez/ada-wiki">Ada Wiki Engine</a> does not manage any storage for the wiki content so that it only focuses on the parsing and rendering aspects.</p><h3>Overview</h3><p>The Ada Wiki engine is organized in several packages:</p><ul><li>Several <a href="https://github.com/stcarrez/ada-wiki/wiki/Wiki_Streams">Wiki stream</a> packages define the interface, types and operations for the Wiki</li></ul><p>engine to read the Wiki or HTML content and for the Wiki renderer to generate the HTML or text outputs.</p><ul><li>The Wiki parser is responsible for parsing HTML or Wiki content according to a</li></ul><p>selected Wiki syntax. It builds the final Wiki document through filters and plugins.</p><p><div class="wiki-img-center"><div class="wiki-img-inner"><img src="/images/Ada/ada-wiki.png" longdesc="ada-wiki.png" alt="ada-wiki.png"></img></div></div></p><ul><li>The <a href="https://github.com/stcarrez/ada-wiki/wiki/Wiki_Filters">Wiki filters</a> provides a simple filter framework that allows to plug specific</li></ul><p>filters when a Wiki document is parsed and processed. Filters are used for the table of content generation, for the HTML filtering, to collect words or links and so on.</p><ul><li>The <a href="https://github.com/stcarrez/ada-wiki/wiki/Wiki_Plugins">Wiki plugins</a> defines the plugin interface that is used by the Wiki engine</li></ul><p>to provide pluggable extensions in the Wiki. Plugins are used for the Wiki template support, to hide some Wiki text content when it is rendered or to interact with other systems.</p><ul><li>The Wiki documents and attributes are used for the representation of the Wiki</li></ul><p>document after the Wiki content is parsed.</p><ul><li>The <a href="https://github.com/stcarrez/ada-wiki/wiki/Wiki_Render">Wiki renderers</a> are the last packages which are used for the rendering of the</li></ul><p>Wiki document to produce the final HTML or text.</p><h3>Building Ada Wiki Engine</h3><p>Download the <a href="http://download.vacs.fr/ada-wiki/ada-wiki-1.0.1.tar.gz">ada-wiki-1.0.1.tar.gz</a> or get the sources from GitHub:</p><pre><code>git clone git@github.com:stcarrez/ada-wiki.git ada-wiki
</code></pre><p>If you are using <a href="https://github.com/stcarrez/ada-util">Ada Utility Library</a> then you can configure with:</p><pre><code>./configure
</code></pre><p>Otherwise, you should configure with:</p><pre><code>./configure --with-ada-util=no
</code></pre><p>Then, build the library:</p><pre><code>make
</code></pre><p>Once complete, you can install it:</p><pre><code>make install
</code></pre><p>To use the library in your Ada project, add the following line in your GNAT project file:</p><pre><code>with "wiki";
</code></pre><h3>Rendering example</h3><p>The rendering example described in this article generates an HTML or text content from a Wiki source file. The example reads the file in one of the supported Wiki syntax and produces the HTML or text. You will find the source file on GitHub in <a href="https://github.com/stcarrez/ada-wiki/blob/master/samples/render.adb">render.adb</a>. The example has the following usage:</p><pre><code>Render a wiki text file into HTML (default) or text
Usage: render [-t] [-m] [-M] [-d] [-c] [-s style] {wiki-file}
-t Render to text only
-m Render a Markdown wiki content
-M Render a Mediawiki wiki content
-d Render a Dotclear wiki content
-g Render a Google wiki content
-c Render a Creole wiki content
-s style Use the CSS style file
</code></pre><h3>Parsing a Wiki Text</h3><p>To render a Wiki text you will first need to parse the Wiki text and produce a Wiki document instance. For this you will need to declare the Wiki document instance and the Wiki parser instance:</p><pre><code class="lang-ada"> with Wiki.Documents;
with Wiki.Parsers;
...
Doc : Wiki.Documents.Document;
Engine : Wiki.Parsers.Parser;
</code></pre><p>The <a href="https://github.com/stcarrez/ada-wiki">Ada Wiki Engine</a> has a filter mechanism that is used while parsing the input and before building the target wiki document instance. Filters are chained together and a filter can do some work on the content it sees such as blocking some content (filtering), collecting some data and doing some transformation on the content. When you want to use a filter, you have to declare an instance of the corresponding filter type.</p><pre><code class="lang-ada"> with Wiki.Filters.Html;
with Wiki.Filters.Autolink;
with Wiki.Filters.TOC;
...
Filter : aliased Wiki.Filters.Html.Html_Filter_Type;
Autolink : aliased Wiki.Filters.Autolink.Autolink_Filter;
TOC : aliased Wiki.Filters.TOC.TOC_Filter;
</code></pre><p>We use the <code>Autolink</code> filter that detects links in the text and transforms them into real links. The <code>TOC</code> filter is used to collect header sections in the Wiki text and builds a table of content. The <code>Html</code> filter is used to filter HTML content that could be contained in a Wiki text. By default it ignores several HTML tags such as html, head, body, title, meta (these tags are silently discarded). Furthermore it has the ability to hide several elements such as style and script (the tag and its content is discarded).</p><p>You will then configure the Wiki engine to build the filter chain and then define the Wiki syntax that the parser must use:</p><pre><code class="lang-ada"> Engine.Add_Filter (TOC'Unchecked_Access);
Engine.Add_Filter (Autolink'Unchecked_Access);
Engine.Add_Filter (Filter'Unchecked_Access);
Engine.Set_Syntax (Syntax);
</code></pre><p>The Wiki engine gets its input from an <code>Input_Stream</code> interface that only defines a <code>Read</code> procedure. The <a href="https://github.com/stcarrez/ada-wiki">Ada Wiki Engine</a> provides several implementations of that interface, one of them is based on the Ada <code>Text_IO</code> package. This is what we are going to use:</p><pre><code class="lang-ada"> with Wiki.Streams.Text_IO;
...
Input : aliased Wiki.Streams.Text_IO.File_Input_Stream;
</code></pre><p>You will then open the input file. If the file contains UTF-8 characters, you may open it as follows:</p><pre><code class="lang-ada"> Input.Open (File_Path, "WCEM=8");
</code></pre><p>where <code>File_Path</code> is a string that represents the file's path.</p><p>Once the Wiki engine is setup and the input file opened, you can parse the Wiki text and build the Wiki document:</p><pre><code class="lang-ada"> Engine.Parse (Input'Unchecked_Access, Doc);
</code></pre><h3>Rendering a Wiki Document</h3><p>After parsing a Wiki text you get a <code>Wiki.Documents.Document</code> instance that you can use as many times as you want. To render the Wiki document, you will first choose a renderer according to the target format that you need. The <a href="https://github.com/stcarrez/ada-wiki">Ada Wiki Engine</a> provides three renderers:</p><ul><li>A Text renderer that produces text outputs,</li><li>A HTML renderer that generates an HTML presentation for the document,</li><li>A Wiki renderer that generates various Wiki syntaxes.</li></ul><p>The renderer needs an output stream instance. We are using the <code>Text_IO</code> implementation:</p><pre><code class="lang-ada"> with Wiki.Stream.Html.Text_IO;
with Wiki.Render.Html;
...
Output : aliased Wiki.Streams.Html.Text_IO.Html_File_Output_Stream;
Renderer : aliased Wiki.Render.Html.Html_Renderer;
</code></pre><p>You will then configure the renderer to tell it the output stream to use. You may enable or not the rendering of Table Of Content and you just use the <code>Render</code> procedure to render the document.</p><pre><code class="lang-ada"> Renderer.Set_Output_Stream (Output'Unchecked_Access);
Renderer.Set_Render_TOC (True);
Renderer.Render (Doc);
</code></pre><p>By default the output stream is configured to write on the standard output. This means that when <code>Render</code> is called, the output will be written to the standard output. You can choose another output stream or open the output stream to a file according to your needs.</p><h3>Conclusion</h3><p>The <a href="https://github.com/stcarrez/ada-wiki">Ada Wiki Engine</a> can be used to parse HTML content, sanitize the result through the HTML filter and convert it to text or to some Wiki syntax (have a look at the <a href="https://github.com/stcarrez/ada-wiki/blob/master/samples/import.adb">import.adb</a> example). The engine can be extended through filters or plugins thus providing some flexible architecture. The library does not impose any storage mechanism. The <a href="https://github.com/stcarrez/ada-wiki">Ada Wiki Engine</a> is the core engine used by <a href="https://github.com/stcarrez/ada-awa/wiki/AWA_Blogs">AWA Blogs</a> and <a href="https://github.com/stcarrez/ada-awa/wiki/AWA_Wikis">AWA Wiki</a> web applications. You may have a look at some online Wiki in the <a href="http://demo.vacs.fr/atlas/wikis/view/1/Main">Atlas Wiki</a> demonstrator.</p></div> Using Ada LZMA to compress and decompress LZMA filesurn:md5:f1c8e1f26d23e919ed671db06cbf54cb2015-12-16T10:25:00+00:002015-12-16T10:25:00+00:00Stephane CarrezTutorialAdacompressiondecompressionlzma
<div class="post-text"><h3>Setup of Ada LZMA binding</h3><p>First download the Ada LZMA binding at <a href="http://download.vacs.fr/ada-lzma/ada-lzma-1.0.0.tar.gz">http://download.vacs.fr/ada-lzma/ada-lzma-1.0.0.tar.gz</a> or at <b>git@github.com:stcarrez/ada-lzma.git</b>, configure, build and install the library with the next commands:</p><pre><code>./configure
make
make install
</code></pre><p>After these steps, you are ready to use the binding and you can add the next line at begining of your <a href="https://www.gnu.org/software/emacs/manual/html_node/ada-mode/Use-GNAT-project-file.html">GNAT project file</a>:</p><pre><code class="lang-ada">with "lzma";
</code></pre><h3>Import Declaration</h3><p>To use the <a href="https://github.com/stcarrez/ada-lzma">Ada LZMA</a> packages, you will first import the following packages in your Ada source code:</p><pre><code class="lang-ada">with Lzma.Base;
with Lzma.Container;
with Lzma.Check;
</code></pre><h3>LZMA Stream Declaration and Initialization</h3><p>The liblzma library uses the <code>lzma_stream</code> type to hold and control the data for the lzma operations. The <code>lzma_stream</code> must be initialized at begining of the compression or decompression and must be kept until the compression or decompression is finished. To use it, you must declare the LZMA stream as follows:</p><pre><code class="lang-ada">Stream : aliased Lzma.Base.lzma_stream := Lzma.Base.LZMA_STREAM_INIT;
</code></pre><p>Most of the liblzma function return a status value of by <code>lzma_ret</code>, you may declare a result variable like this:</p><pre><code class="lang-ada">Result : Lzma.Base.lzma_ret;
</code></pre><h4>Initialization of the lzma_stream</h4><p>After the <code>lzma_stream</code> is declared, you must configure it either for compression or for decompression.</p><h5>Initialize for compression</h5><p>To configure the <code>lzma_stream</code> for compression, you will use the <code>lzma_easy_encode</code> function. The <code>Preset</code> parameter controls the compression level. Higher values provide better compression but are slower and require more memory for the program.</p><pre><code class="lang-ada">Result := Lzma.Container.lzma_easy_encoder (Stream'Unchecked_Access, Lzam.Container.LZMA_PRESET_DEFAULT,
Lzma.Check.LZMA_CHECK_CRC64);
if Result /= Lzma.Base.LZMA_OK then
Ada.Text_IO.Put_Line ("Error initializing the encoder");
end if;
</code></pre><h5>Initialize for decompression</h5><p>For the decompression, you will use the <code>lzma_stream_decoder</code>:</p><pre><code class="lang-ada">Result := Lzma.Container.lzma_stream_decoder (Stream'Unchecked_Access,
Long_Long_Integer'Last,
Lzma.Container.LZMA_CONCATENATED);
</code></pre><h3>Compress or decompress the data</h3><p>The compression and decompression is done by the <code>lzma_code</code> function which is called several times until it returns <code>LZMA_STREAM_END</code> code. Setup the stream 'next_out', 'avail_out', 'next_in' and 'avail_in' and call the lzma_code operation with the action (Lzma.Base.LZMA_RUN or Lzma.Base.LZMA_FINISH):</p><pre><code class="lang-ada">Result := Lzma.Base.lzma_code (Stream'Unchecked_Access, Action);
</code></pre><h3>Release the LZMA stream</h3><p>Close the LZMA stream:</p><pre><code class="lang-ada"> Lzma.Base.lzma_end (Stream'Unchecked_Access);
</code></pre><h3>Sources</h3><p>To better understand and use the library, <a href="https://github.com/stcarrez/ada-lzma/tree/master/samples">use the source Luke</a></p><ul><li><a href="https://raw.githubusercontent.com/stcarrez/ada-lzma/master/samples/compress_easy.adb">compress_easy.adb</a></li><li><a href="https://raw.githubusercontent.com/stcarrez/ada-lzma/master/samples/decompress.adb">decompress.adb</a></li></ul><h3>Download</h3><ul><li><a href="https://github.com/stcarrez/ada-lzma">https://github.com/stcarrez/ada-lzma</a></li><li><a href="http://download.vacs.fr/ada-lzma/ada-lzma-1.0.0.tar.gz">http://download.vacs.fr/ada-lzma/ada-lzma-1.0.0.tar.gz</a></li></ul></div> Review Web Application: Listing the reviewsurn:md5:5461f4f9d59691737c7c02383cd16ae22014-07-20T14:00:00+00:002014-07-20T14:00:00+00:00Stephane CarrezTutorialAWAAda
<div class="post-text"><p>This tutorial has three steps:</p><ul><li>First the definition of the database query,</li><li>The implementation of the Ada review list bean,</li><li>The writing of the <a href="http://en.wikipedia.org/wiki/Facelets">XHTML facelet</a> presentation file.</li></ul><h4>Step 1: Database query to list the reviews</h4><p>Let's start with the database query that we will use to retrieve the reviews.</p><p>Since we need to access the list of reviews from the XHTML files, we will map the SQL query result to a list of <a href="https://code.google.com/p/ada-util/wiki/AdaBeans">Ada Beans</a> objects. For this, an <a href="https://code.google.com/p/ada-ado/wiki/QueryMapping">XML query mapping</a> is created to tell how to map the SQL query result into some Ada record. The XML query mapping is then processed by <a href="https://code.google.com/p/ada-gen/">Dynamo</a> to generate the <a href="https://code.google.com/p/ada-util/wiki/AdaBeans">Ada Beans</a> implementation. The XML query mapping is also read by <a href="https://code.google.com/p/ada-awa/">AWA</a> to get the SQL query to execute.</p><p>A template of the XML query mapping can be added to a project by using the dynamo <code>add-query</code> command. The first parameter is the module name (<code>reviews</code>) and the second parameter the name of the query (<code>list</code>). The command will generate the file <code>db/reviews-list.xml</code>.</p><pre><code>dynamo add-query reviews list
</code></pre><p>The generated XML query mapping is an example of a query. You can replaced it or update it according to your needs. The first part of the XML query mapping is a <code>class</code> declaration that describes the type to represent each row returned by our query. Within the <code>class</code>, a set of <code>property</code> definition describes the class attributes with their type and name.</p><pre><code class="lang-xml"><query-mapping package='Atlas.Reviews.Models'>
<class name="Atlas.Reviews.Models.List_Info" bean="yes">
<comment>The list of reviews.</comment>
<property type='Identifier' name="id">
<comment>the review identifier.</comment>
</property>
<property type='String' name="title">
<comment>the review title.</comment>
</property>
...
</class>
</query-mapping>
</code></pre><p>Following the <code>class</code> declaration, the <code>query</code> declaration describes a query by giving it a name and describing the SQL statement to execute. By having the SQL statement separate and external to the application, we can update, fix and tune the SQL without rebuilding the application. The <a href="https://code.google.com/p/ada-gen/">Dynamo</a> code generator will use the <code>query</code> declaration to generate a query definition that can be referenced and used from the Ada code.</p><p>The SQL statement is defined within the <code>sql</code> XML entity. The optional <code>sql-count</code> XML entity is used to associate a count query that can be used for the pagination.</p><p>We want to display the review with the author's name and email address. The list will be sorted by date to show the newest reviews first. The SQL to execute is the following:</p><pre><code class="lang-xml"><query-mapping package='Atlas.Reviews.Models'>
...
<query name='list'>
<comment>Get the list of reviews</comment>
<sql>
SELECT
r.id,
r.title,
r.site,
r.create_date,
r.allow_comments,
r.reviewer_id,
a.name,
e.email,
r.text
FROM atlas_review AS r
INNER JOIN awa_user AS a ON r.reviewer_id = a.id
INNER JOIN awa_email AS e ON a.email_id = e.id
ORDER BY r.create_date DESC
LIMIT :first, :last
</sql>
<sql-count>
SELECT
count(r.id)
FROM atlas_review AS r
</sql-count>
</query>
</query-mapping>
</code></pre><p>The query has two named parameters represented by <code>:first</code> and <code>:last</code>. These parameters allow to paginate the list of reviews.</p><p>The complete source can be seen in the file: <a href="https://code.google.com/p/ada-awa/source/browse/trunk/awa/samples/db/reviews-list.xml">db/reviews-list.xml</a>.</p><p>Once the XML query is written, the Ada code is generated by <a href="https://code.google.com/p/ada-gen/">Dynamo</a> by reading the UML model and all the XML query mapping defined for the application. <a href="https://code.google.com/p/ada-gen/">Dynamo</a> merges all the definitions into the target Ada packages and generates the Ada code in the <code>src/model</code> directory. You can use the <code>generate</code> make target:</p><pre><code>make generate
</code></pre><p>or run the following command manually:</p><pre><code>dynamo generate db uml/atlas.zargo
</code></pre><p>From the <code>List_Info</code> class definition, <a href="https://code.google.com/p/ada-gen/">Dynamo</a> generates the <code>List_Info</code> tagged record. The record contains all the data members described in the <code>class</code> XML entity description. The <code>List_Info</code> represents one row returned by the SQL query. The attributes of the <code>List_Info</code> can be accessed from the XHTML files by using UEL expression and the property name defined for each attribute.</p><p>To describe the list of rows, <a href="https://code.google.com/p/ada-gen/">Dynamo</a> generates the <code>List_Info_Beans</code> package which instantiates the <code>Util.Beans.Basic.Lists</code> generic package. This provides an Ada vector for the <code>List_Info</code> type and an <a href="https://code.google.com/p/ada-util/wiki/AdaBeans">Ada bean</a> that gives access to the list.</p><pre><code class="lang-ada">package Atlas.Reviews.Models is
...
type List_Info is new Util.Beans.Basic.Readonly_Bean with record
...
package List_Info_Beans is
new Util.Beans.Basic.Lists (Element_Type => List_Info);
package List_Info_Vectors renames List_Info_Beans.Vectors;
subtype List_Info_List_Bean is List_Info_Beans.List_Bean;
subtype List_Info_Vector is List_Info_Vectors.Vector;
Query_List : constant ADO.Queries.Query_Definition_Access;
...
end Atlas.Reviews.Models;
</code></pre><p>The generated code can be seen in <a href="https://code.google.com/p/ada-awa/source/browse/trunk/awa/samples/src/model/atlas-reviews-models.ads#185">src/model/atlas-reviews-models.ads</a>.</p><h4>Step 2: The review list bean</h4><p>In order to access the list of reviews from the XHTML facelet file, we must create an <a href="https://code.google.com/p/ada-util/wiki/AdaBeans">Ada bean</a> that provides the list of reviews. This <a href="https://code.google.com/p/ada-util/wiki/AdaBeans">Ada bean</a> is modelized in the UML model and we define:</p><ul><li>A set of attributes to manage the review list pagination (<code>page</code>, <code>page_size</code>, <code>count</code>)</li><li>An <a href="https://code.google.com/p/ada-util/wiki/AdaBeans">Ada bean</a> action that can be called from the XHTML facelet file (<code>load</code>)</li></ul><p>The <code>Review_List_Bean</code> tagged record will hold the list of reviews for us:</p><pre><code class="lang-ada">package Atlas.Reviews.Beans is
...
type Review_List_Bean is new Atlas.Reviews.Models.Review_List_Bean with record
Module : Atlas.Reviews.Modules.Review_Module_Access := null;
Reviews : aliased Atlas.Reviews.Models.List_Info_List_Bean;
Reviews_Bean : Atlas.Reviews.Models.List_Info_List_Bean_Access;
end record;
type Review_List_Bean_Access is access all Review_List_Bean'Class;
end Atlas.Reviews.Beans;
</code></pre><p>We must now implement the <code>Load</code> operation that was described in the UML model and we are going to use our <code>list</code> query. For this, we use the <code>ADO.Queries.Context</code> to setup the query to retrieve the list of reviews. A call to <code>Set_Query</code> indicates the query that will be used. Since that query needs two parameters (<code>first</code> and <code>last</code>), we use the <code>Bind_Param</code> operation to give the two values. The list of reviews is then retrieved easily by calling the <code>Atlas.Reviews.Models.List</code> operation that was generated by <a href="https://code.google.com/p/ada-gen/">Dynamo</a>.</p><pre><code class="lang-ada">package body Atlas.Reviews.Beans is
...
overriding
procedure Load (Into : in out Review_List_Bean;
Outcome : in out Ada.Strings.Unbounded.Unbounded_String) is
Session : ADO.Sessions.Session := Into.Module.Get_Session;
Query : ADO.Queries.Context;
Count_Query : ADO.Queries.Context;
First : constant Natural := (Into.Page - 1) * Into.Page_Size;
Last : constant Positive := First + Into.Page_Size;
begin
Query.Set_Query (Atlas.Reviews.Models.Query_List);
Count_Query.Set_Count_Query (Atlas.Reviews.Models.Query_List);
Query.Bind_Param (Name => "first", Value => First);
Query.Bind_Param (Name => "last", Value => Last);
Atlas.Reviews.Models.List (Into.Reviews, Session, Query);
Into.Count := ADO.Datasets.Get_Count (Session, Count_Query);
end Load;
end Atlas.Reviews.Beans;
</code></pre><h3>Review list bean creation</h3><p>The <a href="AWA">AWA</a> framework must be able to create an instance of the <code>Review_List_Bean</code> type. For this, we have to declare and implement a constructor function that allocates an instance of the <code>Review_List_Bean</code> type and setup some pre-defined values. When the instance is returned, the list of reviews is not loaded.</p><pre><code class="lang-ada">package body Atlas.Reviews.Beans is
...
function Create_Review_List_Bean (Module : in Atlas.Reviews.Modules.Review_Module_Access)
return Util.Beans.Basic.Readonly_Bean_Access is
Object : constant Review_List_Bean_Access := new Review_List_Bean;
begin
Object.Module := Module;
Object.Reviews_Bean := Object.Reviews'Access;
Object.Page_Size := 20;
Object.Page := 1;
Object.Count := 0;
return Object.all'Access;
end Create_Review_List_Bean;
end Atlas.Reviews.Beans;
</code></pre><p>The constructor function is then registered in the <code>Atlas.Reviews.Modules</code> package within the <code>Initialize</code> procedure. This registration allows to give a name for this constructor function and be able to specify it in the <code>managed-bean</code> bean declaration.</p><pre><code class="lang-ada">package body Atlas.Reviews.Modules is
...
overriding
procedure Initialize (Plugin : in out Review_Module;
App : in AWA.Modules.Application_Access;
Props : in ASF.Applications.Config) is
begin
...
Register.Register (Plugin => Plugin,
Name => "Atlas.Reviews.Beans.Review_List_Bean",
Handler => Atlas.Reviews.Beans.Create_Review_List_Bean'Access);
end Initialize;
end Atlas.Reviews.Modules;
</code></pre><h3>Review list bean declaration</h3><p>The <a href="http://docs.oracle.com/javaee/5/tutorial/doc/bnawq.html">managed-bean XML declaration</a> associates a name to a constructor function that will be called when the name is needed. The scope of the <a href="https://code.google.com/p/ada-util/wiki/AdaBeans">Ada bean</a> is set to <code>request</code> so that a new instance is created for each <a href="http://tools.ietf.org/html/rfc2616#section-9.3">HTTP GET</a> request.</p><pre><code class="lang-xml"> <managed-bean>
<description>The list of reviews</description>
<managed-bean-name>reviewList</managed-bean-name>
<managed-bean-class>Atlas.Reviews.Beans.Review_List_Bean</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
</code></pre><h4>Step 3: Listing the reviews: the XHTML facelet presentation file</h4><p>To load the reviews to be displayed we will use a <a href="http://www.oracle.com/technetwork/articles/java/jsf22-1377252.html">JSF 2.2 view action</a>. The review list page has a parameter <code>page</code> that indicates the page number to be displayed. The <code>f:viewParam</code> allows to retrieve that parameter and configure the <code>reviewList</code> Ada bean with it. Then, the <code>f:viewAction</code> defines the action that will be executed after the view parameters are extracted, validated and passed to the <a href="https://code.google.com/p/ada-util/wiki/AdaBeans">Ada bean</a>. In our case, we will call the <code>load</code> operation on our <code>reviewList</code> <a href="https://code.google.com/p/ada-util/wiki/AdaBeans">Ada bean</a>.</p><pre><code class="lang-xml"><f:metadata>
<f:viewParam id='page' value='#{reviewList.page}' required="false"/>
<f:viewAction action="#{reviewList.load}"/>
</f:metadata>
</code></pre><p>To summarize, the <code>reviewList</code> Ada bean is created, then configured for the pagination and filled with the current page content by running our SQL query.</p><p>The easy part is now to render the list of reviews. The XHTML file uses the <a href="http://demo.vacs.fr/demo/jsf/html/view.html"><h:list></a> component to iterate over the list items and render each of them. At each iteration, the <code><h:list></code> component initializes the <a href="https://code.google.com/p/ada-util/wiki/AdaBeans">Ada bean</a> <code>review</code> to refer to the current row in the review list. We can then access each attribute defined in the XML query mapping by using the property name of that attribute. For example <code>review.title</code> returns the <code>title</code> property.</p><pre><code class="lang-xml"><h:list var="review" value="#{reviewList.reviews}">
<div</code></pre></div> Review Web Application: Creating a reviewurn:md5:8a09dfbba43f631077b82532d414ac742014-06-14T18:29:00+00:002014-06-14T18:29:00+00:00Stephane CarrezTutorialAdaAWA
<div class="post-text"><h3>Adding the review creation form</h3><p>We will start with the presentation layer by adding two pages in our web application. A first page will contain the list of reviews and the second page will contain a form to create or update a review.</p><p><a href="https://github.com/stcarrez/ada-awa/p/ada-awa">AWA</a> uses the <a href="http://en.wikipedia.org/wiki/Facelets">Facelets</a> technology to allow developers write and design the presentation layer of the web application. This technology is commonly used in J2EE applications. A page is represented by an XML file that contains HTML code, includes some stylesheets, Javascript files and makes the link between the presentation and the web application.</p><h4>Adding pages</h4><p><a href="https://github.com/stcarrez/dynamo/">Dynamo</a> provides at least two commands that help in adding presentation files. The <code>add-page</code> command adds a simple page that can be edited and filled with real content. We will use it for the creation of the page to display the list of reviews.</p><pre><code>dynamo add-page reviews/list
</code></pre><p>The <code>add-form</code> command creates another template of page that includes an HTML form to let a user submit some data to the web application.</p><pre><code>dynamo add-form reviews/edit-review
</code></pre><p>These two commands will create the following files and they can now be modified.</p><pre><code>./web/reviews/list.xhtml
./web/reviews/edit-review.xhtml
./web/reviews/forms/edit-review-form.xhtml
</code></pre><h4>The create review form</h4><p>In Facelets, an HTML form is created by using the <code><h:form></code> component from the <a href="http://demo.vacs.fr/demo/jsf/html/view.html">HTML JSF</a> namespace. This component will generate the HTML <code>form</code> tag and it will also manage the form submission.</p><p>The <a href="https://github.com/stcarrez/ada-asf/">ASF framework</a> provides a set of <a href="http://demo.vacs.fr/demo/widgets/view.html">widget components</a> that facilitate the design of web application. The <code><w:inputText></code> component renders a title field with an HTML <code><label></code> and an HTML <code><input></code> text. We will use it to let the user enter the review title and the site URL being reviewed. The HTML <code><textarea></code> is provided by the JSF component <code><h:inputTextArea></code>. The review submit form is defined by the following XML extract:</p><pre><code class="lang-xml"><h:form xmlns:h="http://java.sun.com/jsf/html
xmlns:w="http://code.google.com/p/ada-asf/widget">
<h:inputHidden id='entity-id' value='#{review.id}' required='false'/>
<w:inputText title='Title' value='#{review.title}'/>
<w:inputText title='Site' value='#{review.site}'/>
<h:inputTextArea rows='20' value='#{review.text}'/>
<h:commandButton value='Save'
action='#{review.save}'/>
</h:form>
</code></pre><p>Before closing the <code><h:form></code> component, we will put a <code><h:commandButton></code> that will render the form submit button.</p><h3>How it works</h3><p>Before going further, let's see how all this works. The principle below is exactly the same for a <a href="http://en.wikipedia.org/wiki/JavaServer_Faces">Java Server Faces</a> application.</p><p>First, when the page is rendered the <a href="http://en.wikipedia.org/wiki/Unified_Expression_Language">UEL</a> expressions that it contains are evaluated. The <code>#{review.title}</code>, <code>#{review.site}</code> and <code>#{review.text}</code> are replaced by the content provided by the <code>review</code> object which is an <a href="https://github.com/stcarrez/ada-util/wiki/AdaBeans">Ada Bean</a> provided by the <code>Review_Bean</code> tagged record.</p><p>When the page is submitted by the user, the input values submitted in the form are saved in the <code>review</code> bean, again by using the <a href="http://en.wikipedia.org/wiki/Unified_Expression_Language">UEL</a> expression. The <code><h:commandButton></code> action is then executed. This is also an <a href="http://en.wikipedia.org/wiki/Unified_Expression_Language">UEL</a> that indicates a method to invoke on the bean.</p><p>To sum up, the <a href="http://en.wikipedia.org/wiki/Unified_Expression_Language">UEL</a> makes the binding between the presentation layer in <a href="http://en.wikipedia.org/wiki/Facelets">Facelets</a> files and the Ada or Java beans.</p><p>The <a href="https://github.com/stcarrez/ada-util/wiki/AdaBeans">Ada Bean</a> layer provides getter and setter to allow the <a href="http://en.wikipedia.org/wiki/Unified_Expression_Language">UEL</a> to retrieve and set values. For this, the <code>Review_Bean</code> tagged record implements two operations that are defined in the <a href="https://github.com/stcarrez/ada-util/source/browse/trunk/src/util-beans-basic.ads">Bean</a> interface:</p><pre><code class="lang-ada">overriding
function Get_Value (From : in Review_Bean;
Name : in String) return Util.Beans.Objects.Object;
overriding
procedure Set_Value (From : in out Review_Bean;
Name : in String;
Value : in Util.Beans.Objects.Object);
</code></pre><p>The <code>Get_Value</code> operation is called to retrieve one of the <a href="https://github.com/stcarrez/ada-util/wiki/AdaBeans">Ada Bean</a> member attribute and the <code>Set_Value</code> operation is called during form submission to set the member attribute.</p><p><div class="wiki-img-center"><div class="wiki-img-inner"><img src="/images/samples/demo-awa-request-flow.png" longdesc="Presentation, Ada Beans and Module interactions" alt="demo-awa-request-flow.png"></img></div></div></p><p>Then the form button is pressed, the HTML form is submitted and received by the server. The <code><h:form></code> component identifies the form submission and each input component will validate the input fields. When everything has been validated, the <code><h:commandButton></code> component invokes the <code>Save</code> procedure that is declared as follows in the <code>Review_Bean</code> tagged record:</p><pre><code class="lang-ada">overriding
procedure Save (Bean : in out Review_Bean;
Outcome : in out Ada.Strings.Unbounded.Unbounded_String);
</code></pre><p>In the <a href="https://code.google.com/p/ada-util/wiki/AdaBeans">Ada Bean</a> layer, we have to call the business logic to perform the <code>save</code> operation.</p><p>The <a href="http://en.wikipedia.org/wiki/Business_logic">business logic</a> part is provided by the Ada module whose initial skeleton was generated by <a href="https://github.com/stcarrez/dynamo/">Dynamo</a>. That layer is responsible for defining how the data is created, retrieved and modified. As far as we are concerned, this is rather simple since we only have to verify the permission and save the review object within some transaction. In other modules, several objects may be envolved and more complex rules may be defined for the integrity and validity of these objects.</p><p>The last part of the architecture is the data model layer that was in fact generated by <a href="https://github.com/stcarrez/dynamo/">Dynamo</a> from the UML model. It is responsible for loading and saving Ada objects into the database.</p><h3>The Review_Bean type declaration</h3><p>When we designed our UML model, we have created the <code>Review_Bean</code> UML class and gave that class the <code>Bean</code> stereotype. We also declared two operations (<code>save</code> and <code>delete</code>) on that class. With this definition, Dynamo has generated in the <code>Atlas.Reviews.Models</code> package the <code>Review_Bean</code> abstract type. This type is abstract because we have to implement the <code>Save</code> and <code>Delete</code> operations. These are the two operations that can be called by an action such as used by the <code><h:commandButton></code> component.</p><p>The <code>Atlas.Reviews.Models</code> package is a generated package and it must not be modified. To implement our <a href="https://code.google.com/p/ada-util/wiki/AdaBeans">Ada Bean</a>, we will add the <code>Review_Bean</code> type in our own package: the <code>Atlas.Reviews.Beans</code> package.</p><p>For this the <code>Review_Bean</code> type will inherit from the <code>Atlas.Reviews.Models.Review_Bean</code> type and it will implement the required operations. The type declaration looks like this:</p><pre><code class="lang-ada">package Atlas.Reviews.Beans is
...
type Review_Bean is new Atlas.Reviews.Models.Review_Bean with record
Module : Atlas.Reviews.Modules.Review_Module_Access := null;
end record;
...
</code></pre><h3>The Review_Bean implementation</h3><p>The <code>Save</code> and <code>Delete</code> procedure must be implemented and since the whole <a href="http://en.wikipedia.org/wiki/Business_logic">business logic</a> is managed by the module layer, we just have to call the associated module procedure as follows:</p><pre><code class="lang-ada">overriding
procedure Save (Bean : in out Review_Bean;
Outcome : in out Ada.Strings.Unbounded.Unbounded_String);
begin
Bean.Module.Save (Bean);
end Save;
overriding
procedure Delete (Bean : in out Review_Bean;
Outcome : in out Ada.Strings.Unbounded.Unbounded_String);
begin
Bean.Module.Delete (Bean);
end Delete;
</code></pre><h3>The Review_Bean creation</h3><p>The <a href="https://github.com/stcarrez/ada-awa">AWA</a> framework must be able to create the <code>review</code> bean instance when a page is processed. For this, there are three steps that are necessary:</p><ul><li>we must define a create function whose role is to allocate the <code>Review_Bean</code> instance and return it. At the same time, the function can setup some pre-defined values for the object. The <a href="https://github.com/stcarrez/dynamo/">Dynamo</a> tool has generated for us an example of such function so that there is nothing to do.</li></ul><pre><code class="lang-ada">function Create_Review_Bean (Module : in Atlas.Reviews.Modules.Review_Module_Access)
return Util.Beans.Basic.Readonly_Bean_Access is
Object : constant Review_Bean_Access := new Review_Bean;
begin
Object.Module := Module;
return Object.all'Access;
end Create_Review_Bean;
</code></pre><ul><li>the creation function must be registered in the <a href="https://github.com/stcarrez/ada-awa">AWA</a> framework under a name that identifies the create function. Again, an example of this registration has been generated by Dynamo and we are going to use it as is.</li></ul><pre><code class="lang-ada">Register.Register (Plugin => Plugin,
Name => "Atlas.Reviews.Beans.Reviews_Bean",
Handler => Atlas.Reviews.Beans.Create_Review_Bean'Access);
</code></pre><ul><li>the last step is the configuration step. In the module XML configuration file, we must declare the Ada bean name and indicate what create function must be called to create it. We will use the <a href="http://docs.oracle.com/javaee/5/tutorial/doc/bnawq.html">managed-bean XML declaration</a> that comes from <a href="http://en.wikipedia.org/wiki/JavaServer_Faces">Java Server Faces</a>. We can declare as many Ada beans as we want each of them with a different name.</li></ul><pre><code class="lang-xml"> <managed-bean>
<description>An example of a bean (change description and bean name)</description>
<managed-bean-name>review</managed-bean-name>
<managed-bean-class>Atlas.Reviews.Beans.Reviews_Bean</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
</code></pre><p>When the <a href="http://en.wikipedia.org/wiki/Unified_Expression_Language">UEL</a> expression <code>#{review.title}</code> is used, the AWA framework looks for the Ada bean represented by <code>review</code> and identified by the <code>managed-bean-name</code> entry. It then calls the create function defined by the <code>managed-bean-class</code>. The Ada bean object is then stored either in the <b>request</b> context, a <b>session</b> context or an <b>application</b> context. This is defined by the <code>managed-bean-scope</code> entry. The <b>request</b> scope means that the Ada bean object is created once for each request. Concurrent page accesses will use their own Ada bean object instance. The <b>session</b> scope means that the Ada bean object is shared between requests on the same session. The <b>application</b> scope means that the Ada bean object is global to the application, shared by every request and every user.</p><h3>Adding the module operations</h3><p>Now, we must add two operations on the <a href="http://en.wikipedia.org/wiki/Business_logic">business logic</a> to save a review and delete a review. The <a href="https://github.com/stcarrez/dynamo/">Dynamo</a> code generator provides the <code>add-module-operation</code> command that will help us in this task. Let's run it:</p><pre><code>dynamo add-module-operation reviews review Save
dynamo add-module-operation reviews review Delete
</code></pre><p>The first parameter is the name of the module where the new operation is added. This is the name of the module that was created by using the <code>add-module</code> operation. In our case, this is the <code>reviews</code> module.</p><p>The second parameter is the name of the database entity or database table if you prefer.</p><p>The <code>add-module-operation</code> command modifies the Ada module specification and body to define and implement the following operation:</p><pre><code class="lang-ada">package Atlas.Reviews.Modules is
...
procedure Save (Model : in Review_Module;
Entity : in out Atlas.Reviews.Models.Review_Ref'Class);
...
</code></pre><p>The object to save in the <code>Review</code> table is passed as parameter to the Save operation. The procedure body that was generated is rather simple but functional: it just saves the object in the database within a transaction. In many cases it is ready to use but you may also need to modify the operation to either change the implementation or even add new parameters.</p><h3>Saving our review</h3><p>Before saving our review entity object, we want to associate it with the current user</p></div> Ada Web Application: Building the UML modelurn:md5:1144b71bd4841657c3e45a3a65c14e5c2014-05-18T12:42:00+00:002014-05-18T12:42:00+00:00Stephane CarrezTutorialAdaUML
<div class="post-text"><p>In the <a href="http://blog.vacs.fr/vacs/blogs/post.html?post=2014/05/08/Ada-Web-Application-Setting-up-the-project">Ada Web Application: Setting up the project</a> we have seen how to create a new <a href="http://code.google.com/p/ada-awa">AWA</a> project. In this second article, we will see how to design the UML model, generate the Ada code and create the database tables from our UML design.</p><h2>Introduction</h2><p>A Model driven engineering or <a href="http://en.wikipedia.org/wiki/Model-driven_engineering">MDE</a> promotes the use of models to ease the development of software and systems. The <a href="http://en.wikipedia.org/wiki/Unified_Modeling_Language">Unified Modeling Language</a> is used to modelize various parts of the software. UML is a graphical type modelling language and it has many diagrams but we are only going to use one of them: the <a href="http://en.wikipedia.org/wiki/Class_diagram">Class Diagram</a>.</p><p>The class diagram is probably the most powerful diagram to design, explain and share the data model of any application. It defines the most important data types used by an application with the relation they have with each other. In the class diagram, a class represents an abstraction that encapsulates data member attributes and operations. The class may have relations with others classes.</p><p>For the UML model, we are going to use <a href="http://argouml.tigris.org/">ArgoUML</a> that is a free modelization tool that works pretty well. For the <a href="http://argouml.tigris.org/">ArgoUML</a> setup, we will use two profiles:</p><ul><li>The <a href="https://code.google.com/p/ada-gen/">Dynamo</a> profile that describes the base data types for our UML model. These types are necessary for the code generator to work correctly.</li><li>The <a href="http://code.google.com/p/ada-awa">AWA</a> profile that describes the tables and modules provided by <a href="http://code.google.com/p/ada-awa">AWA</a>. We will need it to get the user UML class definition.</li></ul><p>These UML profiles are located in the <code>/usr/share/dynamo/base/uml</code> directory after <a href="https://code.google.com/p/ada-gen/">Dynamo</a> and <a href="http://code.google.com/p/ada-awa">AWA</a> are installed. To configure <a href="http://argouml.tigris.org/">ArgoUML</a>, go in the <code>Edit -> Settings</code> menu and add the directory in the <code>Default XMI directories</code> list. Beware that you must restart <a href="http://argouml.tigris.org/">ArgoUML</a> to be able to use the new profiles.</p><p><div class="wiki-img-center"><div class="wiki-img-inner"><img src="/images/samples/demo-awa-argouml-setup.png" longdesc="ArgoUML profile setup" alt="demo-awa-argouml-setup.png"></img></div></div></p><h2>Modelize the domain model in UML</h2><p>The UML model must use a number of <a href="https://code.google.com/p/ada-gen/">Dynamo</a> artifacts for the code generation to work properly. The artifact describes some capabilities and behavior for the code generator to perform its work. Stereotype names are enclosed within markers. <a href="https://code.google.com/p/ada-gen/">Dynamo</a> uses the following stereotypes:</p><ul><li>The <code>DataModel</code> stereotype must be applied on the package which contains the model to generate. This stereotype activates the code generation (other packages are not generated).</li><li>The <code>Table</code> stereotype must be applied to the class. It controls which database table and Ada type will be generated.</li><li>The <code>PK</code> stereotype must be defined in at most one attribute of the class. This indicates the primary key for the database table. The attribute type must be an integer or a string. This is a limitation of the Ada code generator.</li><li>The <code>Version</code> stereotype must be applied on the attribute that is used for the <a href="http://en.wikipedia.org/wiki/Optimistic_concurrency_control">optimistic locking</a> implementation of the database layer.</li></ul><p><div class="wiki-img-center"><div class="wiki-img-inner"><img src="/images/samples/demo-awa-uml-review-table.png" longdesc="The Review Table UML Model" alt="demo-awa-uml-review-table.png"></img></div></div></p><p>In our UML model, the <code>Review</code> table is assigned the <code>Table</code> stereotype so that an SQL table will be created as well as an Ada tagged type to represent our table. The <code>id</code> class attribute represents the primary key and thus has the <code>PK</code> stereotype. The <code>version</code> class attribute is the database column used by the <a href="http://en.wikipedia.org/wiki/Optimistic_concurrency_control">optimistic locking</a> implementation provided by <a href="http://code.google.com/p/ada-ado">ADO</a>. This is why is has the <code>Version</code> stereotype. The <code>title</code>, <code>site</code>, <code>create_date</code>, <code>text</code> and <code>allow_comments</code> attributes represent the information we want to store in the database table. They are general purpose attributes and thus don't need any specific stereotype. For each attribute, the <a href="https://code.google.com/p/ada-gen/">Dynamo</a> code generator will generate a getter and a setter operation that can be used in the Ada code.</p><p>To tune the generation, several UML tagged values can be selected and added on the table or on a table attribute. By applying a stereotype to the class, several tagged values can be added. By selecting the <code>Tagged Values</code> tab in ArgoUML we can edit and setup new values. For the <code>Review</code> table, the <code>dynamo.table.name</code> tagged value defines the name of the SQL database table, in our case <code>atlas_review</code>.</p><p><div class="wiki-img-center"><div class="wiki-img-inner"><img src="/images/samples/demo-awa-argouml-review-tagged.png" longdesc="The tagged value for the Review table" alt="demo-awa-argouml-review-tagged.png"></img></div></div></p><p>The <code>text</code> attribute in the <code>Review</code> table is a string that can hold some pretty long text. To control the length of the SQL column, we can set the <code>dynamo.sql.length</code> tagged value and tell what is that length.</p><p><div class="wiki-img-center"><div class="wiki-img-inner"><img src="/images/samples/demo-awa-argouml-text-tagged.png" longdesc="The tagged value for the text column in review table" alt="demo-awa-argouml-text-tagged.png"></img></div></div></p><p>Once the UML model is designed, it is saved in the project directory <code>uml</code>. <a href="https://code.google.com/p/ada-gen/">Dynamo</a> will be able to read the ArgoUML file format (<code>.zargo</code> extension) so there is no need to export the UML in XMI.</p><h3>The Review application UML model</h3><p>The final UML model of our review application is fairly simple. We just added a table and a bean declaration. To benefit from the <a href="https://code.google.com/p/ada-awa/wiki/AWA_Users_Modules">user management</a> in <a href="http://code.google.com/p/ada-awa">AWA</a>, we can use the <code>AWA::Users::Models::User</code> class that is defined in the AWA UML model. The <code>reviewed-by</code> association will create an attribute <code>reviewer</code> in our class. The code generator will generate a <code>Get_Reviewer</code> and <code>Set_Reviewer</code> operation in the Ada code. The SQL table will contain an additional column <code>reviewer</code> that will hold the primary key of the reviewer.</p><p><div class="wiki-img-center"><div class="wiki-img-inner"><img src="/images/samples/demo-awa-uml-review-model.png" longdesc="The Review Web Application UML Model" alt="demo-awa-uml-review-model.png"></img></div></div></p><p>The <code>Review_Bean</code> class is an <a href="https://code.google.com/p/ada-util/wiki/AdaBeans">Ada Bean</a> abstract class that will be generated by the code generator. The <code>Bean</code> stereotype activates the bean code generator and the generator will generate some code support that is necessary to turn the <code>Review_Bean</code> tagged record into an <a href="https://code.google.com/p/ada-util/wiki/AdaBeans">Ada Bean</a> aware type. We will see in the next tutorial that we will only have to implement the <code>save</code> and <code>delete</code> operation that are described in this UML model.</p><h3>Makefile setup</h3><p>The <code>Makefile.in</code> that was generated by the <a href="https://code.google.com/p/ada-gen/">Dynamo</a> <code>create-project</code> command must be updated to setup a number of generation arguments for the UML to Ada code generator. Edit the <code>Makefile.in</code> to change:</p><pre><code>DYNAMO_ARGS=--package Atlas.Reviews.Models db uml/atlas.zargo
</code></pre><p>The <code>--package</code> option tells <a href="https://code.google.com/p/ada-gen/">Dynamo</a> to generate only the model for the specified package. The <code>db</code> directory is the directory that will contain the SQL model files.</p><p>Once the <code>Makefile.in</code> is updated, the <code>Makefile</code> must be updated by using the following command:</p><pre><code>./config.status
</code></pre><p>Or if you prefer, you may run again the <code>configure</code> script to re-configure the whole project.</p><h3>We need the code!!</h3><p>To run the generator, we can use the <code>generate</code> make target:</p><pre><code>make generate
</code></pre><p>The <a href="http://code.google.com/p/ada-gen/">Dynamo</a> code generator reads the file <code>uml/atlas.zargo</code> and the UML model it contains and generates:</p><ul><li>the Ada package <code>Atlas.Reviews.Models</code> which contains the definition of the <code>Review</code> table. The model files are created in the directory <code>src/models</code> which is separate from your Ada sources.</li><li>the SQL files to create the MySQL or SQLite database. Depending on the <a href="http://code.google.com/p/ada-awa">AWA</a> modules which are used, the generated SQL files will contain additional tables that are used by the <a href="http://code.google.com/p/ada-awa">AWA</a> modules. The SQL files are generated in the <code>db/mysql</code> and <code>db/sqlite</code> directories.</li></ul><h3>Let's create the database</h3><p>Until now we designed our application UML model, we have our Ada code generated, but we need a database with the tables for our application. We can do this by using the <code>create-database</code> command in <a href="https://code.google.com/p/ada-gen/">Dynamo</a>. This command needs several arguments:</p><ol><li>The directory that contains the SQL model files. In our case, this is <code>db</code>.</li><li>The information to connect to the database, the database name, the user and its password. This information is passed in the form of a database connection string.</li><li>The name of the database administration account to connect to the server and create the new database.</li><li>The optional password for the database administration account.</li></ol><p>If the MySQL server is running on your host and the admin account does not have any password, you can use the following command:</p><pre><code>dynamo create-database db 'mysql://localhost/demo_atlas?user=demo&password=demo' root
</code></pre><p>The <code>create-database</code> creates the database (<code>demo_atlas</code>) with the tables that are necessary for the application. It also creates the <code>demo</code> user and give it the necessary MySQL grants to connect to the <code>demo_atlas</code> database.</p><h3>The Review Web Application UML video</h3><p>To help you in building the UML model and see who the whole process looks like in reality, I've created the following short video that details the above tutorial steps.</p><iframe width="560" height="315" src="https://www.youtube.com/embed/Mwo6Q2NCsIY" frameborder="0" allowfullscreen></iframe>
<h3>Conclusion</h3><p>Thanks to <a href="http://argouml.tigris.org/">ArgoUML</a> and <a href="https://code.google.com/p/ada-gen/">Dynamo</a>, generating the Ada model and database tables becomes a simple and fun task. We have not written any line of code yet in this Review Web Application project, everything has been generated but we achieved a big progress:</p><ul><li>The Review Web Application server is built and can be launched,</li><li>The database is initialized and contains our application data model schema.</li></ul><p>The next tutorial will explain how to design the review form, implement the operations to create and populate the database with the new review.</p></div> Ada Web Application: Setting up the projecturn:md5:436ec20981effd8148f585ca1f7fbe732014-05-10T15:48:00+00:002014-05-10T15:48:00+00:00Stephane CarrezTutorialAda
<div class="post-text"><p><a href="http://code.google.com/p/ada-awa">Ada Web Application</a> is a complete framework that allows to write web applications using the Ada language. Through a complete web application, the tutorial explains various aspects in setting up and building an application by using <a href="http://code.google.com/p/ada-awa">AWA</a>. The tutorial is split in several articles and they are completed by short videos to show how easy the whole process is.</p><p>The tutorial assumes that you have already installed the following software on your computer:</p><ul><li>The <a href="http://libre.adacore.com/tools/gnat-gpl-edition/">GNAT Ada compiler</a>,</li><li>The <a href="http://libre.adacore.com/tools/gnatbench/">Eclipse and GNATbench</a> plugin,</li><li>The <a href="http://argouml.tigris.org/">ArgoUML</a> modelization tool,</li><li>The <a href="http://code.google.com/p/ada-awa">Ada Web Application</a> framework and its associated dependencies (<a href="http://libre.adacore.com/tools/xmlada/">XML/Ada</a> and <a href="http://libre.adacore.com/tools/aws/">AWS</a>),</li><li>The <a href="https://code.google.com/p/ada-gen/">Dynamo</a> code generator.</li></ul><h3>The review web application</h3><p>The review web application allows users to write reviews about a product, a software or a web site and share them to the Internet community. The community can read the review, participate by adding comments and voting for the reviewed product or software.</p><p><div class="wiki-img-center"><div class="wiki-img-inner"><img src="/images/samples/demo-awa-use-case.png" longdesc="Review Web Application Use Cases" alt="demo-awa-use-case.png"></img></div></div></p><p>The <a href="http://code.google.com/p/ada-awa">AWA</a> framework provides several modules that are ready to be used by our application. The login and user management is handled by the framework so this simplifies a lot the design of our application. We will see in the tutorial how we can leverage this to our review application.</p><p>Because users of our review web application have different roles, we will need permissions to make sure that only reviewers can modify a review. We will see how the <a href="http://code.google.com/p/ada-awa">AWA</a> framework leverages the <a href="https://code.google.com/p/ada-security/">Ada Security</a> library to enforce the permissions.</p><p>The <a href="http://code.google.com/p/ada-awa">AWA</a> framework also integrates three other modules that we are going to use: the <a href="https://code.google.com/p/ada-awa/wiki/AWA_Tags">tags</a>, the <a href="https://code.google.com/p/ada-awa/wiki/AWA_Votes">votes</a> and the <a href="https://code.google.com/p/ada-awa/wiki/AWA_Comments">comments</a>.</p><p>Since many building blocks are already provided by the Ada framework, we will be able to concentrate on our own review application module.</p><h3>Project creation with Dynamo</h3><p>The first step is to create the new project. Since creating a project from scratch is never easy we will use the <a href="https://code.google.com/p/ada-gen/">Dynamo</a> tool to build our initial review web application. Dynamo is a command line tool that provides several commands that help in several development tasks. For the project creation we will give:</p><ul><li>the output directory,</li><li>the project name,</li><li>the license to be used for the project,</li><li>the project author's email address.</li></ul><p>Choose the project name with care as it defines the name of the Ada root package that will be used by the project. For the license, you have the choice between <a href="http://opensource.org/licenses/GPL-2.0">GPL v2</a>, <a href="http://opensource.org/licenses/GPL-3.0">GPL v3</a>, <a href="http://opensource.org/licenses/MIT">MIT</a>, <a href="http://opensource.org/licenses/BSD-3-Clause">BSD 3 clauses</a>, <a href="http://opensource.org/licenses/Apache-2.0">Apache 2</a> or some proprietary license.</p><pre><code>dynamo -o atlas create-project -l apache atlas Stephane.Carrez@gmail.com
</code></pre><p><i>(Of course, change the above email address by your own email address, this is an example!)</i></p><p>The Dynamo project creation will build the <code>atlas</code> directory and populate it with many files:</p><ul><li>A set of configure, Makefile, GNAT project files to build the project,</li><li>A set of Ada files to build your Ada web application,</li><li>A set of presentation files for the web application.</li></ul><p>Once the project is created, we must configure it to find the Ada compiler, libraries and so on. This is done by the following commands:</p><pre><code>cd atlas
./configure
</code></pre><p>At this step, you may even build your new project and start it. The <code>make</code> command will build the Ada files and create the <code>bin/atlas-server</code> executable that represents the web application.</p><pre><code>make generate
make
bin/atlas-server
</code></pre><p>Once the server is started, you may point your browser to the following location: <a href="http://localhost:8080/atlas/index.html">http://localhost:8080/atlas/index.html</a></p><h3>Creating the review module with Dynamo</h3><p>With the <a href="http://code.google.com/p/ada-awa">Ada Web Application</a> framework, a web application is composed of modules where each module brings a specific functionality to the application. <a href="http://code.google.com/p/ada-awa">AWA</a> provides a module for <a href="https://code.google.com/p/ada-awa/wiki/AWA_Users_Modules">user management</a>, another for <a href="https://code.google.com/p/ada-awa/wiki/AWA_Comments">comments</a>, <a href="https://code.google.com/p/ada-awa/wiki/AWA_Tags">tags</a>, <a href="https://code.google.com/p/ada-awa/wiki/AWA_Votes">votes</a>, and many others. The application can decide to use these modules or not. The <a href="http://code.google.com/p/ada-awa">AWA</a> module helps in defining the architecture and designing your web application.</p><p>For the review web application we will create our own module dedicated for the review management. The module will be an Ada child package of our root project package. From the Ada point of view, the final module will be composed of the following packages:</p><ul><li>A <code>Modules</code> package represents the business logic of the module. It is provides operations to access and manage the data owned by the module.</li><li>A <code>Beans</code> package holds the Ada beans that make the link between the presentation layer and business logic.</li><li>A <code>Models</code> package holds the data model to access the database content. This package is generated from UML and will be covered by a next tutorial.</li></ul><p>To help in setting up a new <a href="http://code.google.com/p/ada-awa">AWA</a> module, the Dynamo tool provides the <code>add-module</code> command. You just have to give the name of the module, which is the name of the Ada child package. Let's create our <code>reviews</code> module now:</p><pre><code>dynamo add-module reviews
</code></pre><p>The command generates the new <a href="http://code.google.com/p/ada-awa">AWA</a> module and modifies some existing files to register the new module in the application. You can build your web application at this stage even though the new module will not do anything yet for you.</p><h3>Eclipse setup</h3><p>Launch you Eclipse and create the new project by going to the <b>File -> New -> Project</b> menu. Choose the <b>Ada Project</b> and uncheck the <b>Use default location</b> checkbox so that you can browse your file system and select the <code>atlas</code> directory.</p><p>That's it. If everything went well, you should be able to see the projects files in the Eclipse project explorer.</p><p><div class="wiki-img-center"><div class="wiki-img-inner"><img src="/images/samples/demo-awa-eclipse-project-explorer.png" longdesc="Eclipse project explorer" alt="demo-awa-eclipse-project-explorer.png"></img></div></div></p><h3>The Review Web Application setup video</h3><p>To help you in setting up and see how the whole process looks like in reality, I've created the following short video that details the above tutorial steps.</p><div style='width: 100%; float: left;'>
<iframe width="560" height="315" src="https://www.youtube.com/embed/Qah-cyt7Oxc" frameborder="0" allowfullscreen></iframe>
</div>
<h3>Conclusion</h3><p>The whole process takes less than 3 minutes and gives you the basis to setup and build your new web application. The next tutorial will explain how to use the UML to design and generate the data model for our Review Web Application.</p></div> Reading a program symbol table with Ada BFDurn:md5:b3252b8f4bdaffabc698a9fdf276fa152012-11-11T09:26:00+00:002012-11-11T09:26:00+00:00Stephane CarrezCOFFELFTutorialbinutils
<div class="post-text"><p>The <a href="http://www.gnu.org/software/binutils/">GNU Binutils</a> provides support for reading and writing program files in various formats such as <a href="http://en.wikipedia.org/wiki/Executable_and_Linkable_Format">ELF</a>, <a href="http://en.wikipedia.org/wiki/COFF">COFF</a>. This support is known as the <b>BFD</b>, the Binary File Descriptor library (or the Big F*cking Deal). This article illustrates how an Ada application can use the BFD library to have access to a program symbol table.</p><h2>Declarations</h2><p>The <a href="https://github.com/stcarrez/ada-bfd">Ada BFD</a> library provides a set of Ada bindings that give access to the BFD library. A binary file such as an object file, an executable or an archive is represented by the <code>Bfd.Files.File_Type</code> limited type. The symbol table is represented by the <code>Bfd.Symbols.Symbol_Table</code> limited type. These two types hold internal data used and managed by the BFD library.</p><pre><code class="lang-ada">with Bfd.Files;
with Bfd.Sections;
with Bfd.Symbols;
...
File : Bfd.Files.File_Type;
Symbols : Bfd.Symbols.Symbol_Table;
</code></pre><h2>Opening the BFD file</h2><p>The <code>Open</code> procedure must be called to read the object or executable file whose path is given as argument. The <code>File_Type</code> parameter will be initialized to get access to the binary file information. The <code>Check_Format</code> function must then be called to let the BFD library gather the file format information and verify that it is an object file or an executable.</p><pre><code class="lang-ada">Bfd.Files.Open (File, Path, "");
if Bfd.Files.Check_Format (File, Bfd.Files.OBJECT) then
...
end if;
</code></pre><h2>Loading the symbol table</h2><p>The symbol table is loaded by using the <code>Read_Symbols</code> procedure.</p><pre><code class="lang-ada"> Bfd.Symbols.Read_Symbols (File, Symbols);
</code></pre><p>The resources used by the symbol table will be freed when the symbol table instance is finalized.</p><h2>Looking for a symbol</h2><p>Once the symbol table is loaded, we can use the <code>Get_Symbol</code> function to find a symbol knowing its name. If the symbol is not found, a <code>Null_Symbol</code> is returned.</p><pre><code class="lang-ada">Sym : Bfd.Symbols.Symbol := Bfd.Symbols.Get_Symbol (Symbols, "_main");
...
if Sym /= Bfd.Symbols.Null_Symbol then
-- Symbol found
end if;
</code></pre><p>Each symbol has the following set of information:</p><ul><li>A name (it may not be unique),</li><li>A set of flags that describe the symbol (global, local, weak, constructor, TLS, ...),</li><li>A symbol value,</li><li>A section to which the symbol belongs.</li></ul><pre><code class="lang-ada">Sec : constant Bfd.Sections.Section := Bfd.Symbols.Get_Section (Sym);
Flags : constant Bfd.Symbol_Flags := Bfd.Symbols.Get_Flags (Sym);
Value : constant Bfd.Symbol_Value := Bfd.Symbols.Get_Value (Sym);
</code></pre><p>Before interpreting and using the symbol value returned by <code>Get_Value</code>, you must look at the section to check for an undefined symbol. Indeed, undefined symbols being not yet resolved by the linker they have no value and no section. You can check that by using the <code>Is_Undefined_Section</code> function:</p><pre><code class="lang-ada">if Bfd.Sections.Is_Undefined_Section (Sec) then
Ada.Text_IO.Put_Line ("undefined symbol");
end if;
</code></pre><p>When the symbol is defined, you must look at the flags and also the section information to know more about it.</p><pre><code class="lang-ada">if (Flags and Bfd.Symbols.BSF_GLOBAL) /= 0 then
Ada.Text_IO.Put_Line ("global symbol in section "
& Bfd.Sections.Get_Name (Sec)
& " := " & Bfd.Symbol_Value'Image (Value));
elsif (Flags and Bfd.Symbols.BSF_LOCAL) /= 0 then
Ada.Text_IO.Put_Line ("local symbol in section "
& Bfd.Sections.Get_Name (Sec)
& " := " & Bfd.Symbol_Value'Image (Value));
else
Ada.Text_IO.Put_Line ("other symbol in section "
& Bfd.Sections.Get_Name (Sec)
& " := " & Bfd.Symbol_Value'Image (Value));
end if;
</code></pre><h2>Conclusion and references</h2><p>Reading an executable symbol table has been made fairly simple with the use of the <a href="https://github.com/stcarrez/ada-bfd">Ada BFD</a> library. Furthermore, the library allows to scan the sections, read their content and even use the BFD disassembler.</p><p><a href="https://github.com/stcarrez/ada-bfd/blob/master/samples/symbol.adb">symbol.adb</a><br><a href="http://sourceware.org/binutils/docs/bfd/index.html">BFD Documentation</a></p></div> Using the Facebook APIurn:md5:75dbf3bff7298773c7a99484b82a34e92012-06-05T21:59:07+00:002012-06-05T21:59:07+00:00Stephane CarrezFacebookOAuthTutorial
<div class="post-text"><p>Through this article you will learn how to use the <a href="http://oauth.net/">OAuth 2.0</a> framework to let an application access service provider APIs such as Facebook API, Google+ API and others. Althought this article uses Ada as programming language and Facebook as service provider, most part also applies to other programming languages and other service providers.</p><h2>Overview</h2><p><a href="http://oauth.net/">OAuth 2.0</a> is an open standard for authorization. It is used by service providers as authorization mechanism for most of their APIs. The authorization workflow is depicted below:</p><ul><li>[1], first a user goes in the application which displays a link to the OAuth API provider asking the user to grant access to his data for the application,</li><li>[2], the user clicks on the authenticate link and grants access to the application,</li><li>[3.1], The OAuth server redirects the user to a callback URL and it provides an application grant code,</li><li>[3.3], The application ask the API provider to transform the grant code to an access token,</li><li>[4] The application invokes the API provider with the access token</li></ul><p><div class="wiki-img-center"><div class="wiki-img-inner"><img src="/images/samples/.OAuth_m.jpg" longdesc="OAuth Workflow, juin 2012" alt="OAuth Workflow"></img></div></div></p><h2>Registering the application</h2><p>The first step is to register the application in the service provider (Facebook, Google+, Twitter, ...). This registration process is specific to the provider. From this registration, several elements will be defined:</p><ul><li>An application id is allocated, This identifier is public. This is the <code>client_id</code> parameter in OAuth 2.0.</li><li>An application secret is defined. It must be kept private to the application. This is the <code>secret</code> parameter in OAuth 2.0.</li><li>A callback URL or domain name is registered in the service provider. As far as I'm concerned, I had to register the domain <code>demo.vacs.fr</code>.</li></ul><h2>Facebook OAuth</h2><p>For the OAuth authorization process, we will use the <a href="http://code.google.com/p/ada-security">Ada Security</a> library and its <code>Application</code> type. We will extend that type to expose some EL variables and an EL method that will be used in the authorization process. The <a href="http://blog.vacs.fr/index.php?post/2011/05/02/Ada-Server-Faces-Application-Example-part-3%3A-the-action-bean">Ada Server Faces Application Example part 3: the action bean</a> explains how to do that and many details will no be covered by this article.</p><pre><code class="lang-ada">type Facebook_Auth is new Security.OAuth.Clients.Application
and Util.Beans.Basic.Readonly_Bean
and Util.Beans.Methods.Method_Bean with private;
FB_Auth : aliased Facebook.Facebook_Auth;
</code></pre><p>Before anything we have to initialize the <code>Application</code> type to setup the application identifier, the application secret, the provider URL and a callback URL.</p><pre><code class="lang-ada">FB_Auth.Set_Application_Identifier ("116337738505130");
FB_Auth.Set_Application_Secret ("xxxxxxxxxxx");
FB_Auth.Set_Application_Callback ("http://demo.vacs.fr/oauth/localhost:8080/demo/oauth_callback.html");
</code></pre><p>The first step in the OAuth process is to build the authorization page with the link that redirects the user to the service provider OAuth authorization process. The link must be created by using the <code>Get_State</code> and <code>Get_Auth_Params</code> functions provided by the <code>Application</code> type. The first one generates a secure unique key that will be returned back by the service provider. The second one builds a list of request parameters that are necessary for the service provider to identify the application and redirect the user back to the application once the authentication is done.</p><pre><code class="lang-ada">Id : constant String := "...";
State : constant String := FB_Auth.Get_State (Id);
Params : constant String := FB_Auth.Get_Auth_Params (State, "read_stream");
</code></pre><p>For a Facebook authorization process, the URI would be created as follows:</p><pre><code class="lang-ada">URI : constant String := "https://www.facebook.com/dialog/oauth?" & Params;
</code></pre><p>For another service provider, the process is similar but the URL is different.</p><h3>OAuth callback</h3><p>When the user has granted access to his data, he will be redirected to the callback defined by the application. Most service providers will require that the OAuth callback be a public URL. If you want to run you application on <code>localhost</code> (which is the case when you are developing), you will need a second redirection. If you are using the Apache server, you can easily setup a rewrite rule:</p><pre><code>RewriteRule ^/oauth/localhost:([0-9]+)/(.*) http://localhost:$1/$2 [L,R=302]
</code></pre><p>With the above rewrite rule, the callback given to the OAuth provider would look like:</p><pre><code>http://demo.vacs.fr/oauth/localhost:8080/demo/oauth_callback.html
</code></pre><p>The OAuth provider will first redirect to the public internet site which will redirect again to localhost and port 8080.</p><h3>Getting the OAuth access token</h3><p>The next step is to receive the <code>code</code> parameter from the callback which grants the application access to the service provider API. For this, we will use an XHTML view file and a view action that will be executed when the page is displayed. When this happens, the EL method <code>authenticate</code> will be called on the <code>facebook</code> bean (ie, our <code>FB_Auth</code> instance).</p><pre><code><f:view xmlns:f="http://java.sun.com/jsf/core">
<f:metadata>
<f:viewAction action="#{facebook.authenticate}"/>
</f:metadata>
</f:view>
</code></pre><p>The <code>Authenticate</code> procedure extracts from the request the OAuth <code>state</code> and <code>code</code> parameters. It verifies that the <code>state</code> parameter is a valid key that we submitted and it makes a HTTP POST request on the OAuth service provider to transform the <code>code</code> into an access token. This step is handled by the <a href="http://code.google.com/p/ada-security">Ada Security</a> library through the <code>Request_Access_Token</code> operation.</p><pre><code class="lang-ada">procedure Authenticate (From : in out Facebook_Auth;
Outcome : in out Ada.Strings.Unbounded.Unbounded_String) is
use type Security.OAuth.Clients.Access_Token_Access;
F : constant ASF.Contexts.Faces.Faces_Context_Access := ASF.Contexts.Faces.Current;
State : constant String := F.Get_Parameter (Security.OAuth.State);
Code : constant String := F.Get_Parameter (Security.OAuth.Code);
Session : ASF.Sessions.Session := F.Get_Session;
begin
Log.Info ("Auth code {0} for state {1}", Code, State);
if Session.Is_Valid then
if From.Is_Valid_State (Session.Get_Id, State) then
declare
Acc : Security.OAuth.Clients.Access_Token_Access
:= From.Request_Access_Token (Code);
begin
if Acc /= null then
Log.Info ("Access token is {0}", Acc.Get_Name);
Session.Set_Attribute ("access_token",
Util.Beans.Objects.To_Object (Acc.Get_Name));
end if;
end;
end if;
end if;
end Authenticate;
</code></pre><p>The access token must be saved in the user session or another per-user safe storage so that it can be retrieved later on. The access token can expire and if this happens a fresh new access token must be obtained.</p><h2>Getting the Facebook friends</h2><p>Until now we have dealt with the authorization process. Let's look at using the service provider API and see how the <a href="http://code.google.com/p/ada-util">Ada Utility Library</a> will help in this task.</p><h3>Defining the Ada beans</h3><p>To represent the API result, we will use an Ada bean object that can easily be used from a presentation page. For the Facebook friend, a name and an identifier are necessary:</p><pre><code class="lang-ada">type Friend_Info is new Util.Beans.Basic.Readonly_Bean with record
Name : Util.Beans.Objects.Object;
Id : Util.Beans.Objects.Object;
end record;
type Friend_Info_Access is access all Friend_Info;
</code></pre><p>Having a bean type to represent each friend, we will get a list of friends by instantiating the Ada bean <code>Lists</code> package:</p><pre><code class="lang-ada">package Friend_List is new Util.Beans.Basic.Lists (Element_Type => Friend_Info);
</code></pre><h3>Mapping JSON or XML to Ada</h3><p>The Ada Utility library provides a mechanism that parses JSON or XML and map the result in Ada objects. To be able to read the Facebook friend definition, we have to define an enum and implement a <code>Set_Member</code> procedure. This procedure will be called by the JSON/XML parser when a given data field is recognized and extracted.</p><pre><code class="lang-ada">type Friend_Field_Type is (FIELD_NAME, FIELD_ID);
procedure Set_Member (Into : in out Friend_Info;
Field : in Friend_Field_Type;
Value : in Util.Beans.Objects.Object);
</code></pre><p>The <code>Set_Member</code> procedure is rather simple as it just populates the data record with the value.</p><pre><code class="lang-ada">procedure Set_Member (Into : in out Friend_Info;
Field : in Friend_Field_Type;
Value : in Util.Beans.Objects.Object) is
begin
case Field is
when FIELD_ID =>
Into.Id := Value;
when FIELD_NAME =>
Into.Name := Value;
end case;
end Set_Member;
</code></pre><p>The mapper is a package that defines and controls how to map the JSON/XML data fields into the Ada record by using the <code>Set_Member</code> operation. We just have to instantiate the package. The <code>Record_Mapper</code> generic package will map JSON/XML into the Ada record and the <code>Vector_Mapper</code> will map a list of JSON/XML elements following a given structure into an Ada vector.</p><pre><code class="lang-ada">package Friend_Mapper is
new Util.Serialize.Mappers.Record_Mapper (Element_Type => Friend_Info,
Element_Type_Access => Friend_Info_Access,
Fields => Friend_Field_Type,
Set_Member => Set_Member);
package Friend_Vector_Mapper is
new Util.Serialize.Mappers.Vector_Mapper (Vectors => Friend_List.Vectors,
Element_Mapper => Friend_Mapper);
</code></pre><p>Now we need to control how the JSON/XML fields are mapped to our Ada fields. For this we have to setup the mapping. The Facebook JSON structure is so simple that we can use the default mapping provided by the mapper. For this we use the <code>Add_Default_Mapping</code> procedure. We also have to tell what is the JSON mapping used by the friend vector mapper.</p><pre><code class="lang-ada">Friend_Map : aliased Friend_Mapper.Mapper;
Friend_Vector_Map : aliased Friend_Vector_Mapper.Mapper;
...
Friend_Map.Add_Default_Mapping;
Friend_Vector_Map.Set_Mapping (Friend_Map'Access);
</code></pre><h3>Creating the REST client</h3><p>Now it would be nice if we could get an operation that invokes the service provider API through an HTTP GET operation and put the result in our Ada object. The Facebook friends API returns a list of friends which correspond to our <code>Friend_List.Vectors</code>. To get our operation, we just have to instantiate the <code>Rest_Get_Vector</code> operation with our vector mapper (the generic parameter is a package name).</p><pre><code class="lang-ada">procedure Get_Friends is
new Util.Http.Rest.Rest_Get_Vector (Vector_Mapper => Friend_Vector_Mapper);
</code></pre><h3>Calling the REST client</h3><p>Invoking the service provider API is now as simple as calling a procedure. The URI must include the access token as parameter. The HTTP GET operation must be made using SSL/TLS since this is part of OAuth 2.0.</p><pre><code> List : Friend_List.List_Bean;
...
Get_Friends ("https://graph.facebook.com/me/friends?access_token="
& Token,
Friend_Vector_Map'Access,
"/data",
List.List'Access);
</code></pre><p>Now you are ready to use and access the user's data as easily as other information...</p><h3>References</h3><p><a href="http://code.google.com/p/ada-asf/source/browse/trunk/samples/beans/facebook.ads">facebook.ads</a><br><a href="http://code.google.com/p/ada-asf/source/browse/trunk/samples/beans/facebook.adb">facebook.adb</a><br><a href="http://developers.facebook.com/docs/reference/api/">Facebook API</a></p></div> Process creation in Java and Adaurn:md5:47e5bac2c4b025b8036317e316d097612012-03-16T22:18:07+00:002012-03-16T22:18:07+00:00Stephane CarrezAdaJavaTutorialpipeprocess
<div class="post-text"><p>When developping and integrating applications together it is often useful to launch an external program. By doing this, many integration and implementation details are simplified (this integration technic also avoids license issues for some open source software integration).</p><p>This article explains how to launch an external program in Java and Ada and be able to read the process output to get the result.</p><h2>Java Process Creation</h2><p>The process creation is managed by the <code>ProcessBuilder</code> Java class. An instance of this class holds the necessary information to create and launch a new process. This includes the command line and its arguments, the standard input and outputs, the environment variables and the working directory.</p><p>The process builder instance is created by specifying the command and its arguments. For this the constructor accepts a variable list of parameters of type <code>String</code>.</p><pre><code class="lang-java">import java.lang.ProcessBuilder;
...
final String cmd = "ls";
final String arg1 = "-l";
final ProcessBuilder pb = new ProcessBuilder(cmd, arg1);
</code></pre><p>When the process builder is initialized, we can invoke the <code>start</code> method to create a new process. Each process is then represented by an instance of the <code>Process</code> class. It is possible to invoke <code>start</code> serveral times and each call creates a new process. It is necessary to catch the <code>IOException</code> which can be raised if the process cannot be created.</p><pre><code class="lang-java">import java.lang.Process;
...
try {
final Process p = pb.start();
...
} catch (final IOException ex) {
System.err.println("IO error: " + ex.getLocalizedMessage());
}
</code></pre><p>The <code>Process</code> class gives access to the process output through an input stream represented by the <code>InputStream</code> class. With this input stream, we can read what the process writes on its output. We will use a <code>BufferedReader</code> class to read that output line by line.</p><pre><code class="lang-java">import java.io.*;
...
final InputStream is = p.getInputStream();
final BufferedReader reader = new BufferedReader(new InputStreamReader(is));
</code></pre><p>By using the <code>readLine</code> method, we can read a new line after each call. Once the whole stream is read, we have to close it. Closing the <code>BufferedReader</code> will close the <code>InputStream</code> associated with it.</p><pre><code class="lang-java"> String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
reader.close();
</code></pre><p>Last step is to wait for the process termination and get the exit status: we can use the <code>waitFor</code> method. Since this method can be interrupted, we have to catch the <code>InterruptedException</code>.</p><pre><code class="lang-java"> try {
...
final int exit = p.waitFor();
if (exit != 0) {
System.err.printf("Command exited with status %d\n", exit);
}
} catch (final InterruptedException ex) {
System.err.println("Launch was interrupted...");
}
</code></pre><p>You can get the complete source from the file: <a href="/images/samples/Launch.java">Launch.java</a></p><h2>Ada Process Creation</h2><p>For the Ada example, we will create an application that invokes the <code>nslookup</code> utility to resolve a set of host names. The list of host names is provided to <code>nslookup</code> by writing on its standard input and the result is collected by reading the output.</p><p>We will use the <code>Pipe_Stream</code> to launch the process, write on its input and read its output at the same time. The process is launched by calling the <code>Open</code> procedure and specifying the pipe redirection modes: <code>READ</code> is for reading the process output, <code>WRITE</code> is for writing to its input and <code>READ_WRITE</code> is for both.</p><pre><code class="lang-ada">with Util.Processes;
with Util.Streams.Pipes;
...
Pipe : aliased Util.Streams.Pipes.Pipe_Stream;
Pipe.Open ("nslookup", Util.Processes.READ_WRITE);
</code></pre><p>We can read or write on the pipe directly but using a <code>Print_Stream</code> to write the text and the <code>Buffered_Stream</code> to read the result simplifies the implementation. Both of them are connected to the pipe: the <code>Print_Stream</code> will use the pipe output stream and the <code>Buffered_Stream</code> will use the pipe input stream.</p><pre><code class="lang-ada">with Util.Streams.Buffered;
with Util.Streams.Texts;
...
Buffer : Util.Streams.Buffered.Buffered_Stream;
Print : Util.Streams.Texts.Print_Stream;
begin
-- Write on the process input stream
Buffer.Initialize (null, Pipe'Unchecked_Access, 1024);
Print.Initialize (Pipe'Unchecked_Access);
</code></pre><p>Before reading the process output, we send the input data to be solved by the process. By closing the print stream, we also close the pipe output stream, thus closing the process standard input.</p><pre><code class="lang-ada"> Print.Write ("www.google.com" & ASCII.LF);
Print.Write ("set type=NS" & ASCII.LF);
Print.Write ("www.google.com" & ASCII.LF);
Print.Write ("set type=MX" & ASCII.LF);
Print.Write ("www.google.com" & ASCII.LF);
Print.Close;
</code></pre><p>We can now read the program output by using the <code>Read</code> procedure and get the result in the <code>Content</code> string. The <code>Close</code> procedure is invoked on the pipe to close the pipe (input and output) and wait for the application termination.</p><pre><code class="lang-ada"> Content : Unbounded_String;
-- Read the 'nslookup' output.
Buffer.Read (Content);
Pipe.Close;
</code></pre><p>Once the process has terminated, we can get the exit status by using the <code>Get_Exit_Status</code> function.</p><pre><code class="lang-ada"> Ada.Text_IO.Put_Line ("Exit status: "
& Integer'Image (Pipe.Get_Exit_Status));
</code></pre><h4>References</h4><p><a href="http://code.google.com/p/ada-util/source/browse/trunk/samples/launch.adb">launch.adb</a><br><a href="http://code.google.com/p/ada-util/source/browse/trunk/src/util-streams-pipes.ads">util-streams-pipes.ads</a><br><a href="http://code.google.com/p/ada-util/source/browse/trunk/src/util-streams-buffered.ads">util-streams-buffered.ads</a><br><a href="http://code.google.com/p/ada-util/source/browse/trunk/src/util-streams-texts.ads">util-streams-texts.ads</a></p></div> Ada perfect hash generation with gperfhashurn:md5:7ad85c1533dcc1b5e103c64539e26bf72012-01-17T21:53:00+00:002012-01-17T21:53:00+00:00Stephane CarrezAdageneratorTutorial
<div class="post-text"><p>A <a href="https://en.wikipedia.org/wiki/Perfect_hash_function">perfect hash function</a> is a function that returns a distinct hash number for each keyword of a well defined set. <a href="https://www.gnu.org/software/gperf/">gperf</a> is famous and well known perfect hash generator used for C or C++ languages. Ada is not supported.</p><p>The <a href="https://github.com/stcarrez/ada-util/blob/master/samples/gperfhash.adb">gperfhash</a> is a sample from the <a href="https://github.com/stcarrez/ada-util">Ada Utility Library</a> which generates an Ada package that implements such perfect hash operation. It is not as complete as gperf but allows to easily get a hash operation. The gperfhash tool uses the GNAT package <code>GNAT.Perfect_Hash_Generators</code>.</p><h4>Pre requisite</h4><p>Since the gperfhash tool is provided by the Ada Util samples, you must build these samples with the following command:</p><pre><code>$ gnatmake -Psamples
</code></pre><h4>Define a keyword file</h4><p>First, create a file which contains one keyword on each line. For example, let's write a <code>keywords.txt</code> file which contains the following three keywords:</p><pre><code>int
select
print
</code></pre><h4>Generate the package</h4><p>Run the gperfhash tool and give it the package name.</p><pre><code>$ gperfhash -p Hashing keywords.txt
</code></pre><p>The package defines a <code>Hash</code> and an <code>Is_Keyword</code> function. The Hash function returns a hash number for each string passed as argument. The hash number will be different for each string that matches one of our keyword. You can give a string not in the keyword list, in that case the hash function will return a number that collides with a hash number of one or our keyword.</p><p>The <code>Is_Keyword</code> function allows to check whether a string is a keyword of the list. This is very useful when you just want to know whether a string is a reserved keyword in some application.</p><p>The package specification is the following:</p><pre><code class="lang-ada">-- Generated by gperfhash
package Hashing is
function Hash (S : String) return Natural;
-- Returns true if the string <b>S</b> is a keyword.
function Is_Keyword (S : in String) return Boolean;
type Name_Access is access constant String;
type Keyword_Array is array (Natural range <>) of Name_Access;
Keywords : constant Keyword_Array;
private
...
end Hashing;
</code></pre><h4>How to use the hash</h4><p>Using the perfect hash generator is simple:</p><pre><code class="lang-ada">with Hashing;
if Hashing.Is_Keyword (S) then
-- 'S' is one of our keyword
else
-- No, it's not a keyword
end if;
</code></pre></div> Aunit vs Ahvenurn:md5:d62b95ef4e6756a3324521af67d916312011-11-27T22:32:00+00:002011-11-27T22:32:00+00:00Stephane CarrezAdatestingTutorial
<div class="post-text"><p>AUnit and Ahven are two testing frameworks for Ada. Both of them are inspired from the well known <a href="https://www.junit.org/">JUnit</a> Java framework. Having some issues with the Aunit testing framework, I wanted to explore the use of Ahven. This article gives some comparison elements between the two unit test frameworks. I do not pretend to list all the differences since both frameworks are excellent.</p><p>Writing a unit test is equally simple in both frameworks. They however have some differences that may not be visible at the first glance.</p><h2>AUnit</h2><p><a href="https://libre.adacore.com/libre/tools/aunit/">AUnit</a> is a unit test framework developped by Ed Falis and maintained by <a href="http://www.adacore.com/home/">AdaCore</a>. It is distributed under the <a href="https://www.opensource.org/licenses/gpl-2.0.php">GNU GPL License</a>.</p><p>Some good points:</p><ul><li>AUnit has a good support to report where a test failed. Indeed, the <code>Assert</code> procedures will report the source file and line number where the assertion failed.</li><li>AUnit is also able to dump the exception stack trace in symbolic form. This is useful to find out quickly the source of a problem.</li></ul><p>Some bad points:</p><ul><li>AUnit has several memory leaks which is quite annoying when you want to track memory links with <code>valgrind</code>.</li><li>AUnit does not integrate easily with JUnit-based XML tools. In particular the XML file it creates can be invalid in some cases (special characters in names). More annoying is the fact that the XML format is not compatible with JUnit XML format.</li></ul><h2>Ahven</h2><p><a href="http://ahven.stronglytyped.org/">Ahven</a> is another unit test framework developed by Tero Koskinen. It is distributed under the permissive <a href="https://www.opensource.org/licenses/ISC">ISC License</a>.</p><p>Some good points:</p><ul><li>Ahven license is a better model for proprietary unit tests.</li><li>Ahven generates XML result files which are compatible with Junit XML result files. Integration with automatic build tools such as <a href="https://jenkins-ci.org/">Jenkins</a> is easier.</li><li>Ahven XML result files can integrate the test output (as in JUnit). This is useful to analyze a problem.</li><li>Ahven has a test case timeout which is useful to detect and stop blocking tests.</li></ul><p>Some bad points:</p><ul><li>The lack of precise information in message (source line, exception trace) can be annoying to find out why a test failed.</li></ul><h2>Don't choose and be prepared to use both with Ada Util!</h2><p>The unit tests I've written were done for AUnit and I had arround 329 tests to migrate. To help the migration to Ahven, I wrote a <code>Util.XUnit</code> package which exposes a common interface on top of AUnit or Ahven. It turns out that this is easy and quite small. The package has one specific implementation (spec+body) for both frameworks. All the unit tests have to use it instead of the AUnit or Ahven packages.</p><p>The <a href="https://github.com/stcarrez/ada-util/blob/master/src/tests/aunit/util-xunit.ads">Aunit implementation (util-xunit.ads)</a> defines several types which are also defined in the Ahven implementation.</p><pre><code>package Util.XUnit is
...
subtype Status is AUnit.Status;
Success : constant Status := AUnit.Success;
Failure : constant Status := AUnit.Failure;
subtype Message_String is AUnit.Message_String;
subtype Test_Suite is AUnit.Test_Suites.Test_Suite;
subtype Access_Test_Suite is AUnit.Test_Suites.Access_Test_Suite;
type Test_Case is abstract new AUnit.Simple_Test_Cases.Test_Case with null record;
type Test is abstract new AUnit.Test_Fixtures.Test_Fixture with null record;
...
end Util.XUnit;
</code></pre><p>The XUnit implementation for Ahven is a little bit more complex because all my tests were using AUnit interface, I decided to keep almost that API and thus I had to simulate what is missing or is different.</p><p><a href="https://github.com/stcarrez/ada-util/blob/master/src/tests/ahven/util-xunit.ads">Ahven implementation: util-xunit.ads</a></p><pre><code>package Util.XUnit is
...
type Status is (Success, Failure);
subtype Message_String is String;
subtype Test_Suite is Ahven.Framework.Test_Suite;
type Access_Test_Suite is access all Test_Suite;
type Test_Case is abstract new Ahven.Framework.Test_Case with null record;
type Test is new Ahven.Framework.Test_Case with null record;
...
end Util.XUnit;
</code></pre><p>The choice of the unit test framework is done when the <a href="https://github.com/stcarrez/ada-util">Ada Utility library</a> is configured.</p></div> Thread safe object pool to manage scarce resource in application serversurn:md5:03da6d22b8b5beba466e5b66f76388682011-05-29T21:31:58+00:002011-05-29T21:31:58+00:00Stephane CarrezAdaJavaTutorialmultiprocessing
<div class="post-text"><h3>Problem Description</h3><p>In application servers some resources are expensive and they must be shared. This is the case for a database connection, a frame buffer used for image processing, a connection to a remote server, and so on. The problem is to make available these scarce resources in such a way that:</p><ul><li>a resource is used by only one thread at a time,</li><li>we can control the maximum number of resources used at a time,</li><li>we have some flexibility to define such maximum when configuring the application server,</li><li>and of course the final solution is thread safe.</li></ul><p>The common pattern used in such situation is to use a thread-safe <a href="http://en.wikipedia.org/wiki/Object_pool_pattern">pool of objects</a>. Objects are picked from the pool when needed and restored back to the pool when they are no longer used.</p><h4>Java thread safe object pool</h4><p>Let's see how to implement our object pool in Java. We will use a generic class declaration into which we define a fixed array of objects. The pool array is allocated by the constructor and we will assume it will never change (hence the <code>final</code> keyword).</p><pre><code class="lang-java">public class Pool<T> {
private final T[] objects;
public Pool<T>(int size) {
objects = new T[size];
}
...
}
</code></pre><p>First, we need a <code>getInstance</code> method that picks an object from the pool. The method must be thread safe and it is protected by the <code>synchronized</code> keyword. It there is no object, it has to wait until an object is available. For this, the method invokes <code>wait</code> to sleep until another thread releases an object. To keep track of the number of available objects, we will use an available counter that is decremented each time an object is used.</p><pre><code class="lang-java"> private int available = 0;
private int waiting = 0;
public synchronized T getInstance() {
while (available == 0) {
waiting++;
wait();
waiting--;
}
available--;
return objects[available];
}
</code></pre><p>To know when to wakeup a thread, we keep track of the number of waiters in the <code>waiting</code> counter. A loop is also necessary to make sure we have an available object after being wakeup. Indeed, there is no guarantee that after being notified, we have an available object to return. The call to <code>wait</code> will release the lock on the pool and puts the thread is wait mode.</p><p>Releasing the object is provided by <code>release</code>. The object is put backed in the pool array and the available counter incremented. If some threads are waiting, one of them is awaken by calling <code>notify</code>.</p><pre><code class="lang-java"> public synchronized void release(T obj) {
objects[available] = obj;
available++;
if (waiting) {
notify();
}
}
</code></pre><p>When the application is started, the pool is initialized and some pre-defined objects are inserted.</p><pre><code class="lang-java"> class Item { ... };
...
Pool<Item> pool = new Pool<Item>(10);
for (int i = 0; i < 10; i++) {
pool.release(new Item());
}
</code></pre><h4>Ada thread safe pool</h4><p>The Ada object pool will be defined in a generic package and we will use a <a href="http://www.adaic.org/resources/add_content/standards/05rm/html/RM-9-4.html">protected type</a>. The protected type will guarantee the thread safe behavior of the implementation by making sure that only one thread executes the procedures.</p><pre><code class="lang-ada">generic
type Element_Type is private;
package Util.Concurrent.Pools is
type Element_Array_Access is private;
Null_Element_Array : constant Element_Array_Access;
...
private
type Element_Array is array (Positive range <>) of Element_Type;
type Element_Array_Access is access all Element_Array;
Null_Element_Array : constant Element_Array_Access := null;
end Util.Concurrent.Pools;
</code></pre><p>The Ada protected type is simple with three procedures, we get the <code>Get_Instance</code> and <code>Release</code> as in the Java implementation. The <code>Set_Size</code> will take care of allocating the pool array (a job done by the Java pool constructor).</p><pre><code class="lang-ada">protected type Pool is
entry Get_Instance (Item : out Element_Type);
procedure Release (Item : in Element_Type);
procedure Set_Size (Capacity : in Positive);
private
Available : Natural := 0;
Elements : Element_Array_Access := Null_Element_Array;
end Pool;
</code></pre><p>First, the <code>Get_Instance</code> procedure is defined as an <code>entry</code> so that we can define a condition to enter in it. Indeed, we need at least one object in the pool. Since we keep track of the number of available objects, we will use it as the entry condition. Thanks to this entry condition, the Ada implementation is a lot easier.</p><pre><code class="lang-ada">protected body Pool is
entry Get_Instance (Item : out Element_Type) when Available > 0 is
begin
Item := Elements (Available);
Available := Available - 1;
end Get_Instance;
...
end Pool;
</code></pre><p>The <code>Release</code> operation is also easier as there is no need to wakeup any thread: the Ada runtime will do that for us.</p><pre><code class="lang-ada">protected body Pool is
procedure Release (Item : in Element_Type) is
begin
Available := Available + 1;
Elements (Available) := Item;
end Release;
end Pool;
</code></pre><p>The pool is instantiated:</p><pre><code class="lang-ada">type Connection is ...;
package Connection_Pool is new Util.Concurrent.Pools (Connection);
</code></pre><p>And a pool object can be declared and initialized with some default object:</p><pre><code class="lang-ada">P : Connection_Pool.Pool;
C : Connection;
...
P.Set_Size (Capacity => 10);
for I in 1 .. 10 loop
...
P.Release (C);
end loop;
</code></pre><h4>References</h4><p><a href="http://code.google.com/p/ada-util/source/browse/trunk/src/util-concurrent-pools.ads">util-concurrent-pools.ads</a><br><a href="http://code.google.com/p/ada-util/source/browse/trunk/src/util-concurrent-pools.adb">util-concurrent-pools.adb</a></p></div> Ada Server Faces Application Example part 4: the serverurn:md5:153b8e86130498670410be55d8f325db2011-05-18T19:56:59+00:002011-05-18T19:56:59+00:00Stephane CarrezAdaJSFServletTutorial
<div class="post-text"><p>In previous articles, we have seen that an <a href="http://code.google.com/p/ada-asf/">Ada Server Faces</a> application has a <a href="http://blog.vacs.fr/index.php?post/2011/03/21/Ada-Server-Faces-Application-Example">presentation layer</a> composed of XHTML and CSS files. Similar to <a href="http://www.javaserverfaces.org/">Java Server Faces</a>, <a href="http://code.google.com/p/ada-asf/">Ada Server Faces</a> is a component-based model and we saw how to write the <a href="http://blog.vacs.fr/index.php?post/2011/04/10/Ada-Server-Faces-Application-Example-part-2%3A-the-Ada-beans">Ada beans</a> used by the application. Later, we also learnt how <a href="http://blog.vacs.fr/index.php?post/2011/05/02/Ada-Server-Faces-Application-Example-part-3%3A-the-action-bean">an action bean</a> can have a procedure executed when a button is pressed. Now, how can all these stuff fit together?</p><p>Well, to finish our cylinder volume example, we will see how to put everything together and get our running web application.</p><h3>Application Initialization</h3><p>An <a href="http://code.google.com/p/ada-asf/">Ada Server Faces</a> Application is represented by the <code>Application</code> type which holds all the information to process and dispatch requests. First, let's declare a variable that represents our application.</p><p><i>Note: for the purpose of this article, we will assume that every variable is declared at some package level scope. If those variables are declared in another scope, the </i><i><code>Access</code></i><i> attribute should be replaced by </i><i><code>Unchecked_Access</code></i><i>.</i></p><pre><code class="lang-ada">with ASF.Applications.Main;
...
App : aliased ASF.Applications.Main.Application;
</code></pre><p>To initialize the application, we will also need some configuration properties and a factory object. The configuration properties are used to configure the various components used by ASF. The factory allows to customize some behavior of <a href="http://code.google.com/p/ada-asf/">Ada Server Faces</a>. For now, we will use the default factory.</p><pre><code class="lang-ada">with ASF.Applications;
...
C : ASF.Applications.Config;
Factory : ASF.Applications.Main.Application_Factory;
</code></pre><p>The initialization requires to define some configuration properties. The <code>VIEW_EXT</code> property indicates the URI extension that are recognized by ASF to associate an XHTML file (the <code>compute.html</code> corresponds to the XHTML file <code>compute.xhtml</code>). The <code>VIEW_DIR</code> property defines the root directory where the XHTML files are stored.</p><pre><code class="lang-ada">C.Set (ASF.Applications.VIEW_EXT, ".html");
C.Set (ASF.Applications.VIEW_DIR, "samples/web");
C.Set ("web.dir", "samples/web");
App.Initialize (C, Factory);
</code></pre><h3>Servlets</h3><p>Ada Server Faces uses the <a href="http://blog.vacs.fr/index.php?post/2010/11/11/Ada-Servlet-Example">Ada Servlet framework</a> to receive and dispatch web requests. It provides a <code>Faces_Servlet</code> servlet which can be plugged in the servlet container. This servlet is the entry point for ASF to process incoming requests. We will also need a <code>File_Servlet</code> to process the static files. Note that these servlets are implemented using tagged records and you can easily override the entry points (<code>Do_Get</code> or <code>Do_Post</code>) to implement specific behaviors.</p><pre><code class="lang-ada">with ASF.Servlets.Faces;
with ASF.Servlets.Files;
...
Faces : aliased ASF.Servlets.Faces.Faces_Servlet;
Files : aliased ASF.Servlets.Files.File_Servlet;
</code></pre><p>The servlet instances are registered in the application.</p><pre><code class="lang-ada">App.Add_Servlet (Name => "faces", Server => Faces'Access);
App.Add_Servlet (Name => "files", Server => Files'Access);
</code></pre><p>Once registered, we have to define a mapping that tells which URI path is mapped to the servlet.</p><pre><code class="lang-ada">App.Add_Mapping (Name => "faces", Pattern => "*.html");
App.Add_Mapping (Name => "files", Pattern => "*.css");
</code></pre><p>For the purpose of debugging, ASF provides a servlet filter that can be plugged in the request processing flow. The <code>Dump_Filter</code> will produce a dump of the request with the headers and parameters.</p><pre><code class="lang-ada">with ASF.Filters.Dump;
...
Dump : aliased ASF.Filters.Dump.Dump_Filter;
</code></pre><p>The filter instance is registered as follows:</p><pre><code class="lang-ada">App.Add_Filter (Name => "dump", Filter => Dump'Access);
</code></pre><p>And a mapping is defined to tell which URL will trigger the filter.</p><pre><code class="lang-ada">App.Add_Filter_Mapping (Name => "dump", Pattern => "*.html");
</code></pre><h3>Application and Web Container</h3><p>The application object that we created is similar to a Java Web Application packaged in a <a href="http://en.wikipedia.org/wiki/WAR_%28Sun_file_format%29">WAR</a> file. It represents the application and it must be deployed in a Web Container. With <a href="http://code.google.com/p/ada-asf/">Ada Server Faces</a> this is almost the same, the application needs a Web container. By default, ASF provides a web container based on the excellent <a href="http://libre.adacore.com/libre/tools/aws/">Ada Web Server</a> implementation (other web containers could be provided in the future based on other web servers).</p><pre><code class="lang-ada">with ASF.Server.Web;
...
WS : ASF.Server.Web.AWS_Container;
</code></pre><p>To register the application, we indicate the URI context path to which the application is associated. Several applications can be registered, each of them having a unique URI context path.</p><pre><code class="lang-ada">CONTEXT_PATH : constant String := "/volume";
...
WS.Register_Application (CONTEXT_PATH, App'Access);
</code></pre><h3>Global Objects</h3><p>An application can provide some global objects which will be available during the request processing through the <a href="http://blog.vacs.fr/index.php?post/2010/04/28/Ada-EL-The-JSR-245-Unified-Expression-Language-for-Ada">EL expression</a>. First, we will expose the application context path which allows to write links in the XHTML page that match the URI used for registering the application in the web container.</p><pre><code class="lang-ada">App.Set_Global ("contextPath", CONTEXT_PATH);
</code></pre><p>Below is an example of use of this <code>contextPath</code> variable:</p><pre><code><link media="screen" type="text/css" rel="stylesheet"
href="#{contextPath}/themes/main.css"/>
</code></pre><p>Now, we will register the bean that we created for our application! This was explained in the <a href="http://blog.vacs.fr/index.php?post/2011/04/10/Ada-Server-Faces-Application-Example-part-2%3A-the-Ada-beans">Ada beans</a> previous article.</p><pre><code class="lang-ada">with Volume;
...
Bean : aliased Volume.Compute_Bean;
...
App.Set_Global ("compute", Util.Beans.Objects.To_Object (Bean'Access));
</code></pre><p>''Note: For the purpose of this example, the <code>Compute_Bean</code> is registered as a global object. This means that it will be shared by every request. A future article will explain how to get a session or a request bean as in Java Server Faces.''</p><h3>Starting the server</h3><p>Once the application is registered, we can start our server. Note that since <a href="http://libre.adacore.com/libre/tools/aws/">Ada Web Server</a> starts several threads that listen to requests, the <code>Start</code> procedure does not block and returns as soon as the server is started. The delay is necessary to let the server wait for requests during some time.</p><pre><code class="lang-ada">WS.Start;
delay 1000.0;
</code></pre><h3>What happens to a request?</h3><p>Let's say the server receives a HTTP GET request on <code>/volume/compute.html</code>. Here is what happens:</p><p><img src="/images/samples/volume-flow.png" longdesc="Volume ASF Flow, mai 2011" alt="Volume ASF Flow"></img></p><ul><li><a href="http://libre.adacore.com/libre/tools/aws/">Ada Web Server</a> receives the HTTP request</li><li>It identifies the application that matches <code>/volume</code> (our context path) and gives the control to it</li><li>The application identifies the servlet that processes the remaining URI, which is <code>compute.html</code></li><li>It gives the control to the <code>Dump_Filter</code> filter and then to the <code>Faces_Servlet</code> servlet,</li><li>The faces servlet identifies the XHTML facelet file and reads the <code>compute.xhtml</code> file</li><li>ASF builds the component tree that describes the page and invokes the render response phase</li><li>While rendering, the EL expressions such as <code>#{compute.radius}</code> are evaluated and the value is obtained on our <code>Bean</code> global instance.</li><li>The HTML content is produced as part of the rendering process and returned by AWS.</li></ul><h3>References</h3><p><a href="http://code.google.com/p/ada-asf/source/browse/trunk/samples/asf_volume_server.adb">asf_volume_server.adb</a><br><a href="http://code.google.com/p/ada-asf/source/browse/trunk/samples/volume.ads">volume.ads</a><br><a href="http://code.google.com/p/ada-asf/source/browse/trunk/samples/volume.adb">volume.adb</a><br><a href="http://code.google.com/p/ada-asf/source/browse/trunk/samples/web/compute.xhtml">compute.xhtml</a></p></div> Ada Server Faces Application Example part 3: the action beanurn:md5:c5e72351687b62d4471df9e9b30003a72011-05-03T18:44:25+00:002011-05-03T18:44:25+00:00Stephane CarrezAdaBeanELJSFTutorial
<div class="post-text"><p>In a previous article, I presented in the cylinder volume example the <a href="http://blog.vacs.fr/index.php?post/2011/03/21/Ada-Server-Faces-Application-Example">Ada Server Faces presentation layer</a> and then the <a href="http://blog.vacs.fr/index.php?post/2011/04/10/Ada-Server-Faces-Application-Example-part-2%3A-the-Ada-beans">Ada beans</a> that link the presentation and ASF components together. This article explains how to implement an action bean and have a procedure executed when a button is pressed.</p><h2>Command buttons and method expression</h2><p>We have seen in the <a href="http://blog.vacs.fr/index.php?post/2011/03/21/Ada-Server-Faces-Application-Example">presentation layer</a> how to create a form and have a submit button. This submit button can be associated with an action that will be executed when the button is pressed. The <a href="http://blog.vacs.fr/index.php?post/2010/04/28/Ada-EL-The-JSR-245-Unified-Expression-Language-for-Ada">EL expression</a> is the mechanism by which we create a binding between the XHTML presentation page and the component implemented in Java or Ada. A method expression is a simple EL expression that represents a bean and a method to invoke on that bean. This method expression represent our action.</p><p>A typical use is on the <b>h:commandButton</b> component where we can specify an action to invoke when the button is pressed. This is written as:</p><pre><code><h:commandButton id='run' value='Compute'
action="#{compute.run}"/>
</code></pre><p>The method expression <code>#{compute.run}</code> indicates to execute the method <code>run</code> of the bean identified by <code>compute</code>.</p><h2>Method Bean Declaration</h2><p>Java implements method expressions by using <a href="http://en.wikipedia.org/wiki/Reflection_(computer_programming)">reflection</a>. It is able to look at the methods implemented by an object and then invoke one of these method with some parameters. Since we cannot do this in Ada, some developer help is necessary.</p><p>For this an Ada bean that implements an action must implement the <code>Method_Bean</code> interface. If we take the <code>Compute_Bean</code> type defined in <a href="http://blog.vacs.fr/index.php?post/2011/04/10/Ada-Server-Faces-Application-Example-part-2%3A-the-Ada-beans">the Ada beans</a> previous article, we just have to extend that interface and implement the <code>Get_Method_Bindings</code> function. This function will indicate the methods which are available for an EL expression and somehow how they can be called.</p><pre><code class="lang-ada">with Util.Beans.Methods;
...
type Compute_Bean is new Util.Beans.Basic.Bean
and Util.Beans.Methods.Method_Bean with record
Height : My_Float := -1.0;
Radius : My_Float := -1.0;
Volume: My_Float := -1.0;
end record;
-- This bean provides some methods that can be used in a Method_Expression
overriding
function Get_Method_Bindings (From : in Compute_Bean)
return Util.Beans.Methods.Method_Binding_Array_Access;
</code></pre><p>Our Ada type can now define a method that can be invoked through a method expression. The action bean always receives the bean object as an <b>in out</b> first parameter and it must return the action outcome as an <code>Unbounded_String</code> also as <b>in out</b>.</p><pre><code class="lang-ada"> procedure Run (From : in out Compute_Bean;
Outcome : in out Unbounded_String);
</code></pre><h2>Implement the action</h2><p>The implementation of our action is quite simple. The <code>Radius</code> and <code>Height</code> parameters submitted in the form have been set on the bean before the action is called. We can use them to compute the cylinder volume.</p><pre><code class="lang-ada">procedure Run (From : in out Compute_Bean;
Outcome : in out Unbounded_String) is
V : My_Float;
begin
V := (From.Radius * From.Radius);
V := V * From.Height;
From.Volume := V * 3.141;
Outcome := To_Unbounded_String ("compute");
end Run;
</code></pre><h2>Define the action binding</h2><p>To be able to call the <code>Run</code> procedure from an EL method expression, we have to create a binding object. This binding object will hold the method name as well as a small procedure stub that will somehow tie the method expression to the procedure. This step is easily done by instantiating the <code>ASF.Events.Actions.Action_Method.Bind</code> package.</p><pre><code class="lang-ada">with ASF.Events.Actions;
...
package Run_Binding is
new ASF.Events.Actions.Action_Method.Bind
(Bean => Compute_Bean,
Method => Run,
Name => "run");
</code></pre><h2>Register and expose the action bindings</h2><p>The last step is to implement the <code>Get_Method_Bindings</code> function. Basically it has to return an array of method bindings which indicate the methods provided by the Ada bean.</p><pre><code class="lang-ada">Binding_Array : aliased constant Util.Beans.Methods.Method_Binding_Array
:= (Run_Binding.Proxy'Unchecked_Access, Run_Binding.Proxy'Unchecked_Access);
overriding
function Get_Method_Bindings (From : in Compute_Bean)
return Util.Beans.Methods.Method_Binding_Array_Access is
begin
return Binding_Array'Unchecked_Access;
end Get_Method_Bindings;
</code></pre><h2>What happens now?</h2><p>When the user presses the <code>Compute</code> button, the brower will submit the form and the ASF framework will do the following:</p><ul><li>It will check the validity of input parameters,</li><li>It will save the input parameters on the <code>compute</code> bean,</li><li>It will execute the method expression <code>#{compute.run}</code>:<ul><li>It calls the <code>Get_Method_Bindings</code> function to get a list of valid method,</li><li>Having found the right binding, it calls the binding procedure</li><li>The binding procedure invokes the <code>Run</code> procedure on the object.</li></ul></li></ul><h2>Next time...</h2><p>We have seen the presentation layer, how to implement the Ada bean and this article explained how to implement an action that is called when a button is pressed. The next article will explain how to initialize and build the web application.</p></div> Ada Server Faces Application Example part 2: the Ada beansurn:md5:33ba623b14f8f04b0ab1da29fcdd747a2011-04-10T21:25:17+00:002011-04-10T21:25:17+00:00Stephane CarrezAdaBeanELJSFTutorial
<div class="post-text"><p>The first article explained how to design the presentation page of an <a href="http://code.google.com/p/ada-asf/">Ada Server Faces</a> application. This article presents the Ada beans that are behind the presentation page.</p><h2>Ada Bean and presentation layer</h2><p>We have seen that the presentation page contains components that make references to Ada beans with an EL expression.</p><pre><code><h:inputText id='height' size='10' value='#{compute.height}'>
<f:converter converterId="float" />
</h:inputText>
</code></pre><p>The <code>#{compute.height}</code> is an EL expression that refers to the <code>height</code> property of the Ada bean identified as <code>compute</code>.</p><h2>Writing the Cylinder Ada Bean</h2><p>The Ada bean is a instance of an Ada tagged record that must implement a getter and a setter operation. These operations are invoked through an <a href="/index.php?post/2010/04/28/Ada-EL-The-JSR-245-Unified-Expression-Language-for-Ada">EL expression</a>. Basically the getter is called when the view is rendered and the setter is called when the form is submitted and validated. The <code>Bean</code> interface defines the two operations that must be implemented by the Ada type:</p><pre><code>with Util.Beans.Basic;
with Util.Beans.Objects;
...
type Compute_Bean is new Util.Beans.Basic.Bean with record
Height : My_Float := -1.0;
Radius : My_Float := -1.0;
end record;
-- Get the value identified by the name.
overriding
function Get_Value (From : Compute_Bean;
Name : String) return Util.Beans.Objects.Object;
-- Set the value identified by the name.
overriding
procedure Set_Value (From : in out Compute_Bean;
Name : in String;
Value : in Util.Beans.Objects.Object);
</code></pre><p>The getter and setter will identify the property to get or set through a name. The value is represented by an <code>Object</code> type that can hold several data types (boolean, integer, floats, strings, dates, ...). The getter looks for the name and returns the corresponding value in an <code>Object</code> record. Several <code>To_Object</code> functions helps in creating the result value.</p><pre><code> function Get_Value (From : Compute_Bean;
Name : String) return Util.Beans.Objects.Object is
begin
if Name = "radius" and From.Radius >= 0.0 then
return Util.Beans.Objects.To_Object (Float (From.Radius));
elsif Name = "height" and From.Height >= 0.0 then
return Util.Beans.Objects.To_Object (Float (From.Height));
else
return Util.Beans.Objects.Null_Object;
end if;
end Get_Value;
</code></pre><p>The setter is similar.</p><pre><code> procedure Set_Value (From : in out Compute_Bean;
Name : in String;
Value : in Util.Beans.Objects.Object) is
begin
if Name = "radius" then
From.Radius := My_Float (Util.Beans.Objects.To_Float (Value));
elsif Name = "height" then
From.Height := My_Float (Util.Beans.Objects.To_Float (Value));
end if;
end Set_Value;
</code></pre><h2>Register the Cylinder Ada Bean</h2><p>The next step is to register the cylinder bean and associate it with the <code>compute</code> name. There are several ways to do that but for the purpose of this example, there will be a global instance of the bean. That instance must be <code>aliased</code> so that we can use the <code>Access</code> attributes.</p><pre><code> Bean : aliased Compute_Bean;
</code></pre><p>The Ada bean is registered on the application object by using the <code>Set_Global</code> procedure. This creates a global binding between a name and an <code>Object</code> record. In our case, the object will hold a reference to the Ada bean.</p><pre><code>App : aliased ASF.Applications.Main.Application;
...
App.Set_Global ("compute", Util.Beans.Objects.To_Object (Bean'Unchecked_Access));
</code></pre><h2>Next Time...</h2><p>We have seen how the presentation layer and the Ada beans are associated. The next article will present the action binding that links the form submission action to an Ada bean method.</p></div>