metaproxy  1.13.0
filter_sort.cpp
Go to the documentation of this file.
1 /* This file is part of Metaproxy.
2  Copyright (C) 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 "filter_sort.hpp"
21 #include <metaproxy/package.hpp>
22 #include <metaproxy/util.hpp>
23 
24 #include <yaz/diagbib1.h>
25 #include <yaz/copy_types.h>
26 #include <yaz/log.h>
27 #include <yaz/oid_std.h>
28 #include <libxml/xpath.h>
29 #include <libxml/xpathInternals.h>
30 
31 #include <boost/thread/mutex.hpp>
32 #include <boost/thread/condition.hpp>
33 
34 #include <yaz/zgdu.h>
35 
36 namespace mp = metaproxy_1;
37 namespace yf = mp::filter;
38 
39 namespace metaproxy_1 {
40  namespace filter {
41  class Sort::Impl {
42  friend class Frontend;
43  public:
44  Impl();
45  ~Impl();
46  void process(metaproxy_1::Package & package);
47  void configure(const xmlNode * ptr, bool test_only,
48  const char *path);
49  private:
51  std::string m_xpath_expr;
52  std::string m_namespaces;
54  bool m_debug;
55  boost::mutex m_mutex;
56  boost::condition m_cond_session_ready;
57  std::map<mp::Session, FrontendPtr> m_clients;
58  FrontendPtr get_frontend(mp::Package &package);
59  void release_frontend(mp::Package &package);
60  };
61  class Sort::Record {
62  friend class RecordList;
63  Z_NamePlusRecord *npr;
64  std::string score;
65  void get_xpath(xmlDoc *doc, const char *namespaces,
66  const char *expr, bool debug);
67  bool register_namespaces(xmlXPathContextPtr xpathCtx,
68  const char *nsList);
69  public:
70  Record(Z_NamePlusRecord *n, const char *namespaces,
71  const char *expr, bool debug);
72  ~Record();
73  bool operator < (const Record &rhs) const;
74  };
75  class Sort::RecordList : boost::noncopyable {
76  Odr_oid *syntax;
77  std::list<Record> npr_list;
78  mp::odr m_odr;
79  std::string namespaces;
80  std::string xpath_expr;
81  bool debug;
82  public:
83  bool cmp(Odr_oid *syntax);
84  void add(Z_NamePlusRecord *s);
85  int size();
86  Z_NamePlusRecord *get(int i, bool ascending);
87  void sort();
88  RecordList(Odr_oid *, std::string namespaces,
89  std::string xpath_expr, bool debug);
90  ~RecordList();
91  };
92  class Sort::ResultSet : boost::noncopyable {
93  friend class Frontend;
94  Odr_int hit_count;
95  std::list<RecordListPtr> record_lists;
96  };
97  class Sort::Frontend : boost::noncopyable {
98  friend class Impl;
101  bool m_in_use;
102  std::map<std::string, ResultSetPtr> m_sets;
103  typedef std::map<std::string, ResultSetPtr>::iterator Sets_it;
104  void handle_package(mp::Package &package);
105  void handle_search(mp::Package &package, Z_APDU *apdu_req);
106  void handle_present(mp::Package &package, Z_APDU *apdu_req);
107 
108  void handle_records(mp::Package &package,
109  Z_APDU *apdu_reqq,
110  Z_Records *records,
111  Odr_int start_pos,
112  ResultSetPtr s,
113  Odr_oid *syntax,
114  Z_RecordComposition *comp,
115  const char *resultSetId);
116  public:
117  Frontend(Impl *impl);
118  ~Frontend();
119  };
120  }
121 }
122 
123 static void print_xpath_nodes(xmlNodeSetPtr nodes, FILE* output)
124 {
125  xmlNodePtr cur;
126  int size;
127  int i;
128 
129  assert(output);
130  size = nodes ? nodes->nodeNr : 0;
131 
132  fprintf(output, "Result (%d nodes):\n", size);
133  for (i = 0; i < size; ++i) {
134  assert(nodes->nodeTab[i]);
135 
136  if (nodes->nodeTab[i]->type == XML_NAMESPACE_DECL)
137  {
138  xmlNsPtr ns = (xmlNsPtr)nodes->nodeTab[i];
139  cur = (xmlNodePtr)ns->next;
140  if (cur->ns)
141  fprintf(output, "= namespace \"%s\"=\"%s\" for node %s:%s\n",
142  ns->prefix, ns->href, cur->ns->href, cur->name);
143  else
144  fprintf(output, "= namespace \"%s\"=\"%s\" for node %s\n",
145  ns->prefix, ns->href, cur->name);
146  }
147  else if (nodes->nodeTab[i]->type == XML_ELEMENT_NODE)
148  {
149  cur = nodes->nodeTab[i];
150  if (cur->ns)
151  fprintf(output, "= element node \"%s:%s\"\n",
152  cur->ns->href, cur->name);
153  else
154  fprintf(output, "= element node \"%s\"\n", cur->name);
155  }
156  else
157  {
158  cur = nodes->nodeTab[i];
159  fprintf(output, "= node \"%s\": type %d\n", cur->name, cur->type);
160  }
161  }
162 }
163 
164 bool yf::Sort::Record::register_namespaces(xmlXPathContextPtr xpathCtx,
165  const char *nsList)
166 {
167  xmlChar* nsListDup;
168  xmlChar* prefix;
169  xmlChar* href;
170  xmlChar* next;
171 
172  assert(xpathCtx);
173  assert(nsList);
174 
175  nsListDup = xmlStrdup((const xmlChar *) nsList);
176  if (!nsListDup)
177  return false;
178 
179  next = nsListDup;
180  while (next)
181  {
182  /* skip spaces */
183  while (*next == ' ')
184  next++;
185  if (*next == '\0')
186  break;
187 
188  /* find prefix */
189  prefix = next;
190  next = (xmlChar *) xmlStrchr(next, '=');
191  if (next == NULL)
192  {
193  xmlFree(nsListDup);
194  return false;
195  }
196  *next++ = '\0';
197 
198  /* find href */
199  href = next;
200  next = (xmlChar*)xmlStrchr(next, ' ');
201  if (next)
202  *next++ = '\0';
203 
204  /* do register namespace */
205  if (xmlXPathRegisterNs(xpathCtx, prefix, href) != 0)
206  {
207  xmlFree(nsListDup);
208  return false;
209  }
210  }
211 
212  xmlFree(nsListDup);
213  return true;
214 }
215 
216 
217 
218 void yf::Sort::Record::get_xpath(xmlDoc *doc, const char *namespaces,
219  const char *expr, bool debug)
220 {
221  xmlXPathContextPtr xpathCtx = xmlXPathNewContext(doc);
222  if (xpathCtx)
223  {
224  register_namespaces(xpathCtx, namespaces);
225  xmlXPathObjectPtr xpathObj =
226  xmlXPathEvalExpression((const xmlChar *) expr, xpathCtx);
227  if (xpathObj)
228  {
229  xmlNodeSetPtr nodes = xpathObj->nodesetval;
230  if (debug)
231  print_xpath_nodes(nodes, yaz_log_file());
232  if (nodes)
233  {
234  int i;
235  for (i = 0; i < nodes->nodeNr; i++)
236  {
237  std::string content;
238  xmlNode *ptr = nodes->nodeTab[i];
239  if (ptr->type == XML_ELEMENT_NODE ||
240  ptr->type == XML_ATTRIBUTE_NODE)
241  {
242  content = mp::xml::get_text(ptr->children);
243  }
244  else if (ptr->type == XML_TEXT_NODE)
245  {
246  content = mp::xml::get_text(ptr);
247  }
248  if (content.length())
249  {
250  score = content;
251  break;
252  }
253  }
254  }
255  xmlXPathFreeObject(xpathObj);
256  }
257  xmlXPathFreeContext(xpathCtx);
258  }
259 }
260 
261 yf::Sort::Record::Record(Z_NamePlusRecord *n,
262  const char *namespaces,
263  const char *expr,
264  bool debug) : npr(n)
265 {
266  if (npr->which == Z_NamePlusRecord_databaseRecord)
267  {
268  Z_External *ext = npr->u.databaseRecord;
269 
270  if (ext->which == Z_External_octet &&
271  !oid_oidcmp(ext->direct_reference, yaz_oid_recsyn_xml))
272  {
273  xmlDoc *doc = xmlParseMemory(
274  (const char *) ext->u.octet_aligned->buf,
275  ext->u.octet_aligned->len);
276  if (doc)
277  {
278  get_xpath(doc, namespaces, expr, debug);
279  xmlFreeDoc(doc);
280  }
281  }
282  }
283 }
284 
285 yf::Sort::Record::~Record()
286 {
287 }
288 
289 bool yf::Sort::Record::operator < (const Record &rhs) const
290 {
291  if (strcmp(this->score.c_str(), rhs.score.c_str()) < 0)
292  return true;
293  return false;
294 }
295 
296 yf::Sort::RecordList::RecordList(Odr_oid *syntax,
297  std::string a_namespaces,
298  std::string a_xpath_expr,
299  bool a_debug)
300  : namespaces(a_namespaces), xpath_expr(a_xpath_expr), debug(a_debug)
301 
302 {
303  if (syntax)
304  this->syntax = odr_oiddup(m_odr, syntax);
305  else
306  this->syntax = 0;
307 }
308 
309 yf::Sort::RecordList::~RecordList()
310 {
311 
312 }
313 
314 bool yf::Sort::RecordList::cmp(Odr_oid *syntax)
315 {
316  if ((!this->syntax && !syntax)
317  ||
318  (this->syntax && syntax && !oid_oidcmp(this->syntax, syntax)))
319  return true;
320  return false;
321 }
322 
323 int yf::Sort::RecordList::size()
324 {
325  return npr_list.size();
326 }
327 
328 void yf::Sort::RecordList::add(Z_NamePlusRecord *s)
329 {
330  ODR oi = m_odr;
331  Z_NamePlusRecord *npr = yaz_clone_z_NamePlusRecord(s, oi->mem);
332  Record record(npr, namespaces.c_str(), xpath_expr.c_str(), debug);
333  npr_list.push_back(record);
334 }
335 
336 Z_NamePlusRecord *yf::Sort::RecordList::get(int pos, bool ascending)
337 {
338  std::list<Record>::const_iterator it = npr_list.begin();
339  int i = pos;
340  if (!ascending)
341  i = npr_list.size() - pos - 1;
342  for (; it != npr_list.end(); it++, --i)
343  if (i <= 0)
344  {
345  return it->npr;
346  }
347  return 0;
348 }
349 
350 void yf::Sort::RecordList::sort()
351 {
352  npr_list.sort();
353 }
354 
355 yf::Sort::Sort() : m_p(new Impl)
356 {
357 }
358 
359 yf::Sort::~Sort()
360 { // must have a destructor because of boost::scoped_ptr
361 }
362 
363 void yf::Sort::configure(const xmlNode *xmlnode, bool test_only,
364  const char *path)
365 {
366  m_p->configure(xmlnode, test_only, path);
367 }
368 
369 void yf::Sort::process(mp::Package &package) const
370 {
371  m_p->process(package);
372 }
373 
374 
375 yf::Sort::Frontend::Frontend(Impl *impl) :
376  m_p(impl), m_is_virtual(false), m_in_use(true)
377 {
378 }
379 
380 yf::Sort::Frontend::~Frontend()
381 {
382 }
383 
384 
385 yf::Sort::Impl::Impl() : m_prefetch(20), m_ascending(true), m_debug(false)
386 {
387 }
388 
389 yf::Sort::Impl::~Impl()
390 {
391 }
392 
393 yf::Sort::FrontendPtr yf::Sort::Impl::get_frontend(mp::Package &package)
394 {
395  boost::mutex::scoped_lock lock(m_mutex);
396 
397  std::map<mp::Session,yf::Sort::FrontendPtr>::iterator it;
398 
399  while(true)
400  {
401  it = m_clients.find(package.session());
402  if (it == m_clients.end())
403  break;
404 
405  if (!it->second->m_in_use)
406  {
407  it->second->m_in_use = true;
408  return it->second;
409  }
410  m_cond_session_ready.wait(lock);
411  }
412  FrontendPtr f(new Frontend(this));
413  m_clients[package.session()] = f;
414  f->m_in_use = true;
415  return f;
416 }
417 
418 void yf::Sort::Impl::release_frontend(mp::Package &package)
419 {
420  boost::mutex::scoped_lock lock(m_mutex);
421  std::map<mp::Session,yf::Sort::FrontendPtr>::iterator it;
422 
423  it = m_clients.find(package.session());
424  if (it != m_clients.end())
425  {
426  if (package.session().is_closed())
427  {
428  m_clients.erase(it);
429  }
430  else
431  {
432  it->second->m_in_use = false;
433  }
434  m_cond_session_ready.notify_all();
435  }
436 }
437 
438 void yf::Sort::Impl::configure(const xmlNode *ptr, bool test_only,
439  const char *path)
440 {
441  for (ptr = ptr->children; ptr; ptr = ptr->next)
442  {
443  if (ptr->type != XML_ELEMENT_NODE)
444  continue;
445  if (!strcmp((const char *) ptr->name, "sort"))
446  {
447  const struct _xmlAttr *attr;
448  for (attr = ptr->properties; attr; attr = attr->next)
449  {
450  if (!strcmp((const char *) attr->name, "prefetch"))
451  {
452  m_prefetch = mp::xml::get_int(attr->children, -1);
453  if (m_prefetch < 0)
454  {
455  throw mp::filter::FilterException(
456  "Bad or missing value for attribute " +
457  std::string((const char *) attr->name));
458  }
459  }
460  else if (!strcmp((const char *) attr->name, "xpath"))
461  {
462  m_xpath_expr = mp::xml::get_text(attr->children);
463  }
464  else if (!strcmp((const char *) attr->name, "namespaces"))
465  {
466  m_namespaces = mp::xml::get_text(attr->children);
467  }
468  else if (!strcmp((const char *) attr->name, "ascending"))
469  {
470  m_ascending = mp::xml::get_bool(attr->children, true);
471  }
472  else if (!strcmp((const char *) attr->name, "debug"))
473  {
474  m_debug = mp::xml::get_bool(attr->children, false);
475  }
476  else
477  throw mp::filter::FilterException(
478  "Bad attribute " +
479  std::string((const char *) attr->name));
480  }
481  }
482  else
483  {
484  throw mp::filter::FilterException
485  ("Bad element "
486  + std::string((const char *) ptr->name)
487  + " in sort filter");
488  }
489  }
490  if (m_xpath_expr.length() == 0)
491  {
492  throw mp::filter::FilterException
493  ("Missing xpath attribute for config element in sort filter");
494  }
495 
496 }
497 
498 void yf::Sort::Frontend::handle_records(mp::Package &package,
499  Z_APDU *apdu_req,
500  Z_Records *records,
501  Odr_int start_pos,
502  ResultSetPtr s,
503  Odr_oid *syntax,
504  Z_RecordComposition *comp,
505  const char *resultSetId)
506 {
507  if (records && records->which == Z_Records_DBOSD && start_pos == 1)
508  {
509  std::list<RecordListPtr>::const_iterator it = s->record_lists.begin();
510 
511  for (; it != s->record_lists.end(); it++)
512  if ((*it)->cmp(syntax))
513  return;
514 
515  Z_NamePlusRecordList *nprl = records->u.databaseOrSurDiagnostics;
516  int i; // i is number of records fetched in last response
517 
518  int pos = 1;
519  RecordListPtr rlp(new RecordList(syntax,
520  m_p->m_namespaces.c_str(),
521  m_p->m_xpath_expr.c_str(),
522  m_p->m_debug));
523  for (i = 0; i < nprl->num_records; i++, pos++)
524  rlp->add(nprl->records[i]);
525 
526  int end_pos = m_p->m_prefetch;
527  if (end_pos > s->hit_count)
528  end_pos = s->hit_count;
529  while (i && pos <= end_pos)
530  {
531  mp::odr odr;
532  i = 0;
533 
534  Package present_package(package.session(), package.origin());
535  present_package.copy_filter(package);
536 
537  Z_APDU *p_apdu = zget_APDU(odr, Z_APDU_presentRequest);
538  Z_PresentRequest *p_req = p_apdu->u.presentRequest;
539 
540  *p_req->resultSetStartPoint = pos;
541  *p_req->numberOfRecordsRequested = end_pos - pos + 1;
542  p_req->preferredRecordSyntax = syntax;
543  p_req->resultSetId = odr_strdup(odr, resultSetId);
544  p_req->recordComposition = comp;
545 
546  present_package.request() = p_apdu;
547  present_package.move();
548 
549  Z_GDU *gdu_res = present_package.response().get();
550  if (gdu_res && gdu_res->which == Z_GDU_Z3950 &&
551  gdu_res->u.z3950->which == Z_APDU_presentResponse)
552  {
553  Z_PresentResponse *res = gdu_res->u.z3950->u.presentResponse;
554  Z_Records *records = res->records;
555  if (records && records->which == Z_Records_DBOSD)
556  {
557  Z_NamePlusRecordList *nprl =
558  records->u.databaseOrSurDiagnostics;
559  for (i = 0; i < nprl->num_records; i++, pos++)
560  rlp->add(nprl->records[i]);
561  }
562  }
563  }
564  s->record_lists.push_back(rlp);
565  rlp->sort();
566 
567  for (i = 0; i < nprl->num_records; i++)
568  nprl->records[i] = rlp->get(i, m_p->m_ascending);
569  }
570 }
571 
572 void yf::Sort::Frontend::handle_search(mp::Package &package, Z_APDU *apdu_req)
573 {
574  Z_SearchRequest *req = apdu_req->u.searchRequest;
575  std::string resultSetId = req->resultSetName;
576  mp::odr odr;
577  Odr_oid *syntax = 0;
578 
579  if (req->preferredRecordSyntax)
580  syntax = odr_oiddup(odr, req->preferredRecordSyntax);
581 
582  Sets_it sets_it = m_sets.find(req->resultSetName);
583  if (sets_it != m_sets.end())
584  {
585  // result set already exist
586  // if replace indicator is off: we return diagnostic if
587  // result set already exist.
588  if (*req->replaceIndicator == 0)
589  {
590  Z_APDU *apdu =
591  odr.create_searchResponse(
592  apdu_req,
593  YAZ_BIB1_RESULT_SET_EXISTS_AND_REPLACE_INDICATOR_OFF,
594  0);
595  package.response() = apdu;
596  return;
597  }
598  m_sets.erase(resultSetId);
599  }
600  ResultSetPtr s(new ResultSet);
601  m_sets[resultSetId] = s;
602 
603  Package b_package(package.session(), package.origin());
604  b_package.copy_filter(package);
605  b_package.request() = apdu_req;
606  b_package.move();
607 
608  Z_GDU *gdu_res = b_package.response().get();
609  if (gdu_res && gdu_res->which == Z_GDU_Z3950 && gdu_res->u.z3950->which ==
610  Z_APDU_searchResponse)
611  {
612  Z_SearchResponse *res = gdu_res->u.z3950->u.searchResponse;
613  Z_RecordComposition *record_comp =
614  mp::util::piggyback_to_RecordComposition(odr,
615  *res->resultCount, req);
616  s->hit_count = *res->resultCount;
617  handle_records(package, apdu_req, res->records, 1, s,
618  syntax, record_comp, resultSetId.c_str());
619  package.response() = gdu_res;
620  }
621  else
622  package.response() = b_package.response();
623  if (b_package.session().is_closed())
624  b_package.session().close();
625 }
626 
627 void yf::Sort::Frontend::handle_present(mp::Package &package, Z_APDU *apdu_req)
628 {
629  Z_PresentRequest *req = apdu_req->u.presentRequest;
630  std::string resultSetId = req->resultSetId;
631  mp::odr odr;
632  Odr_oid *syntax = 0;
633  Odr_int start = *req->resultSetStartPoint;
634 
635  if (req->preferredRecordSyntax)
636  syntax = odr_oiddup(odr, req->preferredRecordSyntax);
637 
638  Sets_it sets_it = m_sets.find(resultSetId);
639  if (sets_it == m_sets.end())
640  {
641  Z_APDU *apdu =
642  odr.create_presentResponse(
643  apdu_req,
644  YAZ_BIB1_SPECIFIED_RESULT_SET_DOES_NOT_EXIST,
645  resultSetId.c_str());
646  package.response() = apdu;
647  return;
648  }
649  ResultSetPtr rset = sets_it->second;
650  std::list<RecordListPtr>::const_iterator it = rset->record_lists.begin();
651  for (; it != rset->record_lists.end(); it++)
652  if ((*it)->cmp(req->preferredRecordSyntax))
653  {
654  if (*req->resultSetStartPoint - 1 + *req->numberOfRecordsRequested
655  <= (*it)->size())
656  {
657  int i;
658  Z_APDU *p_apdu = zget_APDU(odr, Z_APDU_presentResponse);
659  Z_PresentResponse *p_res = p_apdu->u.presentResponse;
660 
661  *p_res->nextResultSetPosition = *req->resultSetStartPoint +
662  *req->numberOfRecordsRequested;
663  *p_res->numberOfRecordsReturned =
664  *req->numberOfRecordsRequested;
665  p_res->records = (Z_Records *)
666  odr_malloc(odr, sizeof(*p_res->records));
667  p_res->records->which = Z_Records_DBOSD;
668  Z_NamePlusRecordList *nprl = (Z_NamePlusRecordList *)
669  odr_malloc(odr, sizeof(*nprl));
670  p_res->records->u.databaseOrSurDiagnostics = nprl;
671  nprl->num_records = *req->numberOfRecordsRequested;
672  nprl->records = (Z_NamePlusRecord **)
673  odr_malloc(odr, nprl->num_records * sizeof(*nprl->records));
674  for (i = 0; i < nprl->num_records; i++)
675  {
676  int pos = i + *req->resultSetStartPoint - 1;
677  nprl->records[i] = (*it)->get(pos, m_p->m_ascending);
678  }
679  package.response() = p_apdu;
680  return;
681  }
682  break;
683  }
684 
685 
686  Package b_package(package.session(), package.origin());
687  b_package.copy_filter(package);
688  b_package.request() = apdu_req;
689  b_package.move();
690  Z_GDU *gdu_res = b_package.response().get();
691  if (gdu_res && gdu_res->which == Z_GDU_Z3950 && gdu_res->u.z3950->which ==
692  Z_APDU_presentResponse)
693  {
694  Z_PresentResponse *res = gdu_res->u.z3950->u.presentResponse;
695  handle_records(package, apdu_req, res->records,
696  start, rset, syntax, req->recordComposition,
697  resultSetId.c_str());
698  package.response() = gdu_res;
699  }
700  else
701  package.response() = b_package.response();
702  if (b_package.session().is_closed())
703  b_package.session().close();
704 }
705 
706 void yf::Sort::Frontend::handle_package(mp::Package &package)
707 {
708  Z_GDU *gdu = package.request().get();
709  if (gdu && gdu->which == Z_GDU_Z3950)
710  {
711  Z_APDU *apdu_req = gdu->u.z3950;
712  switch (apdu_req->which)
713  {
714  case Z_APDU_searchRequest:
715  handle_search(package, apdu_req);
716  return;
717  case Z_APDU_presentRequest:
718  handle_present(package, apdu_req);
719  return;
720  }
721  }
722  package.move();
723 }
724 
725 void yf::Sort::Impl::process(mp::Package &package)
726 {
727  FrontendPtr f = get_frontend(package);
728  Z_GDU *gdu = package.request().get();
729 
730  if (f->m_is_virtual)
731  {
732  f->handle_package(package);
733  }
734  else if (gdu && gdu->which == Z_GDU_Z3950 && gdu->u.z3950->which ==
735  Z_APDU_initRequest)
736  {
737  package.move();
738  f->m_is_virtual = true;
739  }
740  else
741  package.move();
742 
743  release_frontend(package);
744 }
745 
746 
747 static mp::filter::Base* filter_creator()
748 {
749  return new mp::filter::Sort;
750 }
751 
752 extern "C" {
753  struct metaproxy_1_filter_struct metaproxy_1_filter_sort = {
754  0,
755  "sort",
757  };
758 }
759 
760 
761 /*
762  * Local variables:
763  * c-basic-offset: 4
764  * c-file-style: "Stroustrup"
765  * indent-tabs-mode: nil
766  * End:
767  * vim: shiftwidth=4 tabstop=8 expandtab
768  */
769 
boost::shared_ptr< RecordList > RecordListPtr
Definition: filter_sort.hpp:37
boost::condition m_cond_session_ready
Definition: filter_sort.cpp:56
std::list< RecordListPtr > record_lists
Definition: filter_sort.cpp:95
boost::shared_ptr< Frontend > FrontendPtr
Definition: filter_sort.hpp:34
boost::scoped_ptr< Impl > m_p
Definition: filter_sort.hpp:39
void release_frontend(mp::Package &package)
void process(metaproxy_1::Package &package)
static void print_xpath_nodes(xmlNodeSetPtr nodes, FILE *output)
std::map< mp::Session, FrontendPtr > m_clients
Definition: filter_sort.cpp:57
struct metaproxy_1_filter_struct metaproxy_1_filter_sort
void configure(const xmlNode *ptr, bool test_only, const char *path)
FrontendPtr get_frontend(mp::Package &package)
static mp::filter::Base * filter_creator()
boost::shared_ptr< ResultSet > ResultSetPtr
Definition: filter_sort.hpp:36
std::map< std::string, ResultSetPtr > m_sets
std::map< std::string, ResultSetPtr >::iterator Sets_it