LogCabin
Client/Client.cc
Go to the documentation of this file.
00001 /* Copyright (c) 2012 Stanford University
00002  * Copyright (c) 2014-2015 Diego Ongaro
00003  *
00004  * Permission to use, copy, modify, and distribute this software for any
00005  * purpose with or without fee is hereby granted, provided that the above
00006  * copyright notice and this permission notice appear in all copies.
00007  *
00008  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES
00009  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
00010  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR
00011  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
00012  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
00013  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
00014  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
00015  */
00016 
00017 #include <string.h>
00018 
00019 #include "include/LogCabin/Client.h"
00020 #include "Client/ClientImpl.h"
00021 #include "Client/MockClientImpl.h"
00022 #include "Core/StringUtil.h"
00023 
00024 namespace LogCabin {
00025 namespace Client {
00026 
00027 namespace {
00028 void throwException(const Result& result, uint64_t timeoutNanos)
00029 {
00030     switch (result.status) {
00031         case Status::OK:
00032             return;
00033         case Status::INVALID_ARGUMENT:
00034             throw InvalidArgumentException(result.error);
00035         case Status::LOOKUP_ERROR:
00036             throw LookupException(result.error);
00037         case Status::TYPE_ERROR:
00038             throw TypeException(result.error);
00039         case Status::CONDITION_NOT_MET:
00040             throw ConditionNotMetException(result.error);
00041         case Status::TIMEOUT:
00042         {
00043             throw TimeoutException(result.error + ": " +
00044                                    Core::StringUtil::toString(timeoutNanos) +
00045                                    "ns");
00046         }
00047     }
00048 }
00049 } // anonymous namespace
00050 
00051 
00052 ////////// Server //////////
00053 
00054 Server::Server(uint64_t serverId, const std::string& addresses)
00055     : serverId(serverId)
00056     , addresses(addresses)
00057 {
00058 }
00059 
00060 Server::Server()
00061     : serverId(~0UL)
00062     , addresses("")
00063 {
00064 }
00065 
00066 Server::Server(const Server& other)
00067     : serverId(other.serverId)
00068     , addresses(other.addresses)
00069 {
00070 }
00071 
00072 Server::~Server()
00073 {
00074 }
00075 
00076 Server&
00077 Server::operator=(const Server& other)
00078 {
00079     serverId = other.serverId;
00080     addresses = other.addresses;
00081     return *this;
00082 }
00083 
00084 ////////// GetConfigurationResult //////////
00085 
00086 GetConfigurationResult::GetConfigurationResult()
00087     : status(OK)
00088     , configuration(0)
00089     , servers()
00090     , error()
00091 {
00092 }
00093 
00094 GetConfigurationResult::~GetConfigurationResult()
00095 {
00096 }
00097 
00098 ////////// ConfigurationResult //////////
00099 
00100 ConfigurationResult::ConfigurationResult()
00101     : status(OK)
00102     , badServers()
00103     , error()
00104 {
00105 }
00106 
00107 ConfigurationResult::~ConfigurationResult()
00108 {
00109 }
00110 
00111 ////////// enum Status //////////
00112 
00113 std::ostream&
00114 operator<<(std::ostream& os, Status status)
00115 {
00116     switch (status) {
00117         case Status::OK:
00118             os << "Status::OK";
00119             break;
00120         case Status::INVALID_ARGUMENT:
00121             os << "Status::INVALID_ARGUMENT";
00122             break;
00123         case Status::LOOKUP_ERROR:
00124             os << "Status::LOOKUP_ERROR";
00125             break;
00126         case Status::TYPE_ERROR:
00127             os << "Status::TYPE_ERROR";
00128             break;
00129         case Status::CONDITION_NOT_MET:
00130             os << "Status::CONDITION_NOT_MET";
00131             break;
00132         case Status::TIMEOUT:
00133             os << "Status::TIMEOUT";
00134             break;
00135     }
00136     return os;
00137 }
00138 
00139 ////////// struct Result //////////
00140 
00141 Result::Result()
00142     : status(Status::OK)
00143     , error()
00144 {
00145 }
00146 
00147 ////////// class Exception //////////
00148 
00149 Exception::Exception(const std::string& error)
00150     : std::runtime_error(error)
00151 {
00152 }
00153 
00154 InvalidArgumentException::InvalidArgumentException(const std::string& error)
00155     : Exception(error)
00156 {
00157 }
00158 
00159 LookupException::LookupException(const std::string& error)
00160     : Exception(error)
00161 {
00162 }
00163 
00164 TypeException::TypeException(const std::string& error)
00165     : Exception(error)
00166 {
00167 }
00168 
00169 ConditionNotMetException::ConditionNotMetException(const std::string& error)
00170     : Exception(error)
00171 {
00172 }
00173 
00174 TimeoutException::TimeoutException(const std::string& error)
00175     : Exception(error)
00176 {
00177 }
00178 
00179 ConfigurationExceptionBad::ConfigurationExceptionBad(const std::string& error)
00180     : Exception(error)
00181 {
00182 }
00183 
00184 ConfigurationExceptionChanged::ConfigurationExceptionChanged(
00185     const std::string& error)
00186     : Exception(error)
00187 {
00188 }
00189 
00190 ////////// TreeDetails //////////
00191 
00192 /**
00193  * Implementation-specific members of Client::Tree.
00194  */
00195 class TreeDetails {
00196   public:
00197     TreeDetails(std::shared_ptr<ClientImpl> clientImpl,
00198                 const std::string& workingDirectory)
00199         : clientImpl(clientImpl)
00200         , workingDirectory(workingDirectory)
00201         , condition()
00202         , timeoutNanos(0)
00203     {
00204     }
00205     /**
00206      * Client implementation.
00207      */
00208     std::shared_ptr<ClientImpl> clientImpl;
00209     /**
00210      * The current working directory for the Tree (an absolute path).
00211      */
00212     std::string workingDirectory;
00213     /**
00214      * If set, specifies a predicate that must hold for operations to take
00215      * effect.
00216      */
00217     Condition condition;
00218     /**
00219      * If nonzero, a relative timeout in nanoseconds for all Tree operations.
00220      */
00221     uint64_t timeoutNanos;
00222 };
00223 
00224 
00225 ////////// Tree //////////
00226 
00227 Tree::Tree(std::shared_ptr<ClientImpl> clientImpl,
00228            const std::string& workingDirectory)
00229     : mutex()
00230     , treeDetails(new TreeDetails(clientImpl, workingDirectory))
00231 {
00232 }
00233 
00234 Tree::Tree(const Tree& other)
00235     : mutex()
00236     , treeDetails(other.getTreeDetails())
00237 {
00238 }
00239 
00240 Tree&
00241 Tree::operator=(const Tree& other)
00242 {
00243     // Hold one lock at a time to avoid deadlock and handle self-assignment.
00244     std::shared_ptr<const TreeDetails> otherTreeDetails =
00245                                             other.getTreeDetails();
00246     std::lock_guard<std::mutex> lockGuard(mutex);
00247     treeDetails = otherTreeDetails;
00248     return *this;
00249 }
00250 
00251 Result
00252 Tree::setWorkingDirectory(const std::string& newWorkingDirectory)
00253 {
00254     // This method sets the working directory regardless of whether it
00255     // succeeds -- that way if it doesn't, future relative paths on this Tree
00256     // will result in errors instead of operating on the prior working
00257     // directory.
00258 
00259     ClientImpl::TimePoint timeout =
00260         ClientImpl::absTimeout(treeDetails->timeoutNanos);
00261 
00262     std::lock_guard<std::mutex> lockGuard(mutex);
00263     std::string realPath;
00264     Result result = treeDetails->clientImpl->canonicalize(
00265                                 newWorkingDirectory,
00266                                 treeDetails->workingDirectory,
00267                                 realPath);
00268     std::shared_ptr<TreeDetails> newTreeDetails(new TreeDetails(*treeDetails));
00269     if (result.status != Status::OK) {
00270         newTreeDetails->workingDirectory = Core::StringUtil::format(
00271                     "invalid from prior call to setWorkingDirectory('%s') "
00272                     "relative to '%s'",
00273                     newWorkingDirectory.c_str(),
00274                     treeDetails->workingDirectory.c_str());
00275         treeDetails = newTreeDetails;
00276         return result;
00277     }
00278     newTreeDetails->workingDirectory = realPath;
00279     treeDetails = newTreeDetails;
00280     return treeDetails->clientImpl->makeDirectory(realPath, "",
00281                                                   treeDetails->condition,
00282                                                   timeout);
00283 }
00284 
00285 void
00286 Tree::setWorkingDirectoryEx(const std::string& workingDirectory)
00287 {
00288     throwException(setWorkingDirectory(workingDirectory),
00289                    treeDetails->timeoutNanos);
00290 }
00291 
00292 std::string
00293 Tree::getWorkingDirectory() const
00294 {
00295     std::shared_ptr<const TreeDetails> treeDetails = getTreeDetails();
00296     return treeDetails->workingDirectory;
00297 }
00298 
00299 Result
00300 Tree::setCondition(const std::string& path, const std::string& value)
00301 {
00302     // This method sets the condition regardless of whether it succeeds -- that
00303     // way if it doesn't, future calls on this Tree will result in errors
00304     // instead of operating on the prior condition.
00305 
00306     std::lock_guard<std::mutex> lockGuard(mutex);
00307     std::string realPath;
00308     std::shared_ptr<TreeDetails> newTreeDetails(new TreeDetails(*treeDetails));
00309     if (path.empty()) {
00310         newTreeDetails->condition = {"", ""};
00311     } else {
00312         Result result = treeDetails->clientImpl->canonicalize(
00313                                     path,
00314                                     treeDetails->workingDirectory,
00315                                     realPath);
00316         if (result.status != Status::OK) {
00317             newTreeDetails->condition = {
00318                 Core::StringUtil::format(
00319                         "invalid from prior call to setCondition('%s') "
00320                         "relative to '%s'",
00321                         path.c_str(),
00322                         treeDetails->workingDirectory.c_str()),
00323                 value,
00324             };
00325             treeDetails = newTreeDetails;
00326             return result;
00327         }
00328         newTreeDetails->condition = {realPath, value};
00329     }
00330     treeDetails = newTreeDetails;
00331     return Result();
00332 }
00333 
00334 void
00335 Tree::setConditionEx(const std::string& path, const std::string& value)
00336 {
00337     throwException(setCondition(path, value), treeDetails->timeoutNanos);
00338 }
00339 
00340 std::pair<std::string, std::string>
00341 Tree::getCondition() const
00342 {
00343     std::shared_ptr<const TreeDetails> treeDetails = getTreeDetails();
00344     return treeDetails->condition;
00345 }
00346 
00347 uint64_t
00348 Tree::getTimeout() const
00349 {
00350     std::shared_ptr<const TreeDetails> treeDetails = getTreeDetails();
00351     return treeDetails->timeoutNanos;
00352 }
00353 
00354 void
00355 Tree::setTimeout(uint64_t nanoseconds)
00356 {
00357     std::lock_guard<std::mutex> lockGuard(mutex);
00358     std::shared_ptr<TreeDetails> newTreeDetails(new TreeDetails(*treeDetails));
00359     newTreeDetails->timeoutNanos = nanoseconds;
00360     treeDetails = newTreeDetails;
00361 }
00362 
00363 Result
00364 Tree::makeDirectory(const std::string& path)
00365 {
00366     std::shared_ptr<const TreeDetails> treeDetails = getTreeDetails();
00367     return treeDetails->clientImpl->makeDirectory(
00368         path,
00369         treeDetails->workingDirectory,
00370         treeDetails->condition,
00371         ClientImpl::absTimeout(treeDetails->timeoutNanos));
00372 }
00373 
00374 void
00375 Tree::makeDirectoryEx(const std::string& path)
00376 {
00377     throwException(makeDirectory(path), treeDetails->timeoutNanos);
00378 }
00379 
00380 Result
00381 Tree::listDirectory(const std::string& path,
00382                     std::vector<std::string>& children) const
00383 {
00384     std::shared_ptr<const TreeDetails> treeDetails = getTreeDetails();
00385     return treeDetails->clientImpl->listDirectory(
00386         path,
00387         treeDetails->workingDirectory,
00388         treeDetails->condition,
00389         ClientImpl::absTimeout(treeDetails->timeoutNanos),
00390         children);
00391 }
00392 
00393 std::vector<std::string>
00394 Tree::listDirectoryEx(const std::string& path) const
00395 {
00396     std::vector<std::string> children;
00397     throwException(listDirectory(path, children), treeDetails->timeoutNanos);
00398     return children;
00399 }
00400 
00401 Result
00402 Tree::removeDirectory(const std::string& path)
00403 {
00404     std::shared_ptr<const TreeDetails> treeDetails = getTreeDetails();
00405     return treeDetails->clientImpl->removeDirectory(
00406         path,
00407         treeDetails->workingDirectory,
00408         treeDetails->condition,
00409         ClientImpl::absTimeout(treeDetails->timeoutNanos));
00410 }
00411 
00412 void
00413 Tree::removeDirectoryEx(const std::string& path)
00414 {
00415     throwException(removeDirectory(path), treeDetails->timeoutNanos);
00416 }
00417 
00418 Result
00419 Tree::write(const std::string& path, const std::string& contents)
00420 {
00421     std::shared_ptr<const TreeDetails> treeDetails = getTreeDetails();
00422     return treeDetails->clientImpl->write(
00423         path,
00424         treeDetails->workingDirectory,
00425         contents,
00426         treeDetails->condition,
00427         ClientImpl::absTimeout(treeDetails->timeoutNanos));
00428 }
00429 
00430 void
00431 Tree::writeEx(const std::string& path, const std::string& contents)
00432 {
00433     throwException(write(path, contents), treeDetails->timeoutNanos);
00434 }
00435 
00436 Result
00437 Tree::read(const std::string& path, std::string& contents) const
00438 {
00439     std::shared_ptr<const TreeDetails> treeDetails = getTreeDetails();
00440     return treeDetails->clientImpl->read(
00441         path,
00442         treeDetails->workingDirectory,
00443         treeDetails->condition,
00444         ClientImpl::absTimeout(treeDetails->timeoutNanos),
00445         contents);
00446 }
00447 
00448 std::string
00449 Tree::readEx(const std::string& path) const
00450 {
00451     std::string contents;
00452     throwException(read(path, contents), treeDetails->timeoutNanos);
00453     return contents;
00454 }
00455 
00456 Result
00457 Tree::removeFile(const std::string& path)
00458 {
00459     std::shared_ptr<const TreeDetails> treeDetails = getTreeDetails();
00460     return treeDetails->clientImpl->removeFile(
00461         path,
00462         treeDetails->workingDirectory,
00463         treeDetails->condition,
00464         ClientImpl::absTimeout(treeDetails->timeoutNanos));
00465 }
00466 
00467 void
00468 Tree::removeFileEx(const std::string& path)
00469 {
00470     throwException(removeFile(path), treeDetails->timeoutNanos);
00471 }
00472 
00473 std::shared_ptr<const TreeDetails>
00474 Tree::getTreeDetails() const
00475 {
00476     std::shared_ptr<const TreeDetails> ret;
00477     std::lock_guard<std::mutex> lockGuard(mutex);
00478     ret = treeDetails;
00479     return ret;
00480 }
00481 
00482 ////////// TestingCallbacks //////////
00483 
00484 TestingCallbacks::TestingCallbacks()
00485 {
00486 }
00487 
00488 TestingCallbacks::~TestingCallbacks()
00489 {
00490 }
00491 
00492 bool
00493 TestingCallbacks::stateMachineQuery(
00494     Protocol::Client::StateMachineQuery_Request& request,
00495     Protocol::Client::StateMachineQuery_Response& response)
00496 {
00497     return false;
00498 }
00499 
00500 bool
00501 TestingCallbacks::stateMachineCommand(
00502     Protocol::Client::StateMachineCommand_Request& request,
00503     Protocol::Client::StateMachineCommand_Response& response)
00504 {
00505     return false;
00506 }
00507 
00508 ////////// Cluster //////////
00509 
00510 Cluster::Cluster(std::shared_ptr<TestingCallbacks> testingCallbacks,
00511                  const std::map<std::string, std::string>& options)
00512     : clientImpl(std::make_shared<MockClientImpl>(
00513         testingCallbacks ? testingCallbacks
00514                          : std::make_shared<TestingCallbacks>()))
00515 {
00516     clientImpl->init("-MOCK-");
00517 }
00518 
00519 Cluster::Cluster(const std::string& hosts,
00520                  const std::map<std::string, std::string>& options)
00521     : clientImpl(std::make_shared<ClientImpl>(options))
00522 {
00523 #if DEBUG // for testing purposes only
00524     if (hosts == "-MOCK-SKIP-INIT-")
00525         return;
00526 #endif
00527     clientImpl->init(hosts);
00528 }
00529 
00530 Cluster::~Cluster()
00531 {
00532 }
00533 
00534 std::pair<uint64_t, Configuration>
00535 Cluster::getConfiguration() const
00536 {
00537     const uint64_t timeout(0);
00538     GetConfigurationResult r = getConfiguration2(timeout);
00539     return std::make_pair(r.configuration, r.servers);
00540 }
00541 
00542 GetConfigurationResult
00543 Cluster::getConfiguration2(uint64_t timeoutNanoseconds) const
00544 {
00545     return clientImpl->getConfiguration(
00546         ClientImpl::absTimeout(timeoutNanoseconds));
00547 }
00548 
00549 GetConfigurationResult
00550 Cluster::getConfiguration2Ex(uint64_t timeoutNanoseconds) const
00551 {
00552     GetConfigurationResult result(getConfiguration2(timeoutNanoseconds));
00553     if (result.status == GetConfigurationResult::TIMEOUT) {
00554         throw TimeoutException(result.error);
00555     }
00556     return result;
00557 }
00558 
00559 ConfigurationResult
00560 Cluster::setConfiguration(uint64_t oldId,
00561                           const Configuration& newConfiguration)
00562 {
00563     const uint64_t timeout(0);
00564     return setConfiguration2(oldId, newConfiguration, timeout);
00565 }
00566 
00567 ConfigurationResult
00568 Cluster::setConfiguration2(uint64_t oldId,
00569                            const Configuration& newConfiguration,
00570                            uint64_t timeoutNanoseconds)
00571 {
00572     return clientImpl->setConfiguration(
00573         oldId, newConfiguration, ClientImpl::absTimeout(timeoutNanoseconds));
00574 }
00575 
00576 ConfigurationResult
00577 Cluster::setConfiguration2Ex(uint64_t oldId,
00578                              const Configuration& newConfiguration,
00579                              uint64_t timeoutNanoseconds)
00580 {
00581     ConfigurationResult result = setConfiguration2(oldId,
00582                                                    newConfiguration,
00583                                                    timeoutNanoseconds);
00584     if (result.status == ConfigurationResult::BAD) {
00585         throw ConfigurationExceptionBad(result.error);
00586     }
00587     if (result.status == ConfigurationResult::CHANGED) {
00588         throw ConfigurationExceptionChanged(result.error);
00589     }
00590     if (result.status == ConfigurationResult::TIMEOUT) {
00591         throw TimeoutException(result.error);
00592     }
00593     return result;
00594 }
00595 
00596 Result
00597 Cluster::getServerInfo(const std::string& host,
00598                        uint64_t timeoutNanoseconds,
00599                        Server& info)
00600 {
00601     return clientImpl->getServerInfo(
00602                 host,
00603                 ClientImpl::absTimeout(timeoutNanoseconds),
00604                 info);
00605 }
00606 
00607 Server
00608 Cluster::getServerInfoEx(const std::string& host,
00609                          uint64_t timeoutNanoseconds)
00610 {
00611     Server info;
00612     throwException(getServerInfo(host, timeoutNanoseconds, info),
00613                    timeoutNanoseconds);
00614     return info;
00615 }
00616 
00617 Result
00618 Cluster::getServerStats(const std::string& host,
00619                         uint64_t timeoutNanoseconds,
00620                         Protocol::ServerStats& stats)
00621 {
00622 
00623     Protocol::ServerControl::ServerStatsGet::Request request;
00624     Protocol::ServerControl::ServerStatsGet::Response response;
00625     Result result = clientImpl->serverControl(
00626                 host,
00627                 ClientImpl::absTimeout(timeoutNanoseconds),
00628                 Protocol::ServerControl::OpCode::SERVER_STATS_GET,
00629                 request,
00630                 response);
00631     stats = response.server_stats();
00632     return result;
00633 }
00634 
00635 Protocol::ServerStats
00636 Cluster::getServerStatsEx(const std::string& host,
00637                           uint64_t timeoutNanoseconds)
00638 {
00639     Protocol::ServerStats stats;
00640     throwException(getServerStats(host, timeoutNanoseconds, stats),
00641                    timeoutNanoseconds);
00642     return stats;
00643 }
00644 
00645 Tree
00646 Cluster::getTree()
00647 {
00648     return Tree(clientImpl, "/");
00649 }
00650 
00651 } // namespace LogCabin::Client
00652 } // namespace LogCabin
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines