Updated license
[anna.git] / example / comm / server / main.cpp
1 // ANNA - Anna is Not Nothingness Anymore
2 //
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
4 //
5 // https://bitbucket.org/testillano/anna
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
9 // are met:
10 //
11 //     * Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 //     * Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following disclaimer
15 // in the documentation and/or other materials provided with the
16 // distribution.
17 //     * Neither the name of Google Inc. nor the names of its
18 // contributors may be used to endorse or promote products derived from
19 // this software without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 //
33 // Authors: eduardo.ramos.testillano@gmail.com
34 //          cisco.tierra@gmail.com
35
36
37 /*
38   Ejemplo de programa servidor. Atiende peticiones aritmeticas, el protocolo de transporte
39   sera el comm::Transport y el contenido del mensaje sera el resutaldo de un comm::Codec con los
40   (x, y, op) -> El resultado sera estos tres componente mas result.
41
42   Para poder probar el sistema de congestion se puede indicar un numero de milisegundos de 
43   retardo aplicados a cada contestacion.
44
45   Los clientes pueden ser: client o kClient
46 */
47 #include <iostream>
48 #include <signal.h>
49
50 #include <anna/core/core.hpp>
51 #include <anna/comm/comm.hpp>
52
53 #include <anna/xml/Node.hpp>
54 #include <anna/xml/Attribute.hpp>
55
56 #include <anna/test/Request.hpp>
57 #include <anna/test/Response.hpp>
58 #include <anna/test/Communicator.hpp>
59
60 using namespace std;
61 using namespace test;
62
63 struct Context {
64    Request request;
65    Response response;
66 };
67
68 class MyCommunicator : public test::Communicator {
69 public:
70    MyCommunicator () : 
71       a_contexts ("Contexts")
72    {;}
73       
74 private:
75    ThreadData <Context> a_contexts;
76
77    void eventReceiveMessage (comm::ClientSocket&, const Message&) throw (RuntimeException);
78 };
79
80 class ArithmeticServer : public comm::Application {
81 public:
82    ArithmeticServer ();
83       
84 private:
85    MyCommunicator a_communicator;
86    comm::ServerSocket* a_serverSocket;
87
88    void initialize () throw (RuntimeException);
89    void run () throw (RuntimeException);
90    xml::Node* asXML (xml::Node* app) const throw ();
91 };
92
93 using namespace std;
94 using namespace anna::comm;
95
96 int main (int argc, const char** argv)
97 {
98    CommandLine& commandLine (CommandLine::instantiate ());
99    ArithmeticServer app;
100
101    srand (time (NULL));
102
103    sigignore (SIGABRT);
104    
105    try {
106       commandLine.initialize (argv, argc);
107       commandLine.verify ();
108
109       Logger::setLevel (Logger::Debug); 
110       string traceFile ("server.");
111       traceFile += anna::functions::asString ((int) getpid ());
112       traceFile += ".trace";      
113       Logger::initialize ("arithmeticServer", new TraceWriter (traceFile.c_str (),4096000));
114  
115       app.start ();
116    }
117    catch (Exception& ex) {
118       cout << ex.asString () << endl;
119    }
120    
121    return 0;
122 }
123
124 ArithmeticServer::ArithmeticServer () : 
125    Application ("arithmeticServer", "Servidor de operaciones aritm�icas", "1.0") 
126 {
127    CommandLine& commandLine (CommandLine::instantiate ());
128       
129    commandLine.add ("p", CommandLine::Argument::Mandatory, "Puerto en el que atender peticiones");
130    commandLine.add ("a", CommandLine::Argument::Mandatory, "Direccin IP en la que atender");
131    commandLine.add ("d", CommandLine::Argument::Mandatory, "Retardo aplicado a la contestacion");
132    commandLine.add ("limit", CommandLine::Argument::Mandatory, "% de ocupacion que permitimos");
133    commandLine.add ("r", CommandLine::Argument::Optional, "Indicador de reuso de direccin", false);
134    commandLine.add ("n", CommandLine::Argument::Optional, "Numero de mensajes a servir", true);
135    commandLine.add ("trace", CommandLine::Argument::Optional, "Nivel de trazas (debug,warning, error,...)");
136 }
137
138 //-----------------------------------------------------------------------------------------
139 // Inicializa el servidor de sockets.
140 //-----------------------------------------------------------------------------------------
141 void ArithmeticServer::initialize () 
142    throw (RuntimeException)
143 {
144    CommandLine& cl (CommandLine::instantiate ());
145
146    int port = cl.getIntegerValue ("p");
147    const comm::Device* device = Network::instantiate ().find (Device::asAddress (cl.getValue ("a")));
148
149    a_serverSocket = new ServerSocket (INetAddress (device, port), cl.exists ("r"));
150    a_serverSocket->setCategory (777);
151 }
152
153 //-----------------------------------------------------------------------------------------
154 // Atiende las peticiones.
155 // Cuando hay un nuevo mensaje invocar�a Communicator::eventReceiveMessage
156 //-----------------------------------------------------------------------------------------
157 void ArithmeticServer::run ()
158    throw (RuntimeException)
159 {
160    CommandLine& cl (CommandLine::instantiate ());
161
162    a_communicator.attach (a_serverSocket);
163    a_communicator.setDelay ((Millisecond)cl.getIntegerValue ("d"));
164
165    if (cl.exists ("n") == true)
166       a_communicator.setMaxMessage (cl.getIntegerValue ("n"));
167
168    if (cl.exists ("trace"))
169       Logger::setLevel (Logger::asLevel (cl.getValue ("trace")));
170
171    CongestionController::instantiate ().setLimit (cl.getIntegerValue ("limit"));
172
173    a_communicator.accept ();
174 }
175
176 xml::Node* ArithmeticServer::asXML (xml::Node* app) const 
177    throw ()
178 {
179    xml::Node* node = app::Application::asXML (app);
180    
181    node->createAttribute ("MaxMessage", a_communicator.getMaxMessage ());
182    node->createAttribute ("Message", a_communicator.getMessage ());
183    
184    return node;
185 }
186
187 //-----------------------------------------------------------------------------------------
188 // Manejador de peticiones.
189 // Calcular�la operacin solicitada y devolver�el resultado.
190 //
191 // clientSocket: Socket cliente por el que podemos responder a la peticin.
192 // transport: Instancia del transporto que ha interpretado el mensaje (getMessage).
193 //-----------------------------------------------------------------------------------------
194 void MyCommunicator::eventReceiveMessage (ClientSocket& clientSocket, const Message& message)
195    throw (RuntimeException)
196 {
197    LOGMETHOD (TraceMethod tm ("MyCommunicator", "eventReceiveMessage", ANNA_FILE_LOCATION));
198
199    if (canContinue (clientSocket) == false)
200       return;   
201
202    delay ();   
203     
204    Context& context = a_contexts.get ();
205    Request& request (context.request);
206    Response& response (context.response);
207
208    request.decode (message.getBody ());
209
210    response.x = request.x;
211    response.y = request.y;
212    response.initTime = request.initTime;
213
214    switch (response.op = request.op) {
215       case '+':
216          response.result = request.x + request.y;
217          break;
218       case '-':
219          response.result = request.x - request.y;
220          break;
221       case '*':
222          response.result = request.x * request.y;
223          break;
224       case '/':
225          response.result = (request.y != 0) ? (request.x / request.y): 0;
226          break;
227    }
228
229    LOGINFORMATION (
230       string msg = anna::functions::asString ("%d %c %d = %d", request.x, request.op, request.y, response.result);
231       msg += anna::functions::asText (" | InitTime: ",  response.initTime);
232       Logger::information (msg, ANNA_FILE_LOCATION);
233    )
234
235    try {
236       clientSocket.send (response);
237    }
238    catch (Exception& ex) {
239       ex.trace ();
240    }
241 }
242