Introduce filter method 'start'
[metaproxy-moved-to-github.git] / src / filter_session_shared.cpp
index e0f835b..f0f84f7 100644 (file)
@@ -1,13 +1,25 @@
-/* $Id: filter_session_shared.cpp,v 1.12 2006-06-19 23:54:02 adam Exp $
-   Copyright (c) 2005-2006, Index Data.
+/* This file is part of Metaproxy.
+   Copyright (C) 2005-2010 Index Data
 
-   See the LICENSE file for details
- */
+Metaproxy is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+Metaproxy is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
 
 #include "config.hpp"
 
-#include "filter.hpp"
-#include "package.hpp"
+#include <metaproxy/filter.hpp>
+#include <metaproxy/package.hpp>
 
 #include <boost/thread/mutex.hpp>
 #include <boost/thread/condition.hpp>
@@ -16,7 +28,7 @@
 #include <boost/shared_ptr.hpp>
 #include <boost/format.hpp>
 
-#include "util.hpp"
+#include <metaproxy/util.hpp>
 #include "filter_session_shared.hpp"
 
 #include <yaz/log.h>
@@ -24,6 +36,7 @@
 #include <yaz/otherinfo.h>
 #include <yaz/diagbib1.h>
 #include <yazpp/z-query.h>
+#include <yazpp/record-cache.h>
 #include <map>
 #include <iostream>
 #include <time.h>
@@ -34,6 +47,7 @@ namespace yf = metaproxy_1::filter;
 namespace metaproxy_1 {
 
     namespace filter {
+        // key for session.. We'll only share sessions with same InitKey
         class SessionShared::InitKey {
         public:
             bool operator < (const SessionShared::InitKey &k) const;
@@ -41,13 +55,13 @@ namespace metaproxy_1 {
             InitKey(const InitKey &);
             ~InitKey();
         private:
-            InitKey &operator = (const InitKey &k);
             char *m_idAuthentication_buf;
             int m_idAuthentication_size;
             char *m_otherInfo_buf;
             int m_otherInfo_size;
             ODR m_odr;
         };
+        // worker thread .. for expiry of sessions
         class SessionShared::Worker {
         public:
             Worker(SessionShared::Rep *rep);
@@ -55,6 +69,7 @@ namespace metaproxy_1 {
         private:
             SessionShared::Rep *m_p;
         };
+        // backend result set
         class SessionShared::BackendSet {
         public:
             std::string m_result_set_id;
@@ -63,6 +78,7 @@ namespace metaproxy_1 {
             yazpp_1::Yaz_Z_Query m_query;
             time_t m_time_last_use;
             void timestamp();
+            yazpp_1::RecordCache m_record_cache;
             BackendSet(
                 const std::string &result_set_id,
                 const Databases &databases,
@@ -70,8 +86,10 @@ namespace metaproxy_1 {
             bool search(
                 Package &frontend_package,
                 const Z_APDU *apdu_req,
-                const BackendInstancePtr bp);
+                const BackendInstancePtr bp,
+                Z_Records **z_records);
         };
+        // backend connection instance
         class SessionShared::BackendInstance {
             friend class Rep;
             friend class BackendClass;
@@ -86,6 +104,7 @@ namespace metaproxy_1 {
             mp::Package * m_close_package;
             ~BackendInstance();
         };
+        // backends of some class (all with same InitKey)
         class SessionShared::BackendClass : boost::noncopyable {
             friend class Rep;
             friend struct Frontend;
@@ -96,7 +115,7 @@ namespace metaproxy_1 {
             BackendInstancePtr get_backend(const Package &package);
             void use_backend(BackendInstancePtr b);
             void release_backend(BackendInstancePtr b);
-            void expire();
+            void expire_class();
             yazpp_1::GDU m_init_request;
             yazpp_1::GDU m_init_response;
             boost::mutex m_mutex_backend_class;
@@ -105,9 +124,13 @@ namespace metaproxy_1 {
             time_t m_backend_expiry_ttl;
             size_t m_backend_set_max;
         public:
-            BackendClass(const yazpp_1::GDU &init_request);
+            BackendClass(const yazpp_1::GDU &init_request,
+                         int resultset_ttl,
+                         int resultset_max,
+                         int session_ttl);
             ~BackendClass();
         };
+        // frontend result set
         class SessionShared::FrontendSet {
             Databases m_databases;
             yazpp_1::Yaz_Z_Query m_query;
@@ -119,14 +142,16 @@ namespace metaproxy_1 {
                 const yazpp_1::Yaz_Z_Query &query);
             FrontendSet();
         };
+        // frontend session
         struct SessionShared::Frontend {
             Frontend(Rep *rep);
             ~Frontend();
             bool m_is_virtual;
             bool m_in_use;
-
+            Z_Options m_init_options;
             void search(Package &package, Z_APDU *apdu);
             void present(Package &package, Z_APDU *apdu);
+            void scan(Package &package, Z_APDU *apdu);
 
             void get_set(mp::Package &package,
                          const Z_APDU *apdu_req,
@@ -141,6 +166,7 @@ namespace metaproxy_1 {
             BackendClassPtr m_backend_class;
             FrontendSets m_frontend_sets;
         };            
+        // representation
         class SessionShared::Rep {
             friend class SessionShared;
             friend struct Frontend;
@@ -153,6 +179,7 @@ namespace metaproxy_1 {
         private:
             void init(Package &package, const Z_GDU *gdu,
                       FrontendPtr frontend);
+            void start();
             boost::mutex m_mutex;
             boost::condition m_cond_session_ready;
             std::map<mp::Session, FrontendPtr> m_clients;
@@ -160,6 +187,9 @@ namespace metaproxy_1 {
             BackendClassMap m_backend_map;
             boost::mutex m_mutex_backend_map;
             boost::thread_group m_thrds;
+            int m_resultset_ttl;
+            int m_resultset_max;
+            int m_session_ttl;
         };
     }
 }
@@ -251,7 +281,15 @@ void yf::SessionShared::BackendClass::remove_backend(BackendInstancePtr b)
     while (it != m_backend_list.end())
     {
         if (*it == b)
+        {
+             mp::odr odr;
+            (*it)->m_close_package->response() = odr.create_close(
+                0, Z_Close_lackOfActivity, 0);
+            (*it)->m_close_package->session().close();
+            (*it)->m_close_package->move();
+            
             it = m_backend_list.erase(it);
+        }
         else
             it++;
     }
@@ -314,7 +352,25 @@ yf::SessionShared::BackendInstancePtr yf::SessionShared::BackendClass::create_ba
 
     init_package.copy_filter(frontend_package);
 
-    init_package.request() = m_init_request;
+    yazpp_1::GDU actual_init_request = m_init_request;
+    Z_GDU *init_pdu = actual_init_request.get();
+
+    assert(init_pdu->which == Z_GDU_Z3950);
+    assert(init_pdu->u.z3950->which == Z_APDU_initRequest);
+
+    Z_InitRequest *req = init_pdu->u.z3950->u.initRequest;
+    ODR_MASK_ZERO(req->options);
+
+    ODR_MASK_SET(req->options, Z_Options_search);
+    ODR_MASK_SET(req->options, Z_Options_present);
+    ODR_MASK_SET(req->options, Z_Options_namedResultSets);
+    ODR_MASK_SET(req->options, Z_Options_scan);
+
+    ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_1);
+    ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_2);
+    ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_3);
+
+    init_package.request() = init_pdu;
 
     init_package.move();
 
@@ -322,13 +378,17 @@ yf::SessionShared::BackendInstancePtr yf::SessionShared::BackendClass::create_ba
 
     m_named_result_sets = false;
     Z_GDU *gdu = init_package.response().get();
-    if (!init_package.session().is_closed()
-        && gdu && gdu->which == Z_GDU_Z3950 
-        && gdu->u.z3950->which == Z_APDU_initResponse)
+    if (init_package.session().is_closed())
     {
+        /* already closed. We don't know why */
+        return null;
+    }
+    else if (gdu && gdu->which == Z_GDU_Z3950 
+             && gdu->u.z3950->which == Z_APDU_initResponse
+             && *gdu->u.z3950->u.initResponse->result)
+    {
+        /* successful init response */
         Z_InitResponse *res = gdu->u.z3950->u.initResponse;
-        if (!*res->result)
-            return null;
         m_init_response = gdu->u.z3950;
         if (ODR_MASK_GET(res->options, Z_Options_namedResultSets))
         {
@@ -337,7 +397,10 @@ yf::SessionShared::BackendInstancePtr yf::SessionShared::BackendClass::create_ba
     }
     else
     {
-        // did not receive an init response or closed
+        /* not init or init rejected */
+        init_package.copy_filter(frontend_package);
+        init_package.session().close();
+        init_package.move();
         return null;
     }
     bp->m_in_use = true;
@@ -350,10 +413,13 @@ yf::SessionShared::BackendInstancePtr yf::SessionShared::BackendClass::create_ba
 }
 
 
-yf::SessionShared::BackendClass::BackendClass(const yazpp_1::GDU &init_request)
+yf::SessionShared::BackendClass::BackendClass(const yazpp_1::GDU &init_request,
+                                              int resultset_ttl,
+                                              int resultset_max,
+                                              int session_ttl)
     : m_named_result_sets(false), m_init_request(init_request),
-      m_sequence_top(0), m_backend_set_ttl(30),
-      m_backend_expiry_ttl(90), m_backend_set_max(10)
+      m_sequence_top(0), m_backend_set_ttl(resultset_ttl),
+      m_backend_expiry_ttl(session_ttl), m_backend_set_max(resultset_max)
 {}
 
 yf::SessionShared::BackendClass::~BackendClass()
@@ -365,6 +431,7 @@ void yf::SessionShared::Rep::init(mp::Package &package, const Z_GDU *gdu,
     Z_InitRequest *req = gdu->u.z3950->u.initRequest;
 
     frontend->m_is_virtual = true;
+    frontend->m_init_options = *req->options;
     InitKey k(req);
     {
         boost::mutex::scoped_lock lock(m_mutex_backend_map);
@@ -372,17 +439,16 @@ void yf::SessionShared::Rep::init(mp::Package &package, const Z_GDU *gdu,
         it = m_backend_map.find(k);
         if (it == m_backend_map.end())
         {
-            BackendClassPtr b(new BackendClass(gdu->u.z3950));
+            BackendClassPtr b(new BackendClass(gdu->u.z3950,
+                                               m_resultset_ttl,
+                                               m_resultset_max,
+                                               m_session_ttl));
             m_backend_map[k] = b;
             frontend->m_backend_class = b;
-            std::cout << "SessionShared::Rep::init new session " 
-                      << frontend->m_backend_class << "\n";
         }
         else
         {
             frontend->m_backend_class = it->second;            
-            std::cout << "SessionShared::Rep::init existing session "
-                      << frontend->m_backend_class << "\n";
         }
     }
     BackendClassPtr bc = frontend->m_backend_class;
@@ -399,10 +465,20 @@ void yf::SessionShared::Rep::init(mp::Package &package, const Z_GDU *gdu,
     else
     {
         boost::mutex::scoped_lock lock(bc->m_mutex_backend_class);
-        Z_GDU *response_gdu = bc->m_init_response.get();
+        yazpp_1::GDU init_response = bc->m_init_response;
+        Z_GDU *response_gdu = init_response.get();
         mp::util::transfer_referenceId(odr, gdu->u.z3950,
                                        response_gdu->u.z3950);
-        package.response() = response_gdu;
+
+        Z_Options *server_options =
+            response_gdu->u.z3950->u.initResponse->options;
+        Z_Options *client_options = &frontend->m_init_options;
+
+        int i;
+        for (i = 0; i<30; i++)
+            if (!ODR_MASK_GET(client_options, i))
+                ODR_MASK_CLEAR(server_options, i);
+        package.response() = init_response;
     }
     if (backend)
         bc->release_backend(backend);
@@ -423,10 +499,16 @@ yf::SessionShared::BackendSet::BackendSet(
     timestamp();
 }
 
+static int get_diagnostic(Z_DefaultDiagFormat *r)
+{
+    return *r->condition;
+}
+
 bool yf::SessionShared::BackendSet::search(
-    Package &frontend_package,
+    mp::Package &frontend_package,
     const Z_APDU *frontend_apdu,
-    const BackendInstancePtr bp)
+    const BackendInstancePtr bp,
+    Z_Records **z_records)
 {
     Package search_package(bp->m_session, frontend_package.origin());
 
@@ -450,56 +532,28 @@ bool yf::SessionShared::BackendSet::search(
     search_package.request() = apdu_req;
 
     search_package.move();
-    
-    Z_Records *z_records_diag = 0;
+
     Z_GDU *gdu = search_package.response().get();
     if (!search_package.session().is_closed()
         && gdu && gdu->which == Z_GDU_Z3950 
         && gdu->u.z3950->which == Z_APDU_searchResponse)
     {
         Z_SearchResponse *b_resp = gdu->u.z3950->u.searchResponse;
-        if (b_resp->records)
-        {
-            if (b_resp->records->which == Z_Records_NSD 
-                || b_resp->records->which == Z_Records_multipleNSD)
-                z_records_diag = b_resp->records;
-        }
-        if (z_records_diag)
-        {
-            if (frontend_apdu->which == Z_APDU_searchRequest)
-            {
-                Z_APDU *f_apdu = odr.create_searchResponse(frontend_apdu, 
-                                                           0, 0);
-                Z_SearchResponse *f_resp = f_apdu->u.searchResponse;
-                f_resp->records = z_records_diag;
-                frontend_package.response() = f_apdu;
-                return false;
-            }
-            if (frontend_apdu->which == Z_APDU_presentRequest)
-            {
-                Z_APDU *f_apdu = odr.create_presentResponse(frontend_apdu, 
-                                                            0, 0);
-                Z_PresentResponse *f_resp = f_apdu->u.presentResponse;
-                f_resp->records = z_records_diag;
-                frontend_package.response() = f_apdu;
-                return false;
-            }
-        }
+        *z_records = b_resp->records;
         m_result_set_size = *b_resp->resultCount;
         return true;
     }
+    Z_APDU *f_apdu = 0;
     if (frontend_apdu->which == Z_APDU_searchRequest)
-    {
-        Z_APDU *f_apdu = 
-            odr.create_searchResponse(frontend_apdu, 1, "Search closed");
-        frontend_package.response() = f_apdu;
-    }
-    if (frontend_apdu->which == Z_APDU_presentRequest)
-    {
-        Z_APDU *f_apdu = 
-            odr.create_presentResponse(frontend_apdu, 1, "Search closed");
-        frontend_package.response() = f_apdu;
-    }
+        f_apdu = odr.create_searchResponse(
+            frontend_apdu, YAZ_BIB1_TEMPORARY_SYSTEM_ERROR, 0);
+    else if (frontend_apdu->which == Z_APDU_presentRequest)
+        f_apdu = odr.create_presentResponse(
+            frontend_apdu, YAZ_BIB1_TEMPORARY_SYSTEM_ERROR, 0);
+    else
+        f_apdu = odr.create_close(
+            frontend_apdu, YAZ_BIB1_TEMPORARY_SYSTEM_ERROR, 0);
+    frontend_package.response() = f_apdu;
     return false;
 }
 
@@ -525,7 +579,6 @@ void yf::SessionShared::Frontend::override_set(
                     found_backend = *it;
                     result_set_id = (*set_it)->m_result_set_id;
                     found_backend->m_sets.erase(set_it);
-                    std::cout << "REUSE TTL SET: " << result_set_id << "\n";
                     return;
                 }
             }
@@ -546,11 +599,9 @@ void yf::SessionShared::Frontend::override_set(
             }
             else
                 result_set_id = "default";
-            std::cout << "AVAILABLE SET: " << result_set_id << "\n";
             return;
         }
     }
-
 }
 
 void yf::SessionShared::Frontend::get_set(mp::Package &package,
@@ -560,11 +611,13 @@ void yf::SessionShared::Frontend::get_set(mp::Package &package,
                                           BackendInstancePtr &found_backend,
                                           BackendSetPtr &found_set)
 {
+    bool session_restarted = false;
+
+restart:
     std::string result_set_id;
     BackendClassPtr bc = m_backend_class;
     {
         boost::mutex::scoped_lock lock(bc->m_mutex_backend_class);
-     
         // look at each backend and see if we have a similar search
         BackendInstanceList::const_iterator it = bc->m_backend_list.begin();
         
@@ -582,8 +635,6 @@ void yf::SessionShared::Frontend::get_set(mp::Package &package,
                         found_backend = *it;
                         bc->use_backend(found_backend);
                         found_set->timestamp();
-                        std::cout << "MATCH SET: " << 
-                            found_set->m_result_set_id << "\n";
                         // found matching set. No need to search again
                         return;
                     }
@@ -598,9 +649,29 @@ void yf::SessionShared::Frontend::get_set(mp::Package &package,
     {
         // create a new backend set (and new set)
         found_backend = bc->create_backend(package);
-        assert(found_backend);
-        std::cout << "NEW " << found_backend << "\n";
-        
+
+        if (!found_backend)
+        {
+            Z_APDU *f_apdu = 0;
+            mp::odr odr;
+            if (apdu_req->which == Z_APDU_searchRequest)
+            {
+                f_apdu = odr.create_searchResponse(
+                        apdu_req, YAZ_BIB1_TEMPORARY_SYSTEM_ERROR, 0);
+            }
+            else if (apdu_req->which == Z_APDU_presentRequest)
+            {
+                f_apdu = odr.create_presentResponse(
+                    apdu_req, YAZ_BIB1_TEMPORARY_SYSTEM_ERROR, 0);
+            }
+            else
+            {
+                f_apdu = odr.create_close(
+                    apdu_req, YAZ_BIB1_TEMPORARY_SYSTEM_ERROR, 0);
+            }
+            package.response() = f_apdu;
+            return;
+        }
         if (bc->m_named_result_sets)
         {
             result_set_id = boost::io::str(
@@ -609,17 +680,80 @@ void yf::SessionShared::Frontend::get_set(mp::Package &package,
         }
         else
             result_set_id = "default";
-        std::cout << "NEW SET: " << result_set_id << "\n";
     }
     // we must search ...
     BackendSetPtr new_set(new BackendSet(result_set_id,
                                          databases, query));
-    if (!new_set->search(package, apdu_req, found_backend))
+    Z_Records *z_records = 0;
+    if (!new_set->search(package, apdu_req, found_backend, &z_records))
     {
-        std::cout << "search error\n";
-        bc->release_backend(found_backend);
+        bc->remove_backend(found_backend);
         return; // search error 
     }
+
+    if (z_records)
+    {
+        int condition = 0;
+        if (z_records->which == Z_Records_NSD)
+        {
+            condition =
+                get_diagnostic(z_records->u.nonSurrogateDiagnostic);
+        }
+        else if (z_records->which == Z_Records_multipleNSD)
+        {
+            if (z_records->u.multipleNonSurDiagnostics->num_diagRecs >= 1
+                && 
+                
+                z_records->u.multipleNonSurDiagnostics->diagRecs[0]->which ==
+                Z_DiagRec_defaultFormat)
+            {
+                condition = get_diagnostic(
+                    z_records->u.multipleNonSurDiagnostics->diagRecs[0]->u.defaultFormat);
+                
+            }
+        }
+        if (!session_restarted &&
+            condition == YAZ_BIB1_TEMPORARY_SYSTEM_ERROR)
+        {
+            bc->remove_backend(found_backend);
+            session_restarted = true;
+            found_backend.reset();
+            goto restart;
+
+        }
+
+        if (condition)
+        {
+            mp::odr odr;
+            if (apdu_req->which == Z_APDU_searchRequest)
+            {
+                Z_APDU *f_apdu = odr.create_searchResponse(apdu_req, 
+                                                           0, 0);
+                Z_SearchResponse *f_resp = f_apdu->u.searchResponse;
+                *f_resp->searchStatus = Z_SearchResponse_none;
+                f_resp->records = z_records;
+                package.response() = f_apdu;
+            }
+            if (apdu_req->which == Z_APDU_presentRequest)
+            {
+                Z_APDU *f_apdu = odr.create_presentResponse(apdu_req, 
+                                                            0, 0);
+                Z_PresentResponse *f_resp = f_apdu->u.presentResponse;
+                f_resp->records = z_records;
+                package.response() = f_apdu;
+            }
+            bc->release_backend(found_backend);
+            return; // search error 
+        }
+    }
+    if (!session_restarted && new_set->m_result_set_size < 0)
+    {
+        bc->remove_backend(found_backend);
+        session_restarted = true;
+        found_backend.reset();
+        goto restart;
+    }
+
     found_set = new_set;
     found_set->timestamp();
     found_backend->m_sets.push_back(found_set);
@@ -662,9 +796,9 @@ void yf::SessionShared::Frontend::search(mp::Package &package,
     BackendInstancePtr found_backend; // null
 
     get_set(package, apdu_req, databases, query, found_backend, found_set);
-
     if (!found_set)
         return;
+
     mp::odr odr;
     Z_APDU *f_apdu = odr.create_searchResponse(apdu_req, 0, 0);
     Z_SearchResponse *f_resp = f_apdu->u.searchResponse;
@@ -709,6 +843,34 @@ void yf::SessionShared::Frontend::present(mp::Package &package,
     if (!found_set)
         return;
 
+    Z_NamePlusRecordList *npr_res = 0;
+    if (found_set->m_record_cache.lookup(odr, &npr_res, 
+                                         *req->resultSetStartPoint,
+                                         *req->numberOfRecordsRequested,
+                                         req->preferredRecordSyntax,
+                                         req->recordComposition))
+    {
+        Z_APDU *f_apdu_res = odr.create_presentResponse(apdu_req, 0, 0);
+        Z_PresentResponse *f_resp = f_apdu_res->u.presentResponse;
+
+        yaz_log(YLOG_LOG, "Found " ODR_INT_PRINTF "+" ODR_INT_PRINTF 
+                " records in cache %p",
+                *req->resultSetStartPoint,                      
+                *req->numberOfRecordsRequested,
+                &found_set->m_record_cache);        
+
+        *f_resp->numberOfRecordsReturned = *req->numberOfRecordsRequested;
+        *f_resp->nextResultSetPosition = 
+            *req->resultSetStartPoint + *req->numberOfRecordsRequested;
+        // f_resp->presentStatus assumed OK.
+        f_resp->records = (Z_Records *) odr_malloc(odr, sizeof(Z_Records));
+        f_resp->records->which = Z_Records_DBOSD;
+        f_resp->records->u.databaseOrSurDiagnostics = npr_res;
+        package.response() = f_apdu_res;
+        bc->release_backend(found_backend);
+        return;
+    }
+                              
     Z_APDU *p_apdu = zget_APDU(odr, Z_APDU_presentRequest);
     Z_PresentRequest *p_req = p_apdu->u.presentRequest;
     p_req->preferredRecordSyntax = req->preferredRecordSyntax;
@@ -740,17 +902,61 @@ void yf::SessionShared::Frontend::present(mp::Package &package,
         f_resp->records = b_resp->records;
         f_resp->otherInfo = b_resp->otherInfo;
         package.response() = f_apdu_res;
+
+        if (b_resp->records && b_resp->records->which ==  Z_Records_DBOSD)
+        {
+            yaz_log(YLOG_LOG, "Adding " ODR_INT_PRINTF "+" ODR_INT_PRINTF
+                    " records to cache %p",
+                    *req->resultSetStartPoint,                      
+                    *f_resp->numberOfRecordsReturned,
+                    &found_set->m_record_cache);        
+            found_set->m_record_cache.add(
+                odr,
+                b_resp->records->u.databaseOrSurDiagnostics,
+                *req->resultSetStartPoint,                      
+                *f_resp->numberOfRecordsReturned);
+        }
         bc->release_backend(found_backend);
     }
     else
     {
         bc->remove_backend(found_backend);
         Z_APDU *f_apdu_res = 
-            odr.create_presentResponse(apdu_req, 1, "present error");
+            odr.create_presentResponse(
+                apdu_req, YAZ_BIB1_TEMPORARY_SYSTEM_ERROR, 0);
         package.response() = f_apdu_res;
     }
 }
 
+void yf::SessionShared::Frontend::scan(mp::Package &frontend_package,
+                                       Z_APDU *apdu_req)
+{
+    BackendClassPtr bc = m_backend_class;
+    BackendInstancePtr backend = bc->get_backend(frontend_package);
+    if (!backend)
+    {
+        mp::odr odr;
+        Z_APDU *apdu = odr.create_scanResponse(
+            apdu_req, YAZ_BIB1_TEMPORARY_SYSTEM_ERROR, 0);
+        frontend_package.response() = apdu;
+    }
+    else
+    {
+        Package scan_package(backend->m_session, frontend_package.origin());
+        scan_package.copy_filter(frontend_package);
+        scan_package.request() = apdu_req;
+        scan_package.move();
+        frontend_package.response() = scan_package.response();
+        if (scan_package.session().is_closed())
+        {
+            frontend_package.session().close();
+            bc->remove_backend(backend);
+        }
+        else
+            bc->release_backend(backend);
+    }
+}
+
 yf::SessionShared::Worker::Worker(SessionShared::Rep *rep) : m_p(rep)
 {
 }
@@ -760,7 +966,7 @@ void yf::SessionShared::Worker::operator() (void)
     m_p->expire();
 }
 
-void yf::SessionShared::BackendClass::expire()
+void yf::SessionShared::BackendClass::expire_class()
 {
     time_t now;
     time(&now);
@@ -768,26 +974,27 @@ void yf::SessionShared::BackendClass::expire()
     BackendInstanceList::iterator bit = m_backend_list.begin();
     while (bit != m_backend_list.end())
     {
-        std::cout << "expiry ";
         time_t last_use = (*bit)->m_time_last_use;
-        if ((now >= last_use && now - last_use > m_backend_expiry_ttl)
+        
+        if ((*bit)->m_in_use)
+        {
+            bit++;
+        }
+        else if ((now >= last_use && now - last_use > m_backend_expiry_ttl)
             || (now < last_use))
         {
             mp::odr odr;
             (*bit)->m_close_package->response() = odr.create_close(
-                0, Z_Close_lackOfActivity, "session expired");
+                0, Z_Close_lackOfActivity, 0);
             (*bit)->m_close_package->session().close();
             (*bit)->m_close_package->move();
 
             bit = m_backend_list.erase(bit);
-            std::cout << "erase";
         }
         else
         {
-            std::cout << "keep";
             bit++;
         }
-        std::cout << std::endl;
     }
 }
 
@@ -799,16 +1006,22 @@ void yf::SessionShared::Rep::expire()
         boost::xtime_get(&xt, boost::TIME_UTC);
         xt.sec += 30;
         boost::thread::sleep(xt);
-        std::cout << "." << std::endl;
         
         BackendClassMap::const_iterator b_it = m_backend_map.begin();
         for (; b_it != m_backend_map.end(); b_it++)
-            b_it->second->expire();
+            b_it->second->expire_class();
     }
 }
 
 yf::SessionShared::Rep::Rep()
 {
+    m_resultset_ttl = 30;
+    m_resultset_max = 10;
+    m_session_ttl = 90;
+}
+
+void yf::SessionShared::Rep::start()
+{
     yf::SessionShared::Worker w(this);
     m_thrds.add_thread(new boost::thread(w));
 }
@@ -820,6 +1033,10 @@ yf::SessionShared::SessionShared() : m_p(new SessionShared::Rep)
 yf::SessionShared::~SessionShared() {
 }
 
+void yf::SessionShared::start() const
+{
+    m_p->start();
+}
 
 yf::SessionShared::Frontend::Frontend(Rep *rep) : m_is_virtual(false), m_p(rep)
 {
@@ -919,6 +1136,10 @@ void yf::SessionShared::process(mp::Package &package) const
         {
             f->present(package, apdu);
         }
+        else if (apdu->which == Z_APDU_scanRequest)
+        {
+            f->scan(package, apdu);
+        }
         else
         {
             mp::odr odr;
@@ -933,6 +1154,54 @@ void yf::SessionShared::process(mp::Package &package) const
     m_p->release_frontend(package);
 }
 
+void yf::SessionShared::configure(const xmlNode *ptr, bool test_only)
+{
+    for (ptr = ptr->children; ptr; ptr = ptr->next)
+    {
+        if (ptr->type != XML_ELEMENT_NODE)
+            continue;
+        if (!strcmp((const char *) ptr->name, "resultset"))
+        {
+            const struct _xmlAttr *attr;
+            for (attr = ptr->properties; attr; attr = attr->next)
+            {
+                if (!strcmp((const char *) attr->name, "ttl"))
+                    m_p->m_resultset_ttl = 
+                        mp::xml::get_int(attr->children, 30);
+                else if (!strcmp((const char *) attr->name, "max"))
+                {
+                    m_p->m_resultset_max = 
+                        mp::xml::get_int(attr->children, 10);
+                }
+                else
+                    throw mp::filter::FilterException(
+                        "Bad attribute " + std::string((const char *)
+                                                       attr->name));
+            }
+        }
+        else if (!strcmp((const char *) ptr->name, "session"))
+        {
+            const struct _xmlAttr *attr;
+            for (attr = ptr->properties; attr; attr = attr->next)
+            {
+                if (!strcmp((const char *) attr->name, "ttl"))
+                    m_p->m_session_ttl = 
+                        mp::xml::get_int(attr->children, 120);
+                else
+                    throw mp::filter::FilterException(
+                        "Bad attribute " + std::string((const char *)
+                                                       attr->name));
+            }
+        }
+        else
+        {
+            throw mp::filter::FilterException("Bad element " 
+                                               + std::string((const char *)
+                                                             ptr->name));
+        }
+    }
+}
+
 static mp::filter::Base* filter_creator()
 {
     return new mp::filter::SessionShared;
@@ -949,8 +1218,9 @@ extern "C" {
 /*
  * Local variables:
  * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
  * indent-tabs-mode: nil
- * c-file-style: "stroustrup"
  * End:
  * vim: shiftwidth=4 tabstop=8 expandtab
  */
+