Commit 3bd5483d authored by Michael Davis's avatar Michael Davis
Browse files

Refactors frontend_ssi.cpp into separate files for each class

parent 7de54158
......@@ -6,7 +6,7 @@ all: test_client frontend_ssi.so
# XrdSsi server plugin
frontend_ssi.so: frontend_ssi.o
frontend_ssi.so: TestSsiServiceProvider.o TestSsiService.o TestSsiRequestProc.o test.pb.o
$(LINK.cc) -shared $^ $(LOADLIBES) $(LDLIBS) -o $@
# XrdSsi test client
......@@ -17,7 +17,9 @@ LDLIBS=-lprotobuf
test_client: test_client.o TestSsiRequest.o test.pb.o
test_client.o: test_client.cpp TestSsiService.h TestSsiRequest.h test.pb.h
test_client.o: test_client.cpp TestSsiClient.h TestSsiRequest.h test.pb.h
test.pb.o: test.pb.h
test.pb.h: test.proto
#protoc3 --cpp_out=. --plugin=protoc-gen-XrdSsi=./protoc-gen-XrdSsi --XrdSsi_out=. test.proto
......@@ -30,4 +32,7 @@ LDLIBS=-lprotobuf -lprotoc
protoc-gen-XrdSsi: protoc-gen-XrdSsi.cpp
clean:
rm -f frontend_ssi.o frontend_ssi.so test_client test_client.o
rm -f frontend_ssi.o frontend_ssi.so \
test_client test_client.o TestSsiRequest.o \
test.pb.cc test.pb.h test.pb.o \
TestSsiRequestProc.o TestSsiService.o TestSsiServiceProvider.o
#ifndef __TEST_SSI_CLIENT_H
#define __TEST_SSI_CLIENT_H
#include <iostream>
#include <stdexcept>
#include <XrdSsi/XrdSsiProvider.hh>
#include <XrdSsi/XrdSsiService.hh>
#include "TestSsiRequest.h"
// Probably 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");
// Class to convert a XRootD error into a std::exception
class XrdSsiException : public std::exception
{
public:
XrdSsiException(const std::string &err_msg) : error_msg(err_msg) {}
XrdSsiException(const XrdSsiErrInfo &eInfo) : error_msg(eInfo.Get()) {}
const char* what() const noexcept { return error_msg.c_str(); }
private:
std::string error_msg;
};
// XrdSsiProviderClient is instantiated and managed by the SSI library
extern XrdSsiProvider *XrdSsiProviderClient;
template <typename RequestType, typename ResponseType>
class TestSsiClient
{
public:
// default constructor to be used on the server side:
TestSsiClient() = delete;
// constructor to be used on the client side (to establish a connection to the server):
TestSsiClient(std::string hostname, int port, int to = 15) : resource(TestSsiResource), timeout(to)
{
XrdSsiErrInfo eInfo;
if(!(serverP = XrdSsiProviderClient->GetService(eInfo, hostname + ":" + std::to_string(port))))
{
throw XrdSsiException(eInfo);
}
}
~TestSsiClient()
{
// The XrdSsiService object cannot be explicitly deleted. The Stop() method deletes the object if
// it is safe to do so. A service object can only be deleted after all requests handed to the object
// have completed, i.e. Finished() has been called on each request.
if(serverP->Stop())
{
std::cerr << "Stopped SSI service" << std::endl;
}
else
{
std::cerr << "Failed to stop SSI service" << std::endl;
// Unpalatable choice here between passing back control to the calling scope with a possible memory leak,
// or trying to force all requests to Finish with possible blocking behaviour...
}
}
void send(RequestType request_msg)
{
// Requests are always executed in the context of a service. They need to correspond to what the service allows.
XrdSsiRequest *requestP;
// Serialize the request object
std::string data;
if(!request_msg.SerializeToString(&data))
{
throw XrdSsiException("SerializeToString() failed");
}
requestP = new TestSsiRequest(data, timeout);
// Transfer ownership of the request to the service object
// TestSsiRequest handles deletion of the data buffer, so we can allow the pointer to go out-of-scope
serverP->ProcessRequest(*requestP, resource);
// Note: it is safe to delete the XrdSsiResource object after ProcessRequest() returns. I don't delete it because
// I am assuming I can reuse it, but I need to check if that is a safe assumption. Perhaps I need to create a new
// resource object for each request?
}
private:
XrdSsiResource resource; // Requests are bound to this resource
XrdSsiService *serverP; // Pointer to XRootD Server object
int timeout; // Server timeout
};
#endif
#include <iostream>
#include "TestSsiRequestProc.h"
void RequestProc::Execute()
{
using namespace std;
const string metadata("Have some metadata!");
const string response("Have a response!");
cerr << "Execute()" << endl;
// Deserialize the Request
//int reqLen;
//const char *reqData = GetRequest(reqLen);
// Parse the request
ReleaseRequestBuffer(); // Optional
// Perform the requested action
// Optional: send alerts
// Optional: send metadata ahead of the response
SetMetadata(metadata.c_str(), metadata.size());
// Send the response
SetResponse(response.c_str(), response.size());
}
void RequestProc::Finished(XrdSsiRequest &rqstR, const XrdSsiRespInfo &rInfo, bool cancel)
{
using namespace std;
cerr << "Finished()" << endl;
// Reclaim any allocated resources
}
#ifndef __TEST_SSI_REQUEST_PROC_H
#define __TEST_SSI_REQUEST_PROC_H
#include <XrdSsi/XrdSsiResponder.hh>
/*
* The XrdSsiResponder class knows how to safely interact with the request object. It allows handling asynchronous
* requests such as cancellation, broken TCP connections, etc.
*
* The XrdSsiResponder class contains all the methods needed to interact with the request object: get the request,
* release storage, send alerts, and post a response.
*
* The Request object will be bound to the XrdSsiResponder object via a call to XrdSsiResponder::BindRequest().
* Once the relationship is established, you no longer need to keep a reference to the request object. The SSI
* framework keeps track of the request object for you.
*
* RequestProc is a kind of agent object that the service object creates for each request that it receives.
*/
class RequestProc : public XrdSsiResponder
{
public:
RequestProc() {}
virtual ~RequestProc() {}
void Execute();
virtual void Finished(XrdSsiRequest &rqstR, const XrdSsiRespInfo &rInfo, bool cancel=false) override;
};
#endif
#include <iostream>
#include "TestSsiService.h"
#include "TestSsiRequestProc.h"
void TestSsiService::ProcessRequest(XrdSsiRequest &reqRef, XrdSsiResource &resRef)
{
using namespace std;
cerr << "Called ProcessRequest()" << endl;
RequestProc theProcessor;
// Bind the processor to the request. This works because the
// it inherited the BindRequest method from XrdSsiResponder.
theProcessor.BindRequest(reqRef);
// Execute the request, upon return the processor is deleted
theProcessor.Execute();
// Unbind the request from the responder (required)
theProcessor.UnBindRequest();
cerr << "ProcessRequest.UnBind()" << endl;
}
#ifndef __TEST_SSI_SERVICE_H
#define __TEST_SSI_SERVICE_H
#include <iostream>
#include <stdexcept>
#include <XrdSsi/XrdSsiProvider.hh>
#include <XrdSsi/XrdSsiService.hh>
#include "TestSsiRequest.h"
// Probably 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");
/*
* Service Object, obtained using GetService() method of the TestSsiServiceProvider factory
*/
// Class to convert a XRootD error into a std::exception
class XrdSsiException : public std::exception
class TestSsiService : public XrdSsiService
{
public:
XrdSsiException(const std::string &err_msg) : error_msg(err_msg) {}
XrdSsiException(const XrdSsiErrInfo &eInfo) : error_msg(eInfo.Get()) {}
const char* what() const noexcept { return error_msg.c_str(); }
private:
std::string error_msg;
};
// XrdSsiProviderClient is instantiated and managed by the SSI library
extern XrdSsiProvider *XrdSsiProviderClient;
template <typename RequestType, typename ResponseType>
class TestSsiService
{
public:
TestSsiService() = delete;
TestSsiService(std::string hostname, int port, int to = 15) : resource(TestSsiResource), timeout(to)
{
XrdSsiErrInfo eInfo;
if(!(serverP = XrdSsiProviderClient->GetService(eInfo, hostname + ":" + std::to_string(port))))
{
throw XrdSsiException(eInfo);
}
}
~TestSsiService()
{
// The XrdSsiService object cannot be explicitly deleted. The Stop() method deletes the object if
// it is safe to do so. A service object can only be deleted after all requests handed to the object
// have completed, i.e. Finished() has been called on each request.
if(serverP->Stop())
{
std::cerr << "Stopped SSI service" << std::endl;
}
else
{
std::cerr << "Failed to stop SSI service" << std::endl;
// Unpalatable choice here between passing back control to the calling scope with a possible memory leak,
// or trying to force all requests to Finish with possible blocking behaviour...
}
}
void send(RequestType request_msg)
{
// Requests are always executed in the context of a service. They need to correspond to what the service allows.
XrdSsiRequest *requestP;
// Serialize the request object
std::string data;
if(!request_msg.SerializeToString(&data))
{
throw XrdSsiException("SerializeToString() failed");
}
requestP = new TestSsiRequest(data, timeout);
// Transfer ownership of the request to the service object
// TestSsiRequest handles deletion of the data buffer, so we can allow the pointer to go out-of-scope
serverP->ProcessRequest(*requestP, resource);
// The pure abstract method ProcessRequest() is called when the client calls its ProcessRequest() method to hand off
// its request and resource objects. The client’s request and resource objects are transmitted to the server and passed
// into the service’s ProcessRequest() method.
// Note: it is safe to delete the XrdSsiResource object after ProcessRequest() returns. I don't delete it because
// I am assuming I can reuse it, but I need to check if that is a safe assumption. Perhaps I need to create a new
// resource object for each request?
}
virtual void ProcessRequest(XrdSsiRequest &reqRef, XrdSsiResource &resRef) override;
private:
XrdSsiResource resource; // Requests are bound to this resource
// Additional virtual methods:
//
// Attach(): optimize handling of detached requests
// Prepare(): perform preauthorization and resource optimization
XrdSsiService *serverP; // Pointer to XRootD Server object
TestSsiService() {}
virtual ~TestSsiService() {}
int timeout; // Server timeout
};
#endif
#include <iostream>
#include "TestSsiServiceProvider.h"
#include "TestSsiService.h"
#include "TestSsiProtobuf.h"
/*
* The service provider object is pointed to by the global pointer XrdSsiProviderServer which you
* must define and set at library load time (i.e. it is a file level global static symbol).
*
* When your library is loaded, the XrdSsiProviderServer symbol is located in the library.
* Initialization fails if the appropriate symbol cannot be found or it is a NULL pointer.
*/
XrdSsiProvider *XrdSsiProviderServer = new TestSsiServiceProvider;
// XrdSsiProviderClient is instantiated and managed by the SSI library
//extern XrdSsiProvider *XrdSsiProviderClient;
bool TestSsiServiceProvider::Init(XrdSsiLogger *logP, XrdSsiCluster *clsP, const std::string cfgFn, const std::string parms, int argc, char **argv)
{
using namespace std;
cerr << "Called Init(" << cfgFn << "," << parms << ")" << endl;
// do some initialisation
initOK = true;
return initOK;
}
XrdSsiService* TestSsiServiceProvider::GetService(XrdSsiErrInfo &eInfo, const std::string &contact, int oHold)
{
using namespace std;
cerr << "Called GetService(" << contact << "," << oHold << ")" << endl;
XrdSsiService *ptr = new TestSsiService;
return ptr;
}
XrdSsiProvider::rStat TestSsiServiceProvider::QueryResource(const char *rName, const char *contact)
{
using namespace std;
/*
* The second argument, contact, is always (invalid?) for a server. It is only used by a client initiated
* query for a resource at a particular endpoint.
*/
cerr << "Called QueryResource(" << rName << "): ";
// Return one of the following values:
//
// XrdSsiProvider::notPresent The resource does not exist.
// XrdSsiProvider::isPresent The resource exists.
// XrdSsiProvider::isPending The resource exists but is not immediately available. (Useful only in clustered
// environments where the resource may be immediately available on some other node.)
if(strcmp(rName, "/test") == 0)
{
cerr << "isPresent" << endl;
return XrdSsiProvider::isPresent;
}
else
{
cerr << "notPresent" << endl;
return XrdSsiProvider::notPresent;
}
}
#ifndef __TEST_SSI_SERVICE_PROVIDER_H
#define __TEST_SSI_SERVICE_PROVIDER_H
#include <XrdSsi/XrdSsiProvider.hh>
/*
* The service provider is used by xrootd to actually process client requests. The three methods that
* you must implement in your derived XrdSsiProvider object are:
*
* Init() -- initialize the object for its intended use.
* GetService() -- Called once (only) after initialisation to obtain an instance of an
* XrdSsiService object.
* QueryResource() -- used to obtain the availability of a resource. Can be called whenever the
* client asks for the resource status.
*/
class TestSsiServiceProvider : public XrdSsiProvider
{
public:
// Init() is always called before any other method
bool Init(XrdSsiLogger *logP, XrdSsiCluster *clsP, const std::string cfgFn, const std::string parms, int argc, char **argv) override;
// The GetService() method must supply a service object
XrdSsiService *GetService(XrdSsiErrInfo &eInfo, const std::string &contact, int oHold=256) override;
// The QueryResource() method determines resource availability
XrdSsiProvider::rStat QueryResource(const char *rName, const char *contact=0) override;
// Constructor and destructor
TestSsiServiceProvider() : initOK(false) {}
virtual ~TestSsiServiceProvider() {}
private:
bool initOK;
};
#endif
#include <iostream>
#include "XrdSsi/XrdSsiResponder.hh"
#include "XrdSsi/XrdSsiProvider.hh"
#include "XrdSsi/XrdSsiService.hh"
/*
* The XrdSsiResponder class knows how to safely interact with the request object. It allows handling asynchronous
* requests such as cancellation, broken TCP connections, etc.
*
* The XrdSsiResponder class contains all the methods needed to interact with the request object: get the request,
* release storage, send alerts, and post a response.
*
* The Request object will be bound to the XrdSsiResponder object via a call to XrdSsiResponder::BindRequest().
* Once the relationship is established, you no longer need to keep a reference to the request object. The SSI
* framework keeps track of the request object for you.
*
* RequestProc is a kind of agent object that the service object creates for each request that it receives.
*/
class RequestProc : public XrdSsiResponder
{
public:
void Execute()
{
using namespace std;
const string metadata("Have some metadata!");
const string response("Have a response!");
cerr << "Execute()" << endl;
//int reqLen;
//char *reqData = GetRequest(reqLen);
// Parse the request
ReleaseRequestBuffer(); // Optional
// Perform the requested action
// Optional: send alerts
// Optional: send metadata ahead of the response
SetMetadata(metadata.c_str(), metadata.size());
// Send the response
SetResponse(response.c_str(), response.size());
}
virtual void Finished(XrdSsiRequest &rqstR, const XrdSsiRespInfo &rInfo, bool cancel=false) override
{
using namespace std;
cerr << "Finished()" << endl;
// Reclaim any allocated resources
}
RequestProc() {}
virtual ~RequestProc() {}
};
/*
* Service Object, obtained using GetService() method of the MyServiceProvider factory
*/
class MyService : public XrdSsiService
{
public:
// The pure abstract method ProcessRequest() is called when the client calls its ProcessRequest() method to hand off
// its request and resource objects. The client’s request and resource objects are transmitted to the server and passed
// into the service’s ProcessRequest() method.
virtual void ProcessRequest(XrdSsiRequest &reqRef, XrdSsiResource &resRef) override;
// Additional virtual methods:
//
// Attach(): optimize handling of detached requests
// Prepare(): perform preauthorization and resource optimization
MyService() {}
virtual ~MyService() {}
};
void MyService::ProcessRequest(XrdSsiRequest &reqRef, XrdSsiResource &resRef)
{
using namespace std;
cerr << "Called ProcessRequest()" << endl;
RequestProc theProcessor;
// Bind the processor to the request. This works because the
// it inherited the BindRequest method from XrdSsiResponder.
theProcessor.BindRequest(reqRef);
// Execute the request, upon return the processor is deleted
theProcessor.Execute();
// Unbind the request from the responder (required)
theProcessor.UnBindRequest();
cerr << "ProcessRequest.UnBind()" << endl;
}