Allow multi filter to do re-routing based on virtual target, i.e.
[metaproxy-moved-to-github.git] / src / filter_multi.cpp
1 /* $Id: filter_multi.cpp,v 1.7 2006-01-18 09:20:30 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             std::map<std::string,std::string> m_target_route;
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 = m_p->m_target_route[*t_it];
305         // b->m_route unset
306         b->m_package = PackagePtr(new Package(s, package.origin()));
307
308         m_backend_list.push_back(BackendPtr(b));
309     }
310     m_is_multi = true;
311
312     // create init request 
313     std::list<BackendPtr>::const_iterator bit;
314     for (bit = m_backend_list.begin(); bit != m_backend_list.end(); bit++)
315     {
316         yp2::odr odr;
317         BackendPtr b = *bit;
318         Z_APDU *init_apdu = zget_APDU(odr, Z_APDU_initRequest);
319         
320         yaz_oi_set_string_oidval(&init_apdu->u.initRequest->otherInfo, odr,
321                                  VAL_PROXY, 1, b->m_vhost.c_str());
322         
323         Z_InitRequest *req = init_apdu->u.initRequest;
324         
325         ODR_MASK_SET(req->options, Z_Options_search);
326         ODR_MASK_SET(req->options, Z_Options_present);
327         ODR_MASK_SET(req->options, Z_Options_namedResultSets);
328         ODR_MASK_SET(req->options, Z_Options_scan);
329         
330         ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_1);
331         ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_2);
332         ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_3);
333         
334         b->m_package->request() = init_apdu;
335
336         b->m_package->copy_filter(package);
337     }
338     multi_move(m_backend_list);
339
340     // create the frontend init response based on each backend init response
341     yp2::odr odr;
342
343     Z_APDU *f_apdu = odr.create_initResponse(gdu->u.z3950, 0, 0);
344     Z_InitResponse *f_resp = f_apdu->u.initResponse;
345
346     ODR_MASK_SET(f_resp->options, Z_Options_search);
347     ODR_MASK_SET(f_resp->options, Z_Options_present);
348     ODR_MASK_SET(f_resp->options, Z_Options_namedResultSets);
349     
350     ODR_MASK_SET(f_resp->protocolVersion, Z_ProtocolVersion_1);
351     ODR_MASK_SET(f_resp->protocolVersion, Z_ProtocolVersion_2);
352     ODR_MASK_SET(f_resp->protocolVersion, Z_ProtocolVersion_3);
353
354     for (bit = m_backend_list.begin(); bit != m_backend_list.end(); bit++)
355     {
356         PackagePtr p = (*bit)->m_package;
357         
358         if (p->session().is_closed()) // if any backend closes, close frontend
359             package.session().close();
360         Z_GDU *gdu = p->response().get();
361         if (gdu && gdu->which == Z_GDU_Z3950 && gdu->u.z3950->which ==
362             Z_APDU_initResponse)
363         {
364             int i;
365             Z_APDU *b_apdu = gdu->u.z3950;
366             Z_InitResponse *b_resp = b_apdu->u.initResponse;
367
368             // common options for all backends
369             for (i = 0; i <= Z_Options_stringSchema; i++)
370             {
371                 if (!ODR_MASK_GET(b_resp->options, i))
372                     ODR_MASK_CLEAR(f_resp->options, i);
373             }
374             // common protocol version
375             for (i = 0; i <= Z_ProtocolVersion_3; i++)
376                 if (!ODR_MASK_GET(b_resp->protocolVersion, i))
377                     ODR_MASK_CLEAR(f_resp->protocolVersion, i);
378             // reject if any of the backends reject
379             if (!*b_resp->result)
380                 *f_resp->result = 0;
381         }
382         else
383         {
384             // if any target does not return init return that (close or
385             // similar )
386             package.response() = p->response();
387             return;
388         }
389     }
390     package.response() = f_apdu;
391 }
392
393 void yf::Multi::Frontend::search(Package &package, Z_APDU *apdu_req)
394 {
395     // create search request 
396     Z_SearchRequest *req = apdu_req->u.searchRequest;
397
398     // save these for later
399     int smallSetUpperBound = *req->smallSetUpperBound;
400     int largeSetLowerBound = *req->largeSetLowerBound;
401     int mediumSetPresentNumber = *req->mediumSetPresentNumber;
402     
403     // they are altered now - to disable piggyback
404     *req->smallSetUpperBound = 0;
405     *req->largeSetLowerBound = 1;
406     *req->mediumSetPresentNumber = 1;
407
408     int default_num_db = req->num_databaseNames;
409     char **default_db = req->databaseNames;
410
411     std::list<BackendPtr>::const_iterator bit;
412     for (bit = m_backend_list.begin(); bit != m_backend_list.end(); bit++)
413     {
414         PackagePtr p = (*bit)->m_package;
415         yp2::odr odr;
416     
417         if (!yp2::util::set_databases_from_zurl(odr, (*bit)->m_vhost,
418                                                 &req->num_databaseNames,
419                                                 &req->databaseNames))
420         {
421             req->num_databaseNames = default_num_db;
422             req->databaseNames = default_db;
423         }
424         p->request() = apdu_req;
425         p->copy_filter(package);
426     }
427     multi_move(m_backend_list);
428
429     // look at each response
430     FrontendSet resultSet(std::string(req->resultSetName));
431
432     int result_set_size = 0;
433     Z_Records *z_records_diag = 0;  // no diagnostics (yet)
434     for (bit = m_backend_list.begin(); bit != m_backend_list.end(); bit++)
435     {
436         PackagePtr p = (*bit)->m_package;
437         
438         if (p->session().is_closed()) // if any backend closes, close frontend
439             package.session().close();
440         
441         Z_GDU *gdu = p->response().get();
442         if (gdu && gdu->which == Z_GDU_Z3950 && gdu->u.z3950->which ==
443             Z_APDU_searchResponse)
444         {
445             Z_APDU *b_apdu = gdu->u.z3950;
446             Z_SearchResponse *b_resp = b_apdu->u.searchResponse;
447          
448             // see we get any errors (AKA diagnstics)
449             if (b_resp->records)
450             {
451                 if (b_resp->records->which == Z_Records_NSD
452                     || b_resp->records->which == Z_Records_multipleNSD)
453                     z_records_diag = b_resp->records;
454                 // we may set this multiple times (TOO BAD!)
455             }
456             BackendSet backendSet;
457             backendSet.m_backend = *bit;
458             backendSet.m_count = *b_resp->resultCount;
459             result_set_size += *b_resp->resultCount;
460             resultSet.m_backend_sets.push_back(backendSet);
461         }
462         else
463         {
464             // if any target does not return search response - return that 
465             package.response() = p->response();
466             return;
467         }
468     }
469
470     yp2::odr odr;
471     Z_APDU *f_apdu = odr.create_searchResponse(apdu_req, 0, 0);
472     Z_SearchResponse *f_resp = f_apdu->u.searchResponse;
473
474     *f_resp->resultCount = result_set_size;
475     if (z_records_diag)
476     {
477         // search error
478         f_resp->records = z_records_diag;
479         package.response() = f_apdu;
480         return;
481     }
482     // assume OK
483     m_sets[resultSet.m_setname] = resultSet;
484
485     int number;
486     yp2::util::piggyback(smallSetUpperBound,
487                          largeSetLowerBound,
488                          mediumSetPresentNumber,
489                          result_set_size,
490                          number);
491     Package pp(package.session(), package.origin());
492     if (number > 0)
493     {
494         pp.copy_filter(package);
495         Z_APDU *p_apdu = zget_APDU(odr, Z_APDU_presentRequest);
496         Z_PresentRequest *p_req = p_apdu->u.presentRequest;
497         p_req->preferredRecordSyntax = req->preferredRecordSyntax;
498         p_req->resultSetId = req->resultSetName;
499         *p_req->resultSetStartPoint = 1;
500         *p_req->numberOfRecordsRequested = number;
501         pp.request() = p_apdu;
502         present(pp, p_apdu);
503         
504         if (pp.session().is_closed())
505             package.session().close();
506         
507         Z_GDU *gdu = pp.response().get();
508         if (gdu && gdu->which == Z_GDU_Z3950 && gdu->u.z3950->which ==
509             Z_APDU_presentResponse)
510         {
511             Z_PresentResponse *p_res = gdu->u.z3950->u.presentResponse;
512             f_resp->records = p_res->records;
513             *f_resp->numberOfRecordsReturned = 
514                 *p_res->numberOfRecordsReturned;
515             *f_resp->nextResultSetPosition = 
516                 *p_res->nextResultSetPosition;
517         }
518         else 
519         {
520             package.response() = pp.response(); 
521             return;
522         }
523     }
524     package.response() = f_apdu; // in this scope because of p
525 }
526
527 void yf::Multi::Frontend::present(Package &package, Z_APDU *apdu_req)
528 {
529     // create present request 
530     Z_PresentRequest *req = apdu_req->u.presentRequest;
531
532     Sets_it it;
533     it = m_sets.find(std::string(req->resultSetId));
534     if (it == m_sets.end())
535     {
536         yp2::odr odr;
537         Z_APDU *apdu = 
538             odr.create_presentResponse(
539                 apdu_req,
540                 YAZ_BIB1_SPECIFIED_RESULT_SET_DOES_NOT_EXIST,
541                 req->resultSetId);
542         package.response() = apdu;
543         return;
544     }
545     std::list<Multi::FrontendSet::PresentJob> jobs;
546     int start = *req->resultSetStartPoint;
547     int number = *req->numberOfRecordsRequested;
548     it->second.round_robin(start, number, jobs);
549
550     std::list<BackendPtr> present_backend_list;
551
552     std::list<BackendSet>::const_iterator bsit;
553     bsit = it->second.m_backend_sets.begin();
554     for (; bsit != it->second.m_backend_sets.end(); bsit++)
555     {
556         std::list<Multi::FrontendSet::PresentJob>::const_iterator jit;
557         int start = -1;
558         int end = -1;
559         
560         for (jit = jobs.begin(); jit != jobs.end(); jit++)
561         {
562             if (jit->m_backend == bsit->m_backend)
563             {
564                 if (start == -1 || jit->m_pos < start)
565                     start = jit->m_pos;
566                 if (end == -1 || jit->m_pos > end)
567                     end = jit->m_pos;
568             }
569         }
570         if (start != -1)
571         {
572             PackagePtr p = bsit->m_backend->m_package;
573
574             *req->resultSetStartPoint = start;
575             *req->numberOfRecordsRequested = end - start + 1;
576             
577             p->request() = apdu_req;
578             p->copy_filter(package);
579
580             present_backend_list.push_back(bsit->m_backend);
581         }
582     }
583     multi_move(present_backend_list);
584
585     // look at each response
586     Z_Records *z_records_diag = 0;
587
588     std::list<BackendPtr>::const_iterator pbit = present_backend_list.begin();
589     for (; pbit != present_backend_list.end(); pbit++)
590     {
591         PackagePtr p = (*pbit)->m_package;
592         
593         if (p->session().is_closed()) // if any backend closes, close frontend
594             package.session().close();
595         
596         Z_GDU *gdu = p->response().get();
597         if (gdu && gdu->which == Z_GDU_Z3950 && gdu->u.z3950->which ==
598             Z_APDU_presentResponse)
599         {
600             Z_APDU *b_apdu = gdu->u.z3950;
601             Z_PresentResponse *b_resp = b_apdu->u.presentResponse;
602          
603             // see we get any errors (AKA diagnstics)
604             if (b_resp->records)
605             {
606                 if (b_resp->records->which != Z_Records_DBOSD)
607                     z_records_diag = b_resp->records;
608                 // we may set this multiple times (TOO BAD!)
609             }
610         }
611         else
612         {
613             // if any target does not return present response - return that 
614             package.response() = p->response();
615             return;
616         }
617     }
618
619     yp2::odr odr;
620     Z_APDU *f_apdu = odr.create_presentResponse(apdu_req, 0, 0);
621     Z_PresentResponse *f_resp = f_apdu->u.presentResponse;
622
623     if (z_records_diag)
624     {
625         f_resp->records = z_records_diag;
626         *f_resp->presentStatus = Z_PresentStatus_failure;
627     }
628     else
629     {
630         f_resp->records = (Z_Records *) odr_malloc(odr, sizeof(Z_Records));
631         Z_Records * records = f_resp->records;
632         records->which = Z_Records_DBOSD;
633         records->u.databaseOrSurDiagnostics =
634             (Z_NamePlusRecordList *)
635             odr_malloc(odr, sizeof(Z_NamePlusRecordList));
636         Z_NamePlusRecordList *nprl = records->u.databaseOrSurDiagnostics;
637         nprl->num_records = jobs.size();
638         nprl->records = (Z_NamePlusRecord**)
639             odr_malloc(odr, sizeof(Z_NamePlusRecord *) * nprl->num_records);
640         int i = 0;
641         std::list<Multi::FrontendSet::PresentJob>::const_iterator jit;
642         for (jit = jobs.begin(); jit != jobs.end(); jit++)
643         {
644             PackagePtr p = jit->m_backend->m_package;
645             
646             Z_GDU *gdu = p->response().get();
647             Z_APDU *b_apdu = gdu->u.z3950;
648             Z_PresentResponse *b_resp = b_apdu->u.presentResponse;
649
650             nprl->records[i++] =
651                 b_resp->records->u.databaseOrSurDiagnostics->
652                 records[jit->m_inside_pos];
653         }
654         *f_resp->nextResultSetPosition = start + i;
655         *f_resp->numberOfRecordsReturned = i;
656     }
657     package.response() = f_apdu;
658 }
659
660 void yf::Multi::Frontend::scan(Package &package, Z_APDU *apdu_req)
661 {
662     if (m_backend_list.size() > 1)
663     {
664         yp2::odr odr;
665         Z_APDU *f_apdu = 
666             odr.create_scanResponse(
667                 apdu_req, YAZ_BIB1_COMBI_OF_SPECIFIED_DATABASES_UNSUPP, 0);
668         package.response() = f_apdu;
669         return;
670     }
671     Z_ScanRequest *req = apdu_req->u.scanRequest;
672
673     int default_num_db = req->num_databaseNames;
674     char **default_db = req->databaseNames;
675
676     std::list<BackendPtr>::const_iterator bit;
677     for (bit = m_backend_list.begin(); bit != m_backend_list.end(); bit++)
678     {
679         PackagePtr p = (*bit)->m_package;
680         yp2::odr odr;
681     
682         if (!yp2::util::set_databases_from_zurl(odr, (*bit)->m_vhost,
683                                                 &req->num_databaseNames,
684                                                 &req->databaseNames))
685         {
686             req->num_databaseNames = default_num_db;
687             req->databaseNames = default_db;
688         }
689         p->request() = apdu_req;
690         p->copy_filter(package);
691     }
692     multi_move(m_backend_list);
693
694     for (bit = m_backend_list.begin(); bit != m_backend_list.end(); bit++)
695     {
696         PackagePtr p = (*bit)->m_package;
697         
698         if (p->session().is_closed()) // if any backend closes, close frontend
699             package.session().close();
700         
701         Z_GDU *gdu = p->response().get();
702         if (gdu && gdu->which == Z_GDU_Z3950 && gdu->u.z3950->which ==
703             Z_APDU_scanResponse)
704         {
705             package.response() = p->response();
706             break;
707         }
708         else
709         {
710             // if any target does not return scan response - return that 
711             package.response() = p->response();
712             return;
713         }
714     }
715 }
716
717 void yf::Multi::process(Package &package) const
718 {
719     FrontendPtr f = m_p->get_frontend(package);
720
721     Z_GDU *gdu = package.request().get();
722     
723     if (gdu && gdu->which == Z_GDU_Z3950 && gdu->u.z3950->which ==
724         Z_APDU_initRequest && !f->m_is_multi)
725     {
726         f->init(package, gdu);
727     }
728     else if (!f->m_is_multi)
729         package.move();
730     else if (gdu && gdu->which == Z_GDU_Z3950)
731     {
732         Z_APDU *apdu = gdu->u.z3950;
733         if (apdu->which == Z_APDU_initRequest)
734         {
735             yp2::odr odr;
736             
737             package.response() = odr.create_close(
738                 apdu,
739                 Z_Close_protocolError,
740                 "double init");
741             
742             package.session().close();
743         }
744         else if (apdu->which == Z_APDU_searchRequest)
745         {
746             f->search(package, apdu);
747         }
748         else if (apdu->which == Z_APDU_presentRequest)
749         {
750             f->present(package, apdu);
751         }
752         else if (apdu->which == Z_APDU_scanRequest)
753         {
754             f->scan(package, apdu);
755         }
756         else
757         {
758             yp2::odr odr;
759             
760             package.response() = odr.create_close(
761                 apdu, Z_Close_protocolError,
762                 "unsupported APDU in filter multi");
763             
764             package.session().close();
765         }
766     }
767     m_p->release_frontend(package);
768 }
769
770 void yp2::filter::Multi::configure(const xmlNode * ptr)
771 {
772     for (ptr = ptr->children; ptr; ptr = ptr->next)
773     {
774         if (ptr->type != XML_ELEMENT_NODE)
775             continue;
776         if (!strcmp((const char *) ptr->name, "target"))
777         {
778             std::string route = yp2::xml::get_route(ptr);
779             std::string target = yp2::xml::get_text(ptr);
780             std::cout << "route=" << route << " target=" << target << "\n";
781             m_p->m_target_route[target] = route;
782         }
783         else if (!strcmp((const char *) ptr->name, "virtual"))
784         {
785             std::list<std::string> targets;
786             std::string vhost;
787             xmlNode *v_node = ptr->children;
788             for (; v_node; v_node = v_node->next)
789             {
790                 if (v_node->type != XML_ELEMENT_NODE)
791                     continue;
792                 
793                 if (yp2::xml::is_element_yp2(v_node, "vhost"))
794                     vhost = yp2::xml::get_text(v_node);
795                 else if (yp2::xml::is_element_yp2(v_node, "target"))
796                     targets.push_back(yp2::xml::get_text(v_node));
797                 else
798                     throw yp2::filter::FilterException
799                         ("Bad element " 
800                          + std::string((const char *) v_node->name)
801                          + " in virtual section"
802                             );
803             }
804             std::string route = yp2::xml::get_route(ptr);
805             add_map_host2hosts(vhost, targets, route);
806             std::list<std::string>::const_iterator it;
807             for (it = targets.begin(); it != targets.end(); it++)
808             {
809                 std::cout << "Add " << vhost << "->" << *it
810                           << "," << route << "\n";
811             }
812         }
813         else
814         {
815             throw yp2::filter::FilterException
816                 ("Bad element " 
817                  + std::string((const char *) ptr->name)
818                  + " in virt_db filter");
819         }
820     }
821 }
822
823 static yp2::filter::Base* filter_creator()
824 {
825     return new yp2::filter::Multi;
826 }
827
828 extern "C" {
829     struct yp2_filter_struct yp2_filter_multi = {
830         0,
831         "multi",
832         filter_creator
833     };
834 }
835
836
837 /*
838  * Local variables:
839  * c-basic-offset: 4
840  * indent-tabs-mode: nil
841  * c-file-style: "stroustrup"
842  * End:
843  * vim: shiftwidth=4 tabstop=8 expandtab
844  */