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