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