session_shared: relay facets for requests
[metaproxy-moved-to-github.git] / src / filter_session_shared.cpp
1 /* This file is part of Metaproxy.
2    Copyright (C) 2005-2013 Index Data
3
4 Metaproxy is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
7 version.
8
9 Metaproxy is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17 */
18
19 #include "config.hpp"
20
21 #include <metaproxy/filter.hpp>
22 #include <metaproxy/package.hpp>
23
24 #include <boost/thread/mutex.hpp>
25 #include <boost/thread/condition.hpp>
26 #include <boost/thread/thread.hpp>
27 #include <boost/thread/xtime.hpp>
28 #include <boost/shared_ptr.hpp>
29 #include <boost/format.hpp>
30
31 #include <metaproxy/util.hpp>
32 #include "filter_session_shared.hpp"
33
34 #include <yaz/copy_types.h>
35 #include <yaz/log.h>
36 #include <yaz/zgdu.h>
37 #include <yaz/otherinfo.h>
38 #include <yaz/diagbib1.h>
39 #include <yazpp/z-query.h>
40 #include <yazpp/record-cache.h>
41 #include <map>
42 #include <iostream>
43 #include <time.h>
44
45 namespace mp = metaproxy_1;
46 namespace yf = metaproxy_1::filter;
47
48 namespace metaproxy_1 {
49
50     namespace filter {
51         // key for session.. We'll only share sessions with same InitKey
52         class SessionShared::InitKey {
53         public:
54             bool operator < (const SessionShared::InitKey &k) const;
55             InitKey(Z_InitRequest *req);
56             InitKey(const InitKey &);
57             ~InitKey();
58         private:
59             char *m_idAuthentication_buf;
60             int m_idAuthentication_size;
61             char *m_otherInfo_buf;
62             int m_otherInfo_size;
63             ODR m_odr;
64         };
65         // worker thread .. for expiry of sessions
66         class SessionShared::Worker {
67         public:
68             Worker(SessionShared::Rep *rep);
69             void operator() (void);
70         private:
71             SessionShared::Rep *m_p;
72         };
73         // backend result set
74         class SessionShared::BackendSet {
75         public:
76             std::string m_result_set_id;
77             Databases m_databases;
78             Odr_int m_result_set_size;
79             yazpp_1::Yaz_Z_Query m_query;
80             time_t m_time_last_use;
81             void timestamp();
82             yazpp_1::RecordCache m_record_cache;
83             BackendSet(
84                 const std::string &result_set_id,
85                 const Databases &databases,
86                 const yazpp_1::Yaz_Z_Query &query);
87             bool search(
88                 Package &frontend_package,
89                 Package &search_package,
90                 const Z_APDU *apdu_req,
91                 const BackendInstancePtr bp,
92                 Z_Records **z_records,
93                 Z_OtherInformation *additionalSearchInfo);
94         };
95         // backend connection instance
96         class SessionShared::BackendInstance {
97             friend class Rep;
98             friend class BackendClass;
99             friend class BackendSet;
100         public:
101             mp::Session m_session;
102             BackendSetList m_sets;
103             bool m_in_use;
104             int m_sequence_this;
105             int m_result_set_sequence;
106             time_t m_time_last_use;
107             mp::Package * m_close_package;
108             ~BackendInstance();
109             void timestamp();
110         };
111         // backends of some class (all with same InitKey)
112         class SessionShared::BackendClass : boost::noncopyable {
113             friend class Rep;
114             friend struct Frontend;
115             bool m_named_result_sets;
116             BackendInstanceList m_backend_list;
117             BackendInstancePtr create_backend(const Package &package);
118             void remove_backend(BackendInstancePtr b);
119             BackendInstancePtr get_backend(const Package &package);
120             void use_backend(BackendInstancePtr b);
121             void release_backend(BackendInstancePtr b);
122             bool expire_instances();
123             yazpp_1::GDU m_init_request;
124             yazpp_1::GDU m_init_response;
125             boost::mutex m_mutex_backend_class;
126             int m_sequence_top;
127             time_t m_backend_set_ttl;
128             time_t m_backend_expiry_ttl;
129             size_t m_backend_set_max;
130             Odr_int m_preferredMessageSize;
131             Odr_int m_maximumRecordSize;
132         public:
133             BackendClass(const yazpp_1::GDU &init_request,
134                          int resultset_ttl,
135                          int resultset_max,
136                          int session_ttl,
137                          Odr_int preferredRecordSize,
138                          Odr_int maximumRecordSize);
139             ~BackendClass();
140         };
141         // frontend result set
142         class SessionShared::FrontendSet {
143             Databases m_databases;
144             yazpp_1::Yaz_Z_Query m_query;
145         public:
146             const Databases &get_databases();
147             const yazpp_1::Yaz_Z_Query &get_query();
148             FrontendSet(
149                 const Databases &databases,
150                 const yazpp_1::Yaz_Z_Query &query);
151             FrontendSet();
152         };
153         // frontend session
154         struct SessionShared::Frontend {
155             Frontend(Rep *rep);
156             ~Frontend();
157             bool m_is_virtual;
158             bool m_in_use;
159             Z_Options m_init_options;
160             void search(Package &package, Z_APDU *apdu);
161             void present(Package &package, Z_APDU *apdu);
162             void scan(Package &package, Z_APDU *apdu);
163
164             int result_set_ref(ODR o,
165                                const Databases &databases,
166                                Z_RPNStructure *s, std::string &rset);
167             void get_set(mp::Package &package,
168                          const Z_APDU *apdu_req,
169                          const Databases &databases,
170                          yazpp_1::Yaz_Z_Query &query,
171                          BackendInstancePtr &found_backend,
172                          BackendSetPtr &found_set);
173             void override_set(BackendInstancePtr &found_backend,
174                               std::string &result_set_id,
175                               const Databases &databases,
176                               bool out_of_sessions);
177
178             Rep *m_p;
179             BackendClassPtr m_backend_class;
180             FrontendSets m_frontend_sets;
181         };
182         // representation
183         class SessionShared::Rep {
184             friend class SessionShared;
185             friend struct Frontend;
186
187             FrontendPtr get_frontend(Package &package);
188             void release_frontend(Package &package);
189             Rep();
190         public:
191             void expire();
192         private:
193             void expire_classes();
194             void stat();
195             void init(Package &package, const Z_GDU *gdu,
196                       FrontendPtr frontend);
197             void start();
198             boost::mutex m_mutex;
199             boost::condition m_cond_session_ready;
200             std::map<mp::Session, FrontendPtr> m_clients;
201
202             BackendClassMap m_backend_map;
203             boost::mutex m_mutex_backend_map;
204             boost::thread_group m_thrds;
205             int m_resultset_ttl;
206             int m_resultset_max;
207             int m_session_ttl;
208             bool m_optimize_search;
209             bool m_restart;
210             int m_session_max;
211             Odr_int m_preferredMessageSize;
212             Odr_int m_maximumRecordSize;
213         };
214     }
215 }
216
217 yf::SessionShared::FrontendSet::FrontendSet(
218     const Databases &databases,
219     const yazpp_1::Yaz_Z_Query &query)
220     : m_databases(databases), m_query(query)
221 {
222 }
223
224 const yf::SessionShared::Databases &
225 yf::SessionShared::FrontendSet::get_databases()
226 {
227     return m_databases;
228 }
229
230 const yazpp_1::Yaz_Z_Query& yf::SessionShared::FrontendSet::get_query()
231 {
232     return m_query;
233 }
234
235 yf::SessionShared::InitKey::InitKey(const InitKey &k)
236 {
237     m_odr = odr_createmem(ODR_ENCODE);
238
239     m_idAuthentication_size =  k.m_idAuthentication_size;
240     m_idAuthentication_buf = (char*)odr_malloc(m_odr, m_idAuthentication_size);
241     memcpy(m_idAuthentication_buf, k.m_idAuthentication_buf,
242            m_idAuthentication_size);
243
244     m_otherInfo_size =  k.m_otherInfo_size;
245     m_otherInfo_buf = (char*)odr_malloc(m_odr, m_otherInfo_size);
246     memcpy(m_otherInfo_buf, k.m_otherInfo_buf,
247            m_otherInfo_size);
248 }
249
250 yf::SessionShared::InitKey::InitKey(Z_InitRequest *req)
251 {
252     m_odr = odr_createmem(ODR_ENCODE);
253
254     Z_IdAuthentication *t = req->idAuthentication;
255     z_IdAuthentication(m_odr, &t, 1, 0);
256     m_idAuthentication_buf =
257         odr_getbuf(m_odr, &m_idAuthentication_size, 0);
258
259     Z_OtherInformation *o = req->otherInfo;
260     z_OtherInformation(m_odr, &o, 1, 0);
261     m_otherInfo_buf = odr_getbuf(m_odr, &m_otherInfo_size, 0);
262 }
263
264 yf::SessionShared::InitKey::~InitKey()
265 {
266     odr_destroy(m_odr);
267 }
268
269 bool yf::SessionShared::InitKey::operator < (const SessionShared::InitKey &k)
270     const
271 {
272     int c;
273     c = mp::util::memcmp2(
274         (void*) m_idAuthentication_buf, m_idAuthentication_size,
275         (void*) k.m_idAuthentication_buf, k.m_idAuthentication_size);
276     if (c < 0)
277         return true;
278     else if (c > 0)
279         return false;
280
281     c = mp::util::memcmp2((void*) m_otherInfo_buf, m_otherInfo_size,
282                           (void*) k.m_otherInfo_buf, k.m_otherInfo_size);
283     if (c < 0)
284         return true;
285     else if (c > 0)
286         return false;
287     return false;
288 }
289
290 void yf::SessionShared::BackendClass::release_backend(BackendInstancePtr b)
291 {
292     boost::mutex::scoped_lock lock(m_mutex_backend_class);
293     b->m_in_use = false;
294 }
295
296
297 void yf::SessionShared::BackendClass::remove_backend(BackendInstancePtr b)
298 {
299     BackendInstanceList::iterator it = m_backend_list.begin();
300
301     while (it != m_backend_list.end())
302     {
303         if (*it == b)
304         {
305              mp::odr odr;
306             (*it)->m_close_package->response() = odr.create_close(
307                 0, Z_Close_lackOfActivity, 0);
308             (*it)->m_close_package->session().close();
309             (*it)->m_close_package->move();
310
311             it = m_backend_list.erase(it);
312         }
313         else
314             it++;
315     }
316 }
317
318
319
320 yf::SessionShared::BackendInstancePtr
321 yf::SessionShared::BackendClass::get_backend(
322     const mp::Package &frontend_package)
323 {
324     {
325         boost::mutex::scoped_lock lock(m_mutex_backend_class);
326
327         BackendInstanceList::const_iterator it = m_backend_list.begin();
328
329         BackendInstancePtr backend1; // null
330
331         for (; it != m_backend_list.end(); it++)
332         {
333             if (!(*it)->m_in_use)
334             {
335                 if (!backend1
336                     || (*it)->m_sequence_this < backend1->m_sequence_this)
337                     backend1 = *it;
338             }
339         }
340         if (backend1)
341         {
342             use_backend(backend1);
343             return backend1;
344         }
345     }
346     return create_backend(frontend_package);
347 }
348
349 void yf::SessionShared::BackendClass::use_backend(BackendInstancePtr backend)
350 {
351     backend->m_in_use = true;
352     backend->m_sequence_this = m_sequence_top++;
353 }
354
355 void yf::SessionShared::BackendInstance::timestamp()
356 {
357     assert(m_in_use);
358     time(&m_time_last_use);
359 }
360
361 yf::SessionShared::BackendInstance::~BackendInstance()
362 {
363     delete m_close_package;
364 }
365
366 yf::SessionShared::BackendInstancePtr yf::SessionShared::BackendClass::create_backend(
367     const mp::Package &frontend_package)
368 {
369     BackendInstancePtr bp(new BackendInstance);
370     BackendInstancePtr null;
371
372     bp->m_close_package =
373         new mp::Package(bp->m_session, frontend_package.origin());
374     bp->m_close_package->copy_filter(frontend_package);
375
376     Package init_package(bp->m_session, frontend_package.origin());
377
378     init_package.copy_filter(frontend_package);
379
380     yazpp_1::GDU actual_init_request = m_init_request;
381     Z_GDU *init_pdu = actual_init_request.get();
382
383     assert(init_pdu->which == Z_GDU_Z3950);
384     assert(init_pdu->u.z3950->which == Z_APDU_initRequest);
385
386     Z_InitRequest *req = init_pdu->u.z3950->u.initRequest;
387     ODR_MASK_ZERO(req->options);
388
389     ODR_MASK_SET(req->options, Z_Options_search);
390     ODR_MASK_SET(req->options, Z_Options_present);
391     ODR_MASK_SET(req->options, Z_Options_namedResultSets);
392     ODR_MASK_SET(req->options, Z_Options_scan);
393
394     ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_1);
395     ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_2);
396     ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_3);
397
398     if (m_preferredMessageSize)
399         *req->preferredMessageSize = m_preferredMessageSize;
400     if (m_maximumRecordSize)
401         *req->maximumRecordSize = m_maximumRecordSize;
402
403     init_package.request() = init_pdu;
404
405     init_package.move();
406
407     boost::mutex::scoped_lock lock(m_mutex_backend_class);
408
409     m_named_result_sets = false;
410     Z_GDU *gdu = init_package.response().get();
411
412     if (gdu && gdu->which == Z_GDU_Z3950
413         && gdu->u.z3950->which == Z_APDU_initResponse)
414     {
415         Z_InitResponse *res = gdu->u.z3950->u.initResponse;
416         m_init_response = gdu->u.z3950;
417         if (ODR_MASK_GET(res->options, Z_Options_namedResultSets))
418         {
419             m_named_result_sets = true;
420         }
421         if (*gdu->u.z3950->u.initResponse->result
422             && !init_package.session().is_closed())
423         {
424             bp->m_in_use = true;
425             time(&bp->m_time_last_use);
426             bp->m_sequence_this = 0;
427             bp->m_result_set_sequence = 0;
428             m_backend_list.push_back(bp);
429             return bp;
430         }
431     }
432     else
433     {
434         yazpp_1::GDU empty_gdu;
435         m_init_response = empty_gdu;
436     }
437
438     if (!init_package.session().is_closed())
439     {
440         init_package.copy_filter(frontend_package);
441         init_package.session().close();
442         init_package.move();
443     }
444     return null;
445 }
446
447
448 yf::SessionShared::BackendClass::BackendClass(const yazpp_1::GDU &init_request,
449                                               int resultset_ttl,
450                                               int resultset_max,
451                                               int session_ttl,
452                                               Odr_int preferredMessageSize,
453                                               Odr_int maximumRecordSize)
454     : m_named_result_sets(false), m_init_request(init_request),
455       m_sequence_top(0), m_backend_set_ttl(resultset_ttl),
456       m_backend_expiry_ttl(session_ttl), m_backend_set_max(resultset_max),
457       m_preferredMessageSize(preferredMessageSize),
458       m_maximumRecordSize(maximumRecordSize)
459 {}
460
461 yf::SessionShared::BackendClass::~BackendClass()
462 {}
463
464 void yf::SessionShared::Rep::stat()
465 {
466     int no_classes = 0;
467     int no_instances = 0;
468     BackendClassMap::const_iterator it;
469     {
470         boost::mutex::scoped_lock lock(m_mutex_backend_map);
471         for (it = m_backend_map.begin(); it != m_backend_map.end(); it++)
472         {
473             BackendClassPtr bc = it->second;
474             no_classes++;
475             BackendInstanceList::iterator bit = bc->m_backend_list.begin();
476             for (; bit != bc->m_backend_list.end(); bit++)
477                 no_instances++;
478         }
479     }
480     yaz_log(YLOG_LOG, "backend classes=%d instances=%d", no_classes,
481         no_instances);
482 }
483
484 void yf::SessionShared::Rep::init(mp::Package &package, const Z_GDU *gdu,
485                                   FrontendPtr frontend)
486 {
487     Z_InitRequest *req = gdu->u.z3950->u.initRequest;
488
489     frontend->m_is_virtual = true;
490     frontend->m_init_options = *req->options;
491     InitKey k(req);
492     {
493         boost::mutex::scoped_lock lock(m_mutex_backend_map);
494         BackendClassMap::const_iterator it;
495         it = m_backend_map.find(k);
496         if (it == m_backend_map.end())
497         {
498             BackendClassPtr b(new BackendClass(gdu->u.z3950,
499                                                m_resultset_ttl,
500                                                m_resultset_max,
501                                                m_session_ttl,
502                                                m_preferredMessageSize,
503                                                m_maximumRecordSize));
504             m_backend_map[k] = b;
505             frontend->m_backend_class = b;
506         }
507         else
508         {
509             frontend->m_backend_class = it->second;
510         }
511     }
512     BackendClassPtr bc = frontend->m_backend_class;
513     mp::odr odr;
514
515     // we only need to get init response from "first" target in
516     // backend class - the assumption being that init response is
517     // same for all
518     if (bc->m_backend_list.size() == 0)
519     {
520         BackendInstancePtr backend = bc->create_backend(package);
521         if (backend)
522             bc->release_backend(backend);
523     }
524
525     yazpp_1::GDU init_response;
526     {
527         boost::mutex::scoped_lock lock(bc->m_mutex_backend_class);
528
529         init_response = bc->m_init_response;
530     }
531
532     if (init_response.get())
533     {
534         Z_GDU *response_gdu = init_response.get();
535         mp::util::transfer_referenceId(odr, gdu->u.z3950,
536                                        response_gdu->u.z3950);
537         Z_InitResponse *init_res = response_gdu->u.z3950->u.initResponse;
538         Z_Options *server_options = init_res->options;
539         Z_Options *client_options = &frontend->m_init_options;
540         int i;
541         for (i = 0; i < 30; i++)
542             if (!ODR_MASK_GET(client_options, i))
543                 ODR_MASK_CLEAR(server_options, i);
544
545         if (!m_preferredMessageSize ||
546             *init_res->preferredMessageSize > *req->preferredMessageSize)
547             *init_res->preferredMessageSize = *req->preferredMessageSize;
548
549         if (!m_maximumRecordSize ||
550             *init_res->maximumRecordSize > *req->maximumRecordSize)
551             *init_res->maximumRecordSize = *req->maximumRecordSize;
552
553         package.response() = init_response;
554         if (!*response_gdu->u.z3950->u.initResponse->result)
555             package.session().close();
556     }
557     else
558     {
559         Z_APDU *apdu =
560             odr.create_initResponse(
561                 gdu->u.z3950, YAZ_BIB1_TEMPORARY_SYSTEM_ERROR,
562                 "session_shared: target closed connection during init");
563         *apdu->u.initResponse->result = 0;
564         package.response() = apdu;
565         package.session().close();
566     }
567 }
568
569 void yf::SessionShared::BackendSet::timestamp()
570 {
571     time(&m_time_last_use);
572 }
573
574 yf::SessionShared::BackendSet::BackendSet(
575     const std::string &result_set_id,
576     const Databases &databases,
577     const yazpp_1::Yaz_Z_Query &query) :
578     m_result_set_id(result_set_id),
579     m_databases(databases), m_result_set_size(0), m_query(query)
580 {
581     timestamp();
582 }
583
584 static int get_diagnostic(Z_DefaultDiagFormat *r)
585 {
586     return *r->condition;
587 }
588
589 bool yf::SessionShared::BackendSet::search(
590     mp::Package &frontend_package,
591     mp::Package &search_package,
592     const Z_APDU *frontend_apdu,
593     const BackendInstancePtr bp,
594     Z_Records **z_records,
595     Z_OtherInformation *additionalSearchInfo)
596 {
597     mp::odr odr;
598     Z_APDU *apdu_req = zget_APDU(odr, Z_APDU_searchRequest);
599     Z_SearchRequest *req = apdu_req->u.searchRequest;
600
601     req->additionalSearchInfo = additionalSearchInfo;
602     req->resultSetName = odr_strdup(odr, m_result_set_id.c_str());
603     req->query = m_query.get_Z_Query();
604
605     req->num_databaseNames = m_databases.size();
606     req->databaseNames = (char**)
607         odr_malloc(odr, req->num_databaseNames * sizeof(char *));
608     Databases::const_iterator it = m_databases.begin();
609     size_t i = 0;
610     for (; it != m_databases.end(); it++)
611         req->databaseNames[i++] = odr_strdup(odr, it->c_str());
612
613     if (frontend_apdu->which == Z_APDU_searchRequest)
614         req->preferredRecordSyntax =
615             frontend_apdu->u.searchRequest->preferredRecordSyntax;
616
617     search_package.request() = apdu_req;
618
619     search_package.move();
620
621     Z_GDU *gdu = search_package.response().get();
622     if (!search_package.session().is_closed()
623         && gdu && gdu->which == Z_GDU_Z3950
624         && gdu->u.z3950->which == Z_APDU_searchResponse)
625     {
626         Z_SearchResponse *b_resp = gdu->u.z3950->u.searchResponse;
627         *z_records = b_resp->records;
628         m_result_set_size = *b_resp->resultCount;
629         // b_resp->additionalSearchInfo;
630         return true;
631     }
632     Z_APDU *f_apdu = 0;
633     const char *addinfo = "session_shared: "
634         "target closed connection during search";
635     if (frontend_apdu->which == Z_APDU_searchRequest)
636         f_apdu = odr.create_searchResponse(
637             frontend_apdu, YAZ_BIB1_TEMPORARY_SYSTEM_ERROR, addinfo);
638     else if (frontend_apdu->which == Z_APDU_presentRequest)
639         f_apdu = odr.create_presentResponse(
640             frontend_apdu, YAZ_BIB1_TEMPORARY_SYSTEM_ERROR, addinfo);
641     else
642         f_apdu = odr.create_close(
643             frontend_apdu, YAZ_BIB1_TEMPORARY_SYSTEM_ERROR, addinfo);
644     frontend_package.response() = f_apdu;
645     return false;
646 }
647
648 void yf::SessionShared::Frontend::override_set(
649     BackendInstancePtr &found_backend,
650     std::string &result_set_id,
651     const Databases &databases,
652     bool out_of_sessions)
653 {
654     BackendClassPtr bc = m_backend_class;
655     BackendInstanceList::const_iterator it = bc->m_backend_list.begin();
656     time_t now;
657     time(&now);
658
659     size_t max_sets = bc->m_named_result_sets ? bc->m_backend_set_max : 1;
660     for (; it != bc->m_backend_list.end(); it++)
661     {
662         if (!(*it)->m_in_use)
663         {
664             BackendSetList::iterator set_it = (*it)->m_sets.begin();
665             for (; set_it != (*it)->m_sets.end(); set_it++)
666             {
667                 if ((max_sets > 1 || (*set_it)->m_databases == databases)
668                     &&
669                     (out_of_sessions ||
670                      now < (*set_it)->m_time_last_use ||
671                      now - (*set_it)->m_time_last_use >= bc->m_backend_set_ttl))
672                 {
673                     found_backend = *it;
674                     result_set_id = (*set_it)->m_result_set_id;
675                     found_backend->m_sets.erase(set_it);
676                     return;
677                 }
678             }
679         }
680     }
681     for (it = bc->m_backend_list.begin(); it != bc->m_backend_list.end(); it++)
682     {
683         if (!(*it)->m_in_use && (*it)->m_sets.size() < max_sets)
684         {
685             found_backend = *it;
686             if (bc->m_named_result_sets)
687             {
688                 result_set_id = boost::io::str(
689                     boost::format("%1%") %
690                     found_backend->m_result_set_sequence);
691                 found_backend->m_result_set_sequence++;
692             }
693             else
694                 result_set_id = "default";
695             return;
696         }
697     }
698 }
699
700 void yf::SessionShared::Frontend::get_set(mp::Package &package,
701                                           const Z_APDU *apdu_req,
702                                           const Databases &databases,
703                                           yazpp_1::Yaz_Z_Query &query,
704                                           BackendInstancePtr &found_backend,
705                                           BackendSetPtr &found_set)
706 {
707     bool session_restarted = false;
708     Z_OtherInformation *additionalSearchInfo = 0;
709
710     if (apdu_req->which == Z_APDU_searchRequest)
711         additionalSearchInfo = apdu_req->u.searchRequest->additionalSearchInfo;
712
713 restart:
714     std::string result_set_id;
715     bool out_of_sessions = false;
716     BackendClassPtr bc = m_backend_class;
717     {
718         boost::mutex::scoped_lock lock(bc->m_mutex_backend_class);
719
720         if ((int) bc->m_backend_list.size() >= m_p->m_session_max)
721             out_of_sessions = true;
722
723         if (m_p->m_optimize_search && !additionalSearchInfo)
724         {
725             // look at each backend and see if we have a similar search
726             BackendInstanceList::const_iterator it = bc->m_backend_list.begin();
727             for (; it != bc->m_backend_list.end(); it++)
728             {
729                 if (!(*it)->m_in_use)
730                 {
731                     BackendSetList::const_iterator set_it = (*it)->m_sets.begin();
732                     for (; set_it != (*it)->m_sets.end(); set_it++)
733                     {
734                         if ((*set_it)->m_databases == databases
735                             && query.match(&(*set_it)->m_query))
736                         {
737                             found_set = *set_it;
738                             found_backend = *it;
739                             bc->use_backend(found_backend);
740                             // found matching set. No need to search again
741                             return;
742                         }
743                     }
744                 }
745             }
746         }
747         override_set(found_backend, result_set_id, databases, out_of_sessions);
748         if (found_backend)
749             bc->use_backend(found_backend);
750     }
751     if (!found_backend)
752     {
753         // create a new backend set (and new set) if we're not out of sessions
754         if (!out_of_sessions)
755             found_backend = bc->create_backend(package);
756
757         if (!found_backend)
758         {
759             Z_APDU *f_apdu = 0;
760             mp::odr odr;
761             const char *addinfo = 0;
762
763             if (out_of_sessions)
764                 addinfo = "session_shared: all sessions in use";
765             else
766                 addinfo = "session_shared: could not create backend";
767             if (apdu_req->which == Z_APDU_searchRequest)
768             {
769                 f_apdu = odr.create_searchResponse(
770                     apdu_req, YAZ_BIB1_TEMPORARY_SYSTEM_ERROR, addinfo);
771             }
772             else if (apdu_req->which == Z_APDU_presentRequest)
773             {
774                 f_apdu = odr.create_presentResponse(
775                     apdu_req, YAZ_BIB1_TEMPORARY_SYSTEM_ERROR, addinfo);
776             }
777             else
778             {
779                 f_apdu = odr.create_close(
780                     apdu_req, YAZ_BIB1_TEMPORARY_SYSTEM_ERROR, addinfo);
781             }
782             package.response() = f_apdu;
783             return;
784         }
785         if (bc->m_named_result_sets)
786         {
787             result_set_id = boost::io::str(
788                 boost::format("%1%") % found_backend->m_result_set_sequence);
789             found_backend->m_result_set_sequence++;
790         }
791         else
792             result_set_id = "default";
793     }
794     found_backend->timestamp();
795
796     // we must search ...
797     BackendSetPtr new_set(new BackendSet(result_set_id,
798                                          databases, query));
799     Z_Records *z_records = 0;
800
801     Package search_package(found_backend->m_session, package.origin());
802     search_package.copy_filter(package);
803
804     if (!new_set->search(package, search_package,
805                          apdu_req, found_backend, &z_records,
806                          additionalSearchInfo))
807     {
808         bc->remove_backend(found_backend);
809         return; // search error
810     }
811
812     if (z_records)
813     {
814         int condition = 0;
815         if (z_records->which == Z_Records_NSD)
816         {
817             condition =
818                 get_diagnostic(z_records->u.nonSurrogateDiagnostic);
819         }
820         else if (z_records->which == Z_Records_multipleNSD)
821         {
822             if (z_records->u.multipleNonSurDiagnostics->num_diagRecs >= 1
823                 &&
824
825                 z_records->u.multipleNonSurDiagnostics->diagRecs[0]->which ==
826                 Z_DiagRec_defaultFormat)
827             {
828                 condition = get_diagnostic(
829                     z_records->u.multipleNonSurDiagnostics->diagRecs[0]->u.defaultFormat);
830
831             }
832         }
833         if (m_p->m_restart && !session_restarted &&
834             condition == YAZ_BIB1_TEMPORARY_SYSTEM_ERROR)
835         {
836             package.log("session_shared", YLOG_LOG, "restart");
837             bc->remove_backend(found_backend);
838             session_restarted = true;
839             found_backend.reset();
840             goto restart;
841
842         }
843
844         if (condition)
845         {
846             mp::odr odr;
847             if (apdu_req->which == Z_APDU_searchRequest)
848             {
849                 Z_APDU *f_apdu = odr.create_searchResponse(apdu_req,
850                                                            0, 0);
851                 Z_SearchResponse *f_resp = f_apdu->u.searchResponse;
852                 *f_resp->searchStatus = Z_SearchResponse_none;
853                 f_resp->records = z_records;
854                 package.response() = f_apdu;
855             }
856             if (apdu_req->which == Z_APDU_presentRequest)
857             {
858                 Z_APDU *f_apdu = odr.create_presentResponse(apdu_req,
859                                                             0, 0);
860                 Z_PresentResponse *f_resp = f_apdu->u.presentResponse;
861                 f_resp->records = z_records;
862                 package.response() = f_apdu;
863             }
864             bc->release_backend(found_backend);
865             return; // search error
866         }
867     }
868     if (m_p->m_restart && !session_restarted && new_set->m_result_set_size < 0)
869     {
870         package.log("session_shared", YLOG_LOG, "restart");
871         bc->remove_backend(found_backend);
872         session_restarted = true;
873         found_backend.reset();
874         goto restart;
875     }
876
877     found_set = new_set;
878     found_set->timestamp();
879     found_backend->m_sets.push_back(found_set);
880 }
881
882 int yf::SessionShared::Frontend::result_set_ref(ODR o,
883                                                 const Databases &databases,
884                                                 Z_RPNStructure *s,
885                                                 std::string &rset)
886 {
887     int ret = 0;
888     switch (s->which)
889     {
890     case Z_RPNStructure_simple:
891         if (s->u.simple->which == Z_Operand_resultSetId)
892         {
893             const char *id = s->u.simple->u.resultSetId;
894             rset = id;
895
896             FrontendSets::iterator fset_it = m_frontend_sets.find(id);
897             if (fset_it == m_frontend_sets.end())
898             {
899                 ret = YAZ_BIB1_SPECIFIED_RESULT_SET_DOES_NOT_EXIST;
900             }
901             else if (fset_it->second->get_databases() != databases)
902             {
903                 ret = YAZ_BIB1_SPECIFIED_RESULT_SET_DOES_NOT_EXIST;
904             }
905             else
906             {
907                 yazpp_1::Yaz_Z_Query query = fset_it->second->get_query();
908                 Z_Query *q = yaz_copy_Z_Query(query.get_Z_Query(), o);
909                 if (q->which == Z_Query_type_1 || q->which == Z_Query_type_101)
910                 {
911                     s->which = q->u.type_1->RPNStructure->which;
912                     s->u.simple = q->u.type_1->RPNStructure->u.simple;
913                 }
914                 else
915                 {
916                     ret = YAZ_BIB1_SPECIFIED_RESULT_SET_DOES_NOT_EXIST;
917                 }
918             }
919         }
920         break;
921     case Z_RPNStructure_complex:
922         ret = result_set_ref(o, databases, s->u.complex->s1, rset);
923         if (!ret)
924             ret = result_set_ref(o, databases, s->u.complex->s2, rset);
925         break;
926     }
927     return ret;
928 }
929
930 void yf::SessionShared::Frontend::search(mp::Package &package,
931                                          Z_APDU *apdu_req)
932 {
933     Z_SearchRequest *req = apdu_req->u.searchRequest;
934     FrontendSets::iterator fset_it =
935         m_frontend_sets.find(req->resultSetName);
936     if (fset_it != m_frontend_sets.end())
937     {
938         // result set already exist
939         // if replace indicator is off: we return diagnostic if
940         // result set already exist.
941         if (*req->replaceIndicator == 0)
942         {
943             mp::odr odr;
944             Z_APDU *apdu =
945                 odr.create_searchResponse(
946                     apdu_req,
947                     YAZ_BIB1_RESULT_SET_EXISTS_AND_REPLACE_INDICATOR_OFF,
948                     0);
949             package.response() = apdu;
950             return;
951         }
952         m_frontend_sets.erase(fset_it);
953     }
954
955     Databases databases;
956     int i;
957     for (i = 0; i < req->num_databaseNames; i++)
958         databases.push_back(req->databaseNames[i]);
959
960
961     yazpp_1::Yaz_Z_Query query;
962     query.set_Z_Query(req->query);
963
964     Z_Query *q = query.get_Z_Query();
965     if (q->which == Z_Query_type_1 || q->which == Z_Query_type_101)
966     {
967         mp::odr odr;
968         std::string rset;
969         int diag = result_set_ref(odr, databases, q->u.type_1->RPNStructure,
970                                   rset);
971         if (diag)
972         {
973             Z_APDU *apdu =
974                 odr.create_searchResponse(
975                     apdu_req,
976                     diag,
977                     rset.c_str());
978             package.response() = apdu;
979             return;
980         }
981         query.set_Z_Query(q);
982     }
983
984     BackendSetPtr found_set; // null
985     BackendInstancePtr found_backend; // null
986
987     get_set(package, apdu_req, databases, query, found_backend, found_set);
988     if (!found_set)
989         return;
990
991     mp::odr odr;
992     Z_APDU *f_apdu = odr.create_searchResponse(apdu_req, 0, 0);
993     Z_SearchResponse *f_resp = f_apdu->u.searchResponse;
994     *f_resp->resultCount = found_set->m_result_set_size;
995     package.response() = f_apdu;
996
997     FrontendSetPtr fset(new FrontendSet(databases, query));
998     m_frontend_sets[req->resultSetName] = fset;
999
1000     m_backend_class->release_backend(found_backend);
1001 }
1002
1003 void yf::SessionShared::Frontend::present(mp::Package &package,
1004                                           Z_APDU *apdu_req)
1005 {
1006     mp::odr odr;
1007     Z_PresentRequest *req = apdu_req->u.presentRequest;
1008
1009     FrontendSets::iterator fset_it =
1010         m_frontend_sets.find(req->resultSetId);
1011
1012     if (fset_it == m_frontend_sets.end())
1013     {
1014         Z_APDU *apdu =
1015             odr.create_presentResponse(
1016                 apdu_req,
1017                 YAZ_BIB1_SPECIFIED_RESULT_SET_DOES_NOT_EXIST,
1018                 req->resultSetId);
1019         package.response() = apdu;
1020         return;
1021     }
1022     FrontendSetPtr fset = fset_it->second;
1023
1024     Databases databases = fset->get_databases();
1025     yazpp_1::Yaz_Z_Query query = fset->get_query();
1026
1027     BackendClassPtr bc = m_backend_class;
1028     BackendSetPtr found_set; // null
1029     BackendInstancePtr found_backend;
1030
1031     get_set(package, apdu_req, databases, query, found_backend, found_set);
1032     if (!found_set)
1033         return;
1034
1035     Z_NamePlusRecordList *npr_res = 0;
1036     if (found_set->m_record_cache.lookup(odr, &npr_res,
1037                                          *req->resultSetStartPoint,
1038                                          *req->numberOfRecordsRequested,
1039                                          req->preferredRecordSyntax,
1040                                          req->recordComposition))
1041     {
1042         Z_APDU *f_apdu_res = odr.create_presentResponse(apdu_req, 0, 0);
1043         Z_PresentResponse *f_resp = f_apdu_res->u.presentResponse;
1044
1045         yaz_log(YLOG_LOG, "Found " ODR_INT_PRINTF "+" ODR_INT_PRINTF
1046                 " records in cache %p",
1047                 *req->resultSetStartPoint,
1048                 *req->numberOfRecordsRequested,
1049                 &found_set->m_record_cache);
1050
1051         *f_resp->numberOfRecordsReturned = *req->numberOfRecordsRequested;
1052         *f_resp->nextResultSetPosition =
1053             *req->resultSetStartPoint + *req->numberOfRecordsRequested;
1054         // f_resp->presentStatus assumed OK.
1055         f_resp->records = (Z_Records *) odr_malloc(odr, sizeof(Z_Records));
1056         f_resp->records->which = Z_Records_DBOSD;
1057         f_resp->records->u.databaseOrSurDiagnostics = npr_res;
1058         package.response() = f_apdu_res;
1059         bc->release_backend(found_backend);
1060         return;
1061     }
1062
1063     found_backend->timestamp();
1064
1065     Z_APDU *p_apdu = zget_APDU(odr, Z_APDU_presentRequest);
1066     Z_PresentRequest *p_req = p_apdu->u.presentRequest;
1067     p_req->preferredRecordSyntax = req->preferredRecordSyntax;
1068     p_req->resultSetId = odr_strdup(odr, found_set->m_result_set_id.c_str());
1069     *p_req->resultSetStartPoint = *req->resultSetStartPoint;
1070     *p_req->numberOfRecordsRequested = *req->numberOfRecordsRequested;
1071     p_req->preferredRecordSyntax = req->preferredRecordSyntax;
1072     p_req->recordComposition = req->recordComposition;
1073
1074     Package present_package(found_backend->m_session, package.origin());
1075     present_package.copy_filter(package);
1076
1077     present_package.request() = p_apdu;
1078
1079     present_package.move();
1080
1081     Z_GDU *gdu = present_package.response().get();
1082     if (!present_package.session().is_closed()
1083         && gdu && gdu->which == Z_GDU_Z3950
1084         && gdu->u.z3950->which == Z_APDU_presentResponse)
1085     {
1086         Z_PresentResponse *b_resp = gdu->u.z3950->u.presentResponse;
1087         Z_APDU *f_apdu_res = odr.create_presentResponse(apdu_req, 0, 0);
1088         Z_PresentResponse *f_resp = f_apdu_res->u.presentResponse;
1089
1090         f_resp->numberOfRecordsReturned = b_resp->numberOfRecordsReturned;
1091         f_resp->nextResultSetPosition = b_resp->nextResultSetPosition;
1092         f_resp->presentStatus= b_resp->presentStatus;
1093         f_resp->records = b_resp->records;
1094         f_resp->otherInfo = b_resp->otherInfo;
1095         package.response() = f_apdu_res;
1096
1097         if (b_resp->records && b_resp->records->which ==  Z_Records_DBOSD)
1098         {
1099 #if 0
1100             yaz_log(YLOG_LOG, "Adding " ODR_INT_PRINTF "+" ODR_INT_PRINTF
1101                     " records to cache %p",
1102                     *req->resultSetStartPoint,
1103                     *f_resp->numberOfRecordsReturned,
1104                     &found_set->m_record_cache);
1105 #endif
1106             found_set->m_record_cache.add(
1107                 odr,
1108                 b_resp->records->u.databaseOrSurDiagnostics,
1109                 *req->resultSetStartPoint,
1110                 *f_resp->numberOfRecordsReturned);
1111         }
1112         bc->release_backend(found_backend);
1113     }
1114     else
1115     {
1116         bc->remove_backend(found_backend);
1117         Z_APDU *f_apdu_res =
1118             odr.create_presentResponse(
1119                 apdu_req, YAZ_BIB1_TEMPORARY_SYSTEM_ERROR,
1120                 "session_shared: target closed connection during present");
1121         package.response() = f_apdu_res;
1122     }
1123 }
1124
1125 void yf::SessionShared::Frontend::scan(mp::Package &frontend_package,
1126                                        Z_APDU *apdu_req)
1127 {
1128     BackendClassPtr bc = m_backend_class;
1129     BackendInstancePtr backend = bc->get_backend(frontend_package);
1130     if (!backend)
1131     {
1132         mp::odr odr;
1133         Z_APDU *apdu = odr.create_scanResponse(
1134             apdu_req, YAZ_BIB1_TEMPORARY_SYSTEM_ERROR,
1135             "session_shared: could not create backend");
1136         frontend_package.response() = apdu;
1137     }
1138     else
1139     {
1140         Package scan_package(backend->m_session, frontend_package.origin());
1141         backend->timestamp();
1142         scan_package.copy_filter(frontend_package);
1143         scan_package.request() = apdu_req;
1144         scan_package.move();
1145         frontend_package.response() = scan_package.response();
1146         if (scan_package.session().is_closed())
1147         {
1148             frontend_package.session().close();
1149             bc->remove_backend(backend);
1150         }
1151         else
1152             bc->release_backend(backend);
1153     }
1154 }
1155
1156 yf::SessionShared::Worker::Worker(SessionShared::Rep *rep) : m_p(rep)
1157 {
1158 }
1159
1160 void yf::SessionShared::Worker::operator() (void)
1161 {
1162     m_p->expire();
1163 }
1164
1165 bool yf::SessionShared::BackendClass::expire_instances()
1166 {
1167     time_t now;
1168     time(&now);
1169     boost::mutex::scoped_lock lock(m_mutex_backend_class);
1170     BackendInstanceList::iterator bit = m_backend_list.begin();
1171     while (bit != m_backend_list.end())
1172     {
1173         time_t last_use = (*bit)->m_time_last_use;
1174
1175         if ((*bit)->m_in_use)
1176         {
1177             bit++;
1178         }
1179         else if (now < last_use || now - last_use > m_backend_expiry_ttl)
1180         {
1181             mp::odr odr;
1182             (*bit)->m_close_package->response() = odr.create_close(
1183                 0, Z_Close_lackOfActivity, 0);
1184             (*bit)->m_close_package->session().close();
1185             (*bit)->m_close_package->move();
1186
1187             bit = m_backend_list.erase(bit);
1188         }
1189         else
1190         {
1191             bit++;
1192         }
1193     }
1194     if (m_backend_list.empty())
1195         return true;
1196     return false;
1197 }
1198
1199 void yf::SessionShared::Rep::expire_classes()
1200 {
1201     boost::mutex::scoped_lock lock(m_mutex_backend_map);
1202     BackendClassMap::iterator b_it = m_backend_map.begin();
1203     while (b_it != m_backend_map.end())
1204     {
1205         if (b_it->second->expire_instances())
1206         {
1207             m_backend_map.erase(b_it);
1208             b_it = m_backend_map.begin();
1209         }
1210         else
1211             b_it++;
1212     }
1213 }
1214
1215 void yf::SessionShared::Rep::expire()
1216 {
1217     while (true)
1218     {
1219         boost::xtime xt;
1220         boost::xtime_get(&xt,
1221 #if BOOST_VERSION >= 105000 
1222                 boost::TIME_UTC_
1223 #else
1224                 boost::TIME_UTC
1225 #endif
1226                   );
1227         xt.sec += m_session_ttl / 3;
1228         boost::thread::sleep(xt);
1229
1230         stat();
1231         expire_classes();
1232     }
1233 }
1234
1235 yf::SessionShared::Rep::Rep()
1236 {
1237     m_resultset_ttl = 30;
1238     m_resultset_max = 10;
1239     m_session_ttl = 90;
1240     m_optimize_search = true;
1241     m_restart = false;
1242     m_session_max = 100;
1243     m_preferredMessageSize = 0;
1244     m_maximumRecordSize = 0;
1245 }
1246
1247 void yf::SessionShared::Rep::start()
1248 {
1249     yf::SessionShared::Worker w(this);
1250     m_thrds.add_thread(new boost::thread(w));
1251 }
1252
1253 yf::SessionShared::SessionShared() : m_p(new SessionShared::Rep)
1254 {
1255 }
1256
1257 yf::SessionShared::~SessionShared() {
1258 }
1259
1260 void yf::SessionShared::start() const
1261 {
1262     m_p->start();
1263 }
1264
1265 yf::SessionShared::Frontend::Frontend(Rep *rep) : m_is_virtual(false), m_p(rep)
1266 {
1267 }
1268
1269 yf::SessionShared::Frontend::~Frontend()
1270 {
1271 }
1272
1273 yf::SessionShared::FrontendPtr yf::SessionShared::Rep::get_frontend(mp::Package &package)
1274 {
1275     boost::mutex::scoped_lock lock(m_mutex);
1276
1277     std::map<mp::Session,yf::SessionShared::FrontendPtr>::iterator it;
1278
1279     while(true)
1280     {
1281         it = m_clients.find(package.session());
1282         if (it == m_clients.end())
1283             break;
1284
1285         if (!it->second->m_in_use)
1286         {
1287             it->second->m_in_use = true;
1288             return it->second;
1289         }
1290         m_cond_session_ready.wait(lock);
1291     }
1292     FrontendPtr f(new Frontend(this));
1293     m_clients[package.session()] = f;
1294     f->m_in_use = true;
1295     return f;
1296 }
1297
1298 void yf::SessionShared::Rep::release_frontend(mp::Package &package)
1299 {
1300     boost::mutex::scoped_lock lock(m_mutex);
1301     std::map<mp::Session,yf::SessionShared::FrontendPtr>::iterator it;
1302
1303     it = m_clients.find(package.session());
1304     if (it != m_clients.end())
1305     {
1306         if (package.session().is_closed())
1307         {
1308             m_clients.erase(it);
1309         }
1310         else
1311         {
1312             it->second->m_in_use = false;
1313         }
1314         m_cond_session_ready.notify_all();
1315     }
1316 }
1317
1318
1319 void yf::SessionShared::process(mp::Package &package) const
1320 {
1321     FrontendPtr f = m_p->get_frontend(package);
1322
1323     Z_GDU *gdu = package.request().get();
1324
1325     if (gdu && gdu->which == Z_GDU_Z3950 && gdu->u.z3950->which ==
1326         Z_APDU_initRequest && !f->m_is_virtual)
1327     {
1328         m_p->init(package, gdu, f);
1329     }
1330     else if (!f->m_is_virtual)
1331         package.move();
1332     else if (gdu && gdu->which == Z_GDU_Z3950)
1333     {
1334         Z_APDU *apdu = gdu->u.z3950;
1335         if (apdu->which == Z_APDU_initRequest)
1336         {
1337             mp::odr odr;
1338
1339             package.response() = odr.create_close(
1340                 apdu,
1341                 Z_Close_protocolError,
1342                 "double init");
1343
1344             package.session().close();
1345         }
1346         else if (apdu->which == Z_APDU_close)
1347         {
1348             mp::odr odr;
1349
1350             package.response() = odr.create_close(
1351                 apdu,
1352                 Z_Close_peerAbort, "received close from client");
1353             package.session().close();
1354         }
1355         else if (apdu->which == Z_APDU_searchRequest)
1356         {
1357             f->search(package, apdu);
1358         }
1359         else if (apdu->which == Z_APDU_presentRequest)
1360         {
1361             f->present(package, apdu);
1362         }
1363         else if (apdu->which == Z_APDU_scanRequest)
1364         {
1365             f->scan(package, apdu);
1366         }
1367         else
1368         {
1369             mp::odr odr;
1370
1371             package.response() = odr.create_close(
1372                 apdu, Z_Close_protocolError,
1373                 "unsupported APDU in filter_session_shared");
1374
1375             package.session().close();
1376         }
1377     }
1378     m_p->release_frontend(package);
1379 }
1380
1381 void yf::SessionShared::configure(const xmlNode *ptr, bool test_only,
1382                                   const char *path)
1383 {
1384     for (ptr = ptr->children; ptr; ptr = ptr->next)
1385     {
1386         if (ptr->type != XML_ELEMENT_NODE)
1387             continue;
1388         if (!strcmp((const char *) ptr->name, "resultset"))
1389         {
1390             const struct _xmlAttr *attr;
1391             for (attr = ptr->properties; attr; attr = attr->next)
1392             {
1393                 if (!strcmp((const char *) attr->name, "ttl"))
1394                     m_p->m_resultset_ttl =
1395                         mp::xml::get_int(attr->children, 30);
1396                 else if (!strcmp((const char *) attr->name, "max"))
1397                 {
1398                     m_p->m_resultset_max =
1399                         mp::xml::get_int(attr->children, 10);
1400                 }
1401                 else if (!strcmp((const char *) attr->name, "optimizesearch"))
1402                 {
1403                     m_p->m_optimize_search =
1404                         mp::xml::get_bool(attr->children, true);
1405                 }
1406                 else if (!strcmp((const char *) attr->name, "restart"))
1407                 {
1408                     m_p->m_restart = mp::xml::get_bool(attr->children, true);
1409                 }
1410                 else
1411                     throw mp::filter::FilterException(
1412                         "Bad attribute " + std::string((const char *)
1413                                                        attr->name));
1414             }
1415         }
1416         else if (!strcmp((const char *) ptr->name, "session"))
1417         {
1418             const struct _xmlAttr *attr;
1419             for (attr = ptr->properties; attr; attr = attr->next)
1420             {
1421                 if (!strcmp((const char *) attr->name, "ttl"))
1422                     m_p->m_session_ttl =
1423                         mp::xml::get_int(attr->children, 90);
1424                 else if (!strcmp((const char *) attr->name, "max"))
1425                     m_p->m_session_max =
1426                         mp::xml::get_int(attr->children, 100);
1427                 else
1428                     throw mp::filter::FilterException(
1429                         "Bad attribute " + std::string((const char *)
1430                                                        attr->name));
1431             }
1432         }
1433         else if (!strcmp((const char *) ptr->name, "init"))
1434         {
1435             const struct _xmlAttr *attr;
1436             for (attr = ptr->properties; attr; attr = attr->next)
1437             {
1438                 if (!strcmp((const char *) attr->name, "maximum-record-size"))
1439                     m_p->m_maximumRecordSize =
1440                         mp::xml::get_int(attr->children, 0);
1441                 else if (!strcmp((const char *) attr->name,
1442                                  "preferred-message-size"))
1443                     m_p->m_preferredMessageSize =
1444                         mp::xml::get_int(attr->children, 0);
1445                 else
1446                     throw mp::filter::FilterException(
1447                         "Bad attribute " + std::string((const char *)
1448                                                        attr->name));
1449             }
1450         }
1451         else
1452         {
1453             throw mp::filter::FilterException("Bad element "
1454                                                + std::string((const char *)
1455                                                              ptr->name));
1456         }
1457     }
1458 }
1459
1460 static mp::filter::Base* filter_creator()
1461 {
1462     return new mp::filter::SessionShared;
1463 }
1464
1465 extern "C" {
1466     struct metaproxy_1_filter_struct metaproxy_1_filter_session_shared = {
1467         0,
1468         "session_shared",
1469         filter_creator
1470     };
1471 }
1472
1473 /*
1474  * Local variables:
1475  * c-basic-offset: 4
1476  * c-file-style: "Stroustrup"
1477  * indent-tabs-mode: nil
1478  * End:
1479  * vim: shiftwidth=4 tabstop=8 expandtab
1480  */
1481