From 146f339b8698c81931508d4e01e4dbfd59d0dae3 Mon Sep 17 00:00:00 2001 From: Michael Davis <michael.davis@cern.ch> Date: Fri, 30 Jun 2017 10:11:13 +0200 Subject: [PATCH] Makes XrdSsi client side callbacks generic --- frontend/Makefile | 2 +- frontend/TestSsiClient.h | 4 +- frontend/TestSsiRequest.cpp | 138 ---------------------------------- frontend/TestSsiRequest.h | 144 ++++++++++++++++++++++++++++++++++++ frontend/test_client.cpp | 32 ++++---- 5 files changed, 165 insertions(+), 155 deletions(-) delete mode 100644 frontend/TestSsiRequest.cpp diff --git a/frontend/Makefile b/frontend/Makefile index 45d9a5e9cc..7ed2459cae 100644 --- a/frontend/Makefile +++ b/frontend/Makefile @@ -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 diff --git a/frontend/TestSsiClient.h b/frontend/TestSsiClient.h index ace4d6eed6..5b649cca54 100644 --- a/frontend/TestSsiClient.h +++ b/frontend/TestSsiClient.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 diff --git a/frontend/TestSsiRequest.cpp b/frontend/TestSsiRequest.cpp deleted file mode 100644 index 31988ae61a..0000000000 --- a/frontend/TestSsiRequest.cpp +++ /dev/null @@ -1,138 +0,0 @@ -#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(); -} - diff --git a/frontend/TestSsiRequest.h b/frontend/TestSsiRequest.h index dd7ed56cea..25455251bf 100644 --- a/frontend/TestSsiRequest.h +++ b/frontend/TestSsiRequest.h @@ -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 diff --git a/frontend/test_client.cpp b/frontend/test_client.cpp index 2b9f5b687c..6c7b438bed 100644 --- a/frontend/test_client.cpp +++ b/frontend/test_client.cpp @@ -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; } -- GitLab