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