Add path to configure method of filter.
[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 "router_flexml.hpp"
22 #include "factory_filter.hpp"
23 #include "factory_static.hpp"
24
25 #include <iostream>
26 #include <map>
27 #include <list>
28 #include <yaz/log.h>
29 #include <yaz/xml_include.h>
30
31 #include <boost/shared_ptr.hpp>
32
33 #include <libxml/xmlversion.h>
34 #include <libxml/parser.h>
35 #include <libxml/tree.h>
36
37 namespace mp = metaproxy_1;
38
39 namespace metaproxy_1 {
40     class RouterFleXML::Route {
41         friend class RouterFleXML::Rep;
42         friend class RouterFleXML::Pos;
43         friend class RouterFleXML;
44         std::list<boost::shared_ptr<const mp::filter::Base> > m_list;
45     };
46     class RouterFleXML::Rep {
47         friend class RouterFleXML;
48         friend class RouterFleXML::Pos;
49         Rep();
50
51         void base(xmlDocPtr doc, mp::FactoryFilter &factory, bool test_only,
52                   const char *file_include_path);
53
54         typedef std::map<std::string,
55                          boost::shared_ptr<const mp::filter::Base > >
56                          IdFilterMap ;
57
58         IdFilterMap m_id_filter_map;
59
60         std::map<std::string,RouterFleXML::Route> m_routes;
61
62         std::string m_start_route;
63
64         std::string m_dl_path;
65
66         void parse_xml_config_dom(xmlDocPtr doc, bool test_only,
67                                   const char *file_include_path);
68
69         void parse_xml_filters(xmlDocPtr doc, const xmlNode *node,
70                                bool test_only, const char *file_include_path);
71         void parse_xml_routes(xmlDocPtr doc, const xmlNode *node,
72                               bool test_only, const char *file_include_path);
73
74         bool m_xinclude;
75     private:
76         FactoryFilter *m_factory; // TODO shared_ptr
77     };
78
79     class RouterFleXML::Pos : public RoutePos {
80     public:
81         virtual const filter::Base *move(const char *route);
82         virtual RoutePos *clone();
83         virtual ~Pos();
84         mp::RouterFleXML::Rep *m_p;
85
86         std::map<std::string, 
87                  RouterFleXML::Route>::iterator m_route_it;
88         std::list<boost::shared_ptr <const mp::filter::Base> >::iterator m_filter_it;
89     };
90 }
91
92 void mp::RouterFleXML::Rep::parse_xml_filters(xmlDocPtr doc,
93                                               const xmlNode *node,
94                                               bool test_only,
95                                               const char *file_include_path)
96 {
97     unsigned int filter_nr = 0;
98     while(node && mp::xml::check_element_mp(node, "filter"))
99     {
100         filter_nr++;
101
102         const struct _xmlAttr *attr;
103         std::string id_value;
104         std::string type_value;
105         for (attr = node->properties; attr; attr = attr->next)
106         {
107             std::string name = std::string((const char *) attr->name);
108             std::string value;
109
110             if (attr->children && attr->children->type == XML_TEXT_NODE)
111                 value = std::string((const char *)attr->children->content);
112
113             if (name == "id")
114                 id_value = value;
115             else if (name == "type")
116                 type_value = value;
117             else
118                 throw mp::XMLError("Only attribute id or type allowed"
119                                     " in filter element. Got " + name);
120         }
121
122         if (!m_factory->exist(type_value))
123         {
124             yaz_log(YLOG_LOG, "Loading %s (dlpath %s)",
125                     type_value.c_str(), m_dl_path.c_str());
126             m_factory->add_creator_dl(type_value, m_dl_path);
127         }
128         mp::filter::Base* filter_base = m_factory->create(type_value);
129
130         filter_base->configure(node, test_only, file_include_path);
131
132         if (m_id_filter_map.find(id_value) != m_id_filter_map.end())
133             throw mp::XMLError("Filter " + id_value + " already defined");
134
135         m_id_filter_map[id_value] =
136             boost::shared_ptr<mp::filter::Base>(filter_base);
137
138         node = mp::xml::jump_to_next(node, XML_ELEMENT_NODE);
139     }
140 }
141
142 void mp::RouterFleXML::Rep::parse_xml_routes(xmlDocPtr doc,
143                                              const xmlNode *node,
144                                              bool test_only,
145                                              const char *file_include_path)
146 {
147     mp::xml::check_element_mp(node, "route");
148
149     unsigned int route_nr = 0;
150     while(mp::xml::is_element_mp(node, "route"))
151     {
152         route_nr++;
153
154         const struct _xmlAttr *attr;
155         std::string id_value;
156         for (attr = node->properties; attr; attr = attr->next)
157         {
158             std::string name = std::string((const char *) attr->name);
159             std::string value;
160             
161             if (attr->children && attr->children->type == XML_TEXT_NODE)
162                 value = std::string((const char *)attr->children->content);
163             
164             if (name == "id")
165                 id_value = value;
166             else
167                 throw mp::XMLError("Only attribute 'id' allowed for"
168                                     " element 'route'."
169                                     " Got " + name);
170         }
171
172         Route route;
173
174         // process <filter> nodes in third level
175         const xmlNode* node3 = mp::xml::jump_to_children(node, XML_ELEMENT_NODE);
176
177         unsigned int filter3_nr = 0;
178         while(node3 && mp::xml::check_element_mp(node3, "filter"))
179         {
180             filter3_nr++;
181             
182             const struct _xmlAttr *attr;
183             std::string refid_value;
184             std::string type_value;
185             for (attr = node3->properties; attr; attr = attr->next)
186             {
187                 std::string name = std::string((const char *) attr->name);
188                 std::string value;
189                 
190                 if (attr->children && attr->children->type == XML_TEXT_NODE)
191                     value = std::string((const char *)attr->children->content);
192                 
193                 if (name == "refid")
194                     refid_value = value;
195                 else if (name == "type")
196                     type_value = value;
197                 else
198                     throw mp::XMLError("Only attribute 'refid' or 'type'"
199                                         " allowed for element 'filter'."
200                                         " Got " + name);
201             }
202             if (refid_value.length())
203             {
204                 std::map<std::string,
205                     boost::shared_ptr<const mp::filter::Base > >::iterator it;
206                 it = m_id_filter_map.find(refid_value);
207                 if (it == m_id_filter_map.end())
208                     throw mp::XMLError("Unknown filter refid "
209                                         + refid_value);
210                 else
211                     route.m_list.push_back(it->second);
212             }
213             else if (type_value.length())
214             {
215                 if (!m_factory->exist(type_value))
216                 {
217                     yaz_log(YLOG_LOG, "Loading %s (dlpath %s)",
218                             type_value.c_str(), m_dl_path.c_str());
219                     m_factory->add_creator_dl(type_value, m_dl_path);
220                 }
221                 mp::filter::Base* filter_base = m_factory->create(type_value);
222
223                 filter_base->configure(node3, test_only, file_include_path);
224                 
225                 route.m_list.push_back(
226                     boost::shared_ptr<mp::filter::Base>(filter_base));
227             }
228             node3 = mp::xml::jump_to_next(node3, XML_ELEMENT_NODE);
229             
230         }
231         std::map<std::string,RouterFleXML::Route>::iterator it;
232         it = m_routes.find(id_value);
233         if (it != m_routes.end())
234             throw mp::XMLError("Route id='" + id_value
235                                 + "' already exist");
236         else
237             m_routes[id_value] = route;
238         node = mp::xml::jump_to_next(node, XML_ELEMENT_NODE);
239     }
240 }
241
242 void mp::RouterFleXML::Rep::parse_xml_config_dom(xmlDocPtr doc,
243                                                  bool test_only,
244                                                  const char *file_include_path)
245 {
246     if (!doc)
247         throw mp::XMLError("Empty XML Document");
248     
249     const xmlNode* root = xmlDocGetRootElement(doc);
250
251     if (file_include_path)
252     {
253         int r = yaz_xml_include_simple((xmlNode *) root, file_include_path);
254         if (r)
255             throw mp::XMLError("YAZ XML Include failed");
256     }
257     
258     mp::xml::check_element_mp(root,  "metaproxy");
259
260     const xmlNode* node = mp::xml::jump_to_children(root, XML_ELEMENT_NODE);
261
262     if (mp::xml::is_element_mp(node, "dlpath"))
263     {
264         m_dl_path = mp::xml::get_text(node);
265         node = mp::xml::jump_to_next(node, XML_ELEMENT_NODE);
266     }
267     // process <start> node which is expected first element node
268     if (mp::xml::check_element_mp(node, "start"))
269     {
270         const struct _xmlAttr *attr;
271         std::string id_value;
272         for (attr = node->properties; attr; attr = attr->next)
273         {
274             std::string name = std::string((const char *) attr->name);
275             std::string value;
276
277             if (attr->children && attr->children->type == XML_TEXT_NODE)
278                 value = std::string((const char *)attr->children->content);
279
280             if (name == "route")
281                 m_start_route = value;
282             else
283                 throw mp::XMLError("Only attribute start allowed"
284                                     " in element 'start'. Got " + name);
285         }
286         node = mp::xml::jump_to_next(node, XML_ELEMENT_NODE);
287     }
288     // process <filters> node if given
289     if (mp::xml::is_element_mp(node, "filters"))
290     {
291         parse_xml_filters(doc, mp::xml::jump_to_children(node,
292                                                          XML_ELEMENT_NODE),
293                           test_only, file_include_path);
294                       
295         node = mp::xml::jump_to_next(node, XML_ELEMENT_NODE);
296     }
297     // process <routes> node which is expected third element node
298     mp::xml::check_element_mp(node, "routes");
299     
300     parse_xml_routes(doc, mp::xml::jump_to_children(node, XML_ELEMENT_NODE),
301                      test_only, file_include_path);
302
303     node = mp::xml::jump_to_next(node, XML_ELEMENT_NODE);
304     if (node)
305     {
306         throw mp::XMLError("Unexpected element " 
307                             + std::string((const char *)node->name));
308     }
309 }        
310
311 mp::RouterFleXML::Rep::Rep() : m_xinclude(false)
312 {
313 }
314
315 void mp::RouterFleXML::Rep::base(xmlDocPtr doc, mp::FactoryFilter &factory,
316                                  bool test_only, const char *file_include_path)
317 {
318     m_factory = &factory;
319     parse_xml_config_dom(doc, test_only, file_include_path);
320     m_start_route = "start";
321 }
322
323 mp::RouterFleXML::RouterFleXML(xmlDocPtr doc, mp::FactoryFilter &factory,
324                                bool test_only, const char *file_include_path)
325     : m_p(new Rep)
326 {
327     m_p->base(doc, factory, test_only, file_include_path);
328 }
329
330 mp::RouterFleXML::RouterFleXML(std::string xmlconf, mp::FactoryFilter &factory,
331     bool test_only) 
332     : m_p(new Rep)
333 {            
334     LIBXML_TEST_VERSION;
335     
336     xmlDocPtr doc = xmlParseMemory(xmlconf.c_str(),
337                                    xmlconf.size());
338     if (!doc)
339         throw mp::XMLError("xmlParseMemory failed");
340     else
341     {
342         m_p->base(doc, factory, test_only, 0);
343         xmlFreeDoc(doc);
344     }
345 }
346
347 mp::RouterFleXML::~RouterFleXML()
348 {
349 }
350
351 const mp::filter::Base *mp::RouterFleXML::Pos::move(const char *route)
352 {
353     if (route && *route)
354     {
355         //std::cout << "move to " << route << "\n";
356         m_route_it = m_p->m_routes.find(route);
357         if (m_route_it == m_p->m_routes.end())
358         {
359             std::cout << "no such route " << route << "\n";
360             throw mp::XMLError("bad route " + std::string(route));
361         }
362         m_filter_it = m_route_it->second.m_list.begin();
363     }
364     if (m_filter_it == m_route_it->second.m_list.end())
365         return 0;
366     const mp::filter::Base *f = (*m_filter_it).get();
367     m_filter_it++;
368     return f;
369 }
370
371 mp::RoutePos *mp::RouterFleXML::createpos() const
372 {
373     mp::RouterFleXML::Pos *p = new mp::RouterFleXML::Pos;
374
375     p->m_route_it = m_p->m_routes.find(m_p->m_start_route);
376     if (p->m_route_it == m_p->m_routes.end())
377     {
378         delete p;
379         return 0;
380     }
381     p->m_filter_it = p->m_route_it->second.m_list.begin();
382     p->m_p = m_p.get();
383     return p;
384 }
385
386 mp::RoutePos *mp::RouterFleXML::Pos::clone()
387 {
388     mp::RouterFleXML::Pos *p = new mp::RouterFleXML::Pos;
389     p->m_filter_it = m_filter_it;
390     p->m_route_it = m_route_it;
391     p->m_p = m_p;
392     return p;
393 }
394
395 mp::RouterFleXML::Pos::~Pos()
396 {
397 }
398
399
400 void mp::RouterFleXML::start()
401 {
402     std::map<std::string,RouterFleXML::Route>::iterator route_it;
403
404     route_it = m_p->m_routes.begin();
405     while (route_it != m_p->m_routes.end())
406     {
407         RouterFleXML::Route route = route_it->second;
408
409         std::list<boost::shared_ptr<const mp::filter::Base> >::iterator it;
410
411         for (it = route.m_list.begin(); it != route.m_list.end(); it++)
412             (*it)->start();
413         route_it++;
414     }
415 }
416
417 /*
418  * Local variables:
419  * c-basic-offset: 4
420  * c-file-style: "Stroustrup"
421  * indent-tabs-mode: nil
422  * End:
423  * vim: shiftwidth=4 tabstop=8 expandtab
424  */
425