cb333db91522495e55fbc1f7eff102b69a311d83
[libt2n] / src / client_wrapper.hxx
1 /***************************************************************************
2  *   Copyright (C) 2008 by Gerd v. Egidy and Reinhard Pfau                 *
3  *   gve@intra2net.com                                                     *
4  *                                                                         *
5  *   This library is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU Lesser General Public License version   *
7  *   2.1 as published by the Free Software Foundation.                     *
8  *                                                                         *
9  *   This library is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU Lesser General Public License for more details.                   *
13  *                                                                         *
14  *   You should have received a copy of the GNU Lesser General Public      *
15  *   License along with this program; if not, write to the                 *
16  *   Free Software Foundation, Inc.,                                       *
17  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
18  ***************************************************************************/
19 #ifndef __LIBT2N_CLIENT_WRAPPER
20 #define __LIBT2N_CLIENT_WRAPPER
21
22 #include <functional>
23
24 #include <boost/config.hpp>
25 #include <boost/bind.hpp>
26 #include <boost/function.hpp>
27 #include <boost/preprocessor.hpp>
28
29 #include <client.hxx>
30 #include <command_client.hxx>
31
32 #ifndef T2N_SINGLETON_WRAPPER_MAX_ARGS
33 #define T2N_SINGLETON_WRAPPER_MAX_ARGS 9
34 #endif
35
36 namespace libt2n
37 {
38
39 /** @brief provide access to client_connection singletons and call-handling function
40
41     This is an abstact base class for use with T2nSingletonWrapper. It provides access
42     to the singleton-client_connection and offers a handle-method which is used for
43     every call to a method on a t2n-server.
44 */
45 class ConnectionWrapper
46 {
47     private:
48         long long command_timeout_usec;
49         long long hello_timeout_usec;
50
51     protected:
52         log_level_values log_level;
53         std::ostream *logstream;
54         void set_logging_on_connection(client_connection& c);
55
56     public:
57         ConnectionWrapper()
58             : log_level(none), logstream(NULL), 
59               command_timeout_usec(command_client::command_timeout_usec_default),
60               hello_timeout_usec(command_client::hello_timeout_usec_default)
61             { }
62
63         virtual ~ConnectionWrapper()
64             { }
65
66         /** @brief return active connection, create new one if not existing
67
68             Return a pointer to an active client_connection. Use detail-data
69             stored within the derived class to create a new connection if needed,
70             otherwise return an alredy active connection. The derived class has
71             to take care of destroying the connection when not needed anymore.
72         */
73         virtual client_connection* get_connection()=0;
74
75         /** @brief this function is called on every execution of a method on a server
76
77             @param stubBase pointer to the command_client executing the call
78             @param f boost::function object containing the method to call and all parameters
79             @retval true if the call was successful and the original return-value of the called function can be used
80                     false if T2nSingletonWrapper has to create a return-value with the default-constructor
81
82             T2nSingletonWrapper will call this function on every execution of a server-side
83             method. This version will just call the function without any special treatment.
84             You can overload this function to implement different error handling strategies.
85         */
86         virtual bool handle(command_client* stubBase, boost::function< void() > f)
87         {
88             f();
89             return true;
90         }
91
92         long long get_command_timeout_usec(void)
93             { return command_timeout_usec; }
94
95         void set_command_timeout_usec(long long _command_timeout_usec)
96             { command_timeout_usec=_command_timeout_usec; }
97
98         long long get_hello_timeout_usec(void)
99             { return hello_timeout_usec; }
100
101         void set_hello_timeout_usec(long long _hello_timeout_usec)
102             { hello_timeout_usec=_hello_timeout_usec; }
103
104         virtual void set_logging(std::ostream *_logstream, log_level_values _log_level);
105
106         std::ostream* get_logstream(log_level_values level);
107 };
108
109 // contains the internal stuff needed for T2nWrapper
110 namespace detail
111 {
112
113     template< typename T >
114     struct TypeWrap
115     {
116         typedef T type;
117     };
118
119     template< >
120     struct TypeWrap< void >
121     {
122         typedef int type;
123     };
124
125     template< typename R >
126     struct Call
127     {
128         typedef boost::function< R() > FuncType;
129
130         FuncType function;
131         R& result;
132
133         Call( FuncType f, R& res ) : function(f), result( res ) {}
134
135         void operator()()
136         {
137             result= function();
138         }
139     };
140
141     template< >
142     struct Call<void>
143     {
144         typedef boost::function< void() > FuncType;
145         typedef TypeWrap< void >::type ResultType;
146
147         FuncType function;
148         ResultType& result;
149
150         Call( FuncType f, ResultType& res ) : function(f), result( res ) {}
151
152         void operator()()
153         {
154             function();
155             result= ResultType();
156         }
157     };
158 } // eo namespace detail
159
160 class T2nSingletonWrapperMessages
161 {
162     protected:
163         static const char* NotInitializedMessage;
164 };
165
166 /** @brief wrap calls to server-side-functions with different error-handling strategies
167
168     Template class to access a process-wide singleton server-connection and to wrap all
169     calls using this connection with an error-handling strategy (e.g. to reconnect when
170     the connection broke). The source looks very complicated due to heavy use of templates,
171     look at the 3rd codegen example to see how to use it.
172
173     @par Example
174     Calling remote methods is usually done via t2n_exec, this saves you from always
175     specifying which T2nSingletonWrapper-template to use when calling T2nSingletonWrapper::exec
176     @code
177     t2n_exec(&cmd_group_t2nexample_client::testfunc)("the answer is %d",42)
178     @endcode
179 */
180 template< class Client >
181 class T2nSingletonWrapper : public T2nSingletonWrapperMessages
182 {
183     private:
184         std::auto_ptr<Client> Stub;
185
186         static std::auto_ptr<T2nSingletonWrapper> SingletonObject;
187         static std::auto_ptr<ConnectionWrapper> WrappedConnection;
188
189         /// @cond
190         // create a prep-method for each possible number of parameters
191 #define _GEN_ARG(z,n,d) Arg ## n arg ##n
192 #define _GEN_PREP(z,n,d) \
193         template< typename R  BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,typename Arg) > \
194         static boost::function< R(Client*) > prep \
195         ( \
196             R(Client::*f)( BOOST_PP_ENUM_PARAMS(n,Arg) ) \
197             BOOST_PP_COMMA_IF(n) \
198             BOOST_PP_ENUM( n, _GEN_ARG, ~ ) \
199         ) \
200         { \
201             return boost::bind< R > \
202                 ( \
203                     f, _1  BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,arg) \
204                 ); \
205         } // eo prep
206
207         BOOST_PP_REPEAT( BOOST_PP_ADD(T2N_SINGLETON_WRAPPER_MAX_ARGS,1) , _GEN_PREP, ~ )
208
209 #undef _GEN_PREP
210 #undef _GEN_ARG
211         /// @endcond
212
213         T2nSingletonWrapper(std::auto_ptr<Client> stub)
214         {
215             Stub=stub;
216         }
217
218         static void init()
219         {
220             if (WrappedConnection.get() == NULL)
221                 throw std::logic_error(NotInitializedMessage);
222
223             std::auto_ptr<Client> stub(new Client(WrappedConnection->get_connection(),
224                 WrappedConnection->get_command_timeout_usec(),
225                 WrappedConnection->get_hello_timeout_usec()));
226
227             SingletonObject=std::auto_ptr<T2nSingletonWrapper>(new T2nSingletonWrapper(stub));
228         }
229
230         template< typename R >
231         static
232         typename detail::TypeWrap<R>::type real_exec( boost::function< R(Client*) > f)
233         {
234             ensure_singleton_there();
235
236             typename detail::TypeWrap<R>::type result;
237
238             // bind our Client-object and the local result
239             detail::Call<R> call( boost::bind( f, SingletonObject->Stub.get()), result );
240
241             // let the wrapper-handler call the fully-bound function
242             if (!WrappedConnection->handle(SingletonObject->Stub.get(),call))
243             {
244                 // create an result with default-constructor if the handler could not
245                 // successfully do a call but no exception occured
246                 result=typename detail::TypeWrap<R>::type();
247             }
248             return result;
249         }
250
251     public:
252
253         /** @brief tell the wrapper which connection to use
254             @param wrappedConnection the connection to establish when needed
255         */
256         static void set_connection(std::auto_ptr<ConnectionWrapper> wrappedConnection)
257         {
258             WrappedConnection=wrappedConnection;
259
260             // reset the singleton to NULL because the singleton must be constructed with current WrappedConnection
261             if (SingletonObject.get() != NULL)
262                 SingletonObject.reset();
263         }
264
265         /// return a pointer to the ConnectionWrapper currently in use
266         static ConnectionWrapper* get_connection_wrapper(void)
267             { return WrappedConnection.get(); }
268
269         /// manually establish the connection without actually executing a call
270         static void ensure_singleton_there(void)
271         {
272             if (SingletonObject.get() == NULL)
273                 init();
274         }
275
276         /// @cond
277         // create an exec-method for each possible number of parameters
278 #define _GEN_PLACEHOLDER(z,n,d) BOOST_PP_CAT(_,BOOST_PP_ADD(n,1))
279 #define _GEN_EXEC(z,n,d) \
280         template< typename R  BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,typename Arg) > \
281         static boost::function< R( BOOST_PP_ENUM_PARAMS(n,Arg) ) > exec \
282         ( \
283             R(Client::*f)( BOOST_PP_ENUM_PARAMS(n,Arg) ) \
284         ) \
285         { \
286             boost::function<R(Client*)>(*p)(R(Client::*)( BOOST_PP_ENUM_PARAMS(n,Arg) ) \
287                     BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,Arg)) \
288                 = &T2nSingletonWrapper::template prep<R BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,Arg) >; \
289             return boost::bind \
290             ( \
291                 T2nSingletonWrapper::template real_exec<R>, \
292                 boost::bind( p, f BOOST_PP_COMMA_IF(n) \
293                 BOOST_PP_ENUM(n, _GEN_PLACEHOLDER, ~ ) ) \
294             ); \
295         } // eo exec
296
297         BOOST_PP_REPEAT( BOOST_PP_ADD(T2N_SINGLETON_WRAPPER_MAX_ARGS,1), _GEN_EXEC, ~ )
298
299 #undef _GEN_EXEC
300 #undef _GEN_PLACEHOLDER
301         /// @endcond
302
303 };
304
305 /// @cond
306 // create an t2n_exec-method for each possible number of parameters
307 #define _GEN_EXEC(z,n,d) \
308         template< class Client, typename R  BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,typename Arg) > \
309         static boost::function< R( BOOST_PP_ENUM_PARAMS(n,Arg) ) > t2n_exec \
310         ( \
311             R(Client::*f)( BOOST_PP_ENUM_PARAMS(n,Arg) ) \
312         ) \
313         { \
314             return T2nSingletonWrapper<Client>::exec(f); \
315         } // eo exec
316
317         BOOST_PP_REPEAT( BOOST_PP_ADD(T2N_SINGLETON_WRAPPER_MAX_ARGS,1), _GEN_EXEC, ~ )
318
319 #undef _GEN_EXEC
320 #undef _GEN_PLACEHOLDER
321 /// @endcond
322
323 }
324 #endif