hash_db.cpp

Go to the documentation of this file.
00001 /*
00002  * hash_db.cpp
00003  *
00004  * Copyright (C) 2006,2010  Thomas A. Vaughan
00005  * All rights reserved.
00006  *
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions are met:
00010  *     * Redistributions of source code must retain the above copyright
00011  *       notice, this list of conditions and the following disclaimer.
00012  *     * Redistributions in binary form must reproduce the above copyright
00013  *       notice, this list of conditions and the following disclaimer in the
00014  *       documentation and/or other materials provided with the distribution.
00015  *     * Neither the name of the <organization> nor the
00016  *       names of its contributors may be used to endorse or promote products
00017  *       derived from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THOMAS A. VAUGHAN ''AS IS'' AND ANY
00020  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00021  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00022  * DISCLAIMED. IN NO EVENT SHALL THOMAS A. VAUGHAN BE LIABLE FOR ANY
00023  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00024  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00025  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00026  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00028  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  *
00030  *
00031  * Implementation of a simple datahash database.
00032  * See hash_db.h
00033  */
00034 
00035 // includes --------------------------------------------------------------------
00036 #include "hash_db.h"            // always include our own header first!
00037 
00038 #include "util/file.h"          // for advisory locking
00039 #include "perf/perf.h"          // performance timers
00040 
00041 #include "datahash/datahash_text.h"     // textfile support for datahashes
00042 #include "datahash/datahash_util.h"     // datahash utility methods
00043 
00044 
00045 namespace hash_db {
00046 
00047 // virtual d'tor implementation
00048 Database::~Database(void) throw() { }
00049 
00050 
00051 static const char * s_fileHeader =
00052         "#\n"
00053         "# hash_db::Database persistence file.\n"
00054         "#\n"
00055         "# This is a simple text-only file for persisting hashes transactionally.\n"
00056         "# Be careful about editing--the database objects use advisory locks on\n"
00057         "# this file so that only one process can access it at a time.\n"
00058         "#\n";
00059 
00060 
00061 ////////////////////////////////////////////////////////////////////////////////
00062 //
00063 //      private static methods
00064 //
00065 ////////////////////////////////////////////////////////////////////////////////
00066 
00067 ////////////////////////////////////////////////////////////////////////////////
00068 //
00069 //      DbImpl -- class that implements the hash_db::Database interface
00070 //
00071 ////////////////////////////////////////////////////////////////////////////////
00072 
00073 class DbImpl : public Database {
00074 public:
00075         ~DbImpl(void) throw() { }
00076 
00077         // public class methods ------------------------------------------------
00078         bool initialize(IN const char * path);
00079 
00080         // hash_db::Database class interface methods ---------------------------
00081         void getIds(OUT SetString& ids);
00082         bool addObject(IN const char * id,
00083                                 IN smart_ptr<Datahash> obj);
00084         smart_ptr<Datahash> getObject(IN const char * id);
00085         bool deleteObject(IN const char * id);
00086         bool commit(void);
00087 
00088 private:
00089         // private helper methods ----------------------------------------------
00090 
00091         // private data members ------------------------------------------------
00092         std::string                     m_path; // path to database file
00093         smart_ptr<Datahash>             m_root; // root data hash
00094         AdvisoryLock                    m_lock;
00095 };
00096 
00097 
00098 
00099 bool
00100 DbImpl::initialize
00101 (
00102 IN const char * path
00103 )
00104 {
00105         perf::Timer timer("hash_db::initialize");
00106         ASSERT(path, "null path");
00107 
00108         // find or create lockfile
00109         std::string lockfile = path;
00110         lockfile += ".lock";
00111         createEmptyFileIfDoesNotExist(lockfile.c_str());
00112 
00113         // attempt to lock, abort entire process if we fail
00114 #ifndef WIN32
00115         if (!m_lock.attemptLock(lockfile.c_str(), LOCK_EX | LOCK_NB)) {
00116                 DPRINTF("Failed to get advisory lock on database file: '%s'",
00117                     path);
00118                 return false;
00119         }
00120 #endif  // WIN32
00121 
00122         // save path
00123         m_path = path;
00124         createEmptyFileIfDoesNotExist(path);
00125 
00126         // okay, read from hash
00127         m_root = readHashFromTextFile(path);
00128         if (!m_root) {
00129                 DPRINTF("Failed to read hash from text file: corrupt?\n");
00130                 return false;
00131         }
00132 
00133         // good to go
00134         return true;
00135 }
00136 
00137 
00138 
00139 void
00140 DbImpl::getIds
00141 (
00142 OUT SetString& ids
00143 )
00144 {
00145         ASSERT(m_root, "null");
00146 
00147         ids.clear();    // initialize
00148 
00149         Datahash::iterator_t i;
00150         m_root->getIterator(i);
00151         std::string k;
00152         hash_value_t hv;
00153         while (m_root->getNextElement(i, k, hv)) {
00154                 const char * name = k.c_str();
00155 
00156                 ASSERT_THROW(eHashDataType_Hash == hv.type,
00157                     "All root children should be complex (hash type)");
00158 
00159                 ids.insert(name);
00160         }
00161 }
00162 
00163 
00164 
00165 bool
00166 DbImpl::addObject
00167 (
00168 IN const char * id,
00169 IN smart_ptr<Datahash> obj
00170 )
00171 {
00172         ASSERT(id, "null id");
00173         ASSERT(obj, "null object hash");
00174 
00175         SetString ids;
00176         this->getIds(ids);
00177 
00178         if (ids.end() != ids.find(id)) {
00179                 DPRINTF("Not adding object: id already exists: '%s'", id);
00180                 return false;
00181         }
00182 
00183         m_root->insert(id, obj);
00184         return true;
00185 }
00186 
00187 
00188 
00189 smart_ptr<Datahash>
00190 DbImpl::getObject
00191 (
00192 IN const char * id
00193 )
00194 {
00195         ASSERT(m_root, "null");
00196         ASSERT(id, "null");
00197 
00198         if (!m_root->count(id)) {
00199                 DPRINTF("Cannot get object--no such id '%s'", id);
00200                 return 0;
00201         }
00202 
00203         smart_ptr<Datahash> obj = getSubhash(m_root, id);
00204         ASSERT(obj, "null retrieval?");
00205 
00206         return obj;
00207 }
00208 
00209 
00210 
00211 bool
00212 DbImpl::deleteObject
00213 (
00214 IN const char * id
00215 )
00216 {
00217         ASSERT(m_root, "null");
00218         ASSERT(id, "null id to delete");
00219 
00220         if (!m_root->count(id)) {
00221                 DPRINTF("Cannot delete object--no such id '%s'", id);
00222                 return false;
00223         }
00224 
00225         m_root->remove(id);
00226 
00227         return true;
00228 }
00229 
00230 
00231 
00232 bool
00233 DbImpl::commit(void)
00234 {
00235         perf::Timer timer("hash_db::commit");
00236         ASSERT(m_root, "null");
00237 
00238         // note: the API we are calling is already atomic
00239         writeHashToTextFile(m_root, m_path.c_str(), s_fileHeader);
00240 
00241         return true;
00242 }
00243 
00244 
00245 
00246 
00247 //////////////////////////////////////////////////////////////////////////////
00248 //
00249 //      public API
00250 //
00251 ////////////////////////////////////////////////////////////////////////////////
00252 
00253 smart_ptr<Database>
00254 Database::create
00255 (
00256 IN const char * path
00257 )
00258 {
00259         ASSERT(path, "null db path");
00260 
00261 #ifdef WIN32
00262         ASSERT_THROW(false,
00263                 "Hash databases are not supported under Windows -- no advisory locking");
00264 #endif  // WIN32
00265 
00266         smart_ptr<DbImpl> local = new DbImpl;
00267         ASSERT(local, "out of memory");
00268 
00269         if (!local->initialize(path)) {
00270                 DPRINTF("Failed to initialize hash_db!");
00271                 return NULL;
00272         }
00273 
00274         return local;
00275 }
00276 
00277 
00278 
00279 };      // hash_db namespace
00280