1220 lines
44 KiB
C++
Executable File
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);
|
|
}
|
|
}
|
|
}
|
|
}
|