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