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