Avoid naked ptr in filter configure MP-623
[metaproxy-moved-to-github.git] / src / router_flexml.cpp
1 /* This file is part of Metaproxy.
2    Copyright (C) Index Data
3
4 Metaproxy is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
7 version.
8
9 Metaproxy is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17 */
18
19 #include "config.hpp"
20 #include <metaproxy/xmlutil.hpp>
21 #include <string.h>
22 #include "router_flexml.hpp"
23 #include "factory_filter.hpp"
24 #include "factory_static.hpp"
25
26 #include <iostream>
27 #include <map>
28 #include <list>
29 #include <yaz/log.h>
30 #include <yaz/xml_include.h>
31
32 #include <boost/shared_ptr.hpp>
33
34 #include <libxml/xmlversion.h>
35 #include <libxml/parser.h>
36 #include <libxml/tree.h>
37
38 namespace mp = metaproxy_1;
39
40 namespace metaproxy_1 {
41     class RouterFleXML::Route {
42         friend class RouterFleXML::Rep;
43         friend class RouterFleXML::Pos;
44         friend class RouterFleXML;
45         std::list<boost::shared_ptr<const mp::filter::Base> > m_list;
46     };
47     class RouterFleXML::Rep {
48         friend class RouterFleXML;
49         friend class RouterFleXML::Pos;
50         Rep();
51
52         void base(xmlDocPtr doc, mp::FactoryFilter &factory, bool test_only,
53                   const char *file_include_path);
54
55         typedef std::map<std::string,
56                          boost::shared_ptr<const mp::filter::Base > >
57                          IdFilterMap ;
58
59         IdFilterMap m_id_filter_map;
60
61         std::map<std::string,RouterFleXML::Route> m_routes;
62
63         std::string m_start_route;
64
65         std::string m_dl_path;
66
67         void parse_xml_config_dom(xmlDocPtr doc, bool test_only,
68                                   const char *file_include_path);
69
70         void parse_xml_filters(xmlDocPtr doc, const xmlNode *node,
71                                bool test_only, const char *file_include_path);
72         void parse_xml_filters1(xmlDocPtr doc, const xmlNode *node,
73                                 bool test_only, const char *file_include_path,
74                                 Route &route);
75         void parse_xml_routes(xmlDocPtr doc, const xmlNode *node,
76                               bool test_only, const char *file_include_path);
77         void check_routes_in_filters(const xmlNode *n);
78
79         bool m_xinclude;
80     private:
81         FactoryFilter *m_factory; // TODO shared_ptr
82     };
83
84     class RouterFleXML::Pos : public RoutePos {
85     public:
86         virtual const filter::Base *move(const char *route);
87         virtual RoutePos *clone();
88         virtual ~Pos();
89         mp::RouterFleXML::Rep *m_p;
90
91         std::map<std::string,
92                  RouterFleXML::Route>::iterator m_route_it;
93         std::list<boost::shared_ptr <const mp::filter::Base> >::iterator m_filter_it;
94     };
95 }
96
97 void mp::RouterFleXML::Rep::parse_xml_filters(xmlDocPtr doc,
98                                               const xmlNode *node,
99                                               bool test_only,
100                                               const char *file_include_path)
101 {
102     unsigned int filter_nr = 0;
103     while (node && mp::xml::check_element_mp(node, "filter"))
104     {
105         filter_nr++;
106
107         const struct _xmlAttr *attr;
108         std::string id_value;
109         std::string type_value;
110         for (attr = node->properties; attr; attr = attr->next)
111         {
112             std::string name = std::string((const char *) attr->name);
113             std::string value;
114
115             if (attr->children && attr->children->type == XML_TEXT_NODE)
116                 value = std::string((const char *)attr->children->content);
117
118             if (name == "id")
119                 id_value = value;
120             else if (name == "type")
121                 type_value = value;
122             else if (name == "base")
123                 ;// Ignore XInclude base attribute.
124             else
125                 throw mp::XMLError("Only attribute id or type allowed"
126                                     " in filter element. Got " + name);
127         }
128
129         if (!m_factory->exist(type_value))
130         {
131             yaz_log(YLOG_LOG, "Loading %s (dlpath %s)",
132                     type_value.c_str(), m_dl_path.c_str());
133             m_factory->add_creator_dl(type_value, m_dl_path);
134         }
135         boost::shared_ptr<mp::filter::Base> fb(m_factory->create(type_value));
136
137         fb->configure(node, test_only, file_include_path);
138
139         if (m_id_filter_map.find(id_value) != m_id_filter_map.end())
140             throw mp::XMLError("Filter " + id_value + " already defined");
141
142         m_id_filter_map[id_value] = fb;
143
144         node = mp::xml::jump_to_next(node, XML_ELEMENT_NODE);
145     }
146 }
147
148 void mp::RouterFleXML::Rep::parse_xml_filters1(xmlDocPtr doc,
149                                               const xmlNode *node,
150                                               bool test_only,
151                                               const char *file_include_path,
152                                               Route &route)
153 {
154     while (node)
155     {
156         if (mp::xml::is_element_mp(node, "filters"))
157         {
158             const xmlNode* node1 =
159                 mp::xml::jump_to_children(node, XML_ELEMENT_NODE);
160
161             parse_xml_filters1(doc, node1, test_only, file_include_path, route);
162         }
163         else if (mp::xml::check_element_mp(node, "filter"))
164         {
165             const struct _xmlAttr *attr;
166             std::string refid_value;
167             std::string type_value;
168             for (attr = node->properties; attr; attr = attr->next)
169             {
170                 std::string name = std::string((const char *) attr->name);
171                 std::string value;
172
173                 if (attr->children && attr->children->type == XML_TEXT_NODE)
174                     value = std::string((const char *)attr->children->content);
175
176                 if (name == "refid")
177                     refid_value = value;
178                 else if (name == "type")
179                     type_value = value;
180                 else if (name == "base")
181                     ;// Ignore XInclude base attribute.
182                 else
183                     throw mp::XMLError("Only attribute 'refid' or 'type'"
184                                        " allowed for element 'filter'."
185                                        " Got " + name);
186             }
187             if (refid_value.length())
188             {
189                 std::map<std::string,
190                          boost::shared_ptr<const mp::filter::Base > >::iterator it;
191                 it = m_id_filter_map.find(refid_value);
192                 if (it == m_id_filter_map.end())
193                     throw mp::XMLError("Unknown filter refid "
194                                        + refid_value);
195                 else
196                     route.m_list.push_back(it->second);
197             }
198             else if (type_value.length())
199             {
200                 if (!m_factory->exist(type_value))
201                 {
202                     yaz_log(YLOG_LOG, "Loading %s (dlpath %s)",
203                             type_value.c_str(), m_dl_path.c_str());
204                     m_factory->add_creator_dl(type_value, m_dl_path);
205                 }
206                 boost::shared_ptr<mp::filter::Base>
207                      filter_base(m_factory->create(type_value));
208
209                 filter_base->configure(node, test_only, file_include_path);
210
211                 route.m_list.push_back(filter_base);
212             }
213
214         }
215         node = mp::xml::jump_to_next(node, XML_ELEMENT_NODE);
216     }
217 }
218
219
220 void mp::RouterFleXML::Rep::parse_xml_routes(xmlDocPtr doc,
221                                              const xmlNode *node,
222                                              bool test_only,
223                                              const char *file_include_path)
224 {
225     mp::xml::check_element_mp(node, "route");
226
227     unsigned int route_nr = 0;
228     while (mp::xml::is_element_mp(node, "route"))
229     {
230         route_nr++;
231
232         const struct _xmlAttr *attr;
233         std::string id_value;
234         for (attr = node->properties; attr; attr = attr->next)
235         {
236             std::string name = std::string((const char *) attr->name);
237             std::string value;
238
239             if (attr->children && attr->children->type == XML_TEXT_NODE)
240                 value = std::string((const char *)attr->children->content);
241
242             if (name == "id")
243                 id_value = value;
244             else if (name == "base")
245                 ;// Ignore XInclude base attribute.
246             else
247                 throw mp::XMLError("Only attribute 'id' allowed for"
248                                     " element 'route'."
249                                     " Got " + name);
250         }
251
252         Route route;
253
254         // process <filter> / <filters> nodes in third level
255         const xmlNode* node3 = mp::xml::jump_to_children(node, XML_ELEMENT_NODE);
256
257
258
259         parse_xml_filters1(doc, node3, test_only, file_include_path, route);
260
261         std::map<std::string,RouterFleXML::Route>::iterator it;
262         it = m_routes.find(id_value);
263         if (it != m_routes.end())
264             throw mp::XMLError("Route id='" + id_value
265                                 + "' already exist");
266         else
267             m_routes[id_value] = route;
268         node = mp::xml::jump_to_next(node, XML_ELEMENT_NODE);
269     }
270 }
271
272 void mp::RouterFleXML::Rep::check_routes_in_filters(const xmlNode *node)
273 {
274     while (node)
275     {
276         if (mp::xml::is_element_mp(node, "filters"))
277         {
278             const xmlNode *n =
279                 mp::xml::jump_to_children(node, XML_ELEMENT_NODE);
280             check_routes_in_filters(n);
281         }
282         else if (mp::xml::is_element_mp(node, "filter"))
283         {
284             const xmlNode *n =
285                 mp::xml::jump_to_children(node, XML_ELEMENT_NODE);
286             while (n)
287             {
288                 const struct _xmlAttr *attr;
289                 // we assume thar that route attribute is only at one level
290                 // below filter.. At least that works for multi and virt_db.
291                 for (attr = n->properties; attr; attr = attr->next)
292                 {
293                     if (!strcmp((const char *) attr->name, "route"))
294                     {
295                         std::string value;
296
297                         if (attr->children && attr->children->type == XML_TEXT_NODE)
298                             value = std::string((const char *)attr->children->content);
299
300                         std::map<std::string,RouterFleXML::Route>::iterator it;
301                         it = m_routes.find(value);
302                         if (it == m_routes.end())
303                         {
304                             throw mp::XMLError("Route '" + value + "' does not exist");
305                         }
306                     }
307                 }
308                 n = mp::xml::jump_to_next(n, XML_ELEMENT_NODE);
309             }
310         }
311         node = mp::xml::jump_to_next(node, XML_ELEMENT_NODE);
312     }
313 }
314
315 void mp::RouterFleXML::Rep::parse_xml_config_dom(xmlDocPtr doc,
316                                                  bool test_only,
317                                                  const char *file_include_path)
318 {
319     if (!doc)
320         throw mp::XMLError("Empty XML Document");
321
322     const xmlNode* root = xmlDocGetRootElement(doc);
323
324     if (file_include_path)
325     {
326         int r = yaz_xml_include_glob((xmlNode *) root, file_include_path,
327             YAZ_FILE_GLOB_FAIL_NOTEXIST);
328         if (r)
329             throw mp::XMLError("YAZ XML Include failed");
330     }
331
332     mp::xml::check_element_mp(root,  "metaproxy");
333
334     const xmlNode* node = mp::xml::jump_to_children(root, XML_ELEMENT_NODE);
335
336     if (mp::xml::is_element_mp(node, "dlpath"))
337     {
338         m_dl_path = mp::xml::get_text(node);
339         node = mp::xml::jump_to_next(node, XML_ELEMENT_NODE);
340     }
341     // process <start> node which is expected first element node
342     if (mp::xml::check_element_mp(node, "start"))
343     {
344         const struct _xmlAttr *attr;
345         for (attr = node->properties; attr; attr = attr->next)
346         {
347             std::string name = std::string((const char *) attr->name);
348             std::string value;
349
350             if (attr->children && attr->children->type == XML_TEXT_NODE)
351                 value = std::string((const char *)attr->children->content);
352
353             if (name == "route")
354                 m_start_route = value;
355             else if (name == "base")
356                 ;// Ignore XInclude base attribute.
357             else
358                 throw mp::XMLError("Only attribute route allowed"
359                                     " in element 'start'. Got " + name);
360         }
361         node = mp::xml::jump_to_next(node, XML_ELEMENT_NODE);
362     }
363     // process <filters> node if given
364     if (mp::xml::is_element_mp(node, "filters"))
365     {
366         parse_xml_filters(doc, mp::xml::jump_to_children(node,
367                                                          XML_ELEMENT_NODE),
368                           test_only, file_include_path);
369
370         node = mp::xml::jump_to_next(node, XML_ELEMENT_NODE);
371     }
372     // process <routes> node which is expected third element node
373     mp::xml::check_element_mp(node, "routes");
374
375     parse_xml_routes(doc, mp::xml::jump_to_children(node, XML_ELEMENT_NODE),
376                      test_only, file_include_path);
377
378     node = mp::xml::jump_to_next(node, XML_ELEMENT_NODE);
379     if (node)
380     {
381         throw mp::XMLError("Unexpected element "
382                             + std::string((const char *)node->name));
383     }
384
385     node = mp::xml::jump_to_children(root, XML_ELEMENT_NODE);
386     while (node)
387     {
388         if (mp::xml::is_element_mp(node, "filters"))
389             check_routes_in_filters(
390                 mp::xml::jump_to_children(node,
391                                           XML_ELEMENT_NODE));
392         else if (mp::xml::is_element_mp(node, "routes"))
393         {
394             const xmlNode* n = mp::xml::jump_to_children(node,
395                                                          XML_ELEMENT_NODE);
396             while (n)
397             {
398                 if (mp::xml::is_element_mp(n, "route"))
399                 {
400                     check_routes_in_filters(
401                         mp::xml::jump_to_children(n, XML_ELEMENT_NODE));
402
403                 }
404                 n = mp::xml::jump_to_next(n, XML_ELEMENT_NODE);
405             }
406         }
407         node = mp::xml::jump_to_next(node, XML_ELEMENT_NODE);
408     }
409 }
410
411 mp::RouterFleXML::Rep::Rep() : m_xinclude(false)
412 {
413 }
414
415 void mp::RouterFleXML::Rep::base(xmlDocPtr doc, mp::FactoryFilter &factory,
416                                  bool test_only, const char *file_include_path)
417 {
418     m_factory = &factory;
419     parse_xml_config_dom(doc, test_only, file_include_path);
420     m_start_route = "start";
421 }
422
423 mp::RouterFleXML::RouterFleXML(xmlDocPtr doc, mp::FactoryFilter &factory,
424                                bool test_only, const char *file_include_path)
425     : m_p(new Rep)
426 {
427     m_p->base(doc, factory, test_only, file_include_path);
428 }
429
430 mp::RouterFleXML::RouterFleXML(std::string xmlconf, mp::FactoryFilter &factory,
431     bool test_only)
432     : m_p(new Rep)
433 {
434     xmlDocPtr doc = xmlParseMemory(xmlconf.c_str(),
435                                    xmlconf.size());
436     if (!doc)
437         throw mp::XMLError("xmlParseMemory failed");
438     else
439     {
440         m_p->base(doc, factory, test_only, 0);
441         xmlFreeDoc(doc);
442     }
443 }
444
445 mp::RouterFleXML::~RouterFleXML()
446 {
447 }
448
449 const mp::filter::Base *mp::RouterFleXML::Pos::move(const char *route)
450 {
451     if (route && *route)
452     {
453         //std::cout << "move to " << route << "\n";
454         m_route_it = m_p->m_routes.find(route);
455         if (m_route_it == m_p->m_routes.end())
456         {
457             std::cout << "no such route " << route << "\n";
458             throw mp::XMLError("bad route " + std::string(route));
459         }
460         m_filter_it = m_route_it->second.m_list.begin();
461     }
462     if (m_filter_it == m_route_it->second.m_list.end())
463         return 0;
464     const mp::filter::Base *f = (*m_filter_it).get();
465     m_filter_it++;
466     return f;
467 }
468
469 mp::RoutePos *mp::RouterFleXML::createpos() const
470 {
471     mp::RouterFleXML::Pos *p = new mp::RouterFleXML::Pos;
472
473     p->m_route_it = m_p->m_routes.find(m_p->m_start_route);
474     if (p->m_route_it == m_p->m_routes.end())
475     {
476         delete p;
477         return 0;
478     }
479     p->m_filter_it = p->m_route_it->second.m_list.begin();
480     p->m_p = m_p.get();
481     return p;
482 }
483
484 mp::RoutePos *mp::RouterFleXML::Pos::clone()
485 {
486     mp::RouterFleXML::Pos *p = new mp::RouterFleXML::Pos;
487     p->m_filter_it = m_filter_it;
488     p->m_route_it = m_route_it;
489     p->m_p = m_p;
490     return p;
491 }
492
493 mp::RouterFleXML::Pos::~Pos()
494 {
495 }
496
497
498 void mp::RouterFleXML::start()
499 {
500     std::map<std::string,RouterFleXML::Route>::iterator route_it;
501
502     route_it = m_p->m_routes.begin();
503     while (route_it != m_p->m_routes.end())
504     {
505         RouterFleXML::Route route = route_it->second;
506
507         std::list<boost::shared_ptr<const mp::filter::Base> >::iterator it;
508
509         for (it = route.m_list.begin(); it != route.m_list.end(); it++)
510             (*it)->start();
511         route_it++;
512     }
513 }
514
515 void mp::RouterFleXML::stop(int signo)
516 {
517     std::map<std::string,RouterFleXML::Route>::iterator route_it;
518
519     route_it = m_p->m_routes.begin();
520     while (route_it != m_p->m_routes.end())
521     {
522         RouterFleXML::Route route = route_it->second;
523
524         std::list<boost::shared_ptr<const mp::filter::Base> >::iterator it;
525
526         for (it = route.m_list.begin(); it != route.m_list.end(); it++)
527             (*it)->stop(signo);
528         route_it++;
529     }
530 }
531
532
533 /*
534  * Local variables:
535  * c-basic-offset: 4
536  * c-file-style: "Stroustrup"
537  * indent-tabs-mode: nil
538  * End:
539  * vim: shiftwidth=4 tabstop=8 expandtab
540  */
541