libmoost
|
00001 /* vim:set ts=3 sw=3 sts=3 et: */ 00028 00029 00030 00031 00032 00033 #ifndef FM_LAST_KVSTORE_MOCK_CONNECTION_HPP 00034 #define FM_LAST_KVSTORE_MOCK_CONNECTION_HPP 00035 00036 #include "i_kyoto_tycoon_connection.h" 00037 00038 #include <string> 00039 #include <vector> 00040 #include <map> 00041 #include <ctime> 00042 00043 #include <boost/thread/mutex.hpp> 00044 00045 namespace moost { namespace kvstore { 00046 00047 class DefaultAccessPolicy 00048 { 00049 public: 00050 template <class StoreT> 00051 static boost::shared_array<char> get(StoreT& store, const char* pkey, size_t ksize, size_t& vsize) 00052 { 00053 std::string key(pkey, ksize); 00054 return store.get(key, vsize); 00055 } 00056 00057 // throws on failure, supply key "fail" to force failure 00058 template <class StoreT> 00059 static void set(StoreT& store, const char* pkey, size_t ksize, const char* pval, size_t vsize) 00060 { 00061 std::string key(pkey, ksize); 00062 store.set(key, pval, vsize); 00063 } 00064 00065 // throws on failure, supply key "fail" to force failure 00066 template <class StoreT> 00067 static void cache(StoreT& store, const char* pkey, size_t ksize, const char* pval, size_t vsize, int expiryTime) 00068 { 00069 std::string key(pkey, ksize); 00070 store.cache(key, pval, vsize, expiryTime); 00071 } 00072 }; 00073 00074 template <class AccessPolicy = DefaultAccessPolicy> 00075 class MockKyotoTycoonConnection : public IKyotoTycoonConnection 00076 { 00077 public: 00078 00079 MockKyotoTycoonConnection() : isOpen_(false) { } 00080 00081 ~MockKyotoTycoonConnection() { close(); } 00082 00083 void open(const std::string& /*host*/, int /*port*/, int /*timeoutMs*/) 00084 { 00085 if (isOpen_) { throw std::runtime_error("The connection is already open"); } 00086 // initialize singleton 00087 store(); 00088 isOpen_ = true; 00089 } 00090 00091 void close() 00092 { 00093 isOpen_ = false; 00094 } 00095 00096 public: 00097 00098 // if key is found then return value is a C-style string 00099 // of length vsize (not including the trailing null byte) 00100 boost::shared_array<char> get(const void* pkey, size_t ksize, size_t& vsize) const 00101 { 00102 assert_data_store_open(); 00103 return AccessPolicy::get(store(), reinterpret_cast<const char *>(pkey), ksize, vsize); 00104 } 00105 00106 // throws on failure, supply key "fail" to force failure 00107 virtual void set(const void* pkey, size_t ksize, const void* pval, size_t vsize) const 00108 { 00109 assert_data_store_open(); 00110 AccessPolicy::set(store(), reinterpret_cast<const char *>(pkey), ksize, reinterpret_cast<const char *>(pval), vsize); 00111 } 00112 00113 // throws on failure, supply key "fail" to force failure 00114 virtual void cache(const void* pkey, size_t ksize, const void* pval, size_t vsize, boost::int64_t expirySecs) const 00115 { 00116 assert_data_store_open(); 00117 boost::int64_t expiryTime = time(0) + expirySecs; 00118 AccessPolicy::cache(store(), reinterpret_cast<const char *>(pkey), ksize, reinterpret_cast<const char *>(pval), vsize, expiryTime); 00119 } 00120 00121 private: 00122 00123 void assert_data_store_open() const 00124 { 00125 assert(isOpen_); 00126 if (!isOpen_) 00127 throw std::runtime_error("Mock Kyoto Tycoon connection not open"); 00128 } 00129 00130 private: 00131 00132 class Store 00133 { 00134 private: 00135 00136 typedef std::vector<char> entry_t; 00137 typedef std::map<std::string, entry_t> store_t; 00138 typedef std::map<std::string, time_t> expiry_store_t; 00139 00140 mutable boost::mutex mutex_; // synchronize access to store and expiry store 00141 store_t db_; 00142 expiry_store_t expiry_; 00143 00144 public: 00145 00146 // return a copy of the stored char* 00147 // or 0 if not found 00148 boost::shared_array<char> get(const std::string& key, size_t& vsize) const 00149 { 00150 boost::shared_array<char> val; 00151 vsize = 0; 00152 time_t expiry; 00153 00154 boost::mutex::scoped_lock lock(mutex_); 00155 00156 if (!get(key, expiry, expiry_) || expiry >= time(0)) 00157 { 00158 entry_t entry; 00159 if (get(key, entry, db_)) 00160 { 00161 vsize = entry.size(); 00162 val.reset(new char[vsize+1]); 00163 memcpy(val.get(), &entry[0], entry.size()); 00164 val[vsize] = 0; 00165 } 00166 } 00167 00168 return val; 00169 } 00170 00171 void set(const std::string& key, const char* val, size_t vsize) 00172 { 00173 boost::mutex::scoped_lock lock(mutex_); 00174 db_[key].assign(val, val + vsize); 00175 // remove any existing expiry time for key 00176 expiry_store_t::iterator it = expiry_.find(key); 00177 if (it != expiry_.end()) 00178 expiry_.erase(it); 00179 } 00180 00181 void cache(const std::string& key, const char* val, size_t vsize, boost::int64_t expirySecs) 00182 { 00183 boost::mutex::scoped_lock lock(mutex_); 00184 db_[key].assign(val, val + vsize); 00185 expiry_[key] = time(0) + static_cast<time_t>(expirySecs); 00186 } 00187 00188 private: 00189 00190 template <typename TKey, typename TVal, typename TMap> 00191 bool get(const TKey& key, TVal& val, TMap& store) const 00192 { 00193 typename TMap::const_iterator it = store.find(key); 00194 bool found = (it != store.end()); 00195 if (found) 00196 val = it->second; 00197 return found; 00198 } 00199 }; 00200 00201 bool isOpen_; 00202 00203 // singleton store 00204 static Store& store() 00205 { 00206 static Store store_; 00207 return store_; 00208 } 00209 }; 00210 00211 }} // end namespace 00212 00213 #endif