1 // ANNA - Anna is Not Nothingness Anymore
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
5 // http://redmine.teslayout.com/projects/anna-suite
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
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
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.
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.
33 // Authors: eduardo.ramos.testillano@gmail.com
34 // cisco.tierra@gmail.com
37 #include <anna/core/tracing/Logger.hpp>
38 #include <anna/core/tracing/TraceMethod.hpp>
40 #include <anna/xml/Node.hpp>
42 #include <anna/comm/Communicator.hpp>
43 #include <anna/comm/Server.hpp>
44 #include <anna/comm/ClientSocket.hpp>
45 #include <anna/comm/Host.hpp>
46 #include <anna/comm/Device.hpp>
47 #include <anna/comm/functions.hpp>
48 #include <anna/comm/internal/RemoteConnection.hpp>
53 comm::Server::Server(const string& name, const comm::Host& host, const int remotePort, const bool autoRecovery, comm::TransportFactory* transportFactory) :
56 a_remotePort(remotePort),
57 a_autoRecovery(autoRecovery),
58 a_transportFactory(transportFactory),
60 a_msMaxConnectionDelay(ClientSocket::DefaultMaxConnectionDelay),
61 a_msMaxWriteDelay(ClientSocket::DefaultMaxWriteDelay),
62 a_receiverFactory(NULL),
63 a_ignoreIncomingMessages(false),
65 // La a_sequence se usa para diferenciar en las trazas y demás las conexiones que puede haber contra un mismo (IP, port)
66 // Se establece desde comm::Host
70 //----------------------------------------------------------------------------------------
71 // (1) Para asegurar que el comm::handler::RemoteConnection no lo mete en la lista de
72 // servidores pendientes de recuperar.
73 //----------------------------------------------------------------------------------------
74 comm::Server::~Server() {
75 if(a_clientSocket == NULL)
78 bool* autoRecovery = const_cast <bool*>(&a_autoRecovery);
79 *autoRecovery = false;
82 Communicator* communicator = functions::component <Communicator> (ANNA_FILE_LOCATION);
83 communicator->detach(a_clientSocket);
84 } catch(Exception& ex) {
90 * Se invoca desde com::handle::RemoteConnection::finalize
91 * [Tx] -> Communicator
93 * El socket se cierra protegido por esta SSCC para asegurar que los recursos se usan/bloquean en el mismo
94 * orden que el usado en el Server::connect.
96 void comm::Server::reset()
97 throw(RuntimeException) {
98 // La SSCC se establece en el método que invoca a éste
99 // Guard guard (*this, "comm::Server::reset");
100 if(a_clientSocket == NULL)
103 a_clientSocket->close();
104 delete a_clientSocket;
105 a_clientSocket = NULL;
108 void comm::Server::setReceiverFactory(comm::ReceiverFactory& receiverFactory)
110 a_receiverFactory = &receiverFactory;
112 if(a_clientSocket != NULL)
113 a_clientSocket->setReceiverFactory(receiverFactory);
117 * El punto conflictivo de llamada es desde el comm::ConnectionRecover::tryRecover
119 * [T1] -> Communicator
121 * (1) La instancia RemoteConnection se libera en el handler::RemoteConnection::finalize.
122 * (2) La espera maxima solo se aplica si ha sido modificada .
123 * (3) La espera para la escritura sobre buffer de salida llenos solo se aplica si ha sido modificada.
125 * Lista de llamadas en 1.11.07
127 [19/11/2009 13:22:07] Local7 | mt.db/Guard.cc (90) | thr: 0xb7819b20 | Guard::lock | Reference: (0x9f86090): comm::Host::createServer
129 [19/11/2009 13:22:07] Information | comm.Host.cc (91) | thr: 0xb7819b20 | comm::Host::createServer | comm::Server { ...
131 [19/11/2009 13:22:07] Local7 | mt.db/Guard.cc (90) | thr: 0xb7819b20 | Guard::lock | Reference: (0x9f865b0): comm::Server::connect
133 [19/11/2009 13:22:07] Local7 | mt.db/Guard.cc (90) | thr: 0xb7819b20 | Guard::lock | Reference: (0xbfd67ea8): comm::Communicator::attach (RemoteConnection)
135 [19/11/2009 13:22:07] Local7 | mt.db/Guard.cc (90) | thr: 0xb7819b20 | Guard::lock | Reference: (0x9f86608): anna::comm::ClientSocket (connect)
137 [19/11/2009 13:22:07] Debug | comm.Socket.cc (171) | thr: 0xb7819b20 | anna::comm::Socket::open | comm::ClientSocket { comm::Socket { Domain: Inet | Type: Stream | comm::TransportFactory { Name: anna::comm::SureTransport | OverQuotaSize: 0 bytes } | fd: 4 | LocalPoint: { <null> } | Bound: false } | RcvBufferSize: -1 bytes | Status: None | MaxConDelay: 200 ms | Reserved: 0 | Pending: 0 | Offset: 0 | ExpectedSize: -1 | Punto remoto: { comm::INetAddress { comm::Device { IP: 127.0.0.1 | Status: Up } | Port: 2000 } } }
139 [19/11/2009 13:22:07] Debug | comm.Socket.cc (219) | thr: 0xb7819b20 | anna::comm::Socket::setBlockingMode | Current: false | Previous: true
141 [19/11/2009 13:22:07] Debug | comm.ClientSocket.cc (286) | thr: 0xb7819b20 | anna::comm::ClientSocket::connect | comm::ClientSocket { comm::Socket { Domain: Inet | Type: Stream | comm::TransportFactory { Name: anna::comm::SureTransport | OverQuotaSize: 0 bytes } | fd: 4 | LocalPoint: { <null> } | Bound: false } | RcvBufferSize: 87380 bytes | Status: Connected | MaxConDelay: 200 ms | Reserved: 0 | Pending: 0 | Offset: 0 | ExpectedSize: -1 | Punto remoto: { comm::INetAddress { comm::Device { IP: 127.0.0.1 | Status: Up } | Port: 2000 } } }
143 [19/11/2009 13:22:07] Local7 | mt.db/Guard.cc (78) | thr: 0xb7819b20 | Guard::deactivate | Reference: (0x9f86608): anna::comm::ClientSocket (connect)
145 [19/11/2009 13:22:07] Notice | comm.Communicator.cc (714) | thr: 0xb7819b20 | comm::Communicator::eventCreateConnection | comm::Server { anna::Resource { Nombre: 127.0.0.1:2000 | Habilitado: true | Disponible: true } | Auto-Recovery: false | comm::ClientSocket { comm::Socket { Domain: Inet | Type: Stream | comm::TransportFactory { Name: anna::comm::SureTransport | OverQuotaSize: 0 bytes } | fd: 4 | LocalPoint: { <null> } | Bound: false } | RcvBufferSize: 87380 bytes | Status: Connected | MaxConDelay: 200 ms | Reserved: 0 | Pending: 0 | Offset: 0 | ExpectedSize: -1 | Punto remoto: { comm::INetAddress { comm::Device { IP: 127.0.0.1 | Status: Up } | Port: 2000 } } } }
147 [19/11/2009 13:22:07] Debug | comm.Communicator.cc (213) | thr: 0xb7819b20 | comm::Communicator::attach | comm::handler::RemoteConnection { comm::Handler { anna::Runnable { Id: Handler4 | Running: false | RequestedStop: false } | fd: 4 } | comm::RemoteConnection { comm::ClientSocket { comm::Socket { Domain: Inet | Type: Stream | comm::TransportFactory { Name: anna::comm::SureTransport | OverQuotaSize: 0 bytes } | fd: 4 | LocalPoint: { <null> } | Bound: false } | RcvBufferSize: 87380 bytes | Status: Connected | MaxConDelay: 200 ms | Reserved: 0 | Pending: 0 | Offset: 0 | ExpectedSize: -1 | Punto remoto: { comm::INetAddress { comm::Device { IP: 127.0.0.1 | Status: Up } | Port: 2000 } } } } }
149 [19/11/2009 13:22:07] Local7 | mt.db/Guard.cc (78) | thr: 0xb7819b20 | Guard::deactivate | Reference: (0xbfd67ea8): comm::Communicator::attach (RemoteConnection)
151 [19/11/2009 13:22:07] Debug | comm.Server.cc (163) | thr: 0xb7819b20 | anna::comm::Server::connect | comm::Server { anna::Resource { Nombre: 127.0.0.1:2000 | Habilitado: true | Disponible: true } | Auto-Recovery: false | comm::ClientSocket { comm::Socket { Domain: Inet | Type: Stream | comm::TransportFactory { Name: anna::comm::SureTransport | OverQuotaSize: 0 bytes } | fd: 4 | LocalPoint: { <null> } | Bound: false } | RcvBufferSize: 87380 bytes | Status: Connected | MaxConDelay: 200 ms | Reserved: 0 | Pending: 0 | Offset: 0 | ExpectedSize: -1 | Punto remoto: { comm::INetAddress { comm::Device { IP: 127.0.0.1 | Status: Up } | Port: 2000 } } } }
153 [19/11/2009 13:22:07] Local7 | mt.db/Guard.cc (78) | thr: 0xb7819b20 | Guard::deactivate | Reference: (0x9f865b0): comm::Server::connect
155 [19/11/2009 13:22:07] Local7 | mt.db/Guard.cc (78) | thr: 0xb7819b20 | Guard::deactivate | Reference: (0x9f86090): comm::Host::createServer
158 void comm::Server::connect()
159 throw(RuntimeException) {
160 LOGMETHOD(TraceMethod tm(Logger::Local7, "comm::Server", "connect", ANNA_FILE_LOCATION));
161 Communicator* communicator = functions::component <Communicator> (ANNA_FILE_LOCATION);
163 * Para asegurar el mismo orden de bloqueo a la hora de tratar la desconexión.
165 Guard guard(communicator, "comm::Communicator from comm::Server::connect");
166 Guard guard2(*this, "comm::Server::connect");
168 if(a_clientSocket != NULL && a_clientSocket->isConnected() == true)
171 Host::const_device_iterator ii;
172 Host::const_device_iterator maxii = a_host.device_end();
175 for(ii = a_host.device_begin(); ii != maxii; ii ++)
176 counter += (Host::device(ii)->getStatus() == Device::Status::Up);
179 const Millisecond msMaxConnectionDelay = a_msMaxConnectionDelay / counter;
180 RemoteConnection* remoteConnection = NULL;
182 for(ii = a_host.device_begin(); ii != maxii && a_clientSocket == NULL; ii ++) {
183 a_clientSocket = allocateClientSocket(INetAddress(Host::device(ii), a_remotePort), a_transportFactory);
185 if(msMaxConnectionDelay != 0)
186 a_clientSocket->setMaxConnectionDelay(msMaxConnectionDelay); // (2)
188 if(a_clientSocket->getMaxWriteDelay() != a_msMaxWriteDelay)
189 a_clientSocket->setMaxWriteDelay(a_msMaxWriteDelay); // (3)
191 if(a_receiverFactory != NULL)
192 a_clientSocket->setReceiverFactory(*a_receiverFactory);
194 a_clientSocket->setIgnoreIncomingMessages(a_ignoreIncomingMessages);
195 remoteConnection = new RemoteConnection(this, a_clientSocket); // (1)
198 communicator->attach(remoteConnection);
199 } catch(RuntimeException& ex) {
200 delete a_clientSocket;
201 delete remoteConnection;
202 a_clientSocket = NULL;
208 if(a_clientSocket == NULL) {
209 string msg(asString());
210 msg += " | Cannot connect to server";
211 throw RuntimeException(msg, ANNA_FILE_LOCATION);
215 string msg("anna::comm::Server::connect | ");
217 Logger::debug(msg, ANNA_FILE_LOCATION)
221 //---------------------------------------------------------------------------------------
222 // (1) Mejora de la version 1.0.8 -> Si el Server no tiene reconexion automatica pero
223 // se intenta usar => se intenta volver a conectar antes de dar el fallo.
224 //---------------------------------------------------------------------------------------
225 comm::ClientSocket* comm::Server::send(Message& message)
226 throw(RuntimeException) {
227 LOGMETHOD(TraceMethod tm(Logger::Local7, "comm::Server", "send", ANNA_FILE_LOCATION));
228 Guard guard(*this, "comm::Server::send");
229 const bool available = isAvailable();
230 const bool enabled = isEnabled();
232 if(a_autoRecovery == false && available == false && enabled == true) // (1)
235 if(available == false || enabled == false) {
236 string msg(asString());
237 msg += " | Server unavailable";
238 throw RuntimeException(msg, ANNA_FILE_LOCATION);
241 a_clientSocket->send(message);
242 return a_clientSocket;
245 comm::ClientSocket* comm::Server::send(Message* message)
246 throw(RuntimeException) {
248 throw RuntimeException("anna::comm::Server::send | Cannot send a NULL message", ANNA_FILE_LOCATION);
250 return send(*message);
253 void comm::Server::setAutoRecovery(bool autoRecovery) throw() {
254 bool* ar = const_cast <bool*>(&a_autoRecovery);
258 bool comm::Server::isAvailable() const
259 throw(RuntimeException) {
260 // Guard guard (*this, "comm::Server::isAvailable");
261 return (a_clientSocket == NULL) ? false : (a_clientSocket->isConnected() && (a_clientSocket->isClosedPending() == false));
264 // Este metodo sea re-escrito en commsec::Server::allocateClientSocket para devolver un commsec::RemoteConnection
265 comm::ClientSocket* comm::Server::allocateClientSocket(const comm::INetAddress& in, comm::TransportFactory* transportFactory) const
267 return new ClientSocket(in, transportFactory);
270 string comm::Server::asString() const
272 string result("comm::Server { ");
273 result += Resource::asString();
274 result += " | Sequence: ";
275 result += functions::asString(a_sequence);
276 result += " | Auto-Recovery: ";
277 result += functions::asString(a_autoRecovery);
280 if(a_clientSocket == NULL) {
282 result += a_host.getName();
283 result += functions::asString(" | RemotePort: %d", a_remotePort);
285 result += a_clientSocket->asString();
287 return result += " }";
290 xml::Node* comm::Server::asXML(xml::Node* parent) const
291 throw(RuntimeException) {
292 xml::Node* result = parent->createChild("comm.Server");
293 result->createAttribute("Host", a_host.getName());
294 result->createAttribute("Sequence", a_sequence);
295 comm::Resource::asAttribute(result);
296 result->createAttribute("AutoRecovery", functions::asString(a_autoRecovery));
297 result->createAttribute("RemotePort", a_remotePort);
298 result->createAttribute("IgnoreIncomingMessages", functions::asString(a_ignoreIncomingMessages));
300 if(a_clientSocket != NULL)
301 result->createAttribute("fd", a_clientSocket->getfd());