Start work on on ZOOM filter
[metaproxy-moved-to-github.git] / src / filter_zoom.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 "filter_zoom.hpp"
21 #include <yaz/zoom.h>
22 #include <metaproxy/package.hpp>
23 #include <metaproxy/util.hpp>
24
25 #include <boost/thread/mutex.hpp>
26 #include <boost/thread/condition.hpp>
27
28 #include <yaz/zgdu.h>
29
30 namespace mp = metaproxy_1;
31 namespace yf = mp::filter;
32
33 namespace metaproxy_1 {
34     namespace filter {
35         class Zoom::Backend {
36             friend class Impl;
37             std::string zurl;
38             ZOOM_connection m_connection;
39             ZOOM_resultset m_resultset;
40         };
41         class Zoom::Frontend {
42             friend class Impl;
43             Impl *m_p;
44             bool m_is_virtual;
45             bool m_in_use;
46             yazpp_1::GDU m_init_gdu;
47         public:
48             Frontend(Impl *impl);
49             ~Frontend();
50         };
51         class Zoom::Impl {
52         public:
53             Impl();
54             ~Impl();
55             void process(metaproxy_1::Package & package);
56             void configure(const xmlNode * ptr);
57         private:
58             FrontendPtr get_frontend(mp::Package &package);
59             void release_frontend(mp::Package &package);
60
61             std::map<mp::Session, FrontendPtr> m_clients;            
62             boost::mutex m_mutex;
63             boost::condition m_cond_session_ready;
64         };
65     }
66 }
67
68 // define Pimpl wrapper forwarding to Impl
69  
70 yf::Zoom::Zoom() : m_p(new Impl)
71 {
72 }
73
74 yf::Zoom::~Zoom()
75 {  // must have a destructor because of boost::scoped_ptr
76 }
77
78 void yf::Zoom::configure(const xmlNode *xmlnode, bool test_only)
79 {
80     m_p->configure(xmlnode);
81 }
82
83 void yf::Zoom::process(mp::Package &package) const
84 {
85     m_p->process(package);
86 }
87
88
89 // define Implementation stuff
90
91 yf::Zoom::Frontend::Frontend(Impl *impl) : 
92     m_p(impl), m_is_virtual(false), m_in_use(true)
93 {
94 }
95
96 yf::Zoom::Frontend::~Frontend()
97 {
98 }
99
100 yf::Zoom::FrontendPtr yf::Zoom::Impl::get_frontend(mp::Package &package)
101 {
102     boost::mutex::scoped_lock lock(m_mutex);
103
104     std::map<mp::Session,yf::Zoom::FrontendPtr>::iterator it;
105     
106     while(true)
107     {
108         it = m_clients.find(package.session());
109         if (it == m_clients.end())
110             break;
111         
112         if (!it->second->m_in_use)
113         {
114             it->second->m_in_use = true;
115             return it->second;
116         }
117         m_cond_session_ready.wait(lock);
118     }
119     FrontendPtr f(new Frontend(this));
120     m_clients[package.session()] = f;
121     f->m_in_use = true;
122     return f;
123 }
124
125 void yf::Zoom::Impl::release_frontend(mp::Package &package)
126 {
127     boost::mutex::scoped_lock lock(m_mutex);
128     std::map<mp::Session,yf::Zoom::FrontendPtr>::iterator it;
129     
130     it = m_clients.find(package.session());
131     if (it != m_clients.end())
132     {
133         if (package.session().is_closed())
134         {
135             m_clients.erase(it);
136         }
137         else
138         {
139             it->second->m_in_use = false;
140         }
141         m_cond_session_ready.notify_all();
142     }
143 }
144
145 yf::Zoom::Impl::Impl()
146 {
147 }
148
149 yf::Zoom::Impl::~Impl()
150
151 }
152
153 void yf::Zoom::Impl::configure(const xmlNode *xmlnode)
154 {
155 }
156
157 void yf::Zoom::Impl::process(mp::Package &package)
158 {
159     FrontendPtr f = get_frontend(package);
160     Z_GDU *gdu = package.request().get();
161
162     if (f->m_is_virtual)
163     {
164         if (gdu->which == Z_GDU_Z3950)
165         {
166             Z_APDU *apdu = gdu->u.z3950;
167             mp::odr odr;
168             
169             package.response() = odr.create_close(
170                 apdu,
171                 Z_Close_protocolError,
172                 "not implemented");
173         }
174         package.session().close();
175     }
176     else if (gdu && gdu->which == Z_GDU_Z3950 && gdu->u.z3950->which ==
177              Z_APDU_initRequest)
178     {
179         Z_InitRequest *req = gdu->u.z3950->u.initRequest;
180         f->m_init_gdu = gdu;
181         
182         mp::odr odr;
183         Z_APDU *apdu = odr.create_initResponse(gdu->u.z3950, 0, 0);
184         Z_InitResponse *resp = apdu->u.initResponse;
185         
186         int i;
187         static const int masks[] = {
188             Z_Options_search,
189             Z_Options_present,
190             -1 
191         };
192         for (i = 0; masks[i] != -1; i++)
193             if (ODR_MASK_GET(req->options, masks[i]))
194                 ODR_MASK_SET(resp->options, masks[i]);
195         
196         static const int versions[] = {
197             Z_ProtocolVersion_1,
198             Z_ProtocolVersion_2,
199             Z_ProtocolVersion_3,
200             -1
201         };
202         for (i = 0; versions[i] != -1; i++)
203             if (ODR_MASK_GET(req->protocolVersion, versions[i]))
204                 ODR_MASK_SET(resp->protocolVersion, versions[i]);
205             else
206                 break;
207         
208         *resp->preferredMessageSize = *req->preferredMessageSize;
209         *resp->maximumRecordSize = *req->maximumRecordSize;
210         
211         package.response() = apdu;
212         f->m_is_virtual = true;
213     }
214     else
215         package.move();
216
217     release_frontend(package);
218 }
219
220
221 static mp::filter::Base* filter_creator()
222 {
223     return new mp::filter::Zoom;
224 }
225
226 extern "C" {
227     struct metaproxy_1_filter_struct metaproxy_1_filter_zoom = {
228         0,
229         "zoom",
230         filter_creator
231     };
232 }
233
234
235 /*
236  * Local variables:
237  * c-basic-offset: 4
238  * c-file-style: "Stroustrup"
239  * indent-tabs-mode: nil
240  * End:
241  * vim: shiftwidth=4 tabstop=8 expandtab
242  */
243