#include #include #include #include #include int cdevClientService::ServerTag = 0; int cdevClientService::HostTag = 0; int cdevClientService::PortTag = 0; cdevGenericServerTagDef cdevClientService::tags; // ***************************************************************************** // * ClientInfo clientInfo : // * This is a static member of the cdevClientService class that contains // * descriptive information about the client process. // ***************************************************************************** ClientInfo cdevClientService::clientInfo; // ***************************************************************************** // * cdevClientTransaction::freeList_ : // * This is the free list for the cdevClientTransaction object that is // * used to reduce run-time memory allocations. // ***************************************************************************** cdevClientTransaction * cdevClientTransaction::freeList_ = NULL; // ***************************************************************************** // * This is the text string that will be used to define the cdevNameServer // * if it has not yet been defined in the cdevDirectory file. // ***************************************************************************** static char * NameServerDefinitionString = "service rns { tags {PV} }\n\ class RNS { messages { query rns; get rns; monitorOn rns; monitorOff rns; monitorEntry rns; monitorEntryOff rns; } }\n\ RNS :\nNameServer\n;\n"; // ***************************************************************************** // * cdevClientService::cdevClientService : // * This is the constructor for the object. Its is responsible for // * initializing the underlying classes and data items. // ***************************************************************************** cdevClientService::cdevClientService ( char * Domain, char * name, cdevSystem & system ) : ServerInterface (), syncCallback (defaultCallback, NULL), domain (strdup(Domain)), cdevService (name, system), transactions (), contexts (), nsCallbackArgs (0) { // ********************************************************************* // * Determine if the NameServer has been provided in the CDEVDDL file. // * If not, add the NameServer to the cdevDirectory. // ********************************************************************* cdevDirectory & nameServer = system.nameServer(); cdevData request; cdevData reply; request.insert("device", "NameServer"); if(nameServer.send("queryClass", request, reply)==CDEV_NOTFOUND) { request.insert("file", NameServerDefinitionString); nameServer.send("update", request, NULL); } // ********************************************************************* // * If the tags used by this service have not yet been defined in the // * cdevGlobalTagTable, then automatically define them here. // ********************************************************************* if(ServerTag==0) { cdevData::insertTag(ServiceTagBase+1, "server"); cdevData::insertTag(ServiceTagBase+2, "host"); cdevData::insertTag(ServiceTagBase+3, "port"); cdevData::tagC2I("server", &ServerTag); cdevData::tagC2I("host", &HostTag); cdevData::tagC2I("port", &PortTag); } // ********************************************************************* // * Install a tag callback that will automicatically be executed each // * time a new tag is added to the cdevGlobalTagTable. // ********************************************************************* cdevData::addTagCallback(this); } // ***************************************************************************** // * cdevClientService::~cdevClientService : // * This is the destructor for the object. It will call release any // * memory that was allocated by the class. // ***************************************************************************** cdevClientService::~cdevClientService ( void ) { AddressIndexIterator iter (&transactions); StringHashIterator sIter(&nsCallbackArgs); cdevClientTransaction * xobj; if(domain) delete domain; for(iter.first(); (xobj = (cdevClientTransaction *)iter.data())!=NULL; iter++) { transactions.remove(iter.key()); delete xobj; } NSCallbackArg * nsData; for(sIter.first(); (nsData = (NSCallbackArg *)sIter.data())!=NULL; sIter++) { delete nsData; } cdevData::delTagCallback(this); } // ***************************************************************************** // * cdevClientService::defaultCallback : // * This is the callback function that is used to support sendNoBlock // * requests that are issued by the cdevRequestObject. // ***************************************************************************** void cdevClientService::defaultCallback (int, void *, cdevRequestObject &, cdevData & ) { } // ***************************************************************************** // * cdevClientService::nameServerCallback : // * This is the callback function that is executed whenever one of the // * connected servers becomes disconnected. This function will call each // * of the request objects associated with the server and notify them // * of the server's change in status. // ***************************************************************************** void cdevClientService::nameServerCallback(int, void *userarg, cdevRequestObject &, cdevData & data) { NSCallbackArg * arg = (NSCallbackArg *)userarg; cdevClientService * cService = arg->service; unsigned short port = 0; int statusCode = -1; cdevData userData; char server[255]; char host[255]; // ********************************************************************* // * Initialize the server and host variables to empty strings, this // * will allow me to detect if the data was actually extracted from the // * cdevData object. // ********************************************************************* *server = 0; *host = 0; // ********************************************************************* // * The status code from the name server should be between 0 and 3. // * If the status code is out of range, display an error and return. // ********************************************************************* data.get("status", &statusCode); if(statusCode<0) { cService->outputError(CDEV_SEVERITY_ERROR, "cdevClientService", "Name Server is down..."); return; } else if(statusCode>3) { cService->outputError(CDEV_SEVERITY_ERROR, "cdevClientService", "Unknown status code (%i) received from Name Server", statusCode); return; } // ********************************************************************* // * Read critical tags from the cdevData object. // ********************************************************************* data.get("name", server, 255); data.get("host", host, 255); data.get("port", &port); // ********************************************************************* // * Determine if a live connection still exists to the specified server // ********************************************************************* ServerHandler * handler = cService->connections.find(server); // ********************************************************************* // * If the handler is NULL this means that the application has already // * become disconnected from the server. Consequently, all restartable // * requests will have to be resent and all other requests will have // * to be terminated. // * For this reason, if the statusCode coming from Name Server is 0, // * it should be set to 3 (indicating a server restart). If the status // * code coming from the server is a 1 then it should be set to 2 // * (indicating a dead server). // ********************************************************************* if(handler==NULL && statusCode==0) statusCode=3; if(handler==NULL && statusCode==1) statusCode=2; // ********************************************************************* // * If the statusCode is 2 (terminated server) and the ServerHandler // * is not equal to NULL, then delete the ServerHandler to force the // * connection to be closed. // ********************************************************************* if(statusCode==2 && handler!=NULL) { delete handler; handler = NULL; } // ********************************************************************* // * Report the condition of the connection. // ********************************************************************* cService->outputError(CDEV_SEVERITY_INFO, "cdevClientService", "Name Server reports server %s in domain %s has been %s", server, cService->getDomain(), ((statusCode==0)?"CONNECTED":((statusCode==1)?"DISCONNECTED": ((statusCode==2)?"TERMINATED":"RESTARTED")))); // ********************************************************************* // * Walk through the list of transactions and process those with a // * matching server name and a differing status code. // ********************************************************************* AddressIndexIterator iter(&cService->transactions); cdevClientTransaction * trans; for(iter.first(); (trans = (cdevClientTransaction *)iter.data())!=NULL; iter++) { cdevTranObj * xobj = trans->xobj; cdevCallback * cb = xobj->userCallback_; // ************************************************************* // * Process transactions associated with this specific server. // ************************************************************* if(trans->statusCode!=statusCode && !strcmp(trans->server, server)) { // ***************************************************** // * Status code of 0 indicates that the server is // * online and operational. We also check the // * ServerHandler to ensure that we still have a // * valid connection. // ***************************************************** if(statusCode==0) { cb->fireCallback(CDEV_RECONNECTED, cb->userarg(), *xobj->reqObj_, userData, 1); if(trans->statusCode<0) { trans->reconnect(host, port, iter.key()); } } // ***************************************************** // * Status code of 1 indicates that the server has // * ceased sending periodic updates to the name server // ***************************************************** else if (statusCode==1) { cb->fireCallback(CDEV_DISCONNECTED, cb->userarg(), *xobj->reqObj_, userData, 1); trans->statusCode = statusCode; } // ***************************************************** // * Status code of 2 indicates that the server has // * terminated and all connections are lost. // ***************************************************** else if (statusCode==2 && trans->restartable) { cb->fireCallback(CDEV_DISCONNECTED, cb->userarg(), *xobj->reqObj_, userData, 1); trans->statusCode = statusCode; } // ***************************************************** // * Status code of 3 indicates that a new server with // * an identical name and domain has taken the place // * of the dead server. If the transaction is // * restartable, resubmit the request to the server. // ***************************************************** else if (statusCode==3 && trans->restartable) { trans->statusCode = -1; trans->reconnect(host, port, iter.key()); } // ***************************************************** // * This section will catch the statusCode 2 and 3 // * events where the request is not restartable and // * will delete these transactions. // ***************************************************** else if (statusCode==2 || statusCode==3) { cb->fireCallback(CDEV_ERROR, cb->userarg(), *xobj->reqObj_, userData, 0); cService->transactions.remove(iter.key()); delete trans; } } } } // ***************************************************************************** // * cdevClientService::outputError : // * This mechanism is used to report errors to the system. // ***************************************************************************** int cdevClientService::outputError(int severity, char *name, char *formatString, ...) { int status; va_list argp; va_start (argp, formatString); status = system_.vreportError(severity, name, NULL, formatString, argp); va_end (argp); return status; } // ***************************************************************************** // * cdevClientService::connect : // * This mechanism will establish a new connection with a server and will // * will return a pointer to the ServerHandler. If the host and port are // * specified in the arguments, then the method will attempt to connect // * using those values without going through the CDEV Name Server. // * // * Note: This method will insert the ServerHandler into the connections // * object and will install it in the Reactor. // ***************************************************************************** ServerHandler * cdevClientService::connect ( char * server, char * host, unsigned short port ) { ServerHandler * handler = NULL; // ********************************************************************* // * First attempt to locate the handler from the existing connections. // ********************************************************************* if(server && *server && (handler = connections.find(server))==NULL) { int errCode = CDEV_SUCCESS; char serverHost[255]; unsigned short serverPort; int serverStatus; // ************************************************************* // * If the host and port have been specified, then use them // * rather than going through the CDEV Name Server. // ************************************************************* if(host!=NULL && *host && port>0) { strcpy(serverHost, host); serverPort = port; } else { cdevData input; cdevData output; cdevRequestObject & rnsReq = cdevRequestObject::attachRef("NameServer", "get"); *serverHost = 0; serverPort = 0; // ***************************************************** // * Insert domain and name in the input request object // * to be sent to the CDEV Name Server. // ***************************************************** input.insert("name", server); input.insert("domain", domain); // ***************************************************** // * Query the Name Server to obtain the hostname and // * port of the specified domain and server. // ***************************************************** if((errCode=rnsReq.send(input, output))!=CDEV_SUCCESS || output.get("host", serverHost, 255)!=CDEV_SUCCESS || output.get("port", &serverPort)!=CDEV_SUCCESS || output.get("status", &serverStatus)!=CDEV_SUCCESS || *serverHost==0 || serverPort<=0 || serverStatus!=0) { errCode = CDEV_ERROR; // ********************************************* // * If the hostname could not be found, report // * the error and set the return value to // * CDEV_ERROR. // ********************************************* outputError(CDEV_SEVERITY_ERROR, "cdevClientService::connect", "Cannot find host for server \"%s\" in domain \"%s\"", server, domain); } } // ************************************************************* // * If the hostname was discovered, then the errCode should be // * CDEV_SUCCESS. Attempt to connect to the server using the // * cdevReactor mechanisms. // ************************************************************* if(errCode==CDEV_SUCCESS) { cdevInetAddr addr; addr.set (serverPort, serverHost); handler = new ServerHandler(server, this); if(handler->open(addr)!=0) { outputError(CDEV_SEVERITY_ERROR, "cdevClientService::connect", "Failed to connect to %s on port %i", serverHost, serverPort); delete handler; handler = NULL; } else { outputError(CDEV_SEVERITY_INFO, "cdevClientService::connect", "Connected to %s on port %i", serverHost, serverPort); connections.insert(handler); // ********************************************* // * This calls the registerFd method of the // * service which will cause the FD Changed // * callbacks to be triggered. // ********************************************* registerFd(handler->getHandle(), 1); // ********************************************* // * Install a Name Server monitor to maintain // * the status of the connection. // * Note: If a monitor has already been // * installed using this informations, then the // * CDEV Name Server service should not create // * a second monitor. // * // * Note: I am using the FifoQueue associated // * with the specific server in order to // * differentiate between the various servers. // ********************************************* cdevData monData; cdevCallback monCb(nameServerCallback, (void *)getCallbackArg(server)); cdevRequestObject & monReq = cdevRequestObject::attachRef("NameServer", "monitorOn"); monData.insert("name", server); monData.insert("domain", domain); monReq.sendCallback(monData, monCb); // ********************************************* // * Here is where a "set ClientInfo" message // * will be sent to the server to provide // * descriptive information about the client. // ********************************************* cdevData tagMap; int * itags; char ** ctags; int ntags; char * binary = NULL; size_t binaryLen = 0; cdevData::readTagTable(itags, ctags, ntags); tagMap.insert(1, itags, ntags); tagMap.insert(2, ctags, ntags); delete itags; delete ctags; handler->setTagChangeFlag(0); cdevMessageBinary packet(handler->getClientID(), 0, 0, 0, 0, CDEV_SERVER_OP, 0, 0, NULL, "set ClientInfo", &clientInfo.getClientData(), NULL, &tagMap); packet.streamOut(&binary, &binaryLen); packet.detachData(); ServerInterface::enqueue(handler, binary, binaryLen); } } } return handler; } // ***************************************************************************** // * cdevClientService::disconnect : // * This mechanism will remove the ServerHandler pointer from the // * connections list and will return the ServerHandler pointer to the // * caller. It is the responsibility of the caller to delete the // * ServerHandler pointer. // ***************************************************************************** ServerHandler * cdevClientService::disconnect ( char * server ) { ServerHandler * handler; // ********************************************************************* // * Find and remove the specified server. This method will also check // * to determine if the handler that is being disconnected is the // * defaultServer - if so, the defaultServerHandler variable will be // * set to NULL. // ********************************************************************* if((handler = connections.remove(server))==defaultServerHandler) { defaultServerHandler = NULL; } // ********************************************************************* // * This calls the registerFd method of the service which will cause // * the FD Changed callbacks to be triggered. // ********************************************************************* if(handler!=NULL && handler->getHandle()!=cdevSocket::INVALID_HANDLE) { registerFd(handler->getHandle(), 0); } return handler; } // ***************************************************************************** // * cdevClientService::flush : // * This is method calls the flush mechanism of the underlying // * ServerInterface class. // ***************************************************************************** int cdevClientService::flush ( void ) { return ServerInterface::flush(); } // ***************************************************************************** // * cdevClientService::pend : // * This method calls the pend mechanism of the underlying // * ServerInterface class. // ***************************************************************************** int cdevClientService::pend ( double seconds, int fd ) { return ServerInterface::pend(seconds, fd); } // ***************************************************************************** // * cdevClientService::getFd : // * This method calls the getFd mechanism of the underlying // * ServerInterface class. // ***************************************************************************** int cdevClientService::getFd( int * &fd, int & numFd ) { return ServerInterface::getFd(fd, numFd); } // ***************************************************************************** // * cdevClientService::poll : // * This is the polling mechanism for the service. // ***************************************************************************** int cdevClientService::poll ( void ) { return ServerInterface::poll(); } // ***************************************************************************** // * cdevClientService::pend : // * This method does not implement the full functionality of the // * cdev prototype. Rather it pends for a default amount of time. // ***************************************************************************** int cdevClientService::pend( int fd) { return pend(0.01, fd); } // ***************************************************************************** // * cdevClientService::getNameServer : // * This method is used to provide a cdevDevice that can be used as the // * name server for the service. It is not implemented here. // ***************************************************************************** int cdevClientService::getNameServer ( cdevDevice * &ns ) { ns = NULL; return CDEV_SUCCESS; } // ***************************************************************************** // * cdevClientService::getRequestObject : // * This is the interface that cdev objects will use to obtain a // * cdevClientRequestObject object. The cdevClientRequestObject // * represents a combined device and message pair that is associated // * with the cdevClientService. // * // * Returns CDEV_SUCCESS on success or CDEV_ERROR on error. // ***************************************************************************** int cdevClientService::getRequestObject ( char * device, char * message, cdevRequestObject * &req) { req = new cdevClientRequestObject (device, message, system_); return (req ? CDEV_SUCCESS : CDEV_ERROR); } // ***************************************************************************** // * cdevClientService::enqueue : // * This is the method that is used by the cdevRequestObject to submit a // * message to the cdevServer when the name of the server is known, but a // * ServerHandler object has not yet been obtained... // ***************************************************************************** int cdevClientService::enqueue ( char * server, cdevData * in, cdevTranObj & xobj ) { ServerHandler * handler = server==NULL?defaultServerHandler:connect(server); return enqueue(handler, in, xobj); } // ***************************************************************************** // * cdevClientService::enqueue : // * This method is used by the cdevRequestObject to enqueue an outbound // * message to the cdevServer when the ServerHandler has already been // * obtained. // ***************************************************************************** int cdevClientService::enqueue ( ServerHandler * handler, cdevData * in, cdevTranObj &xobj) { int result = CDEV_SUCCESS; // ********************************************************************* // * Determine if the message will be processed locally first. // * If not, then submit it to the underlying ServerInterface // * for processing. // ********************************************************************* if(processLocal(in, xobj)!=0) { // ************************************************************* // * Determine if the handler is valid or use the // * defaultServerHandler // ************************************************************* if(handler==NULL) { if(defaultServerHandler!=NULL) { handler=defaultServerHandler; } else if(defaultServerHandler==NULL && defaultServer!=NULL) { handler = (defaultServerHandler=connect(defaultServer)); } else { system_.reportError(CDEV_SEVERITY_ERROR, "cdevClientService::enqueue", xobj.reqObj_, "No default server has been defined for this service"); } } // ************************************************************* // * If a valid ServerHandler has been located. // ************************************************************* if(handler!=NULL) { cdevClientRequestObject * reqObj; int restart; int ctxID; cdevClientTransaction * node; unsigned index; reqObj = (cdevClientRequestObject *)xobj.reqObj_; restart = reqObj->isRequestRestartable(); ctxID = reqObj->getContextID(); node = new cdevClientTransaction(xobj, *handler, restart, in, ctxID); index = transactions.insert(node); if((result=enqueue(handler, *node, index))!=CDEV_SUCCESS) { transactions.remove(index); delete node; } else { // ********************************************* // * If the message is monitorOff, then the // * method will walk through all of the active // * monitors and remove those that exactly match // * all attributes of the request.... // * - Server name // * - Device name // * - Attribute name // * - Context values // ********************************************* if(reqObj->getCommandCode()==cdevClientRequestObject::MONITOR_OFF_COMMAND) { #define xCommand ((cdevClientRequestObject *)trans->xobj->reqObj_)->getCommandCode() #define xDevice xobj->reqObj_->device().name() #define xMessage xobj->reqObj_->message() #define xUserarg xobj->userCallback_->userarg() #define xCallback xobj->userCallback_->callbackFunction() AddressIndexIterator iter(&transactions); cdevClientTransaction * trans; cdevMessageBinary packet; char * device = (char *)reqObj->device().name(); char * binary; size_t binaryLen; for(iter.first(); (trans=(cdevClientTransaction *)iter.data())!=NULL; iter++) { if(xCommand==cdevClientRequestObject::MONITOR_ON_COMMAND && (node->xUserarg==NULL || node->xUserarg==trans->xUserarg) && (node->xCallback==NULL || node->xCallback==trans->xCallback) && !strcmp(node->xDevice, trans->xDevice) && !strcmp(node->xMessage+11, trans->xMessage+10) && !strcmp(node->server, trans->server)) { int cancelTransIdx = iter.key(); // ********************************************** // * Remove the transaction and delete it. // ********************************************** transactions.remove(cancelTransIdx); // ********************************************** // * Fire the callback with the // * partialTransaction flag set to 0 to notify // * the developer that the transaction is // * finished. Note that the status is // * CDEV_WARNING because no actual data is // * being delivered. // ********************************************** fireCallback(CDEV_WARNING, *trans->xobj, NULL, 0); delete trans; // ********************************************** // * Send a packet containing the transaction to // * be destroyed. // ********************************************** packet.set(handler->getClientID(), 0, cancelTransIdx, 0, 0, 0, 0, 1, &device, xobj.reqObj_->message()); packet.streamOut(&binary, &binaryLen); packet.detachData(); ServerInterface::enqueue(handler, binary, binaryLen); } } #undef xCommand #undef xDevice #undef XMessage #undef XUserarg #undef XCallback } } } else result = CDEV_ERROR; } return result; } // ***************************************************************************** // * cdevClientService::enqueue : // * This method is called by the internals of the cdevClientService to // * submit a message to a server. This method is called by either the // * enqueue method (to initially submit a request), or the nameServerCallback // * method ( to resubmit a request when a server has restarted). // ***************************************************************************** int cdevClientService::enqueue ( ServerHandler * handler, cdevClientTransaction &trans, unsigned transID ) { int result; // ********************************************************************* // * If the server handler is valid and a connection has been // * established. // ********************************************************************* if(handler!=NULL) { cdevTranObj * xobj = trans.xobj; cdevClientRequestObject * reqObj = (cdevClientRequestObject *)xobj->reqObj_; char * device = (char *)reqObj->device().name(); int ctxID = trans.contextID; cdevData * in = trans.userData; cdevData * context = NULL; cdevData * tagMap = NULL; int opCode = CDEV_NORMAL_OP; // ************************************************************* // * Set the operation code to CDEV_SERVER_OP if the message // * should be processed by the cdevGenericServer Engine rather // * than the user defined server engine. // ************************************************************* if(reqObj->getMessageCode()==cdevClientRequestObject::GET_CLIENTINFO_MESSAGE || reqObj->getMessageCode()==cdevClientRequestObject::GET_SERVERINFO_MESSAGE) { opCode=CDEV_SERVER_OP; } // ************************************************************* // * Switch to the contextID specified in trans parameter. // ************************************************************* if(handler->getContextID()!=ctxID) { context = contexts.find(ctxID); handler->setContextID(ctxID); } // ************************************************************* // * If the tag map has changed, include it in the transmission // ************************************************************* if(handler->getTagChangeFlag()) { int * itags; char ** ctags; int ntags; cdevData::readTagTable(itags, ctags, ntags); tagMap = new cdevData; tagMap->insert(1, itags, ntags); tagMap->insert(2, ctags, ntags); delete itags; delete ctags; handler->setTagChangeFlag(0); } // ************************************************************* // * Construct the outbound packet. // ************************************************************* cdevMessageBinary packet(handler->getClientID(), transID, 0, 0, 0, opCode, 0, 1, &device, reqObj->message(), in, context, tagMap); // ************************************************************* // * Delete the tag map if it was allocated. // ************************************************************* if(tagMap) delete tagMap; // ************************************************************* // * Extract the binary stream representation of the data and // * then use the detach data mechanism to ensure that the // * binary buffer is not deleted when the packet object is // * destroyed. // ************************************************************* char * binary = NULL; size_t binaryLen = 0; packet.streamOut(&binary, &binaryLen); packet.detachData(); // ************************************************************* // * Enqueue the message into the outbound queue. // ************************************************************* result=ServerInterface::enqueue(handler, binary, binaryLen); } else result = CDEV_ERROR; return result; } // ***************************************************************************** // * cdevClientService::cancel : // * This method is used to cancel a transaction that has taken too long // * to be processed. // ***************************************************************************** int cdevClientService::cancel ( cdevTranObj & xobj ) { AddressIndexIterator iter(&transactions); cdevClientTransaction * node; for(iter.first(); (node=(cdevClientTransaction *)iter.data())!=NULL && node->xobj!=&xobj; iter++); if(node!=NULL) { transactions.remove(iter.key()); delete node; } return CDEV_SUCCESS; } // ***************************************************************************** // * cdevClientService::enqueue : // * This is the mechanism that is used by the underlying ServerInterface // * to return the result of a transmission to the caller. DO NOT DELETE // * THE BINARY PROVIDED TO THIS CLASS. // ***************************************************************************** int cdevClientService::enqueue ( int status, ServerHandler * /*handler*/, char * binary, size_t binaryLen ) { cdevMessage message(binary, binaryLen); cdevClientTransaction * node; if((node=(cdevClientTransaction *)transactions.find(message.getTransIndex()))!=NULL) { int completionCode = (status==FAILED_TO_SEND)?CDEV_ERROR:message.getCompletionCode(); if(status==FAILED_TO_SEND) node->permanent = 0; fireCallback(completionCode, *node->xobj, message.getData(), node->permanent); if(!node->permanent) { transactions.remove(message.getTransIndex()); delete node; } } return CDEV_SUCCESS; } // ***************************************************************************** // * cdevClientService::fireCallback : // * This method is used to deploy a callback on a specific transaction. // ***************************************************************************** void cdevClientService::fireCallback ( int status, cdevTranObj &xobj, cdevData *resultData, int partialTransaction) { static cdevData dummy; if(resultData==NULL) { dummy.remove(); resultData = &dummy; } if(xobj.resultData_!=NULL) *xobj.resultData_ = *resultData; xobj.userCallback_->fireCallback (status, xobj.userCallback_->userarg(), *xobj.reqObj_, xobj.resultData_!=NULL?*xobj.resultData_:*resultData, partialTransaction); } // ***************************************************************************** // * cdevClientService::processLocal: // * This method performs any localized data processing that the service // * might require. If the message is processed locally, this method should // * return 0... otherwise, the method should return non-zero. // ***************************************************************************** int cdevClientService::processLocal ( cdevData * in, cdevTranObj & xobj) { int result = 1; int status = CDEV_SUCCESS; cdevClientRequestObject * reqObj = (cdevClientRequestObject *)xobj.reqObj_; switch(reqObj->getMessageCode()) { case cdevClientRequestObject::GET_SERVERS_MESSAGE: { char queryMsg[256]; cdevData input; cdevData output; cdevRequestObject & nsReq = cdevRequestObject::attachRef("NameServer", "query"); sprintf(queryMsg, "domain=='%s'", getDomain()); input.insert("queryMsg", queryMsg); if((status=nsReq.send(input, output))==CDEV_SUCCESS) { output.changeTag ("name", "value"); } fireCallback(status, xobj, &output); result = CDEV_SUCCESS; } break; case cdevClientRequestObject::GET_DEFAULT_MESSAGE: { cdevData resultData; resultData.insert("value", getDefault()); fireCallback(CDEV_SUCCESS, xobj, &resultData); result = CDEV_SUCCESS; } break; case cdevClientRequestObject::SET_DEFAULT_MESSAGE: { cdevData resultData; char DefaultServer[128]; *DefaultServer = 0; if(in!=NULL && in->get("value", DefaultServer, 128)==CDEV_SUCCESS && *DefaultServer!=0) { setDefault(DefaultServer); } else status=CDEV_ERROR; fireCallback(status, xobj, &resultData); result = CDEV_SUCCESS; } break; case cdevClientRequestObject::DISCONNECT_MESSAGE: { cdevData resultData; char server [128]; int status; if(xobj.reqObj_->getContext().get("server", server, 128)==CDEV_SUCCESS) { disconnect(server); status = CDEV_SUCCESS; } else status = CDEV_ERROR; fireCallback(status, xobj, &resultData); result = CDEV_SUCCESS; } break; default: break; } return result; } // ***************************************************************************** // * cdevClientService::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 packet 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. // ***************************************************************************** int cdevClientService::isPacketValid ( char * binary, size_t binaryLen ) { int result; if(binary!=NULL && binaryLen>0) { cdevMessageBinary message; unsigned int trans; message.getTransIndex(trans); message.attachData(binary, binaryLen); result = transactions.find(trans)?1:0; message.detachData(); } else result = 0; return result; } // ***************************************************************************** // * cdevClientService::callback : // * This method is called by the cdevData object whenever a new tag is // * added. // ***************************************************************************** void cdevClientService::callback ( int, char * ) { for(int idx=0; connections[idx]!=NULL; idx++) { connections[idx]->setTagChangeFlag(1); } } // ***************************************************************************** // * cdevClientService::getCallbackArg : // * This method creates a unique callback argument that can be passed to the // * CDEV Name Server service to allow it to differentiate between two // * servers that are being accessed through the same service. // ***************************************************************************** NSCallbackArg * cdevClientService::getCallbackArg ( char * server ) { NSCallbackArg * ptr; if((ptr = (NSCallbackArg *)nsCallbackArgs.find(server))==NULL) { ptr = new NSCallbackArg(server, this); nsCallbackArgs.insert(ptr->server, ptr); } return ptr; } // ***************************************************************************** // * cdevClientTransaction::operator new: // * Allocation function for the object. It will get the next preallocated // * cdevClientTransaction object from the free list, or, if none are available, // * refill the free list and then return a new cdevClientTransaction object. // ***************************************************************************** void * cdevClientTransaction::operator new ( size_t ) { cdevClientTransaction * result = NULL; if(freeList_==NULL) { freeList_ = ::new cdevClientTransaction[ALLOCATION_COUNT]; for(int i=0; ifreeListNext_; result->freeListNext_ = NULL; } return result; } // ***************************************************************************** // * cdevClientTransaction::delete: // * Rather than deallocating the cdevClientTransaction object, this function // * returns it to the free list where it may be retrieved by a later call // * of new. // ***************************************************************************** void cdevClientTransaction::operator delete ( void * ptr ) { cdevClientTransaction * node = (cdevClientTransaction *)ptr; if(node != NULL) { node->freeListNext_ = freeList_; freeList_ = node; } } // ***************************************************************************** // * cdevClienTransaction::cdevClientTransaction : // * Array constructor for the cdevClientTransaction object. // ***************************************************************************** cdevClientTransaction::cdevClientTransaction (void) : xobj (NULL), permanent (0), statusCode (0), restartable(0), contextID (0), userData (NULL) { *server = 0; } // ***************************************************************************** // * cdevClienTransaction::cdevClientTransaction : // * Constructor for the cdevClientTransaction object. // ***************************************************************************** cdevClientTransaction::cdevClientTransaction (cdevTranObj & XObj, ServerHandler &handler, int Restartable, cdevData * data, unsigned ContextID) : xobj (&XObj), permanent (0), statusCode (0), restartable(Restartable), contextID (ContextID), userData (NULL) { permanent = ((cdevClientRequestObject *)xobj->reqObj_)->getCommandCode()== cdevClientRequestObject::MONITOR_ON_COMMAND; strncpy(server, handler.getServer(), 256); server[255] = 0; if(restartable && data) userData = new cdevData(*data); else userData = data; } // ***************************************************************************** // * cdevClientTransaction::~cdevClientTransaction : // * Destructor for the cdevClientTransaction object. // ***************************************************************************** cdevClientTransaction::~cdevClientTransaction ( void ) { if(xobj) delete xobj; if(restartable && userData) delete userData; } // ***************************************************************************** // * cdevClientTransaction::reconnect : // * This method will attempt to recreate a connection for a server to // * a specified host and port. If the host and port are not specified, // * the the cdevClientService::connect and cdevClientService::find methods // * will be used to reattach to the specified server. // ***************************************************************************** void cdevClientTransaction::reconnect ( char * host, unsigned short port, unsigned key) { cdevClientService &service = (cdevClientService &)xobj->reqObj_->service(); if(key==0) key = service.transactions.find((void *)this); if(restartable && statusCode<0 && key) { ServerHandler *handler = NULL; // ************************************************************* // * Use the find method to locate the ServerHandler if // * possible, otherwise use the connect method to reconnect to // * the server. // ************************************************************* if((handler=service.connections.find(server))==NULL) handler = service.connect(server, host, port); // ************************************************************* // * If a connection was established to the server, then use // * the enqueue method to resubmit the request to the server. // ************************************************************* if(handler!=NULL) { if(service.enqueue(handler, *this, key)==CDEV_SUCCESS) { cdevData cData; statusCode = 0; xobj->userCallback_->fireCallback (CDEV_RECONNECTED, xobj->userCallback_->userarg(), *xobj->reqObj_, cData, 1); } } } }