Local boost::smatch , MP-482
[metaproxy-moved-to-github.git] / src / router_flexml.cpp
1 /* This file is part of Metaproxy.
2    Copyright (C) 2005-2013 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
123                 throw mp::XMLError("Only attribute id or type allowed"
124                                     " in filter element. Got " + name);
125         }
126
127         if (!m_factory->exist(type_value))
128         {
129             yaz_log(YLOG_LOG, "Loading %s (dlpath %s)",
130                     type_value.c_str(), m_dl_path.c_str());
131             m_factory->add_creator_dl(type_value, m_dl_path);
132         }
133         mp::filter::Base* filter_base = m_factory->create(type_value);
134
135         filter_base->configure(node, test_only, file_include_path);
136
137         if (m_id_filter_map.find(id_value) != m_id_filter_map.end())
138             throw mp::XMLError("Filter " + id_value + " already defined");
139
140         m_id_filter_map[id_value] =
141             boost::shared_ptr<mp::filter::Base>(filter_base);
142
143         node = mp::xml::jump_to_next(node, XML_ELEMENT_NODE);
144     }
145 }
146
147 void mp::RouterFleXML::Rep::parse_xml_filters1(xmlDocPtr doc,
148                                               const xmlNode *node,
149                                               bool test_only,
150                                               const char *file_include_path,
151                                               Route &route)
152 {
153     while (node)
154     {
155         if (mp::xml::is_element_mp(node, "filters"))
156         {
157             const xmlNode* node1 =
158                 mp::xml::jump_to_children(node, XML_ELEMENT_NODE);
159
160             parse_xml_filters1(doc, node1, test_only, file_include_path, route);
161         }
162         else if (mp::xml::check_element_mp(node, "filter"))
163         {
164             const struct _xmlAttr *attr;
165             std::string refid_value;
166             std::string type_value;
167             for (attr = node->properties; attr; attr = attr->next)
168             {
169                 std::string name = std::string((const char *) attr->name);
170                 std::string value;
171
172                 if (attr->children && attr->children->type == XML_TEXT_NODE)
173                     value = std::string((const char *)attr->children->content);
174
175                 if (name == "refid")
176                     refid_value = value;
177                 else if (name == "type")
178                     type_value = value;
179                 else
180                     throw mp::XMLError("Only attribute 'refid' or 'type'"
181                                        " allowed for element 'filter'."
182                                        " Got " + name);
183             }
184             if (refid_value.length())
185             {
186                 std::map<std::string,
187                          boost::shared_ptr<const mp::filter::Base > >::iterator it;
188                 it = m_id_filter_map.find(refid_value);
189                 if (it == m_id_filter_map.end())
190                     throw mp::XMLError("Unknown filter refid "
191                                        + refid_value);
192                 else
193                     route.m_list.push_back(it->second);
194             }
195             else if (type_value.length())
196             {
197                 if (!m_factory->exist(type_value))
198                 {
199                     yaz_log(YLOG_LOG, "Loading %s (dlpath %s)",
200                             type_value.c_str(), m_dl_path.c_str());
201                     m_factory->add_creator_dl(type_value, m_dl_path);
202                 }
203                 mp::filter::Base* filter_base = m_factory->create(type_value);
204
205                 filter_base->configure(node, test_only, file_include_path);
206
207                 route.m_list.push_back(
208                     boost::shared_ptr<mp::filter::Base>(filter_base));
209             }
210
211         }
212         node = mp::xml::jump_to_next(node, XML_ELEMENT_NODE);
213     }
214 }
215
216
217 void mp::RouterFleXML::Rep::parse_xml_routes(xmlDocPtr doc,
218                                              const xmlNode *node,
219                                              bool test_only,
220                                              const char *file_include_path)
221 {
222     mp::xml::check_element_mp(node, "route");
223
224     unsigned int route_nr = 0;
225     while (mp::xml::is_element_mp(node, "route"))
226     {
227         route_nr++;
228
229         const struct _xmlAttr *attr;
230         std::string id_value;
231         for (attr = node->properties; attr; attr = attr->next)
232         {
233             std::string name = std::string((const char *) attr->name);
234             std::string value;
235
236             if (attr->children && attr->children->type == XML_TEXT_NODE)
237                 value = std::string((const char *)attr->children->content);
238
239             if (name == "id")
240                 id_value = value;
241             else
242                 throw mp::XMLError("Only attribute 'id' allowed for"
243                                     " element 'route'."
244                                     " Got " + name);
245         }
246
247         Route route;
248
249         // process <filter> / <filters> nodes in third level
250         const xmlNode* node3 = mp::xml::jump_to_children(node, XML_ELEMENT_NODE);
251         parse_xml_filters1(doc, node3, test_only, file_include_path, route);
252
253         std::map<std::string,RouterFleXML::Route>::iterator it;
254         it = m_routes.find(id_value);
255         if (it != m_routes.end())
256             throw mp::XMLError("Route id='" + id_value
257                                 + "' already exist");
258         else
259             m_routes[id_value] = route;
260         node = mp::xml::jump_to_next(node, XML_ELEMENT_NODE);
261     }
262 }
263
264 void mp::RouterFleXML::Rep::check_routes_in_filters(const xmlNode *node)
265 {
266     while (node)
267     {
268         if (mp::xml::is_element_mp(node, "filters"))
269         {
270             const xmlNode *n =
271                 mp::xml::jump_to_children(node, XML_ELEMENT_NODE);
272             check_routes_in_filters(n);
273         }
274         else if (mp::xml::is_element_mp(node, "filter"))
275         {
276             const xmlNode *n =
277                 mp::xml::jump_to_children(node, XML_ELEMENT_NODE);
278             while (n)
279             {
280                 const struct _xmlAttr *attr;
281                 // we assume thar that route attribute is only at one level
282                 // below filter.. At least that works for multi and virt_db.
283                 for (attr = n->properties; attr; attr = attr->next)
284                 {
285                     if (!strcmp((const char *) attr->name, "route"))
286                     {
287                         std::string value;
288
289                         if (attr->children && attr->children->type == XML_TEXT_NODE)
290                             value = std::string((const char *)attr->children->content);
291
292                         std::map<std::string,RouterFleXML::Route>::iterator it;
293                         it = m_routes.find(value);
294                         if (it == m_routes.end())
295                         {
296                             throw mp::XMLError("Route '" + value + "' does not exist");
297                         }
298                     }
299                 }
300                 n = mp::xml::jump_to_next(n, XML_ELEMENT_NODE);
301             }
302         }
303         node = mp::xml::jump_to_next(node, XML_ELEMENT_NODE);
304     }
305 }
306
307 void mp::RouterFleXML::Rep::parse_xml_config_dom(xmlDocPtr doc,
308                                                  bool test_only,
309                                                  const char *file_include_path)
310 {
311     if (!doc)
312         throw mp::XMLError("Empty XML Document");
313
314     const xmlNode* root = xmlDocGetRootElement(doc);
315
316     if (file_include_path)
317     {
318         int r = yaz_xml_include_glob((xmlNode *) root, file_include_path,
319             YAZ_FILE_GLOB_FAIL_NOTEXIST);
320         if (r)
321             throw mp::XMLError("YAZ XML Include failed");
322     }
323
324     mp::xml::check_element_mp(root,  "metaproxy");
325
326     const xmlNode* node = mp::xml::jump_to_children(root, XML_ELEMENT_NODE);
327
328     if (mp::xml::is_element_mp(node, "dlpath"))
329     {
330         m_dl_path = mp::xml::get_text(node);
331         node = mp::xml::jump_to_next(node, XML_ELEMENT_NODE);
332     }
333     // process <start> node which is expected first element node
334     if (mp::xml::check_element_mp(node, "start"))
335     {
336         const struct _xmlAttr *attr;
337         for (attr = node->properties; attr; attr = attr->next)
338         {
339             std::string name = std::string((const char *) attr->name);
340             std::string value;
341
342             if (attr->children && attr->children->type == XML_TEXT_NODE)
343                 value = std::string((const char *)attr->children->content);
344
345             if (name == "route")
346                 m_start_route = value;
347             else
348                 throw mp::XMLError("Only attribute route allowed"
349                                     " in element 'start'. Got " + name);
350         }
351         node = mp::xml::jump_to_next(node, XML_ELEMENT_NODE);
352     }
353     // process <filters> node if given
354     if (mp::xml::is_element_mp(node, "filters"))
355     {
356         parse_xml_filters(doc, mp::xml::jump_to_children(node,
357                                                          XML_ELEMENT_NODE),
358                           test_only, file_include_path);
359
360         node = mp::xml::jump_to_next(node, XML_ELEMENT_NODE);
361     }
362     // process <routes> node which is expected third element node
363     mp::xml::check_element_mp(node, "routes");
364
365     parse_xml_routes(doc, mp::xml::jump_to_children(node, XML_ELEMENT_NODE),
366                      test_only, file_include_path);
367
368     node = mp::xml::jump_to_next(node, XML_ELEMENT_NODE);
369     if (node)
370     {
371         throw mp::XMLError("Unexpected element "
372                             + std::string((const char *)node->name));
373     }
374
375     node = mp::xml::jump_to_children(root, XML_ELEMENT_NODE);
376     while (node)
377     {
378         if (mp::xml::is_element_mp(node, "filters"))
379             check_routes_in_filters(
380                 mp::xml::jump_to_children(node,
381                                           XML_ELEMENT_NODE));
382         else if (mp::xml::is_element_mp(node, "routes"))
383         {
384             const xmlNode* n = mp::xml::jump_to_children(node,
385                                                          XML_ELEMENT_NODE);
386             while (n)
387             {
388                 if (mp::xml::is_element_mp(n, "route"))
389                 {
390                     check_routes_in_filters(
391                         mp::xml::jump_to_children(n, XML_ELEMENT_NODE));
392
393                 }
394                 n = mp::xml::jump_to_next(n, XML_ELEMENT_NODE);
395             }
396         }
397         node = mp::xml::jump_to_next(node, XML_ELEMENT_NODE);
398     }
399 }
400
401 mp::RouterFleXML::Rep::Rep() : m_xinclude(false)
402 {
403 }
404
405 void mp::RouterFleXML::Rep::base(xmlDocPtr doc, mp::FactoryFilter &factory,
406                                  bool test_only, const char *file_include_path)
407 {
408     m_factory = &factory;
409     parse_xml_config_dom(doc, test_only, file_include_path);
410     m_start_route = "start";
411 }
412
413 mp::RouterFleXML::RouterFleXML(xmlDocPtr doc, mp::FactoryFilter &factory,
414                                bool test_only, const char *file_include_path)
415     : m_p(new Rep)
416 {
417     m_p->base(doc, factory, test_only, file_include_path);
418 }
419
420 mp::RouterFleXML::RouterFleXML(std::string xmlconf, mp::FactoryFilter &factory,
421     bool test_only)
422     : m_p(new Rep)
423 {
424     LIBXML_TEST_VERSION;
425
426     xmlDocPtr doc = xmlParseMemory(xmlconf.c_str(),
427                                    xmlconf.size());
428     if (!doc)
429         throw mp::XMLError("xmlParseMemory failed");
430     else
431     {
432         m_p->base(doc, factory, test_only, 0);
433         xmlFreeDoc(doc);
434     }
435 }
436
437 mp::RouterFleXML::~RouterFleXML()
438 {
439 }
440
441 const mp::filter::Base *mp::RouterFleXML::Pos::move(const char *route)
442 {
443     if (route && *route)
444     {
445         //std::cout << "move to " << route << "\n";
446         m_route_it = m_p->m_routes.find(route);
447         if (m_route_it == m_p->m_routes.end())
448         {
449             std::cout << "no such route " << route << "\n";
450             throw mp::XMLError("bad route " + std::string(route));
451         }
452         m_filter_it = m_route_it->second.m_list.begin();
453     }
454     if (m_filter_it == m_route_it->second.m_list.end())
455         return 0;
456     const mp::filter::Base *f = (*m_filter_it).get();
457     m_filter_it++;
458     return f;
459 }
460
461 mp::RoutePos *mp::RouterFleXML::createpos() const
462 {
463     mp::RouterFleXML::Pos *p = new mp::RouterFleXML::Pos;
464
465     p->m_route_it = m_p->m_routes.find(m_p->m_start_route);
466     if (p->m_route_it == m_p->m_routes.end())
467     {
468         delete p;
469         return 0;
470     }
471     p->m_filter_it = p->m_route_it->second.m_list.begin();
472     p->m_p = m_p.get();
473     return p;
474 }
475
476 mp::RoutePos *mp::RouterFleXML::Pos::clone()
477 {
478     mp::RouterFleXML::Pos *p = new mp::RouterFleXML::Pos;
479     p->m_filter_it = m_filter_it;
480     p->m_route_it = m_route_it;
481     p->m_p = m_p;
482     return p;
483 }
484
485 mp::RouterFleXML::Pos::~Pos()
486 {
487 }
488
489
490 void mp::RouterFleXML::start()
491 {
492     std::map<std::string,RouterFleXML::Route>::iterator route_it;
493
494     route_it = m_p->m_routes.begin();
495     while (route_it != m_p->m_routes.end())
496     {
497         RouterFleXML::Route route = route_it->second;
498
499         std::list<boost::shared_ptr<const mp::filter::Base> >::iterator it;
500
501         for (it = route.m_list.begin(); it != route.m_list.end(); it++)
502             (*it)->start();
503         route_it++;
504     }
505 }
506
507 void mp::RouterFleXML::stop()
508 {
509     std::map<std::string,RouterFleXML::Route>::iterator route_it;
510
511     route_it = m_p->m_routes.begin();
512     while (route_it != m_p->m_routes.end())
513     {
514         RouterFleXML::Route route = route_it->second;
515
516         std::list<boost::shared_ptr<const mp::filter::Base> >::iterator it;
517
518         for (it = route.m_list.begin(); it != route.m_list.end(); it++)
519             (*it)->stop();
520         route_it++;
521     }
522 }
523
524
525 /*
526  * Local variables:
527  * c-basic-offset: 4
528  * c-file-style: "Stroustrup"
529  * indent-tabs-mode: nil
530  * End:
531  * vim: shiftwidth=4 tabstop=8 expandtab
532  */
533