Allow separate stacks registration through services operation
[anna.git] / example / http / xmlClient / main.cpp
1 // ANNA - Anna is Not Nothingness Anymore                                                         //
2 //                                                                                                //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo                         //
4 //                                                                                                //
5 // See project site at http://redmine.teslayout.com/projects/anna-suite                           //
6 // See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
7
8
9 /**
10    Realiza peticiones automaticas sobre el servidor HTTP de operaciones aritmeticas.
11
12    La cadencia de envio de mensajes se establece mediante un temporizador.
13    Los operadores se calculan de forma aleatoria.
14
15    El servidor de este cliente: http_server.p o http_rserver.p
16 */
17
18 #include <iostream>
19
20 #include <string.h>
21 #include <unistd.h>
22
23 #include <anna/core/core.hpp>
24 #include <anna/app/functions.hpp>
25 #include <anna/comm/comm.hpp>
26
27 #include <anna/xml/DocumentMemory.hpp>
28 #include <anna/xml/Node.hpp>
29 #include <anna/xml/Parser.hpp>
30
31 #include <anna/timex/Engine.hpp>
32 #include <anna/timex/Clock.hpp>
33
34 #include <anna/http/Request.hpp>
35 #include <anna/http/Response.hpp>
36 #include <anna/http/Transport.hpp>
37 #include <anna/http/Handler.hpp>
38
39 static const Millisecond Resolution(250);
40 static const Millisecond Period(500);
41 static const Millisecond OneSecond(1000);
42
43 class Sender : public anna::timex::Clock {
44 public:
45    Sender ();
46    
47    void setMessageBySecond (const int messageBySecond) throw () { a_messageByTick = messageBySecond / (OneSecond / Period); }
48
49    int getTxMessageCounter () const throw () { return a_txMessageCounter; }
50
51 private:
52    struct Attribute { enum _v { ValueOne, ValueTwo, Operator, Time, Max }; };
53    
54    int a_messageByTick;
55    int a_nquarter;
56    int a_errorCounter;
57    int a_txMessageCounter;
58    http::Request a_httpRequest;
59    xml::Node* a_xmlRequest;
60    xml::Attribute* a_xmlAttributes [Attribute::Max];
61
62    /* Se invoca 4 veces por segundo */
63    bool tick () throw (RuntimeException);
64 };
65
66 class MyHandler : public http::Handler {
67 public:
68    MyHandler () : http::Handler ("http_client::MyHandler") {;}
69
70 private:
71    xml::DocumentMemory a_xmlRequest;
72    xml::Parser a_xmlParser;
73    
74    void evRequest (ClientSocket&, const http::Request&) throw (RuntimeException) {;}
75    void evResponse (ClientSocket&, const http::Response&) throw (RuntimeException);
76    
77 //   static bool isOk (const test::Response& response) throw ();
78 };
79
80 class MyCommunicator : public Communicator {
81 public:
82    MyCommunicator () : Communicator (), a_avgResponseTime (0), a_rxMessageCounter (0) {;}
83
84    void count (const int delay) throw (RuntimeException);
85
86 private:
87    int a_avgResponseTime;
88    int a_rxMessageCounter;
89    MyHandler a_httpHandler;
90
91    
92    void eventReceiveMessage (ClientSocket&, const Message&) throw (RuntimeException);
93
94    void eventBreakConnection (const ClientSocket&) throw ();
95
96    // Sustituye la redefinición de los siguientes métodos
97    using comm::Communicator::eventBreakConnection;
98 /*
99    void eventBreakConnection (Server* server) throw () {
100       comm::Communicator::eventBreakConnection (server);
101    }
102    void eventBreakConnection (const Service* service) throw () { 
103       comm::Communicator::eventBreakConnection (service);
104    }
105 */      
106 };
107
108 class HeavyWIMS20Client : public anna::comm::Application {
109 public:
110    HeavyWIMS20Client ();
111
112    Server* getServer () const throw () { return a_server; }
113    const Sender* getSender () const throw () { return &a_sender; }
114
115 private:
116    MyCommunicator a_communicator;
117    anna::timex::Engine a_timeController;
118    Sender a_sender;
119    Server* a_server;
120
121    void initialize () throw (RuntimeException);
122    void run () throw (RuntimeException);
123 };
124
125 using namespace std;
126
127 int main (int argc, const char** argv)
128 {
129    CommandLine& commandLine (CommandLine::instantiate ());
130    HeavyWIMS20Client app;
131
132    srand (time (NULL));
133
134    try {
135       commandLine.initialize (argv, argc);
136       commandLine.verify ();
137
138       Logger::setLevel (Logger::Information);
139       string traceFile ("client.");
140       traceFile += anna::functions::asString ((int) getpid ());
141       traceFile += ".trace";
142       Logger::initialize ("http_client", new TraceWriter (traceFile.c_str (),4096000));
143
144       app.start ();
145    }
146    catch (Exception& ex) {
147       cout << ex.asString () << endl;
148    }
149
150    return 0;
151 }
152
153 HeavyWIMS20Client::HeavyWIMS20Client () :
154    Application ("http_client", "Cliente HTTP", "1.0"),
155    a_communicator (),
156    a_timeController (OneSecond, Resolution)
157 {
158    CommandLine& commandLine (CommandLine::instantiate ());
159
160    commandLine.add ("p", CommandLine::Argument::Mandatory, "Puerto en el que el servidor atiende respuestas.");
161    commandLine.add ("a", CommandLine::Argument::Mandatory, "Direccin IP Puerto en el que el servidor atiende respuestas.");
162    commandLine.add ("n", CommandLine::Argument::Mandatory, "Numero de mensajes por segundo");
163    commandLine.add ("trace", CommandLine::Argument::Optional, "Nivel de trazas (debug,warning, error,...)");
164 }
165
166 void HeavyWIMS20Client::initialize ()
167    throw (RuntimeException)
168 {
169    CommandLine& cl (CommandLine::instantiate ());
170
171    Network& network = Network::instantiate ();
172
173    a_server = network.createServer (cl.getValue ("a"), cl.getIntegerValue ("p"), true, &http::Transport::getFactory ());
174    a_sender.setMessageBySecond (cl.getIntegerValue ("n"));
175
176    if (cl.exists ("trace"))
177       Logger::setLevel (Logger::asLevel (cl.getValue ("trace")));
178 }
179
180 void HeavyWIMS20Client::run ()
181    throw (RuntimeException)
182 {
183    a_timeController.activate (a_sender);
184
185    a_communicator.accept ();
186 }
187
188 void MyCommunicator::eventReceiveMessage (ClientSocket& clientSocket, const Message& message)
189    throw (RuntimeException)
190 {
191    LOGMETHOD (TraceMethod tm ("MyCommunicator", "eventReceiveMessage", ANNA_FILE_LOCATION));
192
193    if (clientSocket.support (http::Transport::className ()) == false)
194       return;
195
196    a_httpHandler.apply (clientSocket, message);
197 }
198
199 void MyCommunicator::count (const int delay)
200    throw (RuntimeException)
201 {
202    Guard guard (this, "MyCommunicator::count");
203    
204    a_rxMessageCounter ++;
205    a_avgResponseTime += delay;
206 }
207
208 void MyCommunicator::eventBreakConnection (const ClientSocket& clientSocket)
209    throw ()
210 {
211    if (a_rxMessageCounter == 0)
212       return;
213
214    LOGNOTICE (
215       HeavyWIMS20Client& app = static_cast <HeavyWIMS20Client&> (anna::app::functions::getApp ());   
216       string msg ("Tiempo medio respuesta: ");
217       msg += anna::functions::asString (a_avgResponseTime / a_rxMessageCounter);
218       msg += " ms";
219       msg += anna::functions::asText (" | Rx: ", a_rxMessageCounter);
220       msg += anna::functions::asText (" | Tx: ", app.getSender ()->getTxMessageCounter ());
221       Logger::notice (msg, ANNA_FILE_LOCATION);
222
223       cout << msg << endl << endl;
224    );
225    requestStop ();
226    comm::Communicator::eventBreakConnection (clientSocket);
227 }
228
229 Sender::Sender () : Clock ("Sender", Period), 
230    a_messageByTick (0), 
231    a_nquarter (0), 
232    a_errorCounter (0),
233    a_txMessageCounter (0)
234 {
235    a_httpRequest.setMethod (http::Method::Type::Get);
236    a_httpRequest.setURI ("http_xmlclient.p");
237    
238    /**
239     * Crea el documento XML que usaremos para codificar la respuesta y 
240     * pre-localiza los objetos sobre los que tendrá que actuar
241     */
242    a_xmlRequest = new xml::Node ("Request");
243    a_xmlAttributes [Attribute::ValueOne] = a_xmlRequest->createAttribute ("ValueOne", 0);
244    a_xmlAttributes [Attribute::ValueTwo] = a_xmlRequest->createAttribute ("ValueTwo", 0);
245    a_xmlAttributes [Attribute::Operator] = a_xmlRequest->createAttribute ("Operator", 0);
246    a_xmlAttributes [Attribute::Time] = a_xmlRequest->createAttribute ("Millisecond", 0);
247 }
248
249 bool Sender::tick ()
250    throw (RuntimeException)
251 {
252    Server* server = static_cast <HeavyWIMS20Client&> (anna::app::functions::getApp ()).getServer ();
253    Communicator* communicator = anna::app::functions::component <Communicator> (ANNA_FILE_LOCATION);
254
255    if (a_errorCounter > 100) {
256       communicator->requestStop ();
257       Logger::warning ("Terminado por errores continuos en la conexion", ANNA_FILE_LOCATION);
258       return false;
259    }
260
261    for (int n = 0; n < a_messageByTick && communicator->hasRequestedStop () == false; n ++) {
262       a_xmlAttributes [Attribute::ValueOne]->setValue (rand () % 1000);
263       a_xmlAttributes [Attribute::ValueTwo]->setValue (rand () % 1000);
264       a_xmlAttributes [Attribute::Operator]->setValue ("+");
265       a_xmlAttributes [Attribute::Time]->setValue (anna::functions::millisecond ());
266
267       try {
268          a_httpRequest.setBody (a_xmlRequest);
269          server->send (a_httpRequest);
270          a_txMessageCounter ++;
271       }
272       catch (RuntimeException& ex) {
273          a_errorCounter ++;
274          ex.trace ();
275          break;
276       }
277    }
278
279    return true;
280 }
281
282 void MyHandler::evResponse (ClientSocket& clientSocket, const http::Response& response)
283    throw (RuntimeException)
284 {
285    if (response.getStatusCode () != 200) 
286       return;
287       
288    a_xmlRequest.initialize (response.getBody ());
289
290    const xml::Node* root = a_xmlRequest.parse ();
291    
292    const anna::Millisecond now = anna::functions::millisecond ();
293    const anna::Millisecond past(root->getAttribute ("Time")->getIntegerValue ()); 
294    
295    const int delay = now - past; 
296
297    if (delay < 0) {
298       LOGWARNING (
299          string msg = a_xmlRequest.getContentAsCString ();
300          msg += anna::functions::asText (" | Delay: ", delay);
301          Logger::warning (msg, ANNA_FILE_LOCATION);
302       );
303       return;
304    }
305
306    app::functions::component <MyCommunicator> (ANNA_FILE_LOCATION)->count (delay);
307 }