Beginnings of graceful stop
[metaproxy-moved-to-github.git] / src / router_flexml.cpp
1 /* This file is part of Metaproxy.
2    Copyright (C) 2005-2012 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_simple((xmlNode *) root, file_include_path);
319         if (r)
320             throw mp::XMLError("YAZ XML Include failed");
321     }
322     
323     mp::xml::check_element_mp(root,  "metaproxy");
324
325     const xmlNode* node = mp::xml::jump_to_children(root, XML_ELEMENT_NODE);
326
327     if (mp::xml::is_element_mp(node, "dlpath"))
328     {
329         m_dl_path = mp::xml::get_text(node);
330         node = mp::xml::jump_to_next(node, XML_ELEMENT_NODE);
331     }
332     // process <start> node which is expected first element node
333     if (mp::xml::check_element_mp(node, "start"))
334     {
335         const struct _xmlAttr *attr;
336         for (attr = node->properties; attr; attr = attr->next)
337         {
338             std::string name = std::string((const char *) attr->name);
339             std::string value;
340
341             if (attr->children && attr->children->type == XML_TEXT_NODE)
342                 value = std::string((const char *)attr->children->content);
343
344             if (name == "route")
345                 m_start_route = value;
346             else
347                 throw mp::XMLError("Only attribute route allowed"
348                                     " in element 'start'. Got " + name);
349         }
350         node = mp::xml::jump_to_next(node, XML_ELEMENT_NODE);
351     }
352     // process <filters> node if given
353     if (mp::xml::is_element_mp(node, "filters"))
354     {
355         parse_xml_filters(doc, mp::xml::jump_to_children(node,
356                                                          XML_ELEMENT_NODE),
357                           test_only, file_include_path);
358                       
359         node = mp::xml::jump_to_next(node, XML_ELEMENT_NODE);
360     }
361     // process <routes> node which is expected third element node
362     mp::xml::check_element_mp(node, "routes");
363     
364     parse_xml_routes(doc, mp::xml::jump_to_children(node, XML_ELEMENT_NODE),
365                      test_only, file_include_path);
366
367     node = mp::xml::jump_to_next(node, XML_ELEMENT_NODE);
368     if (node)
369     {
370         throw mp::XMLError("Unexpected element " 
371                             + std::string((const char *)node->name));
372     }
373
374     node = mp::xml::jump_to_children(root, XML_ELEMENT_NODE);
375     while (node)
376     {
377         if (mp::xml::is_element_mp(node, "filters"))
378             check_routes_in_filters(
379                 mp::xml::jump_to_children(node,
380                                           XML_ELEMENT_NODE));
381         else if (mp::xml::is_element_mp(node, "routes"))
382         {
383             const xmlNode* n = mp::xml::jump_to_children(node,
384                                                          XML_ELEMENT_NODE);
385             while (n)
386             {
387                 if (mp::xml::is_element_mp(n, "route"))
388                 {
389                     check_routes_in_filters(
390                         mp::xml::jump_to_children(n, XML_ELEMENT_NODE));
391                     
392                 }
393                 n = mp::xml::jump_to_next(n, XML_ELEMENT_NODE);
394             }
395         }
396         node = mp::xml::jump_to_next(node, XML_ELEMENT_NODE);
397     }
398 }
399
400 mp::RouterFleXML::Rep::Rep() : m_xinclude(false)
401 {
402 }
403
404 void mp::RouterFleXML::Rep::base(xmlDocPtr doc, mp::FactoryFilter &factory,
405                                  bool test_only, const char *file_include_path)
406 {
407     m_factory = &factory;
408     parse_xml_config_dom(doc, test_only, file_include_path);
409     m_start_route = "start";
410 }
411
412 mp::RouterFleXML::RouterFleXML(xmlDocPtr doc, mp::FactoryFilter &factory,
413                                bool test_only, const char *file_include_path)
414     : m_p(new Rep)
415 {
416     m_p->base(doc, factory, test_only, file_include_path);
417 }
418
419 mp::RouterFleXML::RouterFleXML(std::string xmlconf, mp::FactoryFilter &factory,
420     bool test_only) 
421     : m_p(new Rep)
422 {            
423     LIBXML_TEST_VERSION;
424     
425     xmlDocPtr doc = xmlParseMemory(xmlconf.c_str(),
426                                    xmlconf.size());
427     if (!doc)
428         throw mp::XMLError("xmlParseMemory failed");
429     else
430     {
431         m_p->base(doc, factory, test_only, 0);
432         xmlFreeDoc(doc);
433     }
434 }
435
436 mp::RouterFleXML::~RouterFleXML()
437 {
438 }
439
440 const mp::filter::Base *mp::RouterFleXML::Pos::move(const char *route)
441 {
442     if (route && *route)
443     {
444         //std::cout << "move to " << route << "\n";
445         m_route_it = m_p->m_routes.find(route);
446         if (m_route_it == m_p->m_routes.end())
447         {
448             std::cout << "no such route " << route << "\n";
449             throw mp::XMLError("bad route " + std::string(route));
450         }
451         m_filter_it = m_route_it->second.m_list.begin();
452     }
453     if (m_filter_it == m_route_it->second.m_list.end())
454         return 0;
455     const mp::filter::Base *f = (*m_filter_it).get();
456     m_filter_it++;
457     return f;
458 }
459
460 mp::RoutePos *mp::RouterFleXML::createpos() const
461 {
462     mp::RouterFleXML::Pos *p = new mp::RouterFleXML::Pos;
463
464     p->m_route_it = m_p->m_routes.find(m_p->m_start_route);
465     if (p->m_route_it == m_p->m_routes.end())
466     {
467         delete p;
468         return 0;
469     }
470     p->m_filter_it = p->m_route_it->second.m_list.begin();
471     p->m_p = m_p.get();
472     return p;
473 }
474
475 mp::RoutePos *mp::RouterFleXML::Pos::clone()
476 {
477     mp::RouterFleXML::Pos *p = new mp::RouterFleXML::Pos;
478     p->m_filter_it = m_filter_it;
479     p->m_route_it = m_route_it;
480     p->m_p = m_p;
481     return p;
482 }
483
484 mp::RouterFleXML::Pos::~Pos()
485 {
486 }
487
488
489 void mp::RouterFleXML::start()
490 {
491     std::map<std::string,RouterFleXML::Route>::iterator route_it;
492
493     route_it = m_p->m_routes.begin();
494     while (route_it != m_p->m_routes.end())
495     {
496         RouterFleXML::Route route = route_it->second;
497
498         std::list<boost::shared_ptr<const mp::filter::Base> >::iterator it;
499
500         for (it = route.m_list.begin(); it != route.m_list.end(); it++)
501             (*it)->start();
502         route_it++;
503     }
504 }
505
506 void mp::RouterFleXML::stop()
507 {
508     std::map<std::string,RouterFleXML::Route>::iterator route_it;
509
510     route_it = m_p->m_routes.begin();
511     while (route_it != m_p->m_routes.end())
512     {
513         RouterFleXML::Route route = route_it->second;
514
515         std::list<boost::shared_ptr<const mp::filter::Base> >::iterator it;
516
517         for (it = route.m_list.begin(); it != route.m_list.end(); it++)
518             (*it)->stop();
519         route_it++;
520     }
521 }
522
523
524 /*
525  * Local variables:
526  * c-basic-offset: 4
527  * c-file-style: "Stroustrup"
528  * indent-tabs-mode: nil
529  * End:
530  * vim: shiftwidth=4 tabstop=8 expandtab
531  */
532