//------------------------------------------------------------------------------ // Copyright (c) 2011-2017 by European Organization for Nuclear Research (CERN) // Author: Krzysztof Jamrog , // Michal Simon //------------------------------------------------------------------------------ // This file is part of the XRootD software suite. // // XRootD is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // XRootD is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with XRootD. If not, see . // // In applying this licence, CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. //------------------------------------------------------------------------------ #ifndef __XRD_CL_OPERATION_HANDLERS_HH__ #define __XRD_CL_OPERATION_HANDLERS_HH__ #include "XrdCl/XrdClFile.hh" #include "XrdCl/XrdClCtx.hh" #include #include #include namespace XrdCl { //---------------------------------------------------------------------------- //! Helper class for unpacking single XAttrStatus from bulk response //---------------------------------------------------------------------------- class UnpackXAttrStatus : public ResponseHandler { public: UnpackXAttrStatus( ResponseHandler *handler ) : handler( handler ) { } //------------------------------------------------------------------------ //! Callback method. //------------------------------------------------------------------------ void HandleResponse( XRootDStatus *status, AnyObject *response ) { // status maybe error for old servers not supporting xattrs if( !status->IsOK() ) { handler->HandleResponse( status, nullptr ); return; } std::vector *bulk = nullptr; response->Get( bulk ); *status = bulk->front().status; handler->HandleResponse( status, nullptr ); delete response; } private: ResponseHandler *handler; }; //---------------------------------------------------------------------------- //! Helper class for unpacking single XAttr from bulk response //---------------------------------------------------------------------------- class UnpackXAttr : public ResponseHandler { public: UnpackXAttr( ResponseHandler *handler ) : handler( handler ) { } //------------------------------------------------------------------------ //! Callback method. //------------------------------------------------------------------------ void HandleResponse( XRootDStatus *status, AnyObject *response ) { // status is always OK for bulk response std::vector *bulk = nullptr; response->Get( bulk ); *status = bulk->front().status; std::string *rsp = new std::string( std::move( bulk->front().value ) ); delete bulk; response->Set( rsp ); handler->HandleResponse( status, response ); } private: ResponseHandler *handler; }; //---------------------------------------------------------------------------- // Helper class for creating null references for particular types // // @arg Response : type for which we need a null reference //---------------------------------------------------------------------------- template struct NullRef { static Response value; }; //---------------------------------------------------------------------------- // Initialize the 'null-reference' //---------------------------------------------------------------------------- template Response NullRef::value; //---------------------------------------------------------------------------- //! Unpack response //! //! @param rsp : AnyObject holding response //! @return : the response //---------------------------------------------------------------------------- template inline Response* GetResponse( AnyObject *rsp ) { Response *ret = nullptr; rsp->Get( ret ); return ret; } //---------------------------------------------------------------------------- //! Unpack response //! //! @param rsp : AnyObject holding response //! @param status : //! @return : the response //---------------------------------------------------------------------------- template inline Response* GetResponse( XRootDStatus *status, AnyObject *rsp ) { if( !status->IsOK() ) return &NullRef::value; return GetResponse( rsp ); } //---------------------------------------------------------------------------- //! Lambda wrapper //! //! @arg ResponseType : type of response returned by the server //---------------------------------------------------------------------------- template class FunctionWrapper: public ResponseHandler { public: //------------------------------------------------------------------------ //! Constructor. // //! @param func : function, functor or lambda (2 arguments) //------------------------------------------------------------------------ FunctionWrapper( std::function handleFunction ) : fun( [handleFunction]( XRootDStatus &s, Response &r, HostList& ){ handleFunction( s, r ); } ) { } //------------------------------------------------------------------------ //! Constructor. // //! @param func : function, functor or lambda (3 arguments) //------------------------------------------------------------------------ FunctionWrapper( std::function handleFunction ) : fun( handleFunction ) { } //------------------------------------------------------------------------ //! Callback method. //------------------------------------------------------------------------ void HandleResponseWithHosts( XRootDStatus *status, AnyObject *response, HostList *hostList ) { std::unique_ptr delst( status ); std::unique_ptr delrsp( response ); std::unique_ptr delhl( hostList ); Response *res = GetResponse( status, response ); fun( *status, *res, *hostList ); } private: //------------------------------------------------------------------------ //! user defined function, functor or lambda //------------------------------------------------------------------------ std::function fun; }; //---------------------------------------------------------------------------- //! Lambda wrapper //! //! Template specialization for responses that return no value (void) //---------------------------------------------------------------------------- template<> class FunctionWrapper : public ResponseHandler { public: //------------------------------------------------------------------------ //! Constructor. // //! @param func : function, functor or lambda (1 argument) //------------------------------------------------------------------------ FunctionWrapper( std::function handleFunction ) : fun( [handleFunction]( XRootDStatus& s, HostList& ){ handleFunction( s ); } ) { } //------------------------------------------------------------------------ //! Constructor. // //! @param func : function, functor or lambda (2 arguments) //------------------------------------------------------------------------ FunctionWrapper( std::function handleFunction ) : fun( handleFunction ) { } //------------------------------------------------------------------------ //! Callback method. //------------------------------------------------------------------------ void HandleResponseWithHosts( XRootDStatus *status, AnyObject *response, HostList *hostList ) { std::unique_ptr delst( status ); std::unique_ptr delrsp( response ); std::unique_ptr delhl( hostList ); fun( *status, *hostList ); } private: //------------------------------------------------------------------------ //! user defined function, functor or lambda //------------------------------------------------------------------------ std::function fun; }; //---------------------------------------------------------------------------- //! Packaged Task wrapper //! //! @arg Response : type of response returned by the server //! @arg Return : type of the value returned by the task //---------------------------------------------------------------------------- template class TaskWrapper: public ResponseHandler { public: //------------------------------------------------------------------------ //! Constructor. // //! @param task : a std::packaged_task //------------------------------------------------------------------------ TaskWrapper( std::packaged_task && task ) : task( std::move( task ) ) { } //------------------------------------------------------------------------ //! Callback method. //------------------------------------------------------------------------ void HandleResponse( XRootDStatus *status, AnyObject *response ) { std::unique_ptr delst( status ); std::unique_ptr delrsp( response ); Response *resp = GetResponse( status, response ); task( *status, *resp ); } private: //------------------------------------------------------------------------ //! user defined task //------------------------------------------------------------------------ std::packaged_task task; }; //---------------------------------------------------------------------------- //! Packaged Task wrapper, specialization for requests that have no response //! except for status. //! //! @arg Response : type of response returned by the server //! @arg Return : type of the value returned by the task //---------------------------------------------------------------------------- template class TaskWrapper: public ResponseHandler { public: //------------------------------------------------------------------------ //! Constructor. // //! @param task : a std::packaged_task //------------------------------------------------------------------------ TaskWrapper( std::packaged_task && task ) : task( std::move( task ) ) { } //------------------------------------------------------------------------ //! Callback method. //------------------------------------------------------------------------ void HandleResponse( XRootDStatus *status, AnyObject *response ) { std::unique_ptr delst( status ); std::unique_ptr delrsp( response ); task( *status ); } private: //------------------------------------------------------------------------ //! user defined task //------------------------------------------------------------------------ std::packaged_task task; }; //---------------------------------------------------------------------------- //! Lambda wrapper //---------------------------------------------------------------------------- class ExOpenFuncWrapper: public ResponseHandler { public: //------------------------------------------------------------------------ //! Constructor. // //! @param func : function, functor or lambda (2 arguments) //------------------------------------------------------------------------ ExOpenFuncWrapper( const Ctx &f, std::function handleFunction ) : f( f ), fun( [handleFunction]( XRootDStatus &s, StatInfo &i, HostList& ){ handleFunction( s, i ); } ) { } //------------------------------------------------------------------------ //! Constructor. // //! @param func : function, functor or lambda (3 arguments) //------------------------------------------------------------------------ ExOpenFuncWrapper( const Ctx &f, std::function handleFunction ) : f( f ), fun( handleFunction ) { } //------------------------------------------------------------------------ //! Callback method. //------------------------------------------------------------------------ void HandleResponseWithHosts( XRootDStatus *status, AnyObject *response, HostList *hostList ) { delete response; std::unique_ptr delst( status ); std::unique_ptr delrsp; std::unique_ptr delhl; StatInfo *info = nullptr; if( status->IsOK() ) { XRootDStatus st = f->Stat( false, info ); delrsp.reset( info ); } else info = &NullRef::value; fun( *status, *info, *hostList ); } private: Ctx f; //------------------------------------------------------------------------ //! user defined function, functor or lambda //------------------------------------------------------------------------ std::function fun; }; //---------------------------------------------------------------------------- //! Pipeline exception, wrapps an XRootDStatus //---------------------------------------------------------------------------- class PipelineException : public std::exception { public: //------------------------------------------------------------------------ //! Constructor from XRootDStatus //------------------------------------------------------------------------ PipelineException( const XRootDStatus &error ) : error( error ), strerr( error.ToString() ) { } //------------------------------------------------------------------------ //! Copy constructor. //------------------------------------------------------------------------ PipelineException( const PipelineException &ex ) : error( ex.error ), strerr( ex.error.ToString() ) { } //------------------------------------------------------------------------ //! Assigment operator //------------------------------------------------------------------------ PipelineException& operator=( const PipelineException &ex ) { error = ex.error; strerr = ex.strerr; return *this; } //------------------------------------------------------------------------ //! inherited from std::exception //------------------------------------------------------------------------ const char* what() const noexcept { return strerr.c_str(); } //------------------------------------------------------------------------ //! @return : the XRootDStatus //------------------------------------------------------------------------ const XRootDStatus& GetError() const { return error; } private: //------------------------------------------------------------------------ //! the XRootDStatus associated with this exception //------------------------------------------------------------------------ XRootDStatus error; std::string strerr; }; //---------------------------------------------------------------------------- //! A wrapper handler for a std::promise / std::future. //! //! @arg Response : response type //---------------------------------------------------------------------------- template class FutureWrapperBase : public ResponseHandler { public: //------------------------------------------------------------------------ //! Constructor, initializes the std::future argument from its //! own std::promise //! //! @param ftr : the future to be linked with this handler //------------------------------------------------------------------------ FutureWrapperBase( std::future &ftr ) : fulfilled( false ) { ftr = prms.get_future(); } //------------------------------------------------------------------------ //! Destructor //------------------------------------------------------------------------ virtual ~FutureWrapperBase() { if( !fulfilled ) SetException( XRootDStatus( stError, errPipelineFailed ) ); } protected: //------------------------------------------------------------------------ //! Set exception in the std::promise / std::future //! //! @param err : the error //------------------------------------------------------------------------ inline void SetException( const XRootDStatus &err ) { std::exception_ptr ex = std::make_exception_ptr( PipelineException( err ) ); prms.set_exception( ex ); fulfilled = true; } //------------------------------------------------------------------------ //! promise that corresponds to the future //------------------------------------------------------------------------ std::promise prms; bool fulfilled; }; //---------------------------------------------------------------------------- //! A wrapper handler for a std::promise / std::future. //! //! @arg Response : response type //---------------------------------------------------------------------------- template class FutureWrapper : public FutureWrapperBase { public: //------------------------------------------------------------------------ //! Constructor, @see FutureWrapperBase //! //! @param ftr : the future to be linked with this handler //------------------------------------------------------------------------ FutureWrapper( std::future &ftr ) : FutureWrapperBase( ftr ) { } //------------------------------------------------------------------------ //! Callback method. //------------------------------------------------------------------------ void HandleResponse( XRootDStatus *status, AnyObject *response ) { std::unique_ptr delst( status ); std::unique_ptr delrsp( response ); if( status->IsOK() ) { Response *resp = GetResponse( response ); if( resp == &NullRef::value ) this->SetException( XRootDStatus( stError, errInternal ) ); else { this->prms.set_value( std::move( *resp ) ); this->fulfilled = true; } } else this->SetException( *status ); } }; //---------------------------------------------------------------------------- //! A wrapper handler for a std::promise / std::future, overload for void type //---------------------------------------------------------------------------- template<> class FutureWrapper : public FutureWrapperBase { public: //------------------------------------------------------------------------ //! Constructor, @see FutureWrapperBase //! //! @param ftr : the future to be linked with this handler //------------------------------------------------------------------------ FutureWrapper( std::future &ftr ) : FutureWrapperBase( ftr ) { } //------------------------------------------------------------------------ //! Callback method. //------------------------------------------------------------------------ void HandleResponse( XRootDStatus *status, AnyObject *response ) { std::unique_ptr delst( status ); std::unique_ptr delrsp( response ); if( status->IsOK() ) { prms.set_value(); fulfilled = true; } else SetException( *status ); } }; //---------------------------------------------------------------------------- //! Wrapper class for raw response handler (ResponseHandler). //---------------------------------------------------------------------------- class RawWrapper : public ResponseHandler { public: //------------------------------------------------------------------------ //! Constructor //! //! @param handler : the actual operation handler //------------------------------------------------------------------------ RawWrapper( ResponseHandler *handler ) : handler( handler ) { } //------------------------------------------------------------------------ //! Callback method (@see ResponseHandler) //! //! Note: does not delete itself because it is assumed that it is owned //! by the PipelineHandler (@see PipelineHandler) //------------------------------------------------------------------------ virtual void HandleResponseWithHosts( XRootDStatus *status, AnyObject *response, HostList *hostList ) { handler->HandleResponseWithHosts( status, response, hostList ); } private: //------------------------------------------------------------------------ //! The actual operation handler (we don't own the pointer) //------------------------------------------------------------------------ ResponseHandler *handler; }; //---------------------------------------------------------------------------- //! A base class for factories, creates ForwardingHandlers from //! ResponseHandler*, ResponseHandler& and std::future //! //! @arg Response : response type //---------------------------------------------------------------------------- template struct RespBase { //------------------------------------------------------------------------ //! A factory method, simply forwards the given handler //! //! @param hdlr : the ResponseHandler that should be wrapped //! @return : a ForwardingHandler instance //------------------------------------------------------------------------ inline static ResponseHandler* Create( ResponseHandler *hdlr ) { return new RawWrapper( hdlr ); } //------------------------------------------------------------------------ //! A factory method, simply forwards the given handler //! //! @param hdlr : the ResponseHandler that should be wrapped //! @return : a ForwardingHandler instance //------------------------------------------------------------------------ inline static ResponseHandler* Create( ResponseHandler &hdlr ) { return new RawWrapper( &hdlr ); } //------------------------------------------------------------------------ //! A factory method //! //! @arg Response : response type //! @param ftr : the std::future that should be wrapped //------------------------------------------------------------------------ inline static ResponseHandler* Create( std::future &ftr ) { return new FutureWrapper( ftr ); } }; //---------------------------------------------------------------------------- //! Factory class, creates ForwardingHandler from std::function, in addition //! to what RespBase provides (@see RespBase) //! //! @arg Response : response type //---------------------------------------------------------------------------- template struct Resp: RespBase { //------------------------------------------------------------------------ //! A factory method //! //! @param func : the function/functor/lambda that should be wrapped //! @return : FunctionWrapper instance //------------------------------------------------------------------------ inline static ResponseHandler* Create( std::function func ) { return new FunctionWrapper( func ); } //------------------------------------------------------------------------ //! A factory method //! //! @param func : the function/functor/lambda that should be wrapped //! @return : FunctionWrapper instance //------------------------------------------------------------------------ inline static ResponseHandler* Create( std::function func ) { return new FunctionWrapper( func ); } //------------------------------------------------------------------------ //! A factory method //! //! @param task : the task that should be wrapped //! @return : TaskWrapper instance //------------------------------------------------------------------------ template inline static ResponseHandler* Create( std::packaged_task &task ) { return new TaskWrapper( std::move( task ) ); } //------------------------------------------------------------------------ //! Make the Create overloads from RespBase visible //------------------------------------------------------------------------ using RespBase::Create; }; //---------------------------------------------------------------------------- //! Factory class, overloads Resp for void type //! //! @arg Response : response type //---------------------------------------------------------------------------- template<> struct Resp: RespBase { //------------------------------------------------------------------------ //! A factory method //! //! @param func : the function/functor/lambda that should be wrapped //! @return : SimpleFunctionWrapper instance //------------------------------------------------------------------------ inline static ResponseHandler* Create( std::function func ) { return new FunctionWrapper( func ); } //------------------------------------------------------------------------ //! A factory method //! //! @param func : the function/functor/lambda that should be wrapped //! @return : SimpleFunctionWrapper instance //------------------------------------------------------------------------ inline static ResponseHandler* Create( std::function func ) { return new FunctionWrapper( func ); } //------------------------------------------------------------------------ //! A factory method //! //! @param task : the task that should be wrapped //! @return : TaskWrapper instance //------------------------------------------------------------------------ template inline static ResponseHandler* Create( std::packaged_task &task ) { return new TaskWrapper( std::move( task ) ); } //------------------------------------------------------------------------ //! Make the Create overloads from RespBase visible //------------------------------------------------------------------------ using RespBase::Create; }; } #endif // __XRD_CL_OPERATIONS_HANDLERS_HH__