Sort targets by order in config rather than alphabetically.
[metaproxy-moved-to-github.git] / src / filter_virt_db.cpp
1 /* $Id: filter_virt_db.cpp,v 1.53 2007-11-26 21:45:08 adam Exp $
2    Copyright (c) 2005-2007, Index Data.
3
4 This file is part of Metaproxy.
5
6 Metaproxy is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 2, or (at your option) any later
9 version.
10
11 Metaproxy is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Metaproxy; see the file LICENSE.  If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19 02111-1307, USA.
20  */
21 /* $Id: filter_virt_db.cpp,v 1.53 2007-11-26 21:45:08 adam Exp $
22    Copyright (c) 2005-2007, Index Data.
23
24    See the LICENSE file for details
25  */
26
27 #include "config.hpp"
28
29 #include "filter.hpp"
30 #include "package.hpp"
31
32 #include <boost/thread/mutex.hpp>
33 #include <boost/thread/condition.hpp>
34 #include <boost/shared_ptr.hpp>
35
36 #include "util.hpp"
37 #include "filter_virt_db.hpp"
38
39 #include <yaz/zgdu.h>
40 #include <yaz/otherinfo.h>
41 #include <yaz/diagbib1.h>
42
43 #include <map>
44 #include <iostream>
45
46 namespace mp = metaproxy_1;
47 namespace yf = mp::filter;
48
49 namespace metaproxy_1 {
50     namespace filter {
51
52         struct VirtualDB::Set {
53             Set(BackendPtr b, std::string setname);
54             Set();
55             ~Set();
56
57             BackendPtr m_backend;
58             std::string m_setname;
59         };
60         struct VirtualDB::Map {
61             Map(std::list<std::string> targets, std::string route);
62             Map();
63             std::list<std::string> m_targets;
64             std::string m_route;
65         };
66         struct VirtualDB::Backend {
67             mp::Session m_backend_session;
68             std::list<std::string> m_frontend_databases;
69             std::list<std::string> m_targets;
70             std::string m_route;
71             bool m_named_result_sets;
72             int m_number_of_sets;
73         };
74         struct VirtualDB::Frontend {
75             Frontend(Rep *rep);
76             ~Frontend();
77             mp::Session m_session;
78             bool m_is_virtual;
79             bool m_in_use;
80             yazpp_1::GDU m_init_gdu;
81             std::list<BackendPtr> m_backend_list;
82             std::map<std::string,VirtualDB::Set> m_sets;
83
84             void search(Package &package, Z_APDU *apdu);
85             void present(Package &package, Z_APDU *apdu);
86             void scan(Package &package, Z_APDU *apdu);
87
88             void close(Package &package);
89             typedef std::map<std::string,VirtualDB::Set>::iterator Sets_it;
90
91             void fixup_package(Package &p, BackendPtr b);
92             void fixup_npr_record(ODR odr, Z_NamePlusRecord *npr,
93                                   BackendPtr b);
94             void fixup_npr_records(ODR odr, Z_Records *records,
95                                    BackendPtr b);
96     
97             BackendPtr lookup_backend_from_databases(
98                 std::list<std::string> databases);
99             BackendPtr create_backend_from_databases(
100                 std::list<std::string> databases,
101                 int &error_code,
102                 std::string &failing_database);
103             
104             BackendPtr init_backend(std::list<std::string> database,
105                                     Package &package,
106                                     int &error_code, std::string &addinfo);
107             Rep *m_p;
108         };            
109         class VirtualDB::Rep {
110             friend class VirtualDB;
111             friend struct Frontend;
112             
113             FrontendPtr get_frontend(Package &package);
114             void release_frontend(Package &package);
115         private:
116             std::map<std::string, VirtualDB::Map>m_maps;
117             typedef std::map<std::string,VirtualDB::Set>::iterator Sets_it;
118             boost::mutex m_mutex;
119             boost::condition m_cond_session_ready;
120             std::map<mp::Session, FrontendPtr> m_clients;
121         };
122     }
123 }
124
125 yf::VirtualDB::BackendPtr yf::VirtualDB::Frontend::lookup_backend_from_databases(
126     std::list<std::string> databases)
127 {
128     std::list<BackendPtr>::const_iterator map_it;
129     map_it = m_backend_list.begin();
130     for (; map_it != m_backend_list.end(); map_it++)
131         if ((*map_it)->m_frontend_databases == databases)
132             return *map_it;
133     BackendPtr null;
134     return null;
135 }
136
137 yf::VirtualDB::BackendPtr yf::VirtualDB::Frontend::create_backend_from_databases(
138     std::list<std::string> databases, int &error_code, std::string &addinfo)
139 {
140     BackendPtr b(new Backend);
141     std::list<std::string>::const_iterator db_it = databases.begin();
142
143     b->m_number_of_sets = 0;
144     b->m_frontend_databases = databases;
145     b->m_named_result_sets = false;
146
147     bool first_route = true;
148
149     std::map<std::string,bool> targets_dedup;
150     for (; db_it != databases.end(); db_it++)
151     {
152         std::map<std::string, VirtualDB::Map>::iterator map_it;
153         map_it = m_p->m_maps.find(mp::util::database_name_normalize(*db_it));
154         if (map_it == m_p->m_maps.end())  // database not found
155         {
156             error_code = YAZ_BIB1_DATABASE_DOES_NOT_EXIST;
157             addinfo = *db_it;
158             BackendPtr ptr;
159             return ptr;
160         }
161         std::list<std::string>::const_iterator t_it =
162             map_it->second.m_targets.begin();
163         for (; t_it != map_it->second.m_targets.end(); t_it++) {
164             if (!targets_dedup[*t_it])
165             {
166                 targets_dedup[*t_it] = true;
167                 b->m_targets.push_back(*t_it);
168             }
169         }
170
171         // see if we have a route conflict.
172         if (!first_route && b->m_route != map_it->second.m_route)
173         {
174             // we have a conflict.. 
175             error_code =  YAZ_BIB1_COMBI_OF_SPECIFIED_DATABASES_UNSUPP;
176             BackendPtr ptr;
177             return ptr;
178         }
179         b->m_route = map_it->second.m_route;
180         first_route = false;
181     }
182     return b;
183 }
184
185 yf::VirtualDB::BackendPtr yf::VirtualDB::Frontend::init_backend(
186     std::list<std::string> databases, mp::Package &package,
187     int &error_code, std::string &addinfo)
188 {
189     BackendPtr b = create_backend_from_databases(databases, error_code,
190                                                  addinfo);
191     if (!b)
192         return b;
193     Package init_package(b->m_backend_session, package.origin());
194     init_package.copy_filter(package);
195
196     mp::odr odr;
197
198     Z_APDU *init_apdu = zget_APDU(odr, Z_APDU_initRequest);
199
200     mp::util::set_vhost_otherinfo(&init_apdu->u.initRequest->otherInfo, odr,
201                                    b->m_targets);
202     Z_InitRequest *req = init_apdu->u.initRequest;
203
204     // copy stuff from Frontend Init Request
205     Z_GDU *org_gdu = m_init_gdu.get();
206     Z_InitRequest *org_init = org_gdu->u.z3950->u.initRequest;
207
208     req->idAuthentication = org_init->idAuthentication;
209     req->implementationId = org_init->implementationId;
210     req->implementationName = org_init->implementationName;
211     req->implementationVersion = org_init->implementationVersion;
212
213     ODR_MASK_SET(req->options, Z_Options_search);
214     ODR_MASK_SET(req->options, Z_Options_present);
215     ODR_MASK_SET(req->options, Z_Options_namedResultSets);
216     ODR_MASK_SET(req->options, Z_Options_scan);
217
218     ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_1);
219     ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_2);
220     ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_3);
221
222     init_package.request() = init_apdu;
223     
224     init_package.move(b->m_route);  // sending init 
225
226     Z_GDU *gdu = init_package.response().get();
227     // we hope to get an init response
228     if (gdu && gdu->which == Z_GDU_Z3950 && gdu->u.z3950->which ==
229         Z_APDU_initResponse)
230     {
231         Z_InitResponse *res = gdu->u.z3950->u.initResponse;
232         if (ODR_MASK_GET(res->options, Z_Options_namedResultSets))
233         {
234             b->m_named_result_sets = true;
235         }
236         if (!*res->result)
237         {
238             error_code = YAZ_BIB1_DATABASE_UNAVAILABLE;
239             mp::util::get_init_diagnostics(res, error_code, addinfo);
240             BackendPtr null;
241             return null; 
242         }
243     }
244     else
245     {
246         error_code = YAZ_BIB1_DATABASE_UNAVAILABLE;
247         // addinfo = database;
248         BackendPtr null;
249         return null;
250     }        
251     if (init_package.session().is_closed())
252     {
253         error_code = YAZ_BIB1_DATABASE_UNAVAILABLE;
254         // addinfo = database;
255         BackendPtr null;
256         return null;
257     }
258
259     m_backend_list.push_back(b);
260     return b;
261 }
262
263 void yf::VirtualDB::Frontend::search(mp::Package &package, Z_APDU *apdu_req)
264 {
265     Z_SearchRequest *req = apdu_req->u.searchRequest;
266     std::string vhost;
267     std::string resultSetId = req->resultSetName;
268     mp::odr odr;
269
270     std::list<std::string> databases;
271     int i;
272     for (i = 0; i<req->num_databaseNames; i++)
273         databases.push_back(req->databaseNames[i]);
274
275     BackendPtr b; // null for now
276     Sets_it sets_it = m_sets.find(req->resultSetName);
277     if (sets_it != m_sets.end())
278     {
279         // result set already exist 
280         // if replace indicator is off: we return diagnostic if
281         // result set already exist.
282         if (*req->replaceIndicator == 0)
283         {
284             Z_APDU *apdu = 
285                 odr.create_searchResponse(
286                     apdu_req,
287                     YAZ_BIB1_RESULT_SET_EXISTS_AND_REPLACE_INDICATOR_OFF,
288                     0);
289             package.response() = apdu;
290             
291             return;
292         } 
293         sets_it->second.m_backend->m_number_of_sets--;
294
295         // pick up any existing backend with a database match
296         std::list<BackendPtr>::const_iterator map_it;
297         map_it = m_backend_list.begin();
298         for (; map_it != m_backend_list.end(); map_it++)
299         {
300             BackendPtr tmp = *map_it;
301             if (tmp->m_frontend_databases == databases)
302                 break;
303         }
304         if (map_it != m_backend_list.end()) 
305             b = *map_it;
306     }
307     else
308     {
309         // new result set.
310
311         // pick up any existing database with named result sets ..
312         // or one which has no result sets.. yet.
313         std::list<BackendPtr>::const_iterator map_it;
314         map_it = m_backend_list.begin();
315         for (; map_it != m_backend_list.end(); map_it++)
316         {
317             BackendPtr tmp = *map_it;
318             if (tmp->m_frontend_databases == databases &&
319                 (tmp->m_named_result_sets ||
320                  tmp->m_number_of_sets == 0))
321                 break;
322         }
323         if (map_it != m_backend_list.end()) 
324             b = *map_it;
325     }
326     if (!b)  // no backend yet. Must create a new one
327     {
328         int error_code;
329         std::string addinfo;
330         b = init_backend(databases, package, error_code, addinfo);
331         if (!b)
332         {
333             // did not get a backend (unavailable somehow?)
334             
335             Z_APDU *apdu = 
336                 odr.create_searchResponse(
337                     apdu_req, error_code, addinfo.c_str());
338             package.response() = apdu;
339             return;
340         }
341     }
342     m_sets.erase(req->resultSetName);
343     // sending search to backend
344     Package search_package(b->m_backend_session, package.origin());
345
346     search_package.copy_filter(package);
347
348     std::string backend_setname;
349     if (b->m_named_result_sets)
350     {
351         backend_setname = std::string(req->resultSetName);
352     }
353     else
354     {
355         backend_setname = "default";
356         req->resultSetName = odr_strdup(odr, backend_setname.c_str());
357     }
358
359     // pick first targets spec and move the databases from it ..
360     std::list<std::string>::const_iterator t_it = b->m_targets.begin();
361     if (t_it != b->m_targets.end())
362     {
363         mp::util::set_databases_from_zurl(odr, *t_it,
364                                                 &req->num_databaseNames,
365                                                 &req->databaseNames);
366     }
367
368     *req->replaceIndicator = 1;
369
370     search_package.request() = yazpp_1::GDU(apdu_req);
371     
372     search_package.move(b->m_route);
373
374     if (search_package.session().is_closed())
375     {
376         package.response() = search_package.response();
377         package.session().close();
378         return;
379     }
380     b->m_number_of_sets++;
381
382     m_sets[resultSetId] = VirtualDB::Set(b, backend_setname);
383     fixup_package(search_package, b);
384     package.response() = search_package.response();
385 }
386
387 yf::VirtualDB::Frontend::Frontend(Rep *rep)
388 {
389     m_p = rep;
390     m_is_virtual = false;
391 }
392
393 void yf::VirtualDB::Frontend::close(mp::Package &package)
394 {
395     std::list<BackendPtr>::const_iterator b_it;
396     
397     for (b_it = m_backend_list.begin(); b_it != m_backend_list.end(); b_it++)
398     {
399         (*b_it)->m_backend_session.close();
400         Package close_package((*b_it)->m_backend_session, package.origin());
401         close_package.copy_filter(package);
402         close_package.move((*b_it)->m_route);
403     }
404     m_backend_list.clear();
405 }
406
407 yf::VirtualDB::Frontend::~Frontend()
408 {
409 }
410
411 yf::VirtualDB::FrontendPtr yf::VirtualDB::Rep::get_frontend(mp::Package &package)
412 {
413     boost::mutex::scoped_lock lock(m_mutex);
414
415     std::map<mp::Session,yf::VirtualDB::FrontendPtr>::iterator it;
416     
417     while(true)
418     {
419         it = m_clients.find(package.session());
420         if (it == m_clients.end())
421             break;
422         
423         if (!it->second->m_in_use)
424         {
425             it->second->m_in_use = true;
426             return it->second;
427         }
428         m_cond_session_ready.wait(lock);
429     }
430     FrontendPtr f(new Frontend(this));
431     m_clients[package.session()] = f;
432     f->m_in_use = true;
433     return f;
434 }
435
436 void yf::VirtualDB::Rep::release_frontend(mp::Package &package)
437 {
438     boost::mutex::scoped_lock lock(m_mutex);
439     std::map<mp::Session,yf::VirtualDB::FrontendPtr>::iterator it;
440     
441     it = m_clients.find(package.session());
442     if (it != m_clients.end())
443     {
444         if (package.session().is_closed())
445         {
446             it->second->close(package);
447             m_clients.erase(it);
448         }
449         else
450         {
451             it->second->m_in_use = false;
452         }
453         m_cond_session_ready.notify_all();
454     }
455 }
456
457 yf::VirtualDB::Set::Set(BackendPtr b, std::string setname)
458     :  m_backend(b), m_setname(setname)
459 {
460 }
461
462
463 yf::VirtualDB::Set::Set()
464 {
465 }
466
467
468 yf::VirtualDB::Set::~Set()
469 {
470 }
471
472 yf::VirtualDB::Map::Map(std::list<std::string> targets, std::string route)
473     : m_targets(targets), m_route(route) 
474 {
475 }
476
477 yf::VirtualDB::Map::Map()
478 {
479 }
480
481 yf::VirtualDB::VirtualDB() : m_p(new VirtualDB::Rep)
482 {
483 }
484
485 yf::VirtualDB::~VirtualDB() {
486 }
487
488 void yf::VirtualDB::Frontend::fixup_npr_record(ODR odr, Z_NamePlusRecord *npr,
489                                              BackendPtr b)
490 {
491     if (npr->databaseName)
492     {
493         std::string b_database = std::string(npr->databaseName);
494
495         // consider each of the frontend databases..
496         std::list<std::string>::const_iterator db_it;
497         for (db_it = b->m_frontend_databases.begin(); 
498              db_it != b->m_frontend_databases.end(); db_it++)
499         {
500             // see which target it corresponds to.. (if any)
501             std::map<std::string,VirtualDB::Map>::const_iterator map_it;
502
503             map_it = m_p->m_maps.find(mp::util::database_name_normalize(*db_it));
504             if (map_it != m_p->m_maps.end())
505             { 
506                 VirtualDB::Map m = map_it->second;
507                 
508                 std::list<std::string>::const_iterator t;
509                 for (t = m.m_targets.begin(); t != m.m_targets.end(); t++)
510                 {
511                     if (*t == b_database)
512                     {
513                         npr->databaseName = odr_strdup(odr, (*db_it).c_str());
514                         return;
515                     }
516                 }
517             }
518             
519         }
520         db_it = b->m_frontend_databases.begin();
521         if (db_it != b->m_frontend_databases.end())
522         {
523             std::string database = *db_it;
524             npr->databaseName = odr_strdup(odr, database.c_str());
525         }
526     }
527 }
528
529 void yf::VirtualDB::Frontend::fixup_npr_records(ODR odr, Z_Records *records,
530                                               BackendPtr b)
531 {
532     if (records && records->which == Z_Records_DBOSD)
533     {
534         Z_NamePlusRecordList *nprlist = records->u.databaseOrSurDiagnostics;
535         int i;
536         for (i = 0; i < nprlist->num_records; i++)
537         {
538             fixup_npr_record(odr, nprlist->records[i], b);
539         }
540     }
541 }
542
543 void yf::VirtualDB::Frontend::fixup_package(mp::Package &p, BackendPtr b)
544 {
545     Z_GDU *gdu = p.response().get();
546     mp::odr odr;
547
548     if (gdu && gdu->which == Z_GDU_Z3950)
549     {
550         Z_APDU *apdu = gdu->u.z3950;
551         if (apdu->which == Z_APDU_presentResponse)
552         {
553             fixup_npr_records(odr, apdu->u.presentResponse->records, b);
554             p.response() = gdu;
555         }
556         else if (apdu->which == Z_APDU_searchResponse)
557         {
558             fixup_npr_records(odr,  apdu->u.searchResponse->records, b);
559             p.response() = gdu;
560         }
561     }
562 }
563
564 void yf::VirtualDB::Frontend::present(mp::Package &package, Z_APDU *apdu_req)
565 {
566     Z_PresentRequest *req = apdu_req->u.presentRequest;
567     std::string resultSetId = req->resultSetId;
568     mp::odr odr;
569
570     Sets_it sets_it = m_sets.find(resultSetId);
571     if (sets_it == m_sets.end())
572     {
573         Z_APDU *apdu = 
574             odr.create_presentResponse(
575                 apdu_req,
576                 YAZ_BIB1_SPECIFIED_RESULT_SET_DOES_NOT_EXIST,
577                 resultSetId.c_str());
578         package.response() = apdu;
579         return;
580     }
581     Session *id =
582         new mp::Session(sets_it->second.m_backend->m_backend_session);
583     
584     // sending present to backend
585     Package present_package(*id, package.origin());
586     present_package.copy_filter(package);
587
588     req->resultSetId = odr_strdup(odr, sets_it->second.m_setname.c_str());
589     
590     present_package.request() = yazpp_1::GDU(apdu_req);
591
592     present_package.move(sets_it->second.m_backend->m_route);
593
594     fixup_package(present_package, sets_it->second.m_backend);
595
596     if (present_package.session().is_closed())
597     {
598         package.response() = present_package.response();
599         package.session().close();
600         return;
601     }
602     else
603     {
604         package.response() = present_package.response();
605     }
606     delete id;
607 }
608
609 void yf::VirtualDB::Frontend::scan(mp::Package &package, Z_APDU *apdu_req)
610 {
611     Z_ScanRequest *req = apdu_req->u.scanRequest;
612     std::string vhost;
613     mp::odr odr;
614
615     std::list<std::string> databases;
616     int i;
617     for (i = 0; i<req->num_databaseNames; i++)
618         databases.push_back(req->databaseNames[i]);
619
620     BackendPtr b;
621     // pick up any existing backend with a database match
622     std::list<BackendPtr>::const_iterator map_it;
623     map_it = m_backend_list.begin();
624     for (; map_it != m_backend_list.end(); map_it++)
625     {
626         BackendPtr tmp = *map_it;
627         if (tmp->m_frontend_databases == databases)
628             break;
629     }
630     if (map_it != m_backend_list.end()) 
631         b = *map_it;
632     if (!b)  // no backend yet. Must create a new one
633     {
634         int error_code;
635         std::string addinfo;
636         b = init_backend(databases, package, error_code, addinfo);
637         if (!b)
638         {
639             // did not get a backend (unavailable somehow?)
640             Z_APDU *apdu =
641                 odr.create_scanResponse(
642                     apdu_req, error_code, addinfo.c_str());
643             package.response() = apdu;
644             
645             return;
646         }
647     }
648     // sending scan to backend
649     Package scan_package(b->m_backend_session, package.origin());
650
651     scan_package.copy_filter(package);
652
653     // pick first targets spec and move the databases from it ..
654     std::list<std::string>::const_iterator t_it = b->m_targets.begin();
655     if (t_it != b->m_targets.end())
656     {
657         mp::util::set_databases_from_zurl(odr, *t_it,
658                                                 &req->num_databaseNames,
659                                                 &req->databaseNames);
660     }
661     scan_package.request() = yazpp_1::GDU(apdu_req);
662     
663     scan_package.move(b->m_route);
664
665     if (scan_package.session().is_closed())
666     {
667         package.response() = scan_package.response();
668         package.session().close();
669         return;
670     }
671     package.response() = scan_package.response();
672 }
673
674
675 void yf::VirtualDB::add_map_db2targets(std::string db, 
676                                      std::list<std::string> targets,
677                                      std::string route)
678 {
679     m_p->m_maps[mp::util::database_name_normalize(db)] 
680         = VirtualDB::Map(targets, route);
681 }
682
683
684 void yf::VirtualDB::add_map_db2target(std::string db, 
685                                     std::string target,
686                                     std::string route)
687 {
688     std::list<std::string> targets;
689     targets.push_back(target);
690
691     m_p->m_maps[mp::util::database_name_normalize(db)]
692         = VirtualDB::Map(targets, route);
693 }
694
695 void yf::VirtualDB::process(mp::Package &package) const
696 {
697     FrontendPtr f = m_p->get_frontend(package);
698
699     Z_GDU *gdu = package.request().get();
700     
701     if (gdu && gdu->which == Z_GDU_Z3950 && gdu->u.z3950->which ==
702         Z_APDU_initRequest && !f->m_is_virtual)
703     {
704         Z_InitRequest *req = gdu->u.z3950->u.initRequest;
705         
706         std::list<std::string> vhosts;
707         mp::util::get_vhost_otherinfo(req->otherInfo, vhosts);
708         if (vhosts.size() == 0)
709         {
710             f->m_init_gdu = gdu;
711             
712             mp::odr odr;
713             Z_APDU *apdu = odr.create_initResponse(gdu->u.z3950, 0, 0);
714             Z_InitResponse *resp = apdu->u.initResponse;
715             
716             int i;
717             static const int masks[] = {
718                 Z_Options_search,
719                 Z_Options_present,
720                 Z_Options_namedResultSets,
721                 Z_Options_scan,
722                 -1 
723             };
724             for (i = 0; masks[i] != -1; i++)
725                 if (ODR_MASK_GET(req->options, masks[i]))
726                     ODR_MASK_SET(resp->options, masks[i]);
727             
728             static const int versions[] = {
729                 Z_ProtocolVersion_1,
730                 Z_ProtocolVersion_2,
731                 Z_ProtocolVersion_3,
732                 -1
733             };
734             for (i = 0; versions[i] != -1; i++)
735                 if (ODR_MASK_GET(req->protocolVersion, versions[i]))
736                     ODR_MASK_SET(resp->protocolVersion, versions[i]);
737                 else
738                     break;
739             
740             package.response() = apdu;
741             f->m_is_virtual = true;
742         }
743         else
744             package.move();
745     }
746     else if (!f->m_is_virtual)
747         package.move();
748     else if (gdu && gdu->which == Z_GDU_Z3950)
749     {
750         Z_APDU *apdu = gdu->u.z3950;
751         if (apdu->which == Z_APDU_initRequest)
752         {
753             mp::odr odr;
754             
755             package.response() = odr.create_close(
756                 apdu,
757                 Z_Close_protocolError,
758                 "double init");
759             
760             package.session().close();
761         }
762         else if (apdu->which == Z_APDU_searchRequest)
763         {
764             f->search(package, apdu);
765         }
766         else if (apdu->which == Z_APDU_presentRequest)
767         {
768             f->present(package, apdu);
769         }
770         else if (apdu->which == Z_APDU_scanRequest)
771         {
772             f->scan(package, apdu);
773         }
774         else if (apdu->which == Z_APDU_close)
775         {
776             package.session().close();
777         }
778         else
779         {
780             mp::odr odr;
781             
782             package.response() = odr.create_close(
783                 apdu, Z_Close_protocolError,
784                 "unsupported APDU in filter_virt_db");
785             
786             package.session().close();
787         }
788     }
789     m_p->release_frontend(package);
790 }
791
792
793 void mp::filter::VirtualDB::configure(const xmlNode * ptr)
794 {
795     for (ptr = ptr->children; ptr; ptr = ptr->next)
796     {
797         if (ptr->type != XML_ELEMENT_NODE)
798             continue;
799         if (!strcmp((const char *) ptr->name, "virtual"))
800         {
801             std::string database;
802             std::list<std::string> targets;
803             xmlNode *v_node = ptr->children;
804             for (; v_node; v_node = v_node->next)
805             {
806                 if (v_node->type != XML_ELEMENT_NODE)
807                     continue;
808                 
809                 if (mp::xml::is_element_mp(v_node, "database"))
810                     database = mp::xml::get_text(v_node);
811                 else if (mp::xml::is_element_mp(v_node, "target"))
812                     targets.push_back(mp::xml::get_text(v_node));
813                 else
814                     throw mp::filter::FilterException
815                         ("Bad element " 
816                          + std::string((const char *) v_node->name)
817                          + " in virtual section"
818                             );
819             }
820             std::string route = mp::xml::get_route(ptr);
821             add_map_db2targets(database, targets, route);
822         }
823         else
824         {
825             throw mp::filter::FilterException
826                 ("Bad element " 
827                  + std::string((const char *) ptr->name)
828                  + " in virt_db filter");
829         }
830     }
831 }
832
833 static mp::filter::Base* filter_creator()
834 {
835     return new mp::filter::VirtualDB;
836 }
837
838 extern "C" {
839     struct metaproxy_1_filter_struct metaproxy_1_filter_virt_db = {
840         0,
841         "virt_db",
842         filter_creator
843     };
844 }
845
846
847 /*
848  * Local variables:
849  * c-basic-offset: 4
850  * indent-tabs-mode: nil
851  * c-file-style: "stroustrup"
852  * End:
853  * vim: shiftwidth=4 tabstop=8 expandtab
854  */