Add anna_ to libraries and examples
[anna.git] / example / http / wims20XClient / 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
30 #include <anna/timex/Engine.hpp>
31 #include <anna/timex/Clock.hpp>
32
33 #include <anna/http/Request.hpp>
34 #include <anna/http/Response.hpp>
35 #include <anna/http/Transport.hpp>
36 #include <anna/http/Handler.hpp>
37
38 #include <anna/http/wims20/ClientSide.hpp>
39
40 static const Millisecond Resolution(250);
41 static const Millisecond Period(500);
42 static const Millisecond OneSecond(1000);
43
44 class Sender : public anna::timex::Clock {
45 public:
46    Sender () : Clock ("Sender", Period), 
47       a_messageByTick (0), 
48       a_nquarter (0), 
49       a_errorCounter (0),
50       a_txMessageCounter (0),
51       a_request (NULL)
52    {
53       a_httpRequest.setMethod (http::Method::Type::Get);
54    }
55
56    void setMessageBySecond (const int messageBySecond) throw () { a_messageByTick = messageBySecond / (OneSecond / Period); }
57    void setRequest (http::wims20::ClientSide* request) throw () { a_request = request; }
58
59    int getTxMessageCounter () const throw () { return a_txMessageCounter; }
60
61 private:
62    int a_messageByTick;
63    int a_nquarter;
64    int a_errorCounter;
65    int a_txMessageCounter;
66    http::Request a_httpRequest;
67    http::wims20::ClientSide* a_request;
68
69    /* Se invoca 4 veces por segundo */
70    bool tick () throw (RuntimeException);
71 };
72
73 class MyHandler : public http::Handler {
74 public:
75    MyHandler () : http::Handler ("http_client::MyHandler") {;}
76
77 private:
78    xml::DocumentMemory a_xmlResponse;
79    
80    void evRequest (ClientSocket&, const http::Request&) throw (RuntimeException) {;}
81    void evResponse (ClientSocket&, const http::Response&) throw (RuntimeException);
82    
83 //   static bool isOk (const test::Response& response) throw ();
84 };
85
86 class MyCommunicator : public comm::Communicator {
87 public:
88    MyCommunicator () : comm::Communicator (), a_avgResponseTime (0), a_rxMessageCounter (0) {;}
89
90    void count (const Millisecond delay) throw (RuntimeException);
91    
92    // Sustituye la redefinición de los siguientes métodos
93    void eventBreakConnection (const comm::ClientSocket&) throw ();
94    using comm::Communicator::eventBreakConnection;
95
96 private:
97    int a_avgResponseTime;
98    int a_rxMessageCounter;
99    MyHandler a_httpHandler;
100    
101    void eventReceiveMessage (ClientSocket&, const Message&) throw (RuntimeException);
102 };
103
104 class HeavyWIMS20Client : public anna::comm::Application {
105 public:
106    HeavyWIMS20Client ();
107
108    Server* getServer () const throw () { return a_server; }
109    const Sender* getSender () const throw () { return &a_sender; }
110
111 private:
112    MyCommunicator a_communicator;
113    anna::timex::Engine a_timeController;
114    Sender a_sender;
115    Server* a_server;
116    http::wims20::ClientSide* a_request;
117
118    void initialize () throw (RuntimeException);
119    void run () throw (RuntimeException);
120 };
121
122 using namespace std;
123
124 int main (int argc, const char** argv)
125 {
126    CommandLine& commandLine (CommandLine::instantiate ());
127    HeavyWIMS20Client app;
128
129    srand (time (NULL));
130
131    try {
132       commandLine.initialize (argv, argc);
133       commandLine.verify ();
134
135       Logger::setLevel (Logger::Information);
136       string traceFile ("client.");
137       traceFile += anna::functions::asString ((int) getpid ());
138       traceFile += ".trace";
139       Logger::initialize ("http_client", new TraceWriter (traceFile.c_str (),4096000));
140
141       app.start ();
142    }
143    catch (Exception& ex) {
144       cout << ex.asString () << endl;
145    }
146
147    return 0;
148 }
149
150 HeavyWIMS20Client::HeavyWIMS20Client () :
151    Application ("http_client", "Cliente HTTP", "1.0"),
152    a_communicator (),
153    a_timeController (OneSecond, Resolution),
154    a_request (NULL)
155 {
156    CommandLine& commandLine (CommandLine::instantiate ());
157
158    commandLine.add ("p", CommandLine::Argument::Mandatory, "Puerto en el que el servidor atiende respuestas.");
159    commandLine.add ("a", CommandLine::Argument::Mandatory, "Direccin IP Puerto en el que el servidor atiende respuestas.");
160    commandLine.add ("n", CommandLine::Argument::Mandatory, "Numero de mensajes por segundo");
161    commandLine.add ("domain", CommandLine::Argument::Mandatory, "http://domain-openapis/path-openapis/serviceID/guid/other_possible_levels?query_parameters");
162    commandLine.add ("path", CommandLine::Argument::Optional, "http://domain-openapis/path-openapis/serviceID/guid/other_possible_levels?query_parameters");
163    commandLine.add ("service", CommandLine::Argument::Mandatory, "http://domain-openapis/path-openapis/serviceID/guid/other_possible_levels?query_parameters");
164    commandLine.add ("guid", CommandLine::Argument::Mandatory, "http://domain-openapis/path-openapis/serviceID/guid/other_possible_levels?query_parameters");
165    commandLine.add ("other", CommandLine::Argument::Optional, "http://domain-openapis/path-openapis/serviceID/guid/other_possible_levels?query_parameters");
166    commandLine.add ("trace", CommandLine::Argument::Optional, "Nivel de trazas (debug,warning, error,...)");
167 }
168
169 void HeavyWIMS20Client::initialize ()
170    throw (RuntimeException)
171 {
172    CommandLine& cl (CommandLine::instantiate ());
173
174    Network& network = Network::instantiate ();
175
176    a_server = network.createServer (cl.getValue ("a"), cl.getIntegerValue ("p"), true, &http::Transport::getFactory ());
177    a_sender.setMessageBySecond (cl.getIntegerValue ("n"));
178
179    if (cl.exists ("trace"))
180       Logger::setLevel (Logger::asLevel (cl.getValue ("trace")));
181
182    const char* domain = cl.getValue ("domain");
183
184    if (cl.exists ("path")) 
185       a_request = new http::wims20::ClientSide (domain, cl.getValue ("path"));
186    else
187       a_request = new http::wims20::ClientSide (domain);
188
189    a_request->setServiceID (cl.getValue ("service"));
190    a_request->setGUID (cl.getValue ("guid"));
191
192    if (cl.exists ("other")) 
193       a_request->addOtherLevel (cl.getValue ("other"));
194    
195    a_sender.setRequest (a_request);
196 }
197
198 void HeavyWIMS20Client::run ()
199    throw (RuntimeException)
200 {
201    a_timeController.activate (a_sender);
202
203    a_communicator.accept ();
204 }
205
206 void MyCommunicator::eventReceiveMessage (ClientSocket& clientSocket, const Message& message)
207    throw (RuntimeException)
208 {
209    LOGMETHOD (TraceMethod tm ("MyCommunicator", "eventReceiveMessage", ANNA_FILE_LOCATION));
210
211    if (clientSocket.support (http::Transport::className ()) == false)
212       return;
213
214    a_httpHandler.apply (clientSocket, message);
215 }
216
217 void MyCommunicator::count (const Millisecond delay)
218    throw (RuntimeException)
219 {
220    Guard guard (this, "MyCommunicator::count");
221    
222    a_rxMessageCounter ++;
223    a_avgResponseTime += delay;
224 }
225
226 void MyCommunicator::eventBreakConnection (const ClientSocket& clientSocket)
227    throw ()
228 {
229    if (a_rxMessageCounter == 0) {
230       LOGWARNING (
231          string msg ("MyCommunicator::eventBreakConnection | ");
232          msg += clientSocket.asString ();
233          Logger::warning (msg, ANNA_FILE_LOCATION);
234       );
235       return;
236    }
237
238    LOGNOTICE (
239       HeavyWIMS20Client& app = static_cast <HeavyWIMS20Client&> (anna::app::functions::getApp ());   
240       string msg ("Tiempo medio respuesta: ");
241       msg += anna::functions::asString (a_avgResponseTime / a_rxMessageCounter);
242       msg += " ms";
243       msg += anna::functions::asText (" | Rx: ", a_rxMessageCounter);
244       msg += anna::functions::asText (" | Tx: ", app.getSender ()->getTxMessageCounter ());
245       Logger::notice (msg, ANNA_FILE_LOCATION);
246
247       cout << msg << endl << endl;
248    );
249    requestStop ();
250    comm::Communicator::eventBreakConnection (clientSocket);
251 }
252
253 bool Sender::tick ()
254    throw (RuntimeException)
255 {
256    Server* server = static_cast <HeavyWIMS20Client&> (anna::app::functions::getApp ()).getServer ();
257    Communicator* communicator = anna::app::functions::component <Communicator> (ANNA_FILE_LOCATION);
258
259    if (a_errorCounter > 50) {
260       communicator->requestStop ();
261       Logger::warning ("Terminado por errores continuos en la conexion", ANNA_FILE_LOCATION);
262       return false;
263    }
264
265    for (int n = 0; n < a_messageByTick && communicator->hasRequestedStop () == false; n ++) {
266       a_request->setParameter ("Operator", "a");
267       a_request->setParameter ("ValueOne", rand () % 1000);
268       a_request->setParameter ("ValueTwo", rand () % 1000);
269       a_request->setParameter ("Time", anna::functions::millisecond ());
270
271       try {         
272          a_request->codeOn (a_httpRequest);
273          server->send (a_httpRequest);
274          a_txMessageCounter ++;
275       }
276       catch (RuntimeException& ex) {
277          a_errorCounter ++;
278          ex.trace ();
279          break;
280       }
281    }
282
283    return true;
284 }
285
286 void MyHandler::evResponse (ClientSocket& clientSocket, const http::Response& response)
287    throw (RuntimeException)
288 {
289    TraceMethod tm ("MyHandler", "evResponse", ANNA_FILE_LOCATION);
290
291    if (response.getStatusCode () != 200) 
292       return;
293    
294    // El servidor implementado en Java retornará un 500 cuando alcanze el nº máximo de mensajes.
295    if (response.getStatusCode () == 500) {
296       LOGWARNING (
297          string msg (" MyHandler::evResponse  | ");
298          msg += response.asString ();
299          msg += " | Servidor java notifica fin de la prueba.";
300          Logger::debug (msg, ANNA_FILE_LOCATION);
301       );      
302       eventBreakConnection (clientSocket);
303       return;      
304    }
305       
306    a_xmlResponse.initialize (response.getBody ());
307
308    const xml::Node* root = a_xmlResponse.parse ();
309    
310    const anna::Millisecond now = anna::functions::millisecond ();
311    const anna::Millisecond past(root->getAttribute ("Time")->getIntegerValue ()); 
312    
313    const int delay = now - past; 
314
315    if (delay < 0) {
316       LOGWARNING (
317          string msg = a_xmlResponse.getContentAsCString ();
318          msg += anna::functions::asText (" | Delay: ", delay);
319          Logger::warning (msg, ANNA_FILE_LOCATION);
320       );
321       return;
322    }
323
324    app::functions::component <MyCommunicator> (ANNA_FILE_LOCATION)->count (Millisecond(delay));
325 }