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