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