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         if (!m_lock.attemptLock(lockfile.c_str(), LOCK_EX | LOCK_NB)) {
00115                 DPRINTF("Failed to get advisory lock on database file: '%s'",
00116                     path);
00117                 return false;
00118         }
00119 
00120         // save path
00121         m_path = path;
00122         createEmptyFileIfDoesNotExist(path);
00123 
00124         // okay, read from hash
00125         m_root = readHashFromTextFile(path);
00126         if (!m_root) {
00127                 DPRINTF("Failed to read hash from text file: corrupt?\n");
00128                 return false;
00129         }
00130 
00131         // good to go
00132         return true;
00133 }
00134 
00135 
00136 
00137 void
00138 DbImpl::getIds
00139 (
00140 OUT SetString& ids
00141 )
00142 {
00143         ASSERT(m_root, "null");
00144 
00145         ids.clear();    // initialize
00146 
00147         Datahash::iterator_t i;
00148         m_root->getIterator(i);
00149         std::string k;
00150         hash_value_t hv;
00151         while (m_root->getNextElement(i, k, hv)) {
00152                 const char * name = k.c_str();
00153 
00154                 ASSERT_THROW(eHashDataType_Hash == hv.type,
00155                     "All root children should be complex (hash type)");
00156 
00157                 ids.insert(name);
00158         }
00159 }
00160 
00161 
00162 
00163 bool
00164 DbImpl::addObject
00165 (
00166 IN const char * id,
00167 IN smart_ptr<Datahash> obj
00168 )
00169 {
00170         ASSERT(id, "null id");
00171         ASSERT(obj, "null object hash");
00172 
00173         SetString ids;
00174         this->getIds(ids);
00175 
00176         if (ids.end() != ids.find(id)) {
00177                 DPRINTF("Not adding object: id already exists: '%s'", id);
00178                 return false;
00179         }
00180 
00181         m_root->insert(id, obj);
00182         return true;
00183 }
00184 
00185 
00186 
00187 smart_ptr<Datahash>
00188 DbImpl::getObject
00189 (
00190 IN const char * id
00191 )
00192 {
00193         ASSERT(m_root, "null");
00194         ASSERT(id, "null");
00195 
00196         if (!m_root->count(id)) {
00197                 DPRINTF("Cannot get object--no such id '%s'", id);
00198                 return 0;
00199         }
00200 
00201         smart_ptr<Datahash> obj = getSubhash(m_root, id);
00202         ASSERT(obj, "null retrieval?");
00203 
00204         return obj;
00205 }
00206 
00207 
00208 
00209 bool
00210 DbImpl::deleteObject
00211 (
00212 IN const char * id
00213 )
00214 {
00215         ASSERT(m_root, "null");
00216         ASSERT(id, "null id to delete");
00217 
00218         if (!m_root->count(id)) {
00219                 DPRINTF("Cannot delete object--no such id '%s'", id);
00220                 return false;
00221         }
00222 
00223         m_root->remove(id);
00224 
00225         return true;
00226 }
00227 
00228 
00229 
00230 bool
00231 DbImpl::commit(void)
00232 {
00233         perf::Timer timer("hash_db::commit");
00234         ASSERT(m_root, "null");
00235 
00236         // note: the API we are calling is already atomic
00237         writeHashToTextFile(m_root, m_path.c_str(), s_fileHeader);
00238 
00239         return true;
00240 }
00241 
00242 
00243 
00244 
00245 //////////////////////////////////////////////////////////////////////////////
00246 //
00247 //      public API
00248 //
00249 ////////////////////////////////////////////////////////////////////////////////
00250 
00251 smart_ptr<Database>
00252 Database::create
00253 (
00254 IN const char * path
00255 )
00256 {
00257         ASSERT(path, "null db path");
00258 
00259         smart_ptr<DbImpl> local = new DbImpl;
00260         ASSERT(local, "out of memory");
00261 
00262         if (!local->initialize(path)) {
00263                 DPRINTF("Failed to initialize hash_db!");
00264                 return NULL;
00265         }
00266 
00267         return local;
00268 }
00269 
00270 
00271 
00272 };      // hash_db namespace
00273