Starting to refactor session cache, eliminated IConfig class.
[shibboleth/cpp-sp.git] / shib-target / shib-ccache.cpp
index c0dee5b..bcbfa9d 100644 (file)
 # include <unistd.h>
 #endif
 
-#include <shib/shib-threads.h>
-
-#include <log4cpp/Category.hh>
-
+#include <ctime>
 #include <algorithm>
 #include <sstream>
 #include <stdexcept>
+#include <shibsp/SPConfig.h>
 
 #ifdef HAVE_LIBDMALLOCXX
 #include <dmalloc.h>
 #endif
 
-using namespace std;
-using namespace log4cpp;
-using namespace saml;
-using namespace shibboleth;
+using namespace shibsp;
 using namespace shibtarget;
+using namespace saml;
+using namespace opensaml::saml2md;
+using namespace xmltooling;
+using namespace log4cpp;
+using namespace std;
+using xmlsignature::CredentialResolver;
 
 static const XMLCh cleanupInterval[] =
 { chLatin_c, chLatin_l, chLatin_e, chLatin_a, chLatin_n, chLatin_u, chLatin_p,
@@ -75,6 +76,10 @@ static const XMLCh propagateErrors[] =
 { chLatin_p, chLatin_r, chLatin_o, chLatin_p, chLatin_a, chLatin_g, chLatin_a, chLatin_t, chLatin_e,
   chLatin_E, chLatin_r, chLatin_r, chLatin_o, chLatin_r, chLatin_s, chNull
 };
+static const XMLCh writeThrough[] =
+{ chLatin_w, chLatin_r, chLatin_i, chLatin_t, chLatin_e,
+  chLatin_T, chLatin_h, chLatin_r, chLatin_o, chLatin_u, chLatin_g, chLatin_h, chNull
+};
 
 
 /*
@@ -170,7 +175,7 @@ public:
 
     string insert(
         const IApplication* application,
-        const IEntityDescriptor* source,
+        const RoleDescriptor* role,
         const char* client_addr,
         const SAMLSubject* subject,
         const char* authnContext,
@@ -194,7 +199,7 @@ StubCache::StubCache(const DOMElement* e) : m_log(&Category::getInstance(SHIBT_L
 
 string StubCache::insert(
     const IApplication* application,
-    const IEntityDescriptor* source,
+    const RoleDescriptor* role,
     const char* client_addr,
     const SAMLSubject* subject,
     const char* authnContext,
@@ -206,7 +211,7 @@ string StubCache::insert(
     in.structure();
     in.addmember("application_id").string(application->getId());
     in.addmember("client_address").string(client_addr);
-    auto_ptr_char provid(source->getId());
+    xmltooling::auto_ptr_char provid(dynamic_cast<EntityDescriptor*>(role->getParent())->getEntityID());
     in.addmember("provider_id").string(provid.get());
     in.addmember("major_version").integer(1);
     in.addmember("minor_version").integer(tokens->getMinorVersion());
@@ -219,10 +224,10 @@ string StubCache::insert(
     os << *tokens;
     in.addmember("tokens.unfiltered").string(os.str().c_str());
 
-    out=ShibTargetConfig::getConfig().getINI()->getListener()->send(in);
+    out=SPConfig::getConfig().getServiceProvider()->getListenerService()->send(in);
     if (out["key"].isstring())
         return out["key"].string();
-    throw InvalidSessionException("A remoted cache insertion operation did not return a usable session key.");
+    throw opensaml::RetryableProfileException("A remoted cache insertion operation did not return a usable session key.");
 }
 
 ISessionCacheEntry* StubCache::find(const char* key, const IApplication* application, const char* client_addr)
@@ -235,7 +240,7 @@ ISessionCacheEntry* StubCache::find(const char* key, const IApplication* applica
     in.addmember("client_address").string(client_addr);
     
     try {
-        out=ShibTargetConfig::getConfig().getINI()->getListener()->send(in);
+        out=SPConfig::getConfig().getServiceProvider()->getListenerService()->send(in);
         if (!out.isstruct()) {
             out.destroy();
             return NULL;
@@ -259,7 +264,7 @@ void StubCache::remove(const char* key, const IApplication* application, const c
     in.addmember("application_id").string(application->getId());
     in.addmember("client_address").string(client_addr);
     
-    ShibTargetConfig::getConfig().getINI()->getListener()->send(in);
+    SPConfig::getConfig().getServiceProvider()->getListenerService()->send(in);
 }
 
 /*
@@ -274,7 +279,7 @@ public:
         MemorySessionCache* cache,
         const char* key,
         const IApplication* application,
-        const IEntityDescriptor* source,
+        const RoleDescriptor* role,
         const char* client_addr,
         const SAMLSubject* subject,
         const char* authnContext,
@@ -284,7 +289,7 @@ public:
         MemorySessionCache* cache,
         const char* key,
         const IApplication* application,
-        const IEntityDescriptor* source,
+        const RoleDescriptor* role,
         const char* client_addr,
         const char* subject,
         const char* authnContext,
@@ -300,7 +305,7 @@ public:
     void unlock() { m_lock->unlock(); }
     
     HRESULT isValid(const IApplication* application, const char* client_addr) const;
-    bool populate() const;            // wraps process of maintaining attributes, returns true iff data reused
+    void populate(const IApplication* application, const EntityDescriptor* source, bool initial=false) const;
     bool checkApplication(const IApplication* application) { return (m_obj["application_id"]==application->getId()); }
     time_t created() const { return m_sessionCreated; }
     time_t lastAccess() const { return m_lastAccess; }
@@ -309,8 +314,8 @@ public:
 private:
     bool hasAttributes(const SAMLResponse& r) const;
     time_t calculateExpiration(const SAMLResponse& r) const;
-    pair<SAMLResponse*,SAMLResponse*> getNewResponse() const;   // wraps an actual query
-    SAMLResponse* filter(const SAMLResponse* r, const IApplication* application, const IEntityDescriptor* source) const;
+    pair<SAMLResponse*,SAMLResponse*> getNewResponse(const IApplication* application, const EntityDescriptor* source) const;
+    SAMLResponse* filter(const SAMLResponse* r, const IApplication* application, const RoleDescriptor* role) const;
   
     time_t m_sessionCreated;
     mutable time_t m_responseExpiration, m_lastAccess, m_lastRetry;
@@ -322,7 +327,7 @@ private:
 /*
  * The actual in-memory session cache implementation.
  */
-class MemorySessionCache : public virtual ISessionCache, public virtual IRemoted
+class MemorySessionCache : public virtual ISessionCache, public virtual Remoted
 {
 public:
     MemorySessionCache(const DOMElement* e);
@@ -332,7 +337,7 @@ public:
 
     string insert(
         const IApplication* application,
-        const IEntityDescriptor* source,
+        const RoleDescriptor* role,
         const char* client_addr,
         const SAMLSubject* subject,
         const char* authnContext,
@@ -346,18 +351,17 @@ public:
     bool setBackingStore(ISessionCacheStore* store);
 
 private:
-    void dormant(const char* key);
-
     const DOMElement* m_root;         // Only valid during initialization
     RWLock* m_lock;
     map<string,MemorySessionCacheEntry*> m_hashtable;
 
     Category* m_log;
-    IRemoted* restoreInsert;
-    IRemoted* restoreFind;
-    IRemoted* restoreRemove;
+    Remoted* restoreInsert;
+    Remoted* restoreFind;
+    Remoted* restoreRemove;
     ISessionCacheStore* m_sink;
 
+    void dormant(const char* key);
     static void* cleanup_fcn(void*);
     bool shutdown;
     CondWait* shutdown_wait;
@@ -366,7 +370,7 @@ private:
     // extracted config settings
     unsigned int m_AATimeout,m_AAConnectTimeout;
     unsigned int m_defaultLifetime,m_retryInterval;
-    bool m_strictValidity,m_propagateErrors;
+    bool m_strictValidity,m_propagateErrors,m_writeThrough;
     friend class MemorySessionCacheEntry;
 };
 
@@ -374,7 +378,7 @@ MemorySessionCacheEntry::MemorySessionCacheEntry(
     MemorySessionCache* cache,
     const char* key,
     const IApplication* application,
-    const IEntityDescriptor* source,
+    const RoleDescriptor* role,
     const char* client_addr,
     const SAMLSubject* subject,
     const char* authnContext,
@@ -388,7 +392,7 @@ MemorySessionCacheEntry::MemorySessionCacheEntry(
     m_obj.addmember("key").string(key);
     m_obj.addmember("client_address").string(client_addr);
     m_obj.addmember("application_id").string(application->getId());
-    auto_ptr_char pid(source->getId());
+    xmltooling::auto_ptr_char pid(dynamic_cast<EntityDescriptor*>(role->getParent())->getEntityID());
     m_obj.addmember("provider_id").string(pid.get());
     m_obj.addmember("major_version").integer(1);
     m_obj.addmember("minor_version").integer(tokens->getMinorVersion());
@@ -408,7 +412,7 @@ MemorySessionCacheEntry::MemorySessionCacheEntry(
 
     if (hasAttributes(*tokens)) {
         // Filter attributes in the response.
-        auto_ptr<SAMLResponse> filtered(filter(tokens, application, source));
+        auto_ptr<SAMLResponse> filtered(filter(tokens, application, role));
         
         // Calculate expiration.
         m_responseExpiration=calculateExpiration(*(filtered.get()));
@@ -422,7 +426,7 @@ MemorySessionCacheEntry::MemorySessionCacheEntry(
 
         // Save actual objects only if we're running inprocess. The subject needs to be
         // owned by the entry, so we'll defer creation of a cloned copy.
-        if (ShibTargetConfig::getConfig().isEnabled(ShibTargetConfig::InProcess)) {
+        if (SPConfig::getConfig().isEnabled(SPConfig::InProcess)) {
             if (m_obj["tokens.filtered"].isstring())
                 m_pFiltered=filtered.release();
         }
@@ -435,7 +439,7 @@ MemorySessionCacheEntry::MemorySessionCacheEntry(
     }
 
     // Transaction Logging
-    auto_ptr_char hname(subject->getNameIdentifier()->getName());
+    xmltooling::auto_ptr_char hname(subject->getNameIdentifier()->getName());
     STConfig& stc=static_cast<STConfig&>(ShibTargetConfig::getConfig());
     stc.getTransactionLog().infoStream() <<
         "New session (ID: " <<
@@ -456,7 +460,7 @@ MemorySessionCacheEntry::MemorySessionCacheEntry(
     MemorySessionCache* cache,
     const char* key,
     const IApplication* application,
-    const IEntityDescriptor* source,
+    const RoleDescriptor* role,
     const char* client_addr,
     const char* subject,
     const char* authnContext,
@@ -479,7 +483,7 @@ MemorySessionCacheEntry::MemorySessionCacheEntry(
     m_obj.addmember("key").string(key);
     m_obj.addmember("client_address").string(client_addr);
     m_obj.addmember("application_id").string(application->getId());
-    auto_ptr_char pid(source->getId());
+    xmltooling::auto_ptr_char pid(dynamic_cast<EntityDescriptor*>(role->getParent())->getEntityID());
     m_obj.addmember("provider_id").string(pid.get());
     m_obj.addmember("subject").string(subject);
     m_obj.addmember("authn_context").string(authnContext);
@@ -488,7 +492,7 @@ MemorySessionCacheEntry::MemorySessionCacheEntry(
     m_obj.addmember("minor_version").integer(minorVersion);
 
     if (hasAttributes(*(unfiltered.get()))) {
-        auto_ptr<SAMLResponse> filtered(filter(unfiltered.get(), application, source));
+        auto_ptr<SAMLResponse> filtered(filter(unfiltered.get(), application, role));
     
         // Calculate expiration.
         m_responseExpiration=calculateExpiration(*(filtered.get()));
@@ -501,7 +505,7 @@ MemorySessionCacheEntry::MemorySessionCacheEntry(
             m_obj.addmember("tokens.filtered").string(fstr.c_str());
 
         // Save actual objects only if we're running inprocess.
-        if (ShibTargetConfig::getConfig().isEnabled(ShibTargetConfig::InProcess)) {
+        if (SPConfig::getConfig().isEnabled(SPConfig::InProcess)) {
             m_pUnfiltered=unfiltered.release();
             if (m_obj["tokens.filtered"].isstring())
                 m_pFiltered=filtered.release();
@@ -517,21 +521,19 @@ MemorySessionCacheEntry::MemorySessionCacheEntry(
 
 MemorySessionCacheEntry::~MemorySessionCacheEntry()
 {
-    if (m_log->isDebugEnabled())
-        m_log->debug("deleting cache entry (ID: %s)", m_obj["key"].string());
     delete m_lock;
 }
 
 HRESULT MemorySessionCacheEntry::isValid(const IApplication* app, const char* client_addr) const
 {
 #ifdef _DEBUG
-    saml::NDC ndc("isValid");
+    xmltooling::NDC ndc("isValid");
 #endif
 
     // Obtain validation rules from application settings.
     bool consistentIPAddress=true;
     int lifetime=0,timeout=0;
-    const IPropertySet* props=app->getPropertySet("Sessions");
+    const PropertySet* props=app->getPropertySet("Sessions");
     if (props) {
         pair<bool,unsigned int> p=props->getUnsignedInt("lifetime");
         if (p.first)
@@ -555,8 +557,19 @@ HRESULT MemorySessionCacheEntry::isValid(const IApplication* app, const char* cl
     }
 
     if (timeout > 0 && now-m_lastAccess >= timeout) {
-        m_log->info("session timed out (ID: %s)", m_obj["key"].string());
-        return SESSION_E_EXPIRED;
+        // May need to query sink first to find out if another cluster member has been used.
+        if (m_cache->m_sink && m_cache->m_writeThrough) {
+            if (NOERROR!=m_cache->m_sink->onRead(m_obj["key"].string(),m_lastAccess))
+                m_log->error("cache store failed to return last access timestamp");
+            if (now-m_lastAccess >= timeout) {
+                m_log->info("session timed out (ID: %s)", m_obj["key"].string());
+                return SESSION_E_EXPIRED;
+            }
+        }
+        else {
+            m_log->info("session timed out (ID: %s)", m_obj["key"].string());
+            return SESSION_E_EXPIRED;
+        }
     }
 
     if (consistentIPAddress) {
@@ -569,7 +582,14 @@ HRESULT MemorySessionCacheEntry::isValid(const IApplication* app, const char* cl
     }
 
     m_lastAccess=now;
-    return populate() ? NOERROR : S_FALSE;
+
+    if (m_cache->m_sink && m_cache->m_writeThrough && timeout > 0) {
+        // Update sink with last access data, if possible.
+        if (FAILED(m_cache->m_sink->onUpdate(m_obj["key"].string(),NULL,m_lastAccess)))
+            m_log->error("cache store failed to update last access timestamp");
+    }
+
+    return NOERROR;
 }
 
 bool MemorySessionCacheEntry::hasAttributes(const SAMLResponse& r) const
@@ -619,18 +639,74 @@ time_t MemorySessionCacheEntry::calculateExpiration(const SAMLResponse& r) const
     return expiration;
 }
 
-bool MemorySessionCacheEntry::populate() const
+void MemorySessionCacheEntry::populate(const IApplication* application, const EntityDescriptor* source, bool initial) const
 {
 #ifdef _DEBUG
-    saml::NDC ndc("populate");
+    xmltooling::NDC ndc("populate");
 #endif
 
     // Do we have any attribute data cached?
     if (m_responseExpiration > 0) {
         // Can we use what we have?
         if (time(NULL) < m_responseExpiration)
-            return true;
-      
+            return;
+        
+        // Possibly check the sink in case another cluster member already refreshed it.
+        if (m_cache->m_sink && m_cache->m_writeThrough) {
+            string tokensFromSink;
+            HRESULT hr=m_cache->m_sink->onRead(m_obj["key"].string(),tokensFromSink);
+            if (FAILED(hr))
+                m_log->error("cache store failed to return updated tokens");
+            else if (hr==NOERROR && tokensFromSink!=m_obj["tokens.unfiltered"].string()) {
+
+                // Bah...find role again.
+                const RoleDescriptor* role=source->getAttributeAuthorityDescriptor(samlconstants::SAML11_PROTOCOL_ENUM);
+                if (!role)
+                    role=source->getAttributeAuthorityDescriptor(samlconstants::SAML10_PROTOCOL_ENUM);
+                if (!role)
+                    role=source->getIDPSSODescriptor(samlconstants::SAML11_PROTOCOL_ENUM);
+                if (!role)
+                    role=source->getIDPSSODescriptor(samlconstants::SAML10_PROTOCOL_ENUM);
+                if (!role) {
+                    throw MetadataException("Unable to locate attribute-issuing role in metadata.");
+                }
+
+                // The tokens in the sink were different.
+                istringstream is(tokensFromSink);
+                auto_ptr<SAMLResponse> respFromSink(new SAMLResponse(is,m_obj["minor_version"].integer()));
+                auto_ptr<SAMLResponse> filteredFromSink(filter(respFromSink.get(),application,role));
+                time_t expFromSink=calculateExpiration(*(filteredFromSink.get()));
+                
+                // Recheck to see if the new tokens are valid.
+                if (expFromSink < time(NULL)) {
+                    m_log->info("loading replacement tokens into memory from cache store");
+                    m_obj["tokens"].destroy();
+                    delete m_pUnfiltered;
+                    delete m_pFiltered;
+                    m_pUnfiltered=m_pFiltered=NULL;
+                    m_obj.addmember("tokens.unfiltered").string(tokensFromSink.c_str());
+
+                    // Serialize filtered assertions (if changes were made).
+                    ostringstream os;
+                    os << *(filteredFromSink.get());
+                    string fstr=os.str();
+                    if (fstr.length() != m_obj.getmember("tokens.unfiltered").strlen())
+                        m_obj.addmember("tokens.filtered").string(fstr.c_str());
+                    
+                    // Save actual objects only if we're running inprocess.
+                    if (SPConfig::getConfig().isEnabled(SPConfig::InProcess)) {
+                        m_pUnfiltered=respFromSink.release();
+                        if (m_obj["tokens.filtered"].isstring())
+                            m_pFiltered=filteredFromSink.release();
+                    }
+
+                    m_responseExpiration=expFromSink;
+                    m_lastRetry=0;
+                    return;
+                }
+            }
+        }
+
         // If we're being strict, dump what we have and reset timestamps.
         if (m_cache->m_strictValidity) {
             m_log->info("strictly enforcing attribute validity, dumping expired data");
@@ -639,12 +715,16 @@ bool MemorySessionCacheEntry::populate() const
             delete m_pFiltered;
             m_pUnfiltered=m_pFiltered=NULL;
             m_responseExpiration=0;
-            m_lastRetry=0; 
+            m_lastRetry=0;
+            if (m_cache->m_sink) {
+                if (FAILED(m_cache->m_sink->onUpdate(m_obj["key"].string(),"")))
+                    m_log->error("cache store returned failure while clearing tokens from entry");
+            }
         }
     }
 
     try {
-        pair<SAMLResponse*,SAMLResponse*> new_responses=getNewResponse();
+        pair<SAMLResponse*,SAMLResponse*> new_responses=getNewResponse(application,source);
         auto_ptr<SAMLResponse> r1(new_responses.first),r2(new_responses.second);
         if (new_responses.first) {
             m_obj["tokens"].destroy();
@@ -669,24 +749,29 @@ bool MemorySessionCacheEntry::populate() const
             m_responseExpiration=calculateExpiration(*new_responses.second);
 
             // Save actual objects only if we're running inprocess.
-            if (ShibTargetConfig::getConfig().isEnabled(ShibTargetConfig::InProcess)) {
+            if (SPConfig::getConfig().isEnabled(SPConfig::InProcess)) {
                 m_pUnfiltered=r1.release();
                 if (m_obj["tokens.filtered"].isstring())
                     m_pFiltered=r2.release();
             }
 
+            // Update backing store.
+            if (!initial && m_cache->m_sink) {
+                if (FAILED(m_cache->m_sink->onUpdate(m_obj["key"].string(),m_obj["tokens.unfiltered"].string())))
+                    m_log->error("cache store returned failure while updating tokens in entry");
+            }
+
             m_lastRetry=0;
             m_log->debug("fetched and stored new response");
             STConfig& stc=static_cast<STConfig&>(ShibTargetConfig::getConfig());
             stc.getTransactionLog().infoStream() <<  "Successful attribute query for session (ID: " << m_obj["key"].string() << ")";
             stc.releaseTransactionLog();
-            return false;
         }
     }
-    catch (SAMLException&) {
+    catch (exception&) {
         if (m_cache->m_propagateErrors)
             throw;
-        m_log->warn("suppressed SAML exception caught while trying to fetch attributes");
+        m_log->warn("suppressed exception caught while trying to fetch attributes");
     }
 #ifndef _DEBUG
     catch (...) {
@@ -695,13 +780,14 @@ bool MemorySessionCacheEntry::populate() const
         m_log->warn("suppressed unknown exception caught while trying to fetch attributes");
     }
 #endif
-    return true;
 }
 
-pair<SAMLResponse*,SAMLResponse*> MemorySessionCacheEntry::getNewResponse() const
+pair<SAMLResponse*,SAMLResponse*> MemorySessionCacheEntry::getNewResponse(
+    const IApplication* application, const EntityDescriptor* source
+    ) const
 {
 #ifdef _DEBUG
-    saml::NDC ndc("getNewResponse");
+    xmltooling::NDC ndc("getNewResponse");
 #endif
 
     // The retryInterval determines how often to poll an AA that might be down.
@@ -727,31 +813,15 @@ pair<SAMLResponse*,SAMLResponse*> MemorySessionCacheEntry::getNewResponse() cons
     stc.releaseTransactionLog();
 
 
-    // Caller must be holding the config lock.
-    // Lookup application for session to get providerId and attributes to request.
-    IConfig* conf=ShibTargetConfig::getConfig().getINI();
-    const IApplication* application=conf->getApplication(m_obj["application_id"].string());
-    if (!application) {
-        m_log->crit("unable to locate application for session, deleted?");
-        throw SAMLException("Unable to locate application for session, deleted?");
-    }
     pair<bool,const XMLCh*> providerID=application->getXMLString("providerId");
     if (!providerID.first) {
         m_log->crit("unable to determine ProviderID for application, not set?");
-        throw SAMLException("Unable to determine ProviderID for application, not set?");
-    }
-
-    // Try this request.
-    Metadata m(application->getMetadataProviders());
-    const IEntityDescriptor* site=m.lookup(m_obj["provider_id"].string());
-    if (!site) {
-        m_log->error("unable to locate identity provider's metadata for attribute query");
-        throw MetadataException("Unable to locate identity provider's metadata for attribute query.");
+        throw ConfigurationException("Unable to determine ProviderID for application, not set?");
     }
 
     // Try to locate an AA role.
-    const IAttributeAuthorityDescriptor* AA=site->getAttributeAuthorityDescriptor(
-        m_obj["minor_version"].integer()==1 ? saml::XML::SAML11_PROTOCOL_ENUM : saml::XML::SAML10_PROTOCOL_ENUM
+    const AttributeAuthorityDescriptor* AA=source->getAttributeAuthorityDescriptor(
+        m_obj["minor_version"].integer()==1 ? samlconstants::SAML11_PROTOCOL_ENUM : samlconstants::SAML10_PROTOCOL_ENUM
         );
     if (!AA) {
         m_log->warn("unable to locate metadata for identity provider's Attribute Authority");
@@ -759,7 +829,7 @@ pair<SAMLResponse*,SAMLResponse*> MemorySessionCacheEntry::getNewResponse() cons
     }
 
     // Get protocol signing policy.
-    const IPropertySet* credUse=application->getCredentialUse(site);
+    const PropertySet* credUse=application->getCredentialUse(source);
     pair<bool,bool> signRequest=credUse ? credUse->getBool("signRequest") : make_pair(false,false);
     pair<bool,const char*> signatureAlg=credUse ? credUse->getString("signatureAlg") : pair<bool,const char*>(false,NULL);
     if (!signatureAlg.first)
@@ -785,8 +855,7 @@ pair<SAMLResponse*,SAMLResponse*> MemorySessionCacheEntry::getNewResponse() cons
         // Build a SAML Request....
         SAMLAttributeQuery* q=new SAMLAttributeQuery(
             new SAMLSubject(nameid),
-            providerID.second,
-            application->getAttributeDesignators().clone()
+            providerID.second
             );
         auto_ptr<SAMLRequest> req(new SAMLRequest(q));
         req->setMinorVersion(m_obj["minor_version"].integer());
@@ -794,10 +863,11 @@ pair<SAMLResponse*,SAMLResponse*> MemorySessionCacheEntry::getNewResponse() cons
         // Sign it?
         if (signRequest.first && signRequest.second && signingCred.first) {
             if (req->getMinorVersion()==1) {
-                Credentials creds(conf->getCredentialsProviders());
-                const ICredResolver* cr=creds.lookup(signingCred.second);
-                if (cr)
+                CredentialResolver* cr=SPConfig::getConfig().getServiceProvider()->getCredentialResolver(signingCred.second);
+                if (cr) {
+                    xmltooling::Locker locker(cr);
                     req->sign(cr->getKey(),cr->getCertificates(),signatureAlg.second,digestAlg.second);
+                }
                 else
                     m_log->error("unable to sign attribute query, specified credential (%s) was not found",signingCred.second);
             }
@@ -809,42 +879,31 @@ pair<SAMLResponse*,SAMLResponse*> MemorySessionCacheEntry::getNewResponse() cons
 
         // Call context object
         ShibHTTPHook::ShibHTTPHookCallContext ctx(credUse,AA);
-        Trust t(application->getTrustProviders());
         
         // Use metadata to locate endpoints.
-        Iterator<const IEndpoint*> endpoints=AA->getAttributeServiceManager()->getEndpoints();
-        while (!response && endpoints.hasNext()) {
-            const IEndpoint* ep=endpoints.next();
+        const vector<AttributeService*>& endpoints=AA->getAttributeServices();
+        for (vector<AttributeService*>::const_iterator ep=endpoints.begin(); !response && ep!=endpoints.end(); ++ep) {
             try {
                 // Get a binding object for this protocol.
-                const SAMLBinding* binding = application->getBinding(ep->getBinding());
+                const SAMLBinding* binding = application->getBinding((*ep)->getBinding());
                 if (!binding) {
-                    auto_ptr_char prot(ep->getBinding());
+                    xmltooling::auto_ptr_char prot((*ep)->getBinding());
                     m_log->warn("skipping binding on unsupported protocol (%s)", prot.get());
                     continue;
                 }
                 static const XMLCh https[] = {chLatin_h, chLatin_t, chLatin_t, chLatin_p, chLatin_s, chColon, chNull};
-                auto_ptr<SAMLResponse> r(binding->send(ep->getLocation(), *(req.get()), &ctx));
+                auto_ptr<SAMLResponse> r(binding->send((*ep)->getLocation(), *(req.get()), &ctx));
                 if (r->isSigned()) {
-                    if (!t.validate(*r,AA))
-                        throw TrustException("Unable to verify signed response message.");
+                    // TODO: trust stuff will be changing anyway...
+                    //if (!t.validate(*r,AA))
+                    //    throw TrustException("Unable to verify signed response message.");
                 }
-                else if (!ctx.isAuthenticated() || XMLString::compareNString(ep->getLocation(),https,6))
-                    throw TrustException("Response message was unauthenticated.");
+                else if (!ctx.isAuthenticated() || XMLString::compareNString((*ep)->getLocation(),https,6))
+                    throw XMLSecurityException("Response message was unauthenticated.");
                 response = r.release();
             }
-            catch (SAMLException& e) {
-                m_log->error("caught SAML exception during SAML attribute query: %s", e.what());
-                // Check for shib:InvalidHandle error and propagate it out.
-                Iterator<saml::QName> codes=e.getCodes();
-                if (codes.size()>1) {
-                    const saml::QName& code=codes[1];
-                    if (!XMLString::compareString(code.getNamespaceURI(),shibboleth::Constants::SHIB_NS) &&
-                        !XMLString::compareString(code.getLocalName(), shibboleth::Constants::InvalidHandle)) {
-                        codes.reset();
-                        throw InvalidHandleException(e.what(),params(),codes);
-                    }
-                }
+            catch (exception& e) {
+                m_log->error("caught exception during SAML attribute query: %s", e.what());
             }
         }
 
@@ -852,7 +911,7 @@ pair<SAMLResponse*,SAMLResponse*> MemorySessionCacheEntry::getNewResponse() cons
             if (signedResponse.first && signedResponse.second && !response->isSigned()) {
                 delete response;
                 m_log->error("unsigned response obtained, but we were told it must be signed.");
-                throw TrustException("Unable to obtain a signed response message.");
+                throw XMLSecurityException("Unable to obtain a signed response message.");
             }
             
             // Iterate over the tokens and apply basic validation.
@@ -860,8 +919,8 @@ pair<SAMLResponse*,SAMLResponse*> MemorySessionCacheEntry::getNewResponse() cons
             Iterator<SAMLAssertion*> assertions=response->getAssertions();
             for (unsigned int a=0; a<assertions.size();) {
                 // Discard any assertions not issued by the right entity.
-                if (XMLString::compareString(site->getId(),assertions[a]->getIssuer())) {
-                    auto_ptr_char bad(assertions[a]->getIssuer());
+                if (XMLString::compareString(source->getEntityID(),assertions[a]->getIssuer())) {
+                    xmltooling::auto_ptr_char bad(assertions[a]->getIssuer());
                     m_log->warn("discarding assertion not issued by (%s), instead by (%s)",m_obj["provider_id"].string(),bad.get());
                     response->removeAssertion(a);
                     continue;
@@ -869,22 +928,22 @@ pair<SAMLResponse*,SAMLResponse*> MemorySessionCacheEntry::getNewResponse() cons
 
                 // Validate the token.
                 try {
-                    application->validateToken(assertions[a],now,AA,application->getTrustProviders());
+                    application->validateToken(assertions[a],now,AA,application->getTrustEngine());
                     a++;
                 }
-                catch (SAMLException&) {
+                catch (exception&) {
                     m_log->warn("assertion failed to validate, removing it from response");
                     response->removeAssertion(a);
                 }
             }
 
             // Run it through the filter.
-            return make_pair(response,filter(response,application,site));
+            return make_pair(response,filter(response,application,AA));
         }
     }
-    catch (SAMLException& e) {
-        m_log->error("caught SAML exception during query to AA: %s", e.what());
-        annotateException(&e,AA);
+    catch (exception& e) {
+        m_log->error("caught exception during query to AA: %s", e.what());
+        throw;
     }
     
     m_log->error("no response obtained");
@@ -892,11 +951,11 @@ pair<SAMLResponse*,SAMLResponse*> MemorySessionCacheEntry::getNewResponse() cons
 }
 
 SAMLResponse* MemorySessionCacheEntry::filter(
-    const SAMLResponse* r, const IApplication* application, const IEntityDescriptor* source
+    const SAMLResponse* r, const IApplication* application, const RoleDescriptor* role
     ) const
 {
 #ifdef _DEBUG
-    saml::NDC ndc("filter");
+    xmltooling::NDC ndc("filter");
 #endif
 
     // Make a copy of the original and process that against the AAP.
@@ -907,11 +966,11 @@ SAMLResponse* MemorySessionCacheEntry::filter(
     for (unsigned long j=0; j < copies.size();) {
         try {
             // Finally, filter the content.
-            AAP::apply(application->getAAPProviders(),*(copies[j]),source);
+            shibboleth::AAP::apply(application->getAAPProviders(),*(copies[j]),role);
             j++;
 
         }
-        catch (SAMLException&) {
+        catch (exception&) {
             m_log->info("no statements remain after AAP, removing assertion");
             copy->removeAssertion(j);
         }
@@ -939,7 +998,7 @@ SAMLResponse* MemorySessionCacheEntry::filter(
                 Iterator<SAMLAttribute*> attrs=state ? state->getAttributes() : EMPTY(SAMLAttribute*);
                 while (attrs.hasNext()) {
                     SAMLAttribute* attr=attrs.next();
-                    auto_ptr_char attrname(attr->getName());
+                    xmltooling::auto_ptr_char attrname(attr->getName());
                     tran.infoStream() << "\t" << attrname.get() << " (" << attr->getValues().size() << " values)";
                 }
             }
@@ -953,51 +1012,58 @@ SAMLResponse* MemorySessionCacheEntry::filter(
 
 MemorySessionCache::MemorySessionCache(const DOMElement* e)
     : m_root(e), m_AATimeout(30), m_AAConnectTimeout(15), m_defaultLifetime(1800), m_retryInterval(300),
-        m_strictValidity(true), m_propagateErrors(false), m_lock(RWLock::create()),
+        m_strictValidity(true), m_propagateErrors(false), m_writeThrough(false), m_lock(RWLock::create()),
         m_log(&Category::getInstance(SHIBT_LOGCAT".SessionCache")),
         restoreInsert(NULL), restoreFind(NULL), restoreRemove(NULL), m_sink(NULL)
 {
-    const XMLCh* tag=m_root->getAttributeNS(NULL,AATimeout);
-    if (tag && *tag) {
-        m_AATimeout = XMLString::parseInt(tag);
-        if (!m_AATimeout)
-            m_AATimeout=30;
-    }
-    SAMLConfig::getConfig().timeout = m_AATimeout;
+    if (m_root) {
+        const XMLCh* tag=m_root->getAttributeNS(NULL,AATimeout);
+        if (tag && *tag) {
+            m_AATimeout = XMLString::parseInt(tag);
+            if (!m_AATimeout)
+                m_AATimeout=30;
+        }
 
-    tag=m_root->getAttributeNS(NULL,AAConnectTimeout);
-    if (tag && *tag) {
-        m_AAConnectTimeout = XMLString::parseInt(tag);
-        if (!m_AAConnectTimeout)
-            m_AAConnectTimeout=15;
-    }
-    SAMLConfig::getConfig().conn_timeout = m_AAConnectTimeout;
-    
-    tag=m_root->getAttributeNS(NULL,defaultLifetime);
-    if (tag && *tag) {
-        m_defaultLifetime = XMLString::parseInt(tag);
-        if (!m_defaultLifetime)
-            m_defaultLifetime=1800;
-    }
+        tag=m_root->getAttributeNS(NULL,AAConnectTimeout);
+        if (tag && *tag) {
+            m_AAConnectTimeout = XMLString::parseInt(tag);
+            if (!m_AAConnectTimeout)
+                m_AAConnectTimeout=15;
+        }
+        
+        tag=m_root->getAttributeNS(NULL,defaultLifetime);
+        if (tag && *tag) {
+            m_defaultLifetime = XMLString::parseInt(tag);
+            if (!m_defaultLifetime)
+                m_defaultLifetime=1800;
+        }
 
-    tag=m_root->getAttributeNS(NULL,retryInterval);
-    if (tag && *tag) {
-        m_retryInterval = XMLString::parseInt(tag);
-        if (!m_retryInterval)
-            m_retryInterval=300;
-    }
-    
-    tag=m_root->getAttributeNS(NULL,strictValidity);
-    if (tag && (*tag==chDigit_0 || *tag==chLatin_f))
-        m_strictValidity=false;
+        tag=m_root->getAttributeNS(NULL,retryInterval);
+        if (tag && *tag) {
+            m_retryInterval = XMLString::parseInt(tag);
+            if (!m_retryInterval)
+                m_retryInterval=300;
+        }
         
-    tag=m_root->getAttributeNS(NULL,propagateErrors);
-    if (tag && (*tag==chDigit_1 || *tag==chLatin_t))
-        m_propagateErrors=true;
+        tag=m_root->getAttributeNS(NULL,strictValidity);
+        if (tag && (*tag==chDigit_0 || *tag==chLatin_f))
+            m_strictValidity=false;
+            
+        tag=m_root->getAttributeNS(NULL,propagateErrors);
+        if (tag && (*tag==chDigit_1 || *tag==chLatin_t))
+            m_propagateErrors=true;
+
+        tag=m_root->getAttributeNS(NULL,writeThrough);
+        if (tag && (*tag==chDigit_1 || *tag==chLatin_t))
+            m_writeThrough=true;
+    }
+
+    SAMLConfig::getConfig().timeout = m_AATimeout;
+    SAMLConfig::getConfig().conn_timeout = m_AAConnectTimeout;
 
     // Register for remoted messages.
-    IListener* listener=ShibTargetConfig::getConfig().getINI()->getListener();
-    if (listener && ShibTargetConfig::getConfig().isEnabled(ShibTargetConfig::OutOfProcess)) {
+    ListenerService* listener=SPConfig::getConfig().getServiceProvider()->getListenerService(false);
+    if (listener && SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {
         restoreInsert=listener->regListener("SessionCache::insert",this);
         restoreFind=listener->regListener("SessionCache::find",this);
         restoreRemove=listener->regListener("SessionCache::remove",this);
@@ -1018,14 +1084,14 @@ MemorySessionCache::~MemorySessionCache()
     cleanup_thread->join(NULL);
 
     // Unregister remoted messages.
-    IListener* listener=ShibTargetConfig::getConfig().getINI()->getListener();
-    if (listener && ShibTargetConfig::getConfig().isEnabled(ShibTargetConfig::OutOfProcess)) {
+    ListenerService* listener=SPConfig::getConfig().getServiceProvider()->getListenerService(false);
+    if (listener && SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {
         listener->unregListener("SessionCache::insert",this,restoreInsert);
         listener->unregListener("SessionCache::find",this,restoreFind);
         listener->unregListener("SessionCache::remove",this,restoreRemove);
     }
 
-    for_each(m_hashtable.begin(),m_hashtable.end(),shibtarget::cleanup<string,MemorySessionCacheEntry>);
+    for_each(m_hashtable.begin(),m_hashtable.end(),xmltooling::cleanup_pair<string,MemorySessionCacheEntry>());
     delete m_lock;
     delete shutdown_wait;
 }
@@ -1084,16 +1150,17 @@ bool MemorySessionCache::setBackingStore(ISessionCacheStore* store)
 DDF MemorySessionCache::receive(const DDF& in)
 {
 #ifdef _DEBUG
-    saml::NDC ndc("receive");
+    xmltooling::NDC ndc("receive");
 #endif
 
     // Find application.
+    xmltooling::Locker confLocker(SPConfig::getConfig().getServiceProvider());
     const char* aid=in["application_id"].string();
-    const IApplication* app=aid ? ShibTargetConfig::getConfig().getINI()->getApplication(aid) : NULL;
+    const IApplication* app=aid ? dynamic_cast<const IApplication*>(SPConfig::getConfig().getServiceProvider()->getApplication(aid)) : NULL;
     if (!app) {
         // Something's horribly wrong.
         m_log->error("couldn't find application (%s) for session", aid ? aid : "(missing)");
-        throw SAMLException("Unable to locate application for session, deleted?");
+        throw ConfigurationException("Unable to locate application for session, deleted?");
     }
 
     if (!strcmp(in.name(),"SessionCache::find")) {
@@ -1112,7 +1179,7 @@ DDF MemorySessionCache::receive(const DDF& in)
             entry->unlock();
             return dup;
         }
-        catch (SAMLException&) {
+        catch (exception&) {
             remove(key,app,client_address);
             throw;
         }
@@ -1138,33 +1205,46 @@ DDF MemorySessionCache::receive(const DDF& in)
             throw SAMLException("Required parameters missing in call to SessionCache::insert");
         int minor=in["minor_version"].integer();
         
-        // Locate role descriptor to use in filtering.
-        Metadata m(app->getMetadataProviders());
-        const IEntityDescriptor* site=m.lookup(provider_id);
+        // Locate entity descriptor to use in filtering.
+        MetadataProvider* m=app->getMetadataProvider();
+        xmltooling::Locker locker(m);
+        const EntityDescriptor* site=m->getEntityDescriptor(provider_id);
         if (!site) {
             m_log->error("unable to locate issuing identity provider's metadata");
             throw MetadataException("Unable to locate identity provider's metadata.");
         }
+        const RoleDescriptor* role=site->getAttributeAuthorityDescriptor(samlconstants::SAML11_PROTOCOL_ENUM);
+        if (!role)
+            role=site->getAttributeAuthorityDescriptor(samlconstants::SAML10_PROTOCOL_ENUM);
+        if (!role)
+            role=site->getIDPSSODescriptor(samlconstants::SAML11_PROTOCOL_ENUM);
+        if (!role)
+            role=site->getIDPSSODescriptor(samlconstants::SAML10_PROTOCOL_ENUM);
+        if (!role) {
+            m_log->error("unable to locate attribute-issuing role in identity provider's metadata");
+            throw MetadataException("Unable to locate attribute-issuing role in identity provider's metadata.");
+        }
+
         // Deserialize XML for insert method.
         istringstream subis(subject);
         auto_ptr<SAMLSubject> pSubject(new SAMLSubject(subis));
         istringstream tokis(tokens);
-        auto_ptr<SAMLResponse> pTokens=new SAMLResponse(tokis,minor);
+        auto_ptr<SAMLResponse> pTokens(new SAMLResponse(tokis,minor));
         
         // Insert the data and return the cache key.
-        string key=insert(app,site,client_address,pSubject.get(),authn_context,pTokens.get());
+        string key=insert(app,role,client_address,pSubject.get(),authn_context,pTokens.get());
         
         DDF out(NULL);
         out.structure();
         out.addmember("key").string(key.c_str());
         return out;
     }
-    throw ListenerException("Unsupported operation ($1)",params(1,in.name()));
+    throw ListenerException("Unsupported operation ($1)",xmltooling::params(1,in.name()));
 }
 
 string MemorySessionCache::insert(
     const IApplication* application,
-    const IEntityDescriptor* source,
+    const RoleDescriptor* role,
     const char* client_addr,
     const SAMLSubject* subject,
     const char* authnContext,
@@ -1172,11 +1252,11 @@ string MemorySessionCache::insert(
     )
 {
 #ifdef _DEBUG
-    saml::NDC ndc("insert");
+    xmltooling::NDC ndc("insert");
 #endif
 
     SAMLIdentifier id;
-    auto_ptr_char key(id);
+    xmltooling::auto_ptr_char key(id);
 
     if (m_log->isDebugEnabled())
         m_log->debug("creating new cache entry for application %s: \"%s\"", application->getId(), key.get());
@@ -1186,17 +1266,22 @@ string MemorySessionCache::insert(
             this,
             key.get(),
             application,
-            source,
+            role,
             client_addr,
             subject,
             authnContext,
             tokens
             )
         );
-    entry->populate();
+    entry->populate(application,dynamic_cast<EntityDescriptor*>(role->getParent()),true);
 
-    if (m_sink)
-        m_sink->onCreate(key.get(),application,entry.get(),1,tokens->getMinorVersion(),entry->created());
+    if (m_sink) {
+        HRESULT hr=m_sink->onCreate(key.get(),application,entry.get(),1,tokens->getMinorVersion(),entry->created());
+        if (FAILED(hr)) {
+            m_log->error("cache store returned failure while storing new entry");
+            throw IOException("Unable to record new session in cache store.");
+        }
+    }
 
     m_lock->wrlock();
     m_hashtable[key.get()]=entry.release();
@@ -1208,7 +1293,7 @@ string MemorySessionCache::insert(
 ISessionCacheEntry* MemorySessionCache::find(const char* key, const IApplication* application, const char* client_addr)
 {
 #ifdef _DEBUG
-    saml::NDC ndc("find");
+    xmltooling::NDC ndc("find");
 #endif
 
     m_log->debug("searching memory cache for key (%s)", key);
@@ -1225,31 +1310,53 @@ ISessionCacheEntry* MemorySessionCache::find(const char* key, const IApplication
         string appid,addr,pid,sub,ac,tokens;
         int major,minor;
         time_t created,accessed;
-        if (!m_sink->onRead(key,appid,addr,pid,sub,ac,tokens,major,minor,created,accessed))
+        HRESULT hr=m_sink->onRead(key,appid,addr,pid,sub,ac,tokens,major,minor,created,accessed);
+        if (hr==S_FALSE)
             return NULL;
-        const IApplication* eapp=ShibTargetConfig::getConfig().getINI()->getApplication(appid.c_str());
+        else if (FAILED(hr)) {
+            m_log->error("cache store returned failure during search");
+            return NULL;
+        }
+        const IApplication* eapp=dynamic_cast<const IApplication*>(SPConfig::getConfig().getServiceProvider()->getApplication(appid.c_str()));
         if (!eapp) {
             // Something's horribly wrong.
             m_log->error("couldn't find application (%s) for session", appid.c_str());
-            m_sink->onDelete(key,0,false);
+            if (FAILED(m_sink->onDelete(key)))
+                m_log->error("cache store returned failure during delete");
             return NULL;
         }
         if (m_log->isDebugEnabled())
-            m_log->debug("loading cache entry (ID: %s) back into memory for application (%s)", key, appid);
+            m_log->debug("loading cache entry (ID: %s) back into memory for application (%s)", key, appid.c_str());
 
-        // Locate role descriptor to use in filtering.
-        Metadata m(eapp->getMetadataProviders());
-        const IEntityDescriptor* site=m.lookup(pid.c_str());
+        // Locate role to use in filtering.
+        MetadataProvider* m=eapp->getMetadataProvider();
+        xmltooling::Locker locker(m);
+        const EntityDescriptor* site=m->getEntityDescriptor(pid.c_str());
         if (!site) {
             m_log->error("unable to locate issuing identity provider's metadata");
-            m_sink->onDelete(key,0,false);
+            if (FAILED(m_sink->onDelete(key)))
+                m_log->error("cache store returned failure during delete");
             return NULL;
         }
+        const RoleDescriptor* role=site->getAttributeAuthorityDescriptor(samlconstants::SAML11_PROTOCOL_ENUM);
+        if (!role)
+            role=site->getAttributeAuthorityDescriptor(samlconstants::SAML10_PROTOCOL_ENUM);
+        if (!role)
+            role=site->getIDPSSODescriptor(samlconstants::SAML11_PROTOCOL_ENUM);
+        if (!role)
+            role=site->getIDPSSODescriptor(samlconstants::SAML10_PROTOCOL_ENUM);
+        if (!role) {
+            m_log->error("unable to locate attribute-issuing role in identity provider's metadata");
+            if (FAILED(m_sink->onDelete(key)))
+                m_log->error("cache store returned failure during delete");
+            return NULL;
+        }
+
         MemorySessionCacheEntry* entry = new MemorySessionCacheEntry(
             this,
             key,
             eapp,
-            site,
+            role,
             addr.c_str(),
             sub.c_str(),
             ac.c_str(),
@@ -1287,32 +1394,28 @@ ISessionCacheEntry* MemorySessionCache::find(const char* key, const IApplication
     try {
         HRESULT hr=i->second->isValid(application, client_addr);
         if (FAILED(hr)) {
-            Metadata m(application->getMetadataProviders());
+            MetadataProvider* m=application->getMetadataProvider();
+            xmltooling::Locker locker(m);
             switch (hr) {
                 case SESSION_E_EXPIRED: {
-                    InvalidSessionException ex(SESSION_E_EXPIRED, "Your session has expired, and you must re-authenticate.");
-                    annotateException(&ex,m.lookup(i->second->getProviderId())); // throws it
+                    opensaml::RetryableProfileException ex("Your session has expired, and you must re-authenticate.");
+                    annotateException(&ex,m->getEntityDescriptor(i->second->getProviderId(),false)); // throws it
                 }
                 
                 case SESSION_E_ADDRESSMISMATCH: {
-                    InvalidSessionException ex(
-                        SESSION_E_ADDRESSMISMATCH,
-                        "Your IP address (%1) does not match the address recorded at the time the session was established.",
-                        params(1,client_addr)
+                    opensaml::RetryableProfileException ex(
+                        "Your IP address ($1) does not match the address recorded at the time the session was established.",
+                        xmltooling::params(1,client_addr)
                         );
-                    annotateException(&ex,m.lookup(i->second->getProviderId())); // throws it
+                    annotateException(&ex,m->getEntityDescriptor(i->second->getProviderId(),false)); // throws it
                 }
                 
                 default: {
-                    InvalidSessionException ex(hr, "Your session is invalid.");
-                    annotateException(&ex,m.lookup(i->second->getProviderId())); // throws it
+                    opensaml::RetryableProfileException ex("Your session is invalid.");
+                    annotateException(&ex,m->getEntityDescriptor(i->second->getProviderId(),false)); // throws it
                 }
             }
         }
-
-        // The data inside the entry changed?
-        if (m_sink && hr==S_FALSE)
-            m_sink->onUpdate(key,i->second->getDDF().getmember("tokens.unfiltered").string());
     }
     catch (...) {
         m_lock->unlock();
@@ -1322,13 +1425,25 @@ ISessionCacheEntry* MemorySessionCache::find(const char* key, const IApplication
     // Lock the cache entry for the caller -- they have to unlock it.
     i->second->lock();
     m_lock->unlock();
+
+    try {
+        // Make sure the entry has valid tokens.
+        MetadataProvider* m=application->getMetadataProvider();
+        xmltooling::Locker locker(m);
+        i->second->populate(application,m->getEntityDescriptor(i->second->getProviderId()));
+    }
+    catch (...) {
+        i->second->unlock();
+        throw;
+    }
+
     return i->second;
 }
 
 void MemorySessionCache::remove(const char* key, const IApplication* application, const char* client_addr)
 {
 #ifdef _DEBUG
-    saml::NDC ndc("remove");
+    xmltooling::NDC ndc("remove");
 #endif
 
     m_log->debug("removing cache entry with key (%s)", key);
@@ -1353,10 +1468,12 @@ void MemorySessionCache::remove(const char* key, const IApplication* application
 
     entry->unlock();
 
-    // Notify sink. Wrapper will make sure entry gets deleted.
-    auto_ptr<ISessionCacheEntry*> entrywrap(entry);
-    if (m_sink)
-        m_sink->onDelete(key,entry->lastAccess(),false);
+    // Notify sink. Smart ptr will make sure entry gets deleted.
+    auto_ptr<ISessionCacheEntry> entrywrap(entry);
+    if (m_sink) {
+        if (FAILED(m_sink->onDelete(key)))
+            m_log->error("cache store failed to delete entry");
+    }
 
     // Transaction Logging
     STConfig& stc=static_cast<STConfig&>(ShibTargetConfig::getConfig());
@@ -1367,7 +1484,7 @@ void MemorySessionCache::remove(const char* key, const IApplication* application
 void MemorySessionCache::dormant(const char* key)
 {
 #ifdef _DEBUG
-    saml::NDC ndc("dormant");
+    xmltooling::NDC ndc("dormant");
 #endif
 
     m_log->debug("purging old cache entry with key (%s)", key);
@@ -1393,16 +1510,18 @@ void MemorySessionCache::dormant(const char* key)
     // we can release the cache entry lock because we know we're not in the cache anymore
     entry->unlock();
 
-    // Notify sink. Wrapper will make sure entry gets deleted.
-    auto_ptr<ISessionCacheEntry*> entrywrap(entry);
-    if (m_sink)
-        m_sink->onDelete(key,entry->lastAccess(),true);
+    auto_ptr<ISessionCacheEntry> entrywrap(entry);
+    if (m_sink && !m_writeThrough) {
+        // Update sink with last access data. Wrapper will make sure entry gets deleted.
+        if (FAILED(m_sink->onUpdate(key,NULL,entry->lastAccess())))
+            m_log->error("cache store failed to update last access timestamp");
+    }
 }
 
 void MemorySessionCache::cleanup()
 {
 #ifdef _DEBUG
-    saml::NDC ndc("cleanup()");
+    xmltooling::NDC ndc("cleanup()");
 #endif
 
     int rerun_timer = 0;
@@ -1433,8 +1552,6 @@ void MemorySessionCache::cleanup()
         if (shutdown)
             break;
 
-        m_log->debug("cleanup thread running...");
-
         // Ok, let's run through the cleanup process and clean out
         // really old sessions.  This is a two-pass process.  The
         // first pass is done holding a read-lock while we iterate over
@@ -1458,14 +1575,16 @@ void MemorySessionCache::cleanup()
         }
         m_lock->unlock();
     
-        m_log->info("purging %d old sessions", stale_keys.size());
+        if (!stale_keys.empty()) {
+            m_log->info("purging %d old sessions", stale_keys.size());
     
-        // Pass 2: walk through the list of stale entries and remove them from the cache
-        for (vector<string>::const_iterator j = stale_keys.begin(); j != stale_keys.end(); j++)
-            dormant(j->c_str());
+            // Pass 2: walk through the list of stale entries and remove them from the cache
+            for (vector<string>::const_iterator j = stale_keys.begin(); j != stale_keys.end(); j++)
+                dormant(j->c_str());
+        }
     }
 
-    m_log->info("Cleanup thread finished.");
+    m_log->info("cleanup thread finished.");
 
     mutex->unlock();
     delete mutex;
@@ -1476,18 +1595,20 @@ void* MemorySessionCache::cleanup_fcn(void* cache_p)
 {
     MemorySessionCache* cache = reinterpret_cast<MemorySessionCache*>(cache_p);
 
+#ifndef WIN32
     // First, let's block all signals 
     Thread::mask_all_signals();
+#endif
 
     // Now run the cleanup process.
     cache->cleanup();
     return NULL;
 }
 
-IPlugIn* MemoryCacheFactory(const DOMElement* e)
+SessionCache* MemoryCacheFactory(const DOMElement* const & e)
 {
     // If this is a long-lived process, we return the "real" cache.
-    if (ShibTargetConfig::getConfig().isEnabled(ShibTargetConfig::OutOfProcess))
+    if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess))
         return new MemorySessionCache(e);
     // Otherwise, we return a stubbed front-end that remotes calls to the real cache.
     return new StubCache(e);