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