File: server.adb

package info (click to toggle)
polyorb 2.11~20140418-3
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 30,012 kB
  • ctags: 465
  • sloc: ada: 273,015; sh: 4,507; makefile: 4,265; python: 1,332; cpp: 1,213; java: 507; ansic: 274; xml: 30; perl: 23; exp: 6
file content (366 lines) | stat: -rw-r--r-- 11,331 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
------------------------------------------------------------------------------
--                                                                          --
--                           POLYORB COMPONENTS                             --
--                                                                          --
--                               S E R V E R                                --
--                                                                          --
--                                 B o d y                                  --
--                                                                          --
--         Copyright (C) 2002-2012, Free Software Foundation, Inc.          --
--                                                                          --
-- This is free software;  you can redistribute it  and/or modify it  under --
-- terms of the  GNU General Public License as published  by the Free Soft- --
-- ware  Foundation;  either version 3,  or (at your option) any later ver- --
-- sion.  This software is distributed in the hope  that it will be useful, --
-- but WITHOUT ANY WARRANTY;  without even the implied warranty of MERCHAN- --
-- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public --
-- License for  more details.                                               --
--                                                                          --
-- You should have received a copy of the GNU General Public License and    --
-- a copy of the GCC Runtime Library Exception along with this program;     --
-- see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see    --
-- <http://www.gnu.org/licenses/>.                                          --
--                                                                          --
--                  PolyORB is maintained by AdaCore                        --
--                     (email: sales@adacore.com)                           --
--                                                                          --
------------------------------------------------------------------------------

with Ada.Characters.Handling; use Ada.Characters.Handling;
with Ada.Text_IO;             use Ada.Text_IO;
with Exceptions;              use Exceptions;
with System.RPC;
with Utils;                   use Utils;

package body Server is

   type String_Access is access String;

   type Message;
   type Message_Access is access Message;
   type Message is record
      Sender       : String_Access;
      Content      : String_Access;
      Next_Message : Message_Access;
   end record;

   protected Message_Board is

      procedure Add_Message
        (Sender  : String;
         Content : String);
      --  Add a message to the message board. Sender_Error and Message_Error
      --  will be raised if the sender or the message are empty.

      function Messages_Count return Natural;
      --  Number of messages in the message board

      function Get_Sender (N : Positive) return String;
      --  Sender of a particular message (No_Such_Message will be raised if
      --  there is no such message).

      function Get_Message (N : Positive) return String;
      --  Content of a particular message (No_Such_Message will be raised if
      --  there is no such message).

   private

      function Get (N : Positive) return Message_Access;
      --  Get a message, and raise No_Such_Message if it does not exist

      Messages : Message_Access := null;
      Count    : Natural        := 0;

   end Message_Board;
   --  Message_Board is a protected structure (we will not have concurrent
   --  calls) in which messages are stored. The messages are stored in reverse
   --  order. This is totally *inefficient*, but well, this is a toy program :)

   type Penpal_Node;
   type Penpal_List is access Penpal_Node;
   type Penpal_Node is record
      Name        : String_Access;
      Penpal      : Penpal_Pointer;
      Next_Penpal : Penpal_List;
   end record;

   type Penpal_Array is array (Positive range <>) of Penpal_Node;
   --  List of penpals with their names. The Next_Penpal field will have
   --  no meaning though.

   protected Penpals_Handler is

      procedure Add (Penpal : Penpal_Pointer);
      --  Add a Penpal to the list, raise Sender_Error if the penpal has not
      --  been initialized.

      function Lookup (Name : String) return Penpal_Pointer;
      --  Lookup a penpal in the list, or raise No_Such_Penpal if no penpal
      --  by this name has been registered.

      function Get_List return Penpal_Array;
      --  Return the list of registered penpals

   private

      function Lookup (Name : String) return Penpal_List;
      --  Lookup a penpal by its name, and return null if no such penpal
      --  has been registered;

      Penpals : Penpal_List := null;

      Count   : Natural := 0;

   end Penpals_Handler;

   ---------------
   -- Broadcast --
   ---------------

   procedure Broadcast (Sender : String; Message : String) is
      Penpals : constant Penpal_Array := Penpals_Handler.Get_List;
   begin
      Put_Line ("Sending a broadcast to " &
                Integer_To_String (Penpals'Length) &
                " registered clients:");
      for I in Penpals'Range loop
         begin
            Put ("   Trying to contact <" & Penpals (I).Name.all & ">... ");
            Flush;
            New_Message (Sender    => Sender,
                         Recipient => Penpals (I).Penpal,
                         Message   => Message);
            Put_Line ("OK");
         exception
            when System.RPC.Communication_Error =>

               --  This penpal is probably dead, ignore the error...

               Put_Line ("fail");
         end;
      end loop;
   end Broadcast;

   -----------------
   -- Get_Message --
   -----------------

   function Get_Message (N : Positive) return String is
   begin
      return Message_Board.Get_Message (N);
   end Get_Message;

   ----------------
   -- Get_Penpal --
   ----------------

   function Get_Penpal (Name : String) return Penpal_Pointer is
   begin
      return Penpals_Handler.Lookup (Name);
   end Get_Penpal;

   ----------------
   -- Get_Sender --
   ----------------

   function Get_Sender (N : Positive) return String is
   begin
      return Message_Board.Get_Sender (N);
   end Get_Sender;

   -------------------
   -- Message_Board --
   -------------------

   protected body Message_Board is

      -----------------
      -- Add_Message --
      -----------------

      procedure Add_Message
        (Sender  : String;
         Content : String)
      is
      begin
         if Sender = "" then
            raise Sender_Error;
         elsif Content = "" then
            raise Message_Error;
         else
            Messages := new Message'(Sender       => new String'(Sender),
                                     Content      => new String'(Content),
                                     Next_Message => Messages);
            Count := Count + 1;
         end if;
      end Add_Message;

      ---------
      -- Get --
      ---------

      function Get (N : Positive) return Message_Access is
         Current : Message_Access := Messages;
      begin
         if N > Count then
            raise No_Such_Message;
         end if;
         for I in N + 1 .. Count loop
            Current := Current.Next_Message;
         end loop;
         return Current;
      end Get;

      -----------------
      -- Get_Message --
      -----------------

      function Get_Message (N : Positive) return String is
      begin
         return Get (N) .Content.all;
      end Get_Message;

      ----------------
      -- Get_Sender --
      ----------------

      function Get_Sender (N : Positive) return String is
      begin
         return Get (N) .Sender.all;
      end Get_Sender;

      --------------------
      -- Messages_Count --
      --------------------

      function Messages_Count return Natural is
      begin
         return Count;
      end Messages_Count;

   end Message_Board;

   ------------------------
   -- Number_Of_Messages --
   ------------------------

   function Number_Of_Messages return Natural is
   begin
      return Message_Board.Messages_Count;
   end Number_Of_Messages;

   ---------------------
   -- Penpals_Handler --
   ---------------------

   protected body Penpals_Handler is

      ---------
      -- Add --
      ---------

      procedure Add (Penpal : Penpal_Pointer) is
         Name    : constant String := Name_Of (Penpal);
         Current : Penpal_List     := Lookup (Name);
      begin
         if Current = null then
            Penpals := new Penpal_Node'(Name        => new String'(Name),
                                        Penpal      => Penpal,
                                        Next_Penpal => Penpals);
            Count := Count + 1;
         else
            Current.Penpal := Penpal;
         end if;
      end Add;

      --------------
      -- Get_List --
      --------------

      function Get_List return Penpal_Array is
         Result  : Penpal_Array (1 .. Count);
         Current : Penpal_List := Penpals;
      begin
         for I in 1 .. Count loop
            Result (I) := Current.all;

            --  Since the Next_Penpal has no meaning here, clear it
            Result (I) .Next_Penpal := null;

            Current := Current.Next_Penpal;
         end loop;
         return Result;
      end Get_List;

      ------------
      -- Lookup --
      ------------

      function Lookup (Name : String) return Penpal_Pointer is
         Current : constant Penpal_List := Lookup (Name);
      begin
         if Current = null then
            raise No_Such_Penpal;
         else
            return Current.Penpal;
         end if;
      end Lookup;

      ------------
      -- Lookup --
      ------------

      function Lookup (Name : String) return Penpal_List is
         Current  : Penpal_List     := Penpals;
         Low_Name : constant String := To_Lower (Name);
      begin
         while Current /= null loop
            begin
               if To_Lower (Current.Name.all) = Low_Name then
                  return Current;
               end if;
            exception
               when System.RPC.Communication_Error =>
                  --  The given name is unreachable, don't give it, return
                  --  null instead.

                  return null;
            end;
            Current := Current.Next_Penpal;
         end loop;
         return null;
      end Lookup;

   end Penpals_Handler;

   ------------------
   -- Post_Message --
   ------------------

   procedure Post_Message
     (Sender  : String;
      Message : String)
   is
   begin
      --  Add the message to the message board

      Put_Line ("Posting a message from <" & Sender & ">: """ &
                Message & """");
      Message_Board.Add_Message (Sender, Message);

      --  For each registered client, send it the message

      Broadcast (Sender, Message);
   end Post_Message;

   --------------
   -- Register --
   --------------

   procedure Register (Penpal : Penpal_Pointer) is
   begin
      Put_Line ("Registering a new penpal <" & Name_Of (Penpal) & ">");
      Penpals_Handler.Add (Penpal);
   end Register;

end Server;