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