1 /* $Id: filter_z3950_client.cpp,v 1.30 2007-01-26 14:49:22 adam Exp $
2 Copyright (c) 2005-2007, Index Data.
4 See the LICENSE file for details
10 #include "package.hpp"
12 #include "filter_z3950_client.hpp"
19 #include <boost/thread/mutex.hpp>
20 #include <boost/thread/condition.hpp>
24 #include <yaz/otherinfo.h>
25 #include <yaz/diagbib1.h>
27 #include <yazpp/socket-manager.h>
28 #include <yazpp/pdu-assoc.h>
29 #include <yazpp/z-assoc.h>
31 namespace mp = metaproxy_1;
32 namespace yf = mp::filter;
34 namespace metaproxy_1 {
36 class Z3950Client::Assoc : public yazpp_1::Z_Assoc{
38 Assoc(yazpp_1::SocketManager *socket_manager,
39 yazpp_1::IPDU_Observable *PDU_Observable,
40 std::string host, int timeout);
45 void recv_GDU(Z_GDU *gdu, int len);
46 yazpp_1::IPDU_Observer* sessionNotify(
47 yazpp_1::IPDU_Observable *the_PDU_Observable,
50 yazpp_1::SocketManager *m_socket_manager;
51 yazpp_1::IPDU_Observable *m_PDU_Observable;
63 class Z3950Client::Rep {
65 // number of seconds to wait before we give up request
67 std::string m_default_target;
68 std::string m_force_target;
70 boost::condition m_cond_session_ready;
71 std::map<mp::Session,Z3950Client::Assoc *> m_clients;
72 Z3950Client::Assoc *get_assoc(Package &package);
73 void send_and_receive(Package &package,
74 yf::Z3950Client::Assoc *c);
75 void release_assoc(Package &package);
82 yf::Z3950Client::Assoc::Assoc(yazpp_1::SocketManager *socket_manager,
83 yazpp_1::IPDU_Observable *PDU_Observable,
84 std::string host, int timeout_sec)
85 : Z_Assoc(PDU_Observable),
86 m_socket_manager(socket_manager), m_PDU_Observable(PDU_Observable),
87 m_package(0), m_in_use(true), m_waiting(false),
88 m_destroyed(false), m_connected(false), m_queue_len(1),
89 m_time_elapsed(0), m_time_max(timeout_sec),
92 // std::cout << "create assoc " << this << "\n";
95 yf::Z3950Client::Assoc::~Assoc()
97 // std::cout << "destroy assoc " << this << "\n";
100 void yf::Z3950Client::Assoc::connectNotify()
107 void yf::Z3950Client::Assoc::failNotify()
115 Z_GDU *gdu = m_package->request().get();
117 if (gdu && gdu->which == Z_GDU_Z3950)
120 m_package->response() = odr.create_close(apdu, Z_Close_peerAbort, 0);
121 m_package->session().close();
125 void yf::Z3950Client::Assoc::timeoutNotify()
128 if (m_time_elapsed >= m_time_max)
136 Z_GDU *gdu = m_package->request().get();
138 if (gdu && gdu->which == Z_GDU_Z3950)
142 m_package->response() =
143 odr.create_close(apdu, Z_Close_lackOfActivity, 0);
145 m_package->response() =
146 odr.create_close(apdu, Z_Close_peerAbort, 0);
148 m_package->session().close();
153 void yf::Z3950Client::Assoc::recv_GDU(Z_GDU *gdu, int len)
158 m_package->response() = gdu;
161 yazpp_1::IPDU_Observer *yf::Z3950Client::Assoc::sessionNotify(
162 yazpp_1::IPDU_Observable *the_PDU_Observable,
169 yf::Z3950Client::Z3950Client() : m_p(new yf::Z3950Client::Rep)
171 m_p->m_timeout_sec = 30;
174 yf::Z3950Client::~Z3950Client() {
177 yf::Z3950Client::Assoc *yf::Z3950Client::Rep::get_assoc(Package &package)
179 // only one thread messes with the clients list at a time
180 boost::mutex::scoped_lock lock(m_mutex);
182 std::map<mp::Session,yf::Z3950Client::Assoc *>::iterator it;
184 Z_GDU *gdu = package.request().get();
185 // only deal with Z39.50
186 if (!gdu || gdu->which != Z_GDU_Z3950)
191 it = m_clients.find(package.session());
192 if (it != m_clients.end())
194 it->second->m_queue_len++;
198 // double init .. NOT working yet
199 if (gdu && gdu->which == Z_GDU_Z3950 &&
200 gdu->u.z3950->which == Z_APDU_initRequest)
202 yazpp_1::SocketManager *s = it->second->m_socket_manager;
203 delete it->second; // destroy Z_Assoc
204 delete s; // then manager
209 if (!it->second->m_in_use)
211 it->second->m_in_use = true;
214 m_cond_session_ready.wait(lock);
217 // new Z39.50 session ..
218 Z_APDU *apdu = gdu->u.z3950;
219 // check that it is init. If not, close
220 if (apdu->which != Z_APDU_initRequest)
224 package.response() = odr.create_close(apdu,
225 Z_Close_protocolError,
226 "First PDU was not an "
227 "Initialize Request");
228 package.session().close();
231 std::string target = m_force_target;
232 if (!target.length())
234 target = m_default_target;
235 std::list<std::string> vhosts;
236 mp::util::remove_vhost_otherinfo(&apdu->u.initRequest->otherInfo,
238 size_t no_vhosts = vhosts.size();
241 std::list<std::string>::const_iterator v_it = vhosts.begin();
244 else if (no_vhosts == 0)
246 if (!target.length())
248 // no default target. So we don't know where to connect
250 package.response() = odr.create_initResponse(
252 YAZ_BIB1_INIT_NEGOTIATION_OPTION_REQUIRED,
253 "z3950_client: No virtal host given");
255 package.session().close();
259 else if (no_vhosts > 1)
262 package.response() = odr.create_initResponse(
264 YAZ_BIB1_COMBI_OF_SPECIFIED_DATABASES_UNSUPP,
265 "z3950_client: Can not cope with multiple vhosts");
266 package.session().close();
270 std::list<std::string> dblist;
272 mp::util::split_zurl(target, host, dblist);
276 ; // z3950_client: Databases in vhost ignored
279 yazpp_1::SocketManager *sm = new yazpp_1::SocketManager;
280 yazpp_1::PDU_Assoc *pdu_as = new yazpp_1::PDU_Assoc(sm);
281 yf::Z3950Client::Assoc *as = new yf::Z3950Client::Assoc(sm, pdu_as,
284 m_clients[package.session()] = as;
288 void yf::Z3950Client::Rep::send_and_receive(Package &package,
289 yf::Z3950Client::Assoc *c)
291 Z_GDU *gdu = package.request().get();
296 if (!gdu || gdu->which != Z_GDU_Z3950)
299 c->m_time_elapsed = 0;
300 c->m_package = &package;
304 c->client(c->m_host.c_str());
305 c->timeout(1); // so timeoutNotify gets called once per second
307 while (!c->m_destroyed && c->m_waiting
308 && c->m_socket_manager->processEvent() > 0)
317 c->m_time_elapsed = 0;
320 // relay the package ..
322 c->send_GDU(gdu, &len);
324 switch(gdu->u.z3950->which)
326 case Z_APDU_triggerResourceControlRequest:
330 // for the rest: wait for a response PDU
331 while (!c->m_destroyed && c->m_waiting
332 && c->m_socket_manager->processEvent() > 0)
338 void yf::Z3950Client::Rep::release_assoc(Package &package)
340 boost::mutex::scoped_lock lock(m_mutex);
341 std::map<mp::Session,yf::Z3950Client::Assoc *>::iterator it;
343 it = m_clients.find(package.session());
344 if (it != m_clients.end())
346 Z_GDU *gdu = package.request().get();
347 if (gdu && gdu->which == Z_GDU_Z3950)
348 { // only Z39.50 packages lock in get_assoc.. release it
349 it->second->m_in_use = false;
350 it->second->m_queue_len--;
353 if (package.session().is_closed())
355 // destroy hint (send_and_receive)
356 it->second->m_destroyed = true;
358 // wait until no one is waiting for it.
359 while (it->second->m_queue_len)
360 m_cond_session_ready.wait(lock);
362 // the Z_Assoc and PDU_Assoc must be destroyed before
363 // the socket manager.. so pull that out.. first..
364 yazpp_1::SocketManager *s = it->second->m_socket_manager;
365 delete it->second; // destroy Z_Assoc
366 delete s; // then manager
369 m_cond_session_ready.notify_all();
373 void yf::Z3950Client::process(Package &package) const
375 yf::Z3950Client::Assoc *c = m_p->get_assoc(package);
378 m_p->send_and_receive(package, c);
380 m_p->release_assoc(package);
383 void yf::Z3950Client::configure(const xmlNode *ptr)
385 for (ptr = ptr->children; ptr; ptr = ptr->next)
387 if (ptr->type != XML_ELEMENT_NODE)
389 if (!strcmp((const char *) ptr->name, "timeout"))
391 m_p->m_timeout_sec = mp::xml::get_int(ptr->children, 30);
393 else if (!strcmp((const char *) ptr->name, "default_target"))
395 m_p->m_default_target = mp::xml::get_text(ptr);
397 else if (!strcmp((const char *) ptr->name, "force_target"))
399 m_p->m_force_target = mp::xml::get_text(ptr);
403 throw mp::filter::FilterException("Bad element "
404 + std::string((const char *)
410 static mp::filter::Base* filter_creator()
412 return new mp::filter::Z3950Client;
416 struct metaproxy_1_filter_struct metaproxy_1_filter_z3950_client = {
426 * indent-tabs-mode: nil
427 * c-file-style: "stroustrup"
429 * vim: shiftwidth=4 tabstop=8 expandtab