Files
cdev-1.7.2n/extensions/cdevGenericServer/lib/cdevClientService.cc
2022-12-13 12:44:04 +01:00

1220 lines
44 KiB
C++
Executable File

#include <cdevClientService.h>
#include <cdevClientRequestObject.h>
#include <cdevMessageBinary.h>
#include <cdevMessage.h>
#include <cdevDirectory.h>
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; i<ALLOCATION_COUNT; i++)
{
freeList_[i].freeListNext_ =
(i<(ALLOCATION_COUNT-1))?&freeList_[i+1]:(cdevClientTransaction *)NULL;
}
}
if(freeList_!=NULL)
{
result = freeList_;
freeList_ = result->freeListNext_;
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);
}
}
}
}