Skip to content
Snippets Groups Projects
Commit 146f339b authored by Michael Davis's avatar Michael Davis
Browse files

Makes XrdSsi client side callbacks generic

parent e363abd8
No related branches found
No related tags found
No related merge requests found
......@@ -15,7 +15,7 @@ all: test_client
LDLIBS=-lprotobuf
test_client: test_client.o TestSsiRequest.o test.pb.o
test_client: test_client.o test.pb.o
test_client.o: test_client.cpp TestSsiClient.h TestSsiRequest.h test.pb.h
......
......@@ -8,7 +8,7 @@
#include "XrdSsiException.h"
#include "TestSsiRequest.h"
// Probably we want to allow multiple resources, e.g. streaming and non-streaming versions of the service
// Perhaps we want to allow multiple resources, e.g. streaming and non-streaming versions of the service?
// Can this be defined in the protobuf definition?
const std::string TestSsiResource("/test");
......@@ -73,7 +73,7 @@ public:
// Requests are always executed in the context of a service. They need to correspond to what the service allows.
XrdSsiRequest *requestP = new TestSsiRequest(request_str, timeout);
XrdSsiRequest *requestP = new TestSsiRequest<RequestType, ResponseType, MetadataType, AlertType> (request_str, timeout);
// Transfer ownership of the request to the service object
// TestSsiRequest handles deletion of the request buffer, so we can allow the pointer to go out-of-scope
......
#include <iostream>
#include "TestSsiRequest.h"
// Process the response
bool TestSsiRequest::ProcessResponse(const XrdSsiErrInfo &eInfo, const XrdSsiRespInfo &rInfo)
{
using namespace std;
cerr << "ProcessResponse() callback called with response type = " << rInfo.State() << endl;
if (eInfo.hasError())
{
// Handle error using the passed eInfo object
cerr << "Error = " << eInfo.Get() << endl;
Finished(); // Returns control of the object to the calling thread
// Deletes rInfo
// Andy says you can now do a "delete this"
delete this; // well, OK, so long as you are 100% sure that this object was allocated by new,
// that the pointer on the calling side will never refer to it again, that the
// destructor of the base class doesn't access any class members, ...
}
else
{
// Arbitrary metadata can be sent ahead of the response data, for example to describe the
// response so that it can be handled in the most optimum way. To access the metadata, call
// GetMetadata() before calling GetResponseData().
int myMetadataLen;
GetMetadata(myMetadataLen);
if(rInfo.rType == XrdSsiRespInfo::isData && myMetadataLen > 0)
{
cerr << "Response has " << myMetadataLen << " bytes of metadata." << endl;
// do something with metadata
// A metadata-only response is indicated when XrdSsiRespInfo::rType is set to isNil (i.e. no response data is present).
#if 0
// clean up
Finished();
delete this;
#endif
}
if(rInfo.rType == XrdSsiRespInfo::isHandle)
{
cerr << "Response is detached, handle = " << endl;
// copy the handle somewhere
// clean up
Finished();
delete this;
}
if(rInfo.rType == XrdSsiRespInfo::isData)
{
// A proper data response type
static const int myBSize = 1024;
char *myBuff = reinterpret_cast<char*>(malloc(myBSize));
GetResponseData(myBuff, myBSize);
}
}
return true; // you should always return true. (So why not make the return type void?)
}
XrdSsiRequest::PRD_Xeq TestSsiRequest::ProcessResponseData(const XrdSsiErrInfo &eInfo, char *myBuff, int myBLen, bool isLast)
{
using namespace std;
// If we can't handle the queue at this time, return XrdSsiRequest::PRD_Hold;
// GetResponseData() above places the data in the allocated buffer, then calls this method with
// the buffer type and length
cerr << "Called ProcessResponseData with myBLen = " << myBLen << ", isLast = " << isLast << endl;
// myBLen can be 0 if there is no relevant response or response is metadata only
// Process the response data of length myBLen placed in myBuff
// If there is more data then get it, else free the buffer and
// indicate to the framework that we are done
if(!isLast)
{
static const int myBSize = 1024;
// Get the next chunk of data (and call this method recursively ... could this cause a stack overflow ?)
GetResponseData(myBuff, myBSize);
}
else
{
myBuff[myBLen] = 0;
cerr << "Contents of myBuff = " << myBuff << endl;
free(myBuff);
Finished(); // Andy says you can now do a "delete this"
delete this; // Note that if request objects are uniform, you may want to re-use them instead
// of deleting them, to avoid the overhead of repeated object creation.
}
return XrdSsiRequest::PRD_Normal; // Indicate what type of post-processing is required (normal in this case)
}
void TestSsiRequest::Alert(XrdSsiRespInfoMsg &aMsg)
{
using namespace std;
int aMsgLen;
char *aMsgData = aMsg.GetMsg(aMsgLen);
// Process the alert
cout << "Received Alert message: " << aMsgData << endl;
// Failure to recycle the message will cause a memory leak
aMsg.RecycleMsg();
}
......@@ -3,6 +3,7 @@
#include <XrdSsi/XrdSsiRequest.hh>
template <typename RequestType, typename ResponseType, typename MetadataType, typename AlertType>
class TestSsiRequest : public XrdSsiRequest
{
public:
......@@ -49,4 +50,147 @@ private:
int request_len;
};
// Process the response
template<class RequestType, class ResponseType, class MetadataType, class AlertType>
bool TestSsiRequest<RequestType, ResponseType, MetadataType, AlertType>::ProcessResponse(const XrdSsiErrInfo &eInfo, const XrdSsiRespInfo &rInfo)
{
using namespace std;
cerr << "ProcessResponse() callback called with response type = " << rInfo.State() << endl;
if (eInfo.hasError())
{
// Handle error using the passed eInfo object
cerr << "Error = " << eInfo.Get() << endl;
Finished(); // Returns control of the object to the calling thread
// Deletes rInfo
// Andy says you can now do a "delete this"
delete this; // well, OK, so long as you are 100% sure that this object was allocated by new,
// that the pointer on the calling side will never refer to it again, that the
// destructor of the base class doesn't access any class members, ...
}
else
{
// Arbitrary metadata can be sent ahead of the response data, for example to describe the
// response so that it can be handled in the most optimum way. To access the metadata, call
// GetMetadata() before calling GetResponseData().
int myMetadataLen;
GetMetadata(myMetadataLen);
if(rInfo.rType == XrdSsiRespInfo::isData && myMetadataLen > 0)
{
cerr << "Response has " << myMetadataLen << " bytes of metadata." << endl;
// do something with metadata
// A metadata-only response is indicated when XrdSsiRespInfo::rType is set to isNil (i.e. no response data is present).
#if 0
// clean up
Finished();
delete this;
#endif
}
if(rInfo.rType == XrdSsiRespInfo::isHandle)
{
cerr << "Response is detached, handle = " << endl;
// copy the handle somewhere
// clean up
Finished();
delete this;
}
if(rInfo.rType == XrdSsiRespInfo::isData)
{
// A proper data response type
static const int myBSize = 1024;
char *myBuff = reinterpret_cast<char*>(malloc(myBSize));
GetResponseData(myBuff, myBSize);
}
}
return true; // you should always return true. (So why not make the return type void?)
}
template<class RequestType, class ResponseType, class MetadataType, class AlertType>
XrdSsiRequest::PRD_Xeq TestSsiRequest<RequestType, ResponseType, MetadataType, AlertType>::ProcessResponseData(const XrdSsiErrInfo &eInfo, char *myBuff, int myBLen, bool isLast)
{
using namespace std;
// If we can't handle the queue at this time, return XrdSsiRequest::PRD_Hold;
// GetResponseData() above places the data in the allocated buffer, then calls this method with
// the buffer type and length
cerr << "Called ProcessResponseData with myBLen = " << myBLen << ", isLast = " << isLast << endl;
// myBLen can be 0 if there is no relevant response or response is metadata only
// Process the response data of length myBLen placed in myBuff
// If there is more data then get it, else free the buffer and
// indicate to the framework that we are done
if(!isLast)
{
static const int myBSize = 1024;
// Get the next chunk of data (and call this method recursively ... could this cause a stack overflow ?)
GetResponseData(myBuff, myBSize);
}
else
{
myBuff[myBLen] = 0;
cerr << "Contents of myBuff = " << myBuff << endl;
free(myBuff);
Finished(); // Andy says you can now do a "delete this"
delete this; // Note that if request objects are uniform, you may want to re-use them instead
// of deleting them, to avoid the overhead of repeated object creation.
}
return XrdSsiRequest::PRD_Normal; // Indicate what type of post-processing is required (normal in this case)
}
template<class RequestType, class ResponseType, class MetadataType, class AlertType>
void TestSsiRequest<RequestType, ResponseType, MetadataType, AlertType>::Alert(XrdSsiRespInfoMsg &aMsg)
{
using namespace std;
int aMsgLen;
char *aMsgData = aMsg.GetMsg(aMsgLen);
// Process the alert
cout << "Received Alert message: " << aMsgData << endl;
// Failure to recycle the message will cause a memory leak
aMsg.RecycleMsg();
}
#endif
......@@ -5,6 +5,8 @@
int main(int argc, char *argv[])
{
// Host:port of XRootD server
const std::string host = "localhost";
const int port = 10400;
......@@ -33,20 +35,6 @@ int main(int argc, char *argv[])
// Send the Request to the Service
test_ssi_service.send(request);
// Wait for the response callback
std::cout << "Request sent, going to sleep..." << std::endl;
int wait_secs = 40;
while(--wait_secs)
{
std::cerr << ".";
sleep(1);
}
std::cout << "All done, exiting." << std::endl;
}
catch (std::exception& e)
{
......@@ -55,7 +43,23 @@ int main(int argc, char *argv[])
return 1;
}
// Wait for the response callback
std::cout << "Request sent, going to sleep..." << std::endl;
int wait_secs = 20;
while(--wait_secs)
{
std::cerr << ".";
sleep(1);
}
std::cout << "All done, exiting." << std::endl;
// Optional: Delete all global objects allocated by libprotobuf
google::protobuf::ShutdownProtobufLibrary();
return 0;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment