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