libt2n, arnied: (gerd) set logging on existing connections too, show t2n-debugging...
[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 template< class Client >
174 class T2nSingletonWrapper : public T2nSingletonWrapperMessages
175 {
176     private:
177         std::auto_ptr<Client> Stub;
178
179         static std::auto_ptr<T2nSingletonWrapper> SingletonObject;
180         static std::auto_ptr<ConnectionWrapper> WrappedConnection;
181
182         // create a prep-method for each possible number of parameters
183 #define _GEN_ARG(z,n,d) Arg ## n arg ##n
184 #define _GEN_PREP(z,n,d) \
185         template< typename R  BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,typename Arg) > \
186         static boost::function< R(Client*) > prep \
187         ( \
188             R(Client::*f)( BOOST_PP_ENUM_PARAMS(n,Arg) ) \
189             BOOST_PP_COMMA_IF(n) \
190             BOOST_PP_ENUM( n, _GEN_ARG, ~ ) \
191         ) \
192         { \
193             return boost::bind< R > \
194                 ( \
195                     f, _1  BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,arg) \
196                 ); \
197         } // eo prep
198
199         BOOST_PP_REPEAT( BOOST_PP_ADD(T2N_SINGLETON_WRAPPER_MAX_ARGS,1) , _GEN_PREP, ~ )
200
201 #undef _GEN_PREP
202 #undef _GEN_ARG
203
204         T2nSingletonWrapper(std::auto_ptr<Client> stub)
205         {
206             Stub=stub;
207         }
208
209         static void init()
210         {
211             if (WrappedConnection.get() == NULL)
212                 throw std::logic_error(NotInitializedMessage);
213
214             std::auto_ptr<Client> stub(new Client(WrappedConnection->get_connection(),
215                 WrappedConnection->get_command_timeout_usec(),
216                 WrappedConnection->get_hello_timeout_usec()));
217
218             SingletonObject=std::auto_ptr<T2nSingletonWrapper>(new T2nSingletonWrapper(stub));
219         }
220
221         template< typename R >
222         static
223         typename detail::TypeWrap<R>::type real_exec( boost::function< R(Client*) > f)
224         {
225             ensure_singleton_there();
226
227             typename detail::TypeWrap<R>::type result;
228
229             // bind our Client-object and the local result
230             detail::Call<R> call( boost::bind( f, SingletonObject->Stub.get()), result );
231
232             // let the wrapper-handler call the fully-bound function
233             if (!WrappedConnection->handle(SingletonObject->Stub.get(),call))
234             {
235                 // create an result with default-constructor if the handler could not
236                 // successfully do a call but no exception occured
237                 result=typename detail::TypeWrap<R>::type();
238             }
239             return result;
240         }
241
242     public:
243
244         static void set_connection(std::auto_ptr<ConnectionWrapper> wrappedConnection)
245         {
246             WrappedConnection=wrappedConnection;
247
248             // reset the singleton to NULL because the singleton must be constructed with current WrappedConnection
249             if (SingletonObject.get() != NULL)
250                 SingletonObject.reset();
251         }
252         static ConnectionWrapper* get_connection_wrapper(void)
253             { return WrappedConnection.get(); }
254
255         static void ensure_singleton_there(void)
256         {
257             if (SingletonObject.get() == NULL)
258                 init();
259         }
260
261         // create an exec-method for each possible number of parameters
262 #define _GEN_PLACEHOLDER(z,n,d) BOOST_PP_CAT(_,BOOST_PP_ADD(n,1))
263 #define _GEN_EXEC(z,n,d) \
264         template< typename R  BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,typename Arg) > \
265         static boost::function< R( BOOST_PP_ENUM_PARAMS(n,Arg) ) > exec \
266         ( \
267             R(Client::*f)( BOOST_PP_ENUM_PARAMS(n,Arg) ) \
268         ) \
269         { \
270             boost::function<R(Client*)>(*p)(R(Client::*)( BOOST_PP_ENUM_PARAMS(n,Arg) ) \
271                     BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,Arg)) \
272                 = &T2nSingletonWrapper::template prep<R BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,Arg) >; \
273             return boost::bind \
274             ( \
275                 T2nSingletonWrapper::template real_exec<R>, \
276                 boost::bind( p, f BOOST_PP_COMMA_IF(n) \
277                 BOOST_PP_ENUM(n, _GEN_PLACEHOLDER, ~ ) ) \
278             ); \
279         } // eo exec
280
281         BOOST_PP_REPEAT( BOOST_PP_ADD(T2N_SINGLETON_WRAPPER_MAX_ARGS,1), _GEN_EXEC, ~ )
282
283 #undef _GEN_EXEC
284 #undef _GEN_PLACEHOLDER
285
286 };
287
288 // create an t2n_exec-method for each possible number of parameters
289 #define _GEN_EXEC(z,n,d) \
290         template< class Client, typename R  BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,typename Arg) > \
291         static boost::function< R( BOOST_PP_ENUM_PARAMS(n,Arg) ) > t2n_exec \
292         ( \
293             R(Client::*f)( BOOST_PP_ENUM_PARAMS(n,Arg) ) \
294         ) \
295         { \
296             return T2nSingletonWrapper<Client>::exec(f); \
297         } // eo exec
298
299         BOOST_PP_REPEAT( BOOST_PP_ADD(T2N_SINGLETON_WRAPPER_MAX_ARGS,1), _GEN_EXEC, ~ )
300
301 #undef _GEN_EXEC
302 #undef _GEN_PLACEHOLDER
303
304 }
305 #endif