#include #include #include #include #include // ***************************************************************************** // * cdevClientRequestObject::cdevClientRequestObject : // * This constructor initializes the internals of a device/message // * pair associated with a cdevClientService based service. // * // * Returns nothing. // * // * Remember it is the responsibility of the requestObject to ensure // * that the message is not laden with extraneous spaces. // ***************************************************************************** cdevClientRequestObject::cdevClientRequestObject ( char * device, char * msg, cdevSystem & system) : cdevRequestObject(device, msg, system), syncCallback(defaultCallback, (void *)&sendStatus), handler(NULL), contextID(-1) { char message[256]; char * ptr; *server = 0; *DDL_server = 0; // ********************************************************************* // * Remove any extra spaces from the message. // * Copy user supplied msg to a message buffer to allow // * modification in this routine since the user supplied // * msg could be a constant string // ********************************************************************* strncpy (message, msg, sizeof (message) - 1); message[255] = 0; for(ptr=message; *ptr!=0; ptr++) { if(isspace(*ptr)) { char * nextChar; for(nextChar=ptr+1; isspace(*nextChar) && *nextChar!=0; nextChar++); if(nextChar!=ptr+1) strcpy(ptr+1, nextChar); *ptr=' '; } } // ********************************************************************* // * Remove all trailing spaces from the message. // ********************************************************************* for(ptr = message+(strlen(message)-1); ptr>=message && isspace(*ptr); ptr--) { *ptr=0; } // ********************************************************************* // * Determine the commandCode from the message string. // ********************************************************************* if(!strncmp(message, "get ", 4)) commandCode = GET_COMMAND; else if(!strncmp(message, "set ", 4)) commandCode = SET_COMMAND; else if(!strncmp(message, "monitorOn ", 10)) commandCode = MONITOR_ON_COMMAND; else if(!strncmp(message, "monitorOff ", 11)) commandCode = MONITOR_OFF_COMMAND; else commandCode = OTHER_COMMAND; // ********************************************************************* // * Determine the messageCode from the message string. // ********************************************************************* if(!strcmp(message, "get servers")) messageCode = GET_SERVERS_MESSAGE; else if(!strcmp(message, "get default")) messageCode = GET_DEFAULT_MESSAGE; else if(!strcmp(message, "set default")) messageCode = SET_DEFAULT_MESSAGE; else if(!strcmp(message, "disconnect")) messageCode = DISCONNECT_MESSAGE; else if(!strcmp(message, "get ClientInfo")) messageCode = GET_CLIENTINFO_MESSAGE; else if(!strcmp(message, "get ServerInfo")) messageCode = GET_SERVERINFO_MESSAGE; else messageCode = OTHER_MESSAGE; // ********************************************************************* // * Attempt to read the server name from the DDL file. // ********************************************************************* cdevData output; sprintf (DDL_server, "resolveServiceData %s %s", device, message); if((system.nameServer()).send(DDL_server, NULL, &output)==CDEV_SUCCESS) { *DDL_server = 0; output.get ("server", DDL_server, 256); } else *DDL_server = 0; } // ***************************************************************************** // * cdevClientRequestObject::~cdevClientRequestObject: // * This is the destructor for the object. It will free any memory that it // * has allocated and remove itself from its associated ServerHandler // * callback. // ***************************************************************************** cdevClientRequestObject::~cdevClientRequestObject ( void ) { if(handler!=NULL) handler->unregisterServerCallback(this); } // ***************************************************************************** // * cdevClientRequestObject::getState : // * Returns the connection state of the cdevClientRequestObject. // ***************************************************************************** int cdevClientRequestObject::getState ( void ) { ServerHandler * Handler = NULL; return (getServerHandler(&Handler)==CDEV_SUCCESS)?CDEV_STATE_NOTCONNECTED:CDEV_STATE_CONNECTED; } // ***************************************************************************** // * cdevClientRequestObject::className : // * Obtains the name of this class as a string. // ***************************************************************************** const char * cdevClientRequestObject::className ( void ) const { return "cdevClientRequestObject"; } // ***************************************************************************** // * cdevClientRequestObject::setContext : // * This method is called to set the context of the request object. The // * first step in this process is to determine if the caller has specified // * a server in the context. If so, the server name should be set to that // * value. Next call the setContext method of the underlying // * cdevRequestObject... // ***************************************************************************** int cdevClientRequestObject::setContext (cdevData& cxt) { cdevClientService * svc = (cdevClientService *)service_; int result = CDEV_SUCCESS; int newContextID = 0; if((newContextID = svc->contexts.insert(cxt))!=contextID) { ServerHandler * Handler = NULL; if(handler!=NULL) handler->unregisterServerCallback(this); contextID = newContextID; *server = 0; handler = NULL; cxt.get ("server", server, 256); getServerHandler(&Handler); result = cdevRequestObject::setContext(cxt); } return result; } // ***************************************************************************** // * cdevClientRequestObject::sendNoBlock : // * This function allows the caller to submit an asynchronous message to the // * server for processing. // ***************************************************************************** int cdevClientRequestObject::sendNoBlock (cdevData & in, cdevData & out) { return sendNoBlock(&in, &out); } int cdevClientRequestObject::sendNoBlock (cdevData * in, cdevData & out) { return sendNoBlock(in, &out); } int cdevClientRequestObject::sendNoBlock (cdevData & in, cdevData * out) { return sendNoBlock(&in, out); } int cdevClientRequestObject::sendNoBlock (cdevData * in, cdevData * out) { cdevClientService * svc = (cdevClientService *)service_; cdevTranObj * xobj = new cdevTranObj(&system_, this, out, &svc->syncCallback); ServerHandler * Handler = NULL; int status = CDEV_SUCCESS; xobj->disableDeleteCbk(); if((status=getServerHandler(&Handler))==CDEV_SUCCESS) { status = svc->enqueue(Handler, in, *xobj); } else delete xobj; return status; } // ***************************************************************************** // * cdevClientRequestObject::sendCallback : // * This function allows the caller to submit an asynchronous message to the // * server for processing. // ***************************************************************************** int cdevClientRequestObject::sendCallback (cdevData & in, cdevCallback & callback) { return sendCallback(&in, callback); } int cdevClientRequestObject::sendCallback (cdevData * in, cdevCallback & callback) { cdevCallback * cb = new cdevCallback(callback); cdevClientService * svc = (cdevClientService *)service_; cdevTranObj * xobj = new cdevTranObj(&system_, this, NULL, cb); ServerHandler * Handler = NULL; int status = CDEV_SUCCESS; xobj->enableDeleteCbk(); if((status=getServerHandler(&Handler))==CDEV_SUCCESS) { status = svc->enqueue(Handler, in, *xobj); } else delete xobj; return status; } // ***************************************************************************** // * cdevClientRequestObject::send : // * The send interface is used to provide synchronous I/O with the service. // * // * Returns CDEV_SUCCESS on success or CDEV_ERROR on error. // ***************************************************************************** int cdevClientRequestObject::send ( cdevData & in, cdevData & out ) { return send(&in, &out); } int cdevClientRequestObject::send ( cdevData * in, cdevData & out ) { return send(in, &out); } int cdevClientRequestObject::send ( cdevData & in, cdevData * out ) { return send(&in, out); } int cdevClientRequestObject::send ( cdevData * in, cdevData * out ) { int status = CDEV_SUCCESS; cdevClientService * svc = (cdevClientService *)service_; cdevTranObj * xobj = new cdevTranObj(&system_, this, out, &syncCallback); ServerHandler * Handler; sendStatus.completionCode = 0; sendStatus.finished = 0; xobj->disableDeleteCbk(); if((status=getServerHandler(&Handler))==CDEV_SUCCESS) { if((status = svc->enqueue(Handler, in, *xobj))==CDEV_SUCCESS) { // ***************************************************** // * I used to wait for a response here only if the outbound // * cdevData object was non-null. However, that provided // * unexpected behavior to the client. Now I wait whether // * output data is expected or not. // ***************************************************** cdevTimeValue t(waitPeriod()); cdevClock timer; timer.schedule(NULL,t); // ***************************************************** // * WAITING WITH system_.pend(): // * Previously I was using system_.pend() to process // * events while waiting for the service to respond. // * This resulted in a lock-up when the connection // * could not be established or if the connection // * collapsed while in use. // * // * WAITING WITH system_.poll(): // * When in a heavy inbound traffic situation, the // * calls from other services will trample all over // * the inbound data coming from the server. // * This results in unreliable delivery and processing // * of messages from the server. // * // * WAITING WITH service_.poll(): // * So far so good. // ***************************************************** while(!sendStatus.finished && !timer.expired()) service_->poll(); if (!sendStatus.finished) { svc->cancel(*xobj); status = CDEV_ERROR; system_.reportError( CDEV_SEVERITY_ERROR, "cdevRequestObject", this, "Server failed to respond after %.1f seconds", (float)waitPeriod()); } else { status = sendStatus.completionCode; } } } else delete xobj; return status; } // ***************************************************************************** // * cdevClientRequestObject::defaultCallback : // * This is the callback function that is used by the object to // * receive callbacks for send operations. // ***************************************************************************** void cdevClientRequestObject::defaultCallback (int status, void * user, cdevRequestObject &, cdevData &) { SendStatus * result = (SendStatus *)user; if(result) { result->completionCode = status; result->finished = 1; } } // ***************************************************************************** // * cdevClientRequestObject::executeServerHandlerCallback : // * The requestObject registers itself with the ServerHandler when it // * attaches to it... If the Serverhandler is destroyed, it will // * call this method on all of the registered request objects in order // * to notify them that it is going away. This allows the request // * object to clear the pointer to the ServerHandler. // ***************************************************************************** void cdevClientRequestObject::executeServerHandlerCallback (ServerHandler * Handler) { if(Handler==handler) { /* * system_.reportError(CDEV_SEVERITY_INFO, "cdevRequestObject", * this, "Connection to %s broken for %s:%s", * Handler->get_server(), device().name(), message()); */ handler=NULL; } } // ***************************************************************************** // * cdevClientRequestObject::getServerHandler : // * This method is used to obtain the ServerHandler for this request // * object... // * // * The following rules will be followed: // * // * 1) If a server has been specified in the context, then that // * server will be used. // * // * - otherwise - // * // * 2) If a server has been specified in the service data of the // * DDL file, then that server will be used. // * // * - otherwise - // * // * 3) The default server as specified at the cdevClientService will // * be used. // ***************************************************************************** int cdevClientRequestObject::getServerHandler (ServerHandler ** Handler) { cdevClientService * svc = (cdevClientService *)service_; int result = CDEV_SUCCESS; *Handler = NULL; if (*server==0 && *DDL_server!=0) strcpy(server, DDL_server); if (*server!=0 && handler==NULL) { if((*Handler = svc->connect(server))==NULL) result = CDEV_ERROR; else { handler = *Handler; handler->registerServerCallback(this); } } else *Handler = handler; return result; } // ***************************************************************************** // * cdevClientRequestObject::isRequestRestartable : // * This method allows the caller to determine if the request object // * is restartable. If a server goes down and then a new server comes // * up in its place, this method will be called for each request object // * that has an outstanding request that has not been serviced. // * If the isRequestRestartable method returns 1, the request will be // * sent to the new server - otherwise, the request will be terminated. // ***************************************************************************** int cdevClientRequestObject::isRequestRestartable ( void ) { int result = 0; switch(getCommandCode()) { case GET_COMMAND: case MONITOR_ON_COMMAND: result = 1; break; case SET_COMMAND: case MONITOR_OFF_COMMAND: default: result = 0; break; } return result; } // ***************************************************************************** // * cdevClientRequestObject::getContextID : // * This method will retrieve the identifier of the context that is // * currently in use by this object. // ***************************************************************************** int cdevClientRequestObject::getContextID ( void ) { return contextID; } // ***************************************************************************** // * getCommandCode : // * This method will return the current value of the commandCode variable. // * This variable is used to identify the VERB that is utilized by this // * cdevClientRequestObject. The default set of verbs are "get", "set", // * "monitorOn", and "monitorOff". // ***************************************************************************** int cdevClientRequestObject::getCommandCode ( void ) { return commandCode; } // ***************************************************************************** // * getMessageCode : // * This method will return the current value of the messageCode variable. // * This variable is used to identify the message that is utilized by this // * cdevClientRequestObject. The default set of supported messages are // * "get servers", "get default", "set default", and "disconnect". // ***************************************************************************** int cdevClientRequestObject::getMessageCode ( void ) { return messageCode; }