#include "cdevClock.h" #include "ServerInterface.h" cdevReactor ServerInterface::Reactor; // ***************************************************************************** // * ServerConnectionList::ServerConnectionList : // * Constructor for the list of active connections that is being managed // * by the ServerHandler. // ***************************************************************************** ServerConnectionList::ServerConnectionList ( void ) { maxItems = ALLOCATION_COUNT; items = (ServerHandler **)malloc(maxItems*sizeof(ServerHandler *)); memset(items, 0, maxItems*sizeof(ServerHandler *)); } // ***************************************************************************** // * ServerConnectionList::~ServerConnectionList : // * Destructor for the list of active connections that is being managed // * by the ServerHandler. // ***************************************************************************** ServerConnectionList::~ServerConnectionList ( void ) { free (items); } // ***************************************************************************** // * ServerConnectionList::find : // * Locates a ServerHandler object that is associated with the specified // * server name. // ***************************************************************************** ServerHandler * ServerConnectionList::find ( char * server ) { int i; for(i=0; igetServer()); i++); return (igetServer(); for(i=0; igetServer()); i++); if(i>=maxItems) { items = (ServerHandler **) realloc(items, 2*maxItems*sizeof(ServerHandler *)); memset(&items[maxItems], 0, maxItems*sizeof(ServerHandler *)); maxItems*=2; } if(items[i]==NULL) { items[i] = handler; result = 0; } } return result; } // ***************************************************************************** // * ServerConnectionList::remove : // * Removes the ServerHandler object specified by handler if it exists in // * list. // ***************************************************************************** int ServerConnectionList::remove ( ServerHandler * handler ) { int result = -1; if(handler!=NULL) { int i; for(i=0; idequeue(&buf, &len)==0) delete buf; connections.remove(handler); delete handler; } // ********************************************************************* // * Iterate through the queues associated with servers and remove // * all messages that have not been transmitted. // ********************************************************************* StringHashIterator iter(&connectionQueues); iter.first(); while((buf = iter.key())!=NULL) { FifoQueue * queue = (FifoQueue *)iter.data(); connectionQueues.remove(buf); while(queue->dequeue(&buf, &len)==0) delete buf; delete queue; iter.first(); } } // ***************************************************************************** // * ServerInterface::getDefault : // * This method allows the caller to obtain a pointer to the name of the // * default server. // ***************************************************************************** char * ServerInterface::getDefault ( void ) { return defaultServer; } // ***************************************************************************** // * ServerInterface::setDefault : // * This method allows the caller to set the name of the default server. // ***************************************************************************** void ServerInterface::setDefault ( char * Default ) { if(defaultServer!=NULL) { delete defaultServer; defaultServer = NULL; defaultServerHandler = NULL; } if(Default!=NULL && *Default) { defaultServer = strdup(Default); defaultServerHandler = connect(defaultServer); } } // ***************************************************************************** // * ServerInterface::connect : // * This is a stub method that should be overridden by the developer in // * a child class. // ***************************************************************************** ServerHandler * ServerInterface::connect ( char *, char *, unsigned short ) { return NULL; } // ***************************************************************************** // * ServerInterface::disconnect : // * This is a stub method that should be overridden by the developer in // * a child class. // ***************************************************************************** ServerHandler * ServerInterface::disconnect ( char * ) { return NULL; } // ***************************************************************************** // * ServerInterface::enqueue : // * This enqueue mechanism is called by the client if they have already // * identified the server that they wish to communicate with and have // * obtained a pointer to its ServerHandler object. // * // * If the specified server handler is NULL then the message will be // * submitted to the default server. // ***************************************************************************** int ServerInterface::enqueue( ServerHandler * handler, char * binary, size_t binaryLen ) { int result; if(handler!=NULL || (handler=defaultServerHandler)!=NULL || (defaultServer!=NULL && (handler=(defaultServerHandler=connect(defaultServer)))!=NULL)) { handler->enqueue(binary, binaryLen); result = CDEV_SUCCESS; } else { outputError(CDEV_SEVERITY_ERROR, "ServerInterface::enqueue", "No default server has been specified"); result = CDEV_ERROR; } return result; } // ***************************************************************************** // * ServerInterface::getQueue : // * This method allows the caller to obtain a pointer to the FifoQueue // * associated with a specific server. This queue is then attached to // * the ServerHandler that is connected to that server. // ***************************************************************************** FifoQueue * ServerInterface::getQueue ( char * server ) { FifoQueue * queue = (FifoQueue *)connectionQueues.find(server); if(queue==NULL) { queue=new FifoQueue; connectionQueues.insert(server, (void *)queue); } return queue; } // ***************************************************************************** // * ServerInterface::isPacketValid : // * This method is called by the ServerHandler after it has been restarted. // * If the ServerHandler inherits packets that are already in the queue, it // * will call this method for each pcket in order to determine if they are // * valid. If the packets are no longer valid (i.e., there is no longer // * a cdevTransObj associated with them, they will be deleted. Otherwise, // * they will be submitted to the server. // * // * Because this functionality will be handled in a derived class, this // * method will always return 1 to indicate that the packet is valid. // ***************************************************************************** int ServerInterface::isPacketValid ( char * binary, size_t binaryLen ) { return (binary!=NULL && binaryLen>0)?1:0; } // ***************************************************************************** // * ServerInterface::getFd : // * This mechanism is used to obtain a list of the file descriptors that // * are used by the ServerInterface. These file descriptors may then be // * used for polling. // ***************************************************************************** int ServerInterface::getFd (int * &fd, int &numFd ) { int idx = 0; numFd = 0; while(connections[idx]!=NULL) { if((numFd+1)>maxFd) { maxFd*=2; fdList = (int *)realloc(fdList, maxFd*sizeof(int)); } fdList[numFd++] = connections[idx++]->getHandle(); } if(numFd) fd = fdList; else fd = NULL; return CDEV_SUCCESS; } // ***************************************************************************** // * ServerInterface::flush : // * This mechanism is used to flush all outbound buffers to their respective // * servers. // ***************************************************************************** int ServerInterface::flush ( void ) { // ********************************************************************* // * First - determine if there is any outbound data that needs to be // * processed. Walk through the connectionQueue objects - which exist // * even if the connection has been lost - and count up the number of // * queues that are not empty. // ********************************************************************* StringHashIterator iter(&connectionQueues); FifoQueue * queue = NULL; int needsFlush = 0; // ********************************************************************* // * Invoke a brief reactor.checkHandlers in order to cause any // * dead ServerHandlers to be removed from the Reactor. Then // * attempt to reattach to any ServerHandlers that have // * outbound data. // ********************************************************************* char * server; Reactor.checkHandlers(); for(iter.first(); (server=iter.key())!=NULL; iter++) { queue = (FifoQueue *)iter.data(); if(queue && !queue->empty()) { needsFlush++; connect(server); } } // ********************************************************************* // * At this point he needsFlush variable will be non-zero if any of // * the queues contain outbound data. The remainder of this code // * only needs to be performed if this is the case. // ********************************************************************* if(needsFlush) { // ************************************************************* // * Provide a maximum timeout value of 5 seconds. If the system // * cannot be flushed in that amount of time - then an error or // * a hang must exist on the other side. // ************************************************************* cdevTimeValue t(5.0); cdevClock timer; timer.schedule(NULL, t); // ************************************************************* // * While there are sockets that have outbound data AND the // * timer has not expired - process events. // ************************************************************* int idx = 0; while(needsFlush && !timer.expired()) { needsFlush = 0; for(idx=0; connections[idx]!=NULL; idx++) { if(!connections[idx]->empty()) { connections[idx]->setMask(cdevEventHandler::WRITE_MASK|cdevEventHandler::EXCEPT_MASK); needsFlush++; } else connections[idx]->setMask(cdevEventHandler::READ_MASK|cdevEventHandler::EXCEPT_MASK); } if(needsFlush) Reactor.handleEvents(1.0, cdevReactor::UNTIL_EVENT); } // ********************************************************************* // * Walk through all of the ServerHandlers and restore them to // * READ_MASK mode. // ********************************************************************* if(needsFlush) { for(idx=0; connections[idx]!=NULL; idx++) { connections[idx]->setMask(cdevEventHandler::READ_MASK|cdevEventHandler::EXCEPT_MASK); } } } return CDEV_SUCCESS; } // ***************************************************************************** // * ServerInterface::flush : // * This mechanism is used to flush all outbound buffers to their respective // * servers. // ***************************************************************************** int ServerInterface::flush ( int fd ) { // ********************************************************************* // * Walk through each item and locate the handler that needs to be // * flushed. If the connection is located, then handle events until // * it is empty. // ********************************************************************* int i; for(i=0; connections[i]!=NULL && connections[i]->getHandle()!=fd; i++); if(connections[i]!=NULL) { cdevTimeValue t(5.0); cdevClock timer; timer.schedule(NULL, t); connections[i]->setMask(cdevEventHandler::WRITE_MASK|cdevEventHandler::EXCEPT_MASK); while(connections[i]!=NULL && !connections[i]->empty() && !timer.expired()) { Reactor.handleEvents(1.0, cdevReactor::UNTIL_EVENT); } // ************************************************************* // * If an error occurs during the write, the ServerHandler may // * have been deleted - therefore, check to ensure that the // * connection is valid prior to restoring its mask. // ************************************************************* if(connections[i]!=NULL) { connections[i]->setMask(cdevEventHandler::READ_MASK|cdevEventHandler::EXCEPT_MASK); } } return CDEV_SUCCESS; } // ***************************************************************************** // * ServerInterface::pend : // * Pends for the specified period of time. The int parameter is not used // * in this implementation. In later designs it may be used to allow the // * interface to pend on a specific file descriptor. // ***************************************************************************** int ServerInterface::pend ( double seconds, int ) { // ********************************************************************* // * Test to determine if there are any ServerHandlers in operation. // ********************************************************************* if(connections[0]!=NULL) { // ************************************************************* // * Flush outbound requests if necessary. // ************************************************************* flush(); // ************************************************************* // * Handle events for the specified period. // ************************************************************* Reactor.handleEvents(seconds); } return CDEV_SUCCESS; } // ***************************************************************************** // * ServerInterface::poll : // * This method polls the cdevReactor to see if any events are ready to be // * processed on any of the sockets. If events are ready, then they are // * processed immediately - otherwise, the function returns immediately. // ***************************************************************************** int ServerInterface::poll ( void ) { // ********************************************************************* // * Test to determine if there are any ServerHandlers in operation. // ********************************************************************* if(connections[0]!=NULL) { // ************************************************************* // * Flush outbound requests if necessary. // ************************************************************* flush(); // ************************************************************* // * Handle events for 0 seconds - effectively polling. // ************************************************************* Reactor.handleEvents(0.0001, cdevReactor::UNTIL_EVENT); } return CDEV_SUCCESS; }