Fixes and improvements
[anna.git] / example / http / wims20RServer / 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   Ejemplo de programa servidor. Atiende peticiones aritmeticas, el protocolo de transporte sera HTTP
11   ver http::Transport y el contenido del mensaje sera el resutaldo de un comm::Codec con los
12   (x, y, op) -> El resultado sera estos tres componente mas result.
13
14   Para poder probar el sistema de congestion se puede indicar un numero de milisegundos de
15   retardo aplicados a cada contestacion.
16   
17   Los clientes pueden ser: wims20_kclient.p wims20_client.p
18 */
19 #include <iostream>
20
21 #include <anna/core/core.hpp>
22
23 #include <anna/xml/Node.hpp>
24 #include <anna/xml/Attribute.hpp>
25
26 #include <anna/comm/comm.hpp>
27
28 #include <anna/http/Request.hpp>
29 #include <anna/http/Response.hpp>
30 #include <anna/http/Handler.hpp>
31 #include <anna/http/Transport.hpp>
32 #include <anna/http/functions.hpp>
33
34 #include <anna/http/wims20/ServerSide.hpp>
35
36 using namespace std;
37
38 class MyCommunicator : public comm::Communicator {
39 public:
40    MyCommunicator () {;}
41 };
42
43 class MyHandler : public http::Handler {
44 public:
45    MyHandler () : http::Handler ("wims20_rserver::MyHandler"), a_request (NULL) {;}
46
47    static const char* className () throw () { return "wims20_rserver::ReceiverFactory"; }
48
49 private:
50    MyCommunicator* a_communicator;
51    http::wims20::ServerSide* a_request;
52
53    void initialize () throw (RuntimeException);
54    void evRequest (ClientSocket&, const http::Request& request) throw (RuntimeException);
55    void evResponse (ClientSocket&, const http::Response&) throw (RuntimeException) {;}
56 };
57
58 class HTTPArithmeticServer : public comm::Application {
59 public:
60    HTTPArithmeticServer ();
61       
62 private:
63    MyCommunicator a_communicator;
64    ReceiverFactoryImpl <MyHandler> a_receiverFactory;
65    comm::ServerSocket* a_serverSocket;
66
67    void initialize () throw (RuntimeException);
68    void run () throw (RuntimeException);
69 };
70
71 using namespace std;
72 using namespace anna::comm;
73
74 int main (int argc, const char** argv)
75 {
76    CommandLine& commandLine (CommandLine::instantiate ());
77    HTTPArithmeticServer app;
78
79    http::functions::initialize ();
80
81    srand (time (NULL));
82
83    try {
84       commandLine.initialize (argv, argc);
85       commandLine.verify ();
86
87       Logger::setLevel (Logger::Debug); 
88       Logger::initialize ("wims20_server", new anna::TraceWriter ("file.trace", 4048000));
89  
90       app.start ();
91    }
92    catch (Exception& ex) {
93       cout << ex.asString () << endl;
94    }
95    
96    return 0;
97 }
98
99 HTTPArithmeticServer::HTTPArithmeticServer () : 
100    Application ("wims20_rserver", "Servidor HTTP/WIMS 2.0 de operaciones aritmeticas (iRS)", "1.0") 
101 {
102    CommandLine& commandLine (CommandLine::instantiate ());
103       
104    commandLine.add ("p", CommandLine::Argument::Mandatory, "Puerto en el que atender peticiones");
105    commandLine.add ("a", CommandLine::Argument::Mandatory, "Direccion IP en la que atender");
106    commandLine.add ("trace", CommandLine::Argument::Optional, "Nivel de trazas (debug,warning, error,...)");
107    commandLine.add ("domain", CommandLine::Argument::Mandatory, "Domain indicado en la peticion WIMS 2.0");
108    commandLine.add ("path", CommandLine::Argument::Optional, "Path indicado en la peticion WIMS 2.0");
109 }
110
111 void HTTPArithmeticServer::initialize () 
112    throw (RuntimeException)
113 {
114    CommandLine& cl (CommandLine::instantiate ());
115
116    int port = cl.getIntegerValue ("p");
117    const comm::Device* device = Network::instantiate ().find (Device::asAddress (cl.getValue ("a")));
118
119    comm::TransportFactory* ttff = &http::Transport::getFactory ();
120
121    a_serverSocket = new ServerSocket (INetAddress (device, port), cl.exists ("r"), ttff);
122    a_serverSocket->setReceiverFactory (a_receiverFactory);
123 }
124
125 void HTTPArithmeticServer::run ()
126    throw (RuntimeException)
127 {
128    CommandLine& cl (CommandLine::instantiate ());
129
130    a_communicator.attach (a_serverSocket);
131
132    if (cl.exists ("trace"))
133       Logger::setLevel (Logger::asLevel (cl.getValue ("trace")));
134
135    a_communicator.accept ();
136 }
137
138 void MyHandler::initialize ()
139    throw (RuntimeException)
140 {
141    CommandLine& cl (CommandLine::instantiate ());
142
143    string domain = cl.getValue ("domain");
144
145    if (cl.exists ("path")) {
146       string path = cl.getValue ("path");
147       a_request = new http::wims20::ServerSide (domain, path);
148    }
149    else
150       a_request = new http::wims20::ServerSide (domain);
151   
152    a_communicator = app::functions::component <MyCommunicator> (ANNA_FILE_LOCATION);
153    allocateResponse ()->createHeader (http::Header::Type::Date); 
154 }
155
156 // Recibe una petición de operación aritmética y visualiza el resultado por pantalla.
157 // Para mantener la simplicidad del ejemplo sólo notifica al cliente si ha ido bien 
158 // o no, pero no deuuelve ningún resultado
159 //
160 // Definimos que nuestro servicio deberá recibir una petición REST de la forma:
161 //   http://<domain>/<path>/math/guest@tid.es/<op>?X=nnn&Y=nnn
162 // Observar que la <op> correspondería con las parte other_possible_levels
163 // de la especificación WIMS 2.0
164 void MyHandler::evRequest (ClientSocket& clientSocket, const http::Request& request)
165    throw (RuntimeException)
166 {
167    LOGMETHOD (TraceMethod tm ("MyReceiver", "apply", ANNA_FILE_LOCATION));
168
169    a_request->decode (request);
170
171    cout << a_request->asString () << endl << endl;
172
173    http::Response* response = allocateResponse ();
174
175    if (a_request->getServiceID () != "math" || a_request->getGUID () != "user@tid.es") {
176       response->setStatusCode (401);
177       clientSocket.send (*response);
178       return;
179    }
180
181    if (a_request->hasOtherLevels () == false) {
182       response->setStatusCode (416);
183       clientSocket.send (*response);
184       return;
185    }
186
187    // El primer nivel indica la operación a realizar
188    const std::string& op = *http::wims20::ServerSide::otherLevel (a_request->other_level_begin ());
189
190    int x, y;
191
192    try {
193       x = a_request->getIntegerValue ("X");
194       y = a_request->getIntegerValue ("Y");
195       response->setStatusCode (200);
196
197       if (op == "sum") 
198          cout << x << " + " << y << " = " << x + y << endl << endl;
199       else if (op == "sub") 
200          cout << x << " - " << y << " = " << x - y << endl << endl;
201       else if (op == "mul") 
202          cout << x << " * " << y << " = " << x * y << endl << endl;
203       else if (op == "div") {
204          if (y != 0)
205             cout << x << " / " << y << " = " << x / y << endl << endl;
206          else {
207             response->setStatusCode (400);
208             response->setReasonPhrase ("Division por cero");
209          }
210       }
211       else 
212          response->setStatusCode (501);
213    }
214    catch (RuntimeException& ex) {
215       ex.trace ();
216       response->setStatusCode (500);
217       response->setReasonPhrase (ex.getText ());
218    }
219    
220    try {
221       clientSocket.send (*response);
222    }
223    catch (Exception& ex) {
224       ex.trace ();
225    }
226 }
227