//////////////////////////////////////////////////////////////////////////////// /// @brief V8-cluster bridge /// /// @file /// /// DISCLAIMER /// /// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// /// Copyright holder is triAGENS GmbH, Cologne, Germany /// /// @author Jan Steemann /// @author Copyright 2011-2014, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// #include "v8-cluster.h" #include "BasicsC/common.h" #include "VocBase/server.h" #include "Cluster/AgencyComm.h" #include "Cluster/ClusterInfo.h" #include "Cluster/ServerState.h" #include "Cluster/ClusterComm.h" #include "V8/v8-conv.h" #include "V8/v8-globals.h" #include "V8/v8-utils.h" using namespace std; using namespace triagens::basics; using namespace triagens::arango; // ----------------------------------------------------------------------------- // --SECTION-- agency functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief creates a v8 exception object //////////////////////////////////////////////////////////////////////////////// static v8::Handle CreateAgencyException (AgencyCommResult const& result) { v8::HandleScope scope; TRI_v8_global_t* v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData(); const std::string errorDetails = result.errorDetails(); v8::Handle errorMessage = v8::String::New(errorDetails.c_str(), errorDetails.size()); v8::Handle errorObject = v8::Exception::Error(errorMessage)->ToObject(); errorObject->Set(v8::String::New("code"), v8::Number::New(result.httpCode())); errorObject->Set(v8::String::New("errorNum"), v8::Number::New(result.errorCode())); errorObject->Set(v8::String::New("errorMessage"), errorMessage); errorObject->Set(v8::String::New("error"), v8::True()); v8::Handle proto = v8g->ArangoErrorTempl->NewInstance(); if (! proto.IsEmpty()) { errorObject->SetPrototype(proto); } return scope.Close(errorObject); } //////////////////////////////////////////////////////////////////////////////// /// @brief compares and swaps a value in the agency //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_CasAgency (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() < 3) { TRI_V8_EXCEPTION_USAGE(scope, "cas(, , , , , )"); } const std::string key = TRI_ObjectToString(argv[0]); TRI_json_t* oldJson = TRI_ObjectToJson(argv[1]); if (oldJson == 0) { TRI_V8_EXCEPTION_PARAMETER(scope, "cannot convert to JSON"); } TRI_json_t* newJson = TRI_ObjectToJson(argv[2]); if (newJson == 0) { TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, oldJson); TRI_V8_EXCEPTION_PARAMETER(scope, "cannot convert to JSON"); } double ttl = 0.0; if (argv.Length() > 3) { ttl = TRI_ObjectToDouble(argv[3]); } double timeout = 1.0; if (argv.Length() > 4) { timeout = TRI_ObjectToDouble(argv[4]); } bool shouldThrow = false; if (argv.Length() > 5) { shouldThrow = TRI_ObjectToBoolean(argv[5]); } AgencyComm comm; AgencyCommResult result = comm.casValue(key, oldJson, newJson, ttl, timeout); TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, newJson); TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, oldJson); if (! result.successful()) { if (! shouldThrow) { return scope.Close(v8::False()); } return scope.Close(v8::ThrowException(CreateAgencyException(result))); } return scope.Close(v8::True()); } //////////////////////////////////////////////////////////////////////////////// /// @brief creates a directory in the agency //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_CreateDirectoryAgency (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 1) { TRI_V8_EXCEPTION_USAGE(scope, "createDirectory()"); } const std::string key = TRI_ObjectToString(argv[0]); AgencyComm comm; AgencyCommResult result = comm.createDirectory(key); if (! result.successful()) { return scope.Close(v8::ThrowException(CreateAgencyException(result))); } return scope.Close(v8::True()); } //////////////////////////////////////////////////////////////////////////////// /// @brief whether or not the agency is enabled //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_IsEnabledAgency (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 0) { TRI_V8_EXCEPTION_USAGE(scope, "isEnabled()"); } const std::string prefix = AgencyComm::prefix(); return scope.Close(v8::Boolean::New(! prefix.empty())); } //////////////////////////////////////////////////////////////////////////////// /// @brief increase the version number //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_IncreaseVersionAgency (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 1) { TRI_V8_EXCEPTION_USAGE(scope, "increaseVersion()"); } const std::string key = TRI_ObjectToString(argv[0]); AgencyComm comm; if (! comm.increaseVersion(key)) { TRI_V8_EXCEPTION_MESSAGE(scope, TRI_ERROR_INTERNAL, "unable to increase version"); } return scope.Close(v8::Boolean::New(true)); } //////////////////////////////////////////////////////////////////////////////// /// @brief gets a value from the agency //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_GetAgency (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() < 1) { TRI_V8_EXCEPTION_USAGE(scope, "get(, , )"); } const std::string key = TRI_ObjectToString(argv[0]); bool recursive = false; bool withIndexes = false; if (argv.Length() > 1) { recursive = TRI_ObjectToBoolean(argv[1]); } if (argv.Length() > 2) { withIndexes = TRI_ObjectToBoolean(argv[2]); } AgencyComm comm; AgencyCommResult result = comm.getValues(key, recursive); if (! result.successful()) { return scope.Close(v8::ThrowException(CreateAgencyException(result))); } result.parse("", false); v8::Handle l = v8::Object::New(); if (withIndexes) { std::map::const_iterator it = result._values.begin(); while (it != result._values.end()) { const std::string key = (*it).first; TRI_json_t const* json = (*it).second._json; const std::string idx = StringUtils::itoa((*it).second._index); if (json != 0) { v8::Handle sub = v8::Object::New(); sub->Set(v8::String::New("value"), TRI_ObjectJson(json)); sub->Set(v8::String::New("index"), v8::String::New(idx.c_str(), idx.size())); l->Set(v8::String::New(key.c_str(), key.size()), sub); } ++it; } } else { // return just the value for each key std::map::const_iterator it = result._values.begin(); while (it != result._values.end()) { const std::string key = (*it).first; TRI_json_t const* json = (*it).second._json; if (json != 0) { l->Set(v8::String::New(key.c_str(), key.size()), TRI_ObjectJson(json)); } ++it; } } return scope.Close(l); } //////////////////////////////////////////////////////////////////////////////// /// @brief lists a directory from the agency //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_ListAgency (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() < 1) { TRI_V8_EXCEPTION_USAGE(scope, "list(, , )"); } const std::string key = TRI_ObjectToString(argv[0]); bool recursive = false; if (argv.Length() > 1) { recursive = TRI_ObjectToBoolean(argv[1]); } bool flat = false; if (argv.Length() > 2) { flat = TRI_ObjectToBoolean(argv[2]); } AgencyComm comm; AgencyCommResult result = comm.getValues(key, recursive); if (! result.successful()) { return scope.Close(v8::ThrowException(CreateAgencyException(result))); } // return just the value for each key result.parse("", true); std::map::const_iterator it = result._values.begin(); // skip first entry if (it != result._values.end()) { ++it; } if (flat) { v8::Handle l = v8::Array::New(); uint32_t i = 0; while (it != result._values.end()) { const std::string key = (*it).first; l->Set(i++, v8::String::New(key.c_str(), key.size())); ++it; } return scope.Close(l); } else { v8::Handle l = v8::Object::New(); while (it != result._values.end()) { const std::string key = (*it).first; const bool isDirectory = (*it).second._isDir; l->Set(v8::String::New(key.c_str(), key.size()), v8::Boolean::New(isDirectory)); ++it; } return scope.Close(l); } } //////////////////////////////////////////////////////////////////////////////// /// @brief acquires a read-lock in the agency //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_LockReadAgency (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() < 1) { TRI_V8_EXCEPTION_USAGE(scope, "lockRead(, , )"); } const std::string part = TRI_ObjectToString(argv[0]); double ttl = 0.0; if (argv.Length() > 1) { ttl = TRI_ObjectToDouble(argv[1]); } double timeout = 0.0; if (argv.Length() > 2) { timeout = TRI_ObjectToDouble(argv[2]); } AgencyComm comm; if (! comm.lockRead(part, ttl, timeout)) { TRI_V8_EXCEPTION_MESSAGE(scope, TRI_ERROR_INTERNAL, "unable to acquire lock"); } return scope.Close(v8::True()); } //////////////////////////////////////////////////////////////////////////////// /// @brief acquires a write-lock in the agency //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_LockWriteAgency (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() < 1) { TRI_V8_EXCEPTION_USAGE(scope, "lockWrite(, , )"); } const std::string part = TRI_ObjectToString(argv[0]); double ttl = 0.0; if (argv.Length() > 1) { ttl = TRI_ObjectToDouble(argv[1]); } double timeout = 0.0; if (argv.Length() > 2) { timeout = TRI_ObjectToDouble(argv[2]); } AgencyComm comm; if (! comm.lockWrite(part, ttl, timeout)) { TRI_V8_EXCEPTION_MESSAGE(scope, TRI_ERROR_INTERNAL, "unable to acquire lock"); } return scope.Close(v8::True()); } //////////////////////////////////////////////////////////////////////////////// /// @brief releases a read-lock in the agency //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_UnlockReadAgency (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() > 2) { TRI_V8_EXCEPTION_USAGE(scope, "unlockRead(, )"); } const std::string part = TRI_ObjectToString(argv[0]); double timeout = 0.0; if (argv.Length() > 1) { timeout = TRI_ObjectToDouble(argv[1]); } AgencyComm comm; if (! comm.unlockRead(part, timeout)) { TRI_V8_EXCEPTION_MESSAGE(scope, TRI_ERROR_INTERNAL, "unable to release lock"); } return scope.Close(v8::True()); } //////////////////////////////////////////////////////////////////////////////// /// @brief releases a write-lock in the agency //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_UnlockWriteAgency (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() > 2) { TRI_V8_EXCEPTION_USAGE(scope, "unlockWrite(, )"); } const std::string part = TRI_ObjectToString(argv[0]); double timeout = 0.0; if (argv.Length() > 1) { timeout = TRI_ObjectToDouble(argv[1]); } AgencyComm comm; if (! comm.unlockWrite(part, timeout)) { TRI_V8_EXCEPTION_MESSAGE(scope, TRI_ERROR_INTERNAL, "unable to release lock"); } return scope.Close(v8::True()); } //////////////////////////////////////////////////////////////////////////////// /// @brief removes a value from the agency //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_RemoveAgency (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() < 1) { TRI_V8_EXCEPTION_USAGE(scope, "remove(, )"); } const std::string key = TRI_ObjectToString(argv[0]); bool recursive = false; if (argv.Length() > 1) { recursive = TRI_ObjectToBoolean(argv[1]); } AgencyComm comm; AgencyCommResult result = comm.removeValues(key, recursive); if (! result.successful()) { return scope.Close(v8::ThrowException(CreateAgencyException(result))); } return scope.Close(v8::True()); } //////////////////////////////////////////////////////////////////////////////// /// @brief sets a value in the agency //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_SetAgency (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() < 2) { TRI_V8_EXCEPTION_USAGE(scope, "set(, , )"); } const std::string key = TRI_ObjectToString(argv[0]); TRI_json_t* json = TRI_ObjectToJson(argv[1]); if (json == 0) { TRI_V8_EXCEPTION_PARAMETER(scope, "cannot convert to JSON"); } double ttl = 0.0; if (argv.Length() > 2) { ttl = TRI_ObjectToDouble(argv[2]); } AgencyComm comm; AgencyCommResult result = comm.setValue(key, json, ttl); TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json); if (! result.successful()) { return scope.Close(v8::ThrowException(CreateAgencyException(result))); } return scope.Close(v8::True()); } //////////////////////////////////////////////////////////////////////////////// /// @brief watches a value in the agency //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_WatchAgency (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() < 1) { TRI_V8_EXCEPTION_USAGE(scope, "watch(, , , )"); } const std::string key = TRI_ObjectToString(argv[0]); double timeout = 1.0; uint64_t waitIndex = 0; bool recursive = false; if (argv.Length() > 1) { waitIndex = TRI_ObjectToUInt64(argv[1], true); } if (argv.Length() > 2) { timeout = TRI_ObjectToDouble(argv[2]); } if (argv.Length() > 3) { recursive = TRI_ObjectToBoolean(argv[3]); } AgencyComm comm; AgencyCommResult result = comm.watchValue(key, waitIndex, timeout, recursive); if (result._statusCode == 0) { // watch timed out return scope.Close(v8::False()); } if (! result.successful()) { return scope.Close(v8::ThrowException(CreateAgencyException(result))); } result.parse("", false); std::map::const_iterator it = result._values.begin(); v8::Handle l = v8::Object::New(); while (it != result._values.end()) { const std::string key = (*it).first; TRI_json_t* json = (*it).second._json; if (json != 0) { l->Set(v8::String::New(key.c_str(), key.size()), TRI_ObjectJson(json)); } ++it; } return scope.Close(l); } //////////////////////////////////////////////////////////////////////////////// /// @brief returns the agency endpoints //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_EndpointsAgency (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 0) { TRI_V8_EXCEPTION_USAGE(scope, "endpoints()"); } std::vector endpoints = AgencyComm::getEndpoints(); // make the list of endpoints unique std::sort(endpoints.begin(), endpoints.end()); endpoints.assign(endpoints.begin(), std::unique(endpoints.begin(), endpoints.end())); v8::Handle l = v8::Array::New(); for (size_t i = 0; i < endpoints.size(); ++i) { const std::string endpoint = endpoints[i]; l->Set((uint32_t) i, v8::String::New(endpoint.c_str(), endpoint.size())); } return scope.Close(l); } //////////////////////////////////////////////////////////////////////////////// /// @brief returns the agency prefix //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_PrefixAgency (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() > 1) { TRI_V8_EXCEPTION_USAGE(scope, "prefix()"); } bool strip = false; if (argv.Length() > 0) { strip = TRI_ObjectToBoolean(argv[0]); } const std::string prefix = AgencyComm::prefix(); if (strip && prefix.size() > 2) { return scope.Close(v8::String::New(prefix.c_str() + 1, prefix.size() - 2)); } return scope.Close(v8::String::New(prefix.c_str(), prefix.size())); } //////////////////////////////////////////////////////////////////////////////// /// @brief sets the agency prefix //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_SetPrefixAgency (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 1) { TRI_V8_EXCEPTION_USAGE(scope, "setPrefix()"); } const bool result = AgencyComm::setPrefix(TRI_ObjectToString(argv[0])); return scope.Close(v8::Boolean::New(result)); } //////////////////////////////////////////////////////////////////////////////// /// @brief creates a uniqid //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_UniqidAgency (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() < 1 || argv.Length() > 3) { TRI_V8_EXCEPTION_USAGE(scope, "uniqid(, , )"); } const std::string key = TRI_ObjectToString(argv[0]); uint64_t count = 1; if (argv.Length() > 1) { count = TRI_ObjectToUInt64(argv[1], true); } if (count < 1 || count > 10000000) { TRI_V8_EXCEPTION_PARAMETER(scope, " is invalid"); } double timeout = 0.0; if (argv.Length() > 2) { timeout = TRI_ObjectToDouble(argv[2]); } AgencyComm comm; AgencyCommResult result = comm.uniqid(key, count, timeout); if (! result.successful() || result._index == 0) { return scope.Close(v8::ThrowException(CreateAgencyException(result))); } const std::string value = StringUtils::itoa(result._index); return scope.Close(v8::String::New(value.c_str(), value.size())); } //////////////////////////////////////////////////////////////////////////////// /// @brief returns the agency version //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_VersionAgency (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 0) { TRI_V8_EXCEPTION_USAGE(scope, "version()"); } AgencyComm comm; const std::string version = comm.getVersion(); return scope.Close(v8::String::New(version.c_str(), version.size())); } // ----------------------------------------------------------------------------- // --SECTION-- cluster info functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief whether or not a specific database exists //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_DoesDatabaseExistClusterInfo (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 1) { TRI_V8_EXCEPTION_USAGE(scope, "doesDatabaseExist()"); } const bool result = ClusterInfo::instance()->doesDatabaseExist(TRI_ObjectToString(argv[0]), true); return scope.Close(v8::Boolean::New(result)); } //////////////////////////////////////////////////////////////////////////////// /// @brief get the list of databases in the cluster //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_ListDatabases (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 0) { TRI_V8_EXCEPTION_USAGE(scope, "listDatabases()"); } vector res = ClusterInfo::instance()->listDatabases(true); v8::Handle a = v8::Array::New(res.size()); vector::iterator it; int count = 0; for (it = res.begin(); it != res.end(); ++it) { a->Set(count++, v8::String::New(it->c_str(), it->size())); } return scope.Close(a); } //////////////////////////////////////////////////////////////////////////////// /// @brief flush the caches (used for testing only) //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_FlushClusterInfo (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 0) { TRI_V8_EXCEPTION_USAGE(scope, "flush()"); } ClusterInfo::instance()->flush(); return scope.Close(v8::True()); } //////////////////////////////////////////////////////////////////////////////// /// @brief get the info about a collection in Plan //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_GetCollectionInfoClusterInfo (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 2) { TRI_V8_EXCEPTION_USAGE(scope, "getCollectionInfo(, )"); } TRI_shared_ptr ci = ClusterInfo::instance()->getCollection(TRI_ObjectToString(argv[0]), TRI_ObjectToString(argv[1])); v8::Handle result = v8::Object::New(); const std::string cid = triagens::basics::StringUtils::itoa(ci->id()); const std::string& name = ci->name(); result->Set(v8::String::New("id"), v8::String::New(cid.c_str(), cid.size())); result->Set(v8::String::New("name"), v8::String::New(name.c_str(), name.size())); result->Set(v8::String::New("type"), v8::Number::New((int) ci->type())); result->Set(v8::String::New("status"), v8::Number::New((int) ci->status())); const string statusString = ci->statusString(); result->Set(v8::String::New("statusString"), v8::String::New(statusString.c_str(), statusString.size())); result->Set(v8::String::New("deleted"), v8::Boolean::New(ci->deleted())); result->Set(v8::String::New("doCompact"), v8::Boolean::New(ci->doCompact())); result->Set(v8::String::New("isSystem"), v8::Boolean::New(ci->isSystem())); result->Set(v8::String::New("isVolatile"), v8::Boolean::New(ci->isVolatile())); result->Set(v8::String::New("waitForSync"), v8::Boolean::New(ci->waitForSync())); result->Set(v8::String::New("journalSize"), v8::Number::New(ci->journalSize())); const std::vector& sks = ci->shardKeys(); v8::Handle shardKeys = v8::Array::New(sks.size()); for (uint32_t i = 0, n = sks.size(); i < n; ++i) { shardKeys->Set(i, v8::String::New(sks[i].c_str(), sks[i].size())); } result->Set(v8::String::New("shardKeys"), shardKeys); const std::map& sis = ci->shardIds(); v8::Handle shardIds = v8::Object::New(); std::map::const_iterator it = sis.begin(); while (it != sis.end()) { shardIds->Set(v8::String::New((*it).first.c_str(), (*it).first.size()), v8::String::New((*it).second.c_str(), (*it).second.size())); ++it; } result->Set(v8::String::New("shards"), shardIds); // TODO: fill "indexes" v8::Handle indexes = v8::Array::New(); result->Set(v8::String::New("indexes"), indexes); return scope.Close(result); } //////////////////////////////////////////////////////////////////////////////// /// @brief get the info about a collection in Current //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_GetCollectionInfoCurrentClusterInfo (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 3) { TRI_V8_EXCEPTION_USAGE(scope, "getCollectionInfoCurrent(, , )"); } ShardID shardID = TRI_ObjectToString(argv[2]); TRI_shared_ptr ci = ClusterInfo::instance()->getCollection( TRI_ObjectToString(argv[0]), TRI_ObjectToString(argv[1])); v8::Handle result = v8::Object::New(); // First some stuff from Plan for which Current does not make sense: const std::string cid = triagens::basics::StringUtils::itoa(ci->id()); const std::string& name = ci->name(); result->Set(v8::String::New("id"), v8::String::New(cid.c_str(), cid.size())); result->Set(v8::String::New("name"), v8::String::New(name.c_str(), name.size())); TRI_shared_ptr cic = ClusterInfo::instance()->getCollectionCurrent( TRI_ObjectToString(argv[0]), cid); result->Set(v8::String::New("type"), v8::Number::New((int) ci->type())); // Now the Current information, if we actually got it: TRI_vocbase_col_status_e s = cic->status(shardID); result->Set(v8::String::New("status"), v8::Number::New((int) cic->status(shardID))); if (s == TRI_VOC_COL_STATUS_CORRUPTED) { return scope.Close(result); } const string statusString = TRI_GetStatusStringCollectionVocBase(s); result->Set(v8::String::New("statusString"), v8::String::New(statusString.c_str(), statusString.size())); result->Set(v8::String::New("deleted"), v8::Boolean::New(cic->deleted(shardID))); result->Set(v8::String::New("doCompact"), v8::Boolean::New(cic->doCompact(shardID))); result->Set(v8::String::New("isSystem"), v8::Boolean::New(cic->isSystem(shardID))); result->Set(v8::String::New("isVolatile"), v8::Boolean::New(cic->isVolatile(shardID))); result->Set(v8::String::New("waitForSync"), v8::Boolean::New(cic->waitForSync(shardID))); result->Set(v8::String::New("journalSize"), v8::Number::New(cic->journalSize(shardID))); const std::string serverID = cic->responsibleServer(shardID); result->Set(v8::String::New("DBServer"), v8::String::New(serverID.c_str(), serverID.size())); // TODO: fill "indexes" v8::Handle indexes = v8::Array::New(); result->Set(v8::String::New("indexes"), indexes); // Finally, report any possible error: bool error = cic->error(shardID); result->Set(v8::String::New("error"), v8::Boolean::New(error)); if (error) { result->Set(v8::String::New("errorNum"), v8::Number::New(cic->errorNum(shardID))); const string errorMessage = cic->errorMessage(shardID); result->Set(v8::String::New("errorMessage"), v8::String::New(errorMessage.c_str(), errorMessage.size())); } return scope.Close(result); } //////////////////////////////////////////////////////////////////////////////// /// @brief get the responsible server //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_GetResponsibleServerClusterInfo (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 1) { TRI_V8_EXCEPTION_USAGE(scope, "getResponsibleServer()"); } const std::string result = ClusterInfo::instance()->getResponsibleServer(TRI_ObjectToString(argv[0])); return scope.Close(v8::String::New(result.c_str(), result.size())); } //////////////////////////////////////////////////////////////////////////////// /// @brief get the server endpoint for a server //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_GetServerEndpointClusterInfo (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 1) { TRI_V8_EXCEPTION_USAGE(scope, "getServerEndpoint()"); } const std::string result = ClusterInfo::instance()->getServerEndpoint(TRI_ObjectToString(argv[0])); return scope.Close(v8::String::New(result.c_str(), result.size())); } //////////////////////////////////////////////////////////////////////////////// /// @brief returns the DBServers currently registered //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_GetDBServers (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 0) { TRI_V8_EXCEPTION_USAGE(scope, "DBServers()"); } std::vector DBServers = ClusterInfo::instance()->getCurrentDBServers(); v8::Handle l = v8::Array::New(); for (size_t i = 0; i < DBServers.size(); ++i) { ServerID const sid = DBServers[i]; l->Set((uint32_t) i, v8::String::New(sid.c_str(), sid.size())); } return scope.Close(l); } //////////////////////////////////////////////////////////////////////////////// /// @brief reloads the cache of DBServers currently registered //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_ReloadDBServers (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 0) { TRI_V8_EXCEPTION_USAGE(scope, "reloadDBServers()"); } ClusterInfo::instance()->loadCurrentDBServers(); return scope.Close(v8::Undefined()); } //////////////////////////////////////////////////////////////////////////////// /// @brief returns a unique id //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_UniqidClusterInfo (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() > 1) { TRI_V8_EXCEPTION_USAGE(scope, "uniqid()"); } uint64_t count = 1; if (argv.Length() > 0) { count = TRI_ObjectToUInt64(argv[0], true); } if (count == 0) { TRI_V8_EXCEPTION_PARAMETER(scope, " is invalid"); } uint64_t value = ClusterInfo::instance()->uniqid(); if (value == 0) { TRI_V8_EXCEPTION_MESSAGE(scope, TRI_ERROR_INTERNAL, "unable to generate unique id"); } const std::string id = StringUtils::itoa(value); return scope.Close(v8::String::New(id.c_str(), id.size())); } // ----------------------------------------------------------------------------- // --SECTION-- server state functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief return the servers address //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_AddressServerState (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 0) { TRI_V8_EXCEPTION_USAGE(scope, "address()"); } const std::string address = ServerState::instance()->getAddress(); return scope.Close(v8::String::New(address.c_str(), address.size())); } //////////////////////////////////////////////////////////////////////////////// /// @brief flush the server state (used for testing only) //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_FlushServerState (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 0) { TRI_V8_EXCEPTION_USAGE(scope, "flush()"); } ServerState::instance()->flush(); return scope.Close(v8::True()); } //////////////////////////////////////////////////////////////////////////////// /// @brief return the servers id //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_IdServerState (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 0) { TRI_V8_EXCEPTION_USAGE(scope, "id()"); } const std::string id = ServerState::instance()->getId(); return scope.Close(v8::String::New(id.c_str(), id.size())); } //////////////////////////////////////////////////////////////////////////////// /// @brief returns the data path //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_DataPathServerState (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 0) { TRI_V8_EXCEPTION_USAGE(scope, "dataPath()"); } const std::string path = ServerState::instance()->getDataPath(); return scope.Close(v8::String::New(path.c_str(), path.size())); } //////////////////////////////////////////////////////////////////////////////// /// @brief returns the log path //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_LogPathServerState (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 0) { TRI_V8_EXCEPTION_USAGE(scope, "logPath()"); } const std::string path = ServerState::instance()->getLogPath(); return scope.Close(v8::String::New(path.c_str(), path.size())); } //////////////////////////////////////////////////////////////////////////////// /// @brief returns the agent path //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_AgentPathServerState (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 0) { TRI_V8_EXCEPTION_USAGE(scope, "agentPath()"); } const std::string path = ServerState::instance()->getAgentPath(); return scope.Close(v8::String::New(path.c_str(), path.size())); } //////////////////////////////////////////////////////////////////////////////// /// @brief returns the arangod path //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_ArangodPathServerState (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 0) { TRI_V8_EXCEPTION_USAGE(scope, "arangodPath()"); } const std::string path = ServerState::instance()->getArangodPath(); return scope.Close(v8::String::New(path.c_str(), path.size())); } //////////////////////////////////////////////////////////////////////////////// /// @brief returns the javascript startup path //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_JavaScriptPathServerState (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 0) { TRI_V8_EXCEPTION_USAGE(scope, "javaScriptPath()"); } const std::string path = ServerState::instance()->getJavaScriptPath(); return scope.Close(v8::String::New(path.c_str(), path.size())); } //////////////////////////////////////////////////////////////////////////////// /// @brief returns the DBserver config //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_DBserverConfigServerState (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 0) { TRI_V8_EXCEPTION_USAGE(scope, "dbserverConfig()"); } const std::string path = ServerState::instance()->getDBserverConfig(); return scope.Close(v8::String::New(path.c_str(), path.size())); } //////////////////////////////////////////////////////////////////////////////// /// @brief returns the coordinator config //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_CoordinatorConfigServerState (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 0) { TRI_V8_EXCEPTION_USAGE(scope, "coordinatorConfig()"); } const std::string path = ServerState::instance()->getCoordinatorConfig(); return scope.Close(v8::String::New(path.c_str(), path.size())); } //////////////////////////////////////////////////////////////////////////////// /// @brief returns true, if the dispatcher frontend should be disabled //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_DisableDipatcherFrontendServerState (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 0) { TRI_V8_EXCEPTION_USAGE(scope, "disableDispatcherFrontend()"); } return scope.Close(v8::Boolean::New(ServerState::instance()->getDisableDispatcherFrontend())); } //////////////////////////////////////////////////////////////////////////////// /// @brief returns true, if the dispatcher kickstarter should be disabled //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_DisableDipatcherKickstarterServerState (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 0) { TRI_V8_EXCEPTION_USAGE(scope, "disableDispatcherKickstarter()"); } return scope.Close(v8::Boolean::New(ServerState::instance()->getDisableDispatcherKickstarter())); } //////////////////////////////////////////////////////////////////////////////// /// @brief return whether the cluster is initialised //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_InitialisedServerState (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 0) { TRI_V8_EXCEPTION_USAGE(scope, "initialised()"); } return scope.Close(v8::Boolean::New(ServerState::instance()->initialised())); } //////////////////////////////////////////////////////////////////////////////// /// @brief whether or not the server is a coordinator //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_IsCoordinatorServerState (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 0) { TRI_V8_EXCEPTION_USAGE(scope, "isCoordinator()"); } return scope.Close(v8::Boolean::New(ServerState::instance()->getRole() == ServerState::ROLE_COORDINATOR)); } //////////////////////////////////////////////////////////////////////////////// /// @brief returns the server role //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_RoleServerState (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 0) { TRI_V8_EXCEPTION_USAGE(scope, "role()"); } const std::string role = ServerState::roleToString(ServerState::instance()->getRole()); return scope.Close(v8::String::New(role.c_str(), role.size())); } //////////////////////////////////////////////////////////////////////////////// /// @brief sets the server id (used for testing) //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_SetIdServerState (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 1) { TRI_V8_EXCEPTION_USAGE(scope, "setId()"); } const std::string id = TRI_ObjectToString(argv[0]); ServerState::instance()->setId(id); return scope.Close(v8::True()); } //////////////////////////////////////////////////////////////////////////////// /// @brief sets the server role (used for testing) //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_SetRoleServerState (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 1) { TRI_V8_EXCEPTION_USAGE(scope, "setRole()"); } const std::string role = TRI_ObjectToString(argv[0]); ServerState::RoleEnum r = ServerState::stringToRole(role); if (r == ServerState::ROLE_UNDEFINED) { TRI_V8_EXCEPTION_PARAMETER(scope, " is invalid"); } ServerState::instance()->setRole(r); return scope.Close(v8::True()); } //////////////////////////////////////////////////////////////////////////////// /// @brief returns the server state //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_StatusServerState (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 0) { TRI_V8_EXCEPTION_USAGE(scope, "status()"); } const std::string state = ServerState::stateToString(ServerState::instance()->getState()); return scope.Close(v8::String::New(state.c_str(), state.size())); } //////////////////////////////////////////////////////////////////////////////// /// @brief prepare to send a request /// /// this is used for asynchronous as well as synchronous requests. //////////////////////////////////////////////////////////////////////////////// static void PrepareClusterCommRequest ( v8::Arguments const& argv, triagens::rest::HttpRequest::HttpRequestType& reqType, string& destination, string& path, string& body, map* headerFields, ClientTransactionID& clientTransactionID, CoordTransactionID& coordTransactionID, double& timeout) { TRI_v8_global_t* v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData(); assert(argv.Length() >= 4); reqType = triagens::rest::HttpRequest::HTTP_REQUEST_GET; if (argv[0]->IsString()) { TRI_Utf8ValueNFC UTF8(TRI_UNKNOWN_MEM_ZONE, argv[0]); string methstring = *UTF8; reqType = triagens::rest::HttpRequest::translateMethod(methstring); if (reqType == triagens::rest::HttpRequest::HTTP_REQUEST_ILLEGAL) { reqType = triagens::rest::HttpRequest::HTTP_REQUEST_GET; } } destination = TRI_ObjectToString(argv[1]); string dbname = TRI_ObjectToString(argv[2]); path = TRI_ObjectToString(argv[3]); path = "/_db/" + dbname + path; body.clear(); if (argv.Length() > 4) { body = TRI_ObjectToString(argv[4]); } if (argv.Length() > 5 && argv[5]->IsObject()) { v8::Handle obj = argv[5].As(); v8::Handle props = obj->GetOwnPropertyNames(); uint32_t i; for (i = 0; i < props->Length(); ++i) { v8::Handle prop = props->Get(i); v8::Handle val = obj->Get(prop); string propstring = TRI_ObjectToString(prop); string valstring = TRI_ObjectToString(val); if (propstring != "") { headerFields->insert(pair(propstring, valstring)); } } } clientTransactionID = ""; coordTransactionID = 0; timeout = 24*3600.0; if (argv.Length() > 6 && argv[6]->IsObject()) { v8::Handle opt = argv[6].As(); if (opt->Has(v8g->ClientTransactionIDKey)) { clientTransactionID = TRI_ObjectToString(opt->Get(v8g->ClientTransactionIDKey)); } if (opt->Has(v8g->CoordTransactionIDKey)) { coordTransactionID = TRI_ObjectToUInt64(opt->Get(v8g->CoordTransactionIDKey), true); } if (opt->Has(v8g->TimeoutKey)) { timeout = TRI_ObjectToDouble(opt->Get(v8g->TimeoutKey)); } } if (clientTransactionID == "") { clientTransactionID = StringUtils::itoa(TRI_NewTickServer()); } if (coordTransactionID == 0) { coordTransactionID = TRI_NewTickServer(); } if (timeout == 0) { timeout = 24*3600.0; } } //////////////////////////////////////////////////////////////////////////////// /// @brief prepare a ClusterCommResult for JavaScript //////////////////////////////////////////////////////////////////////////////// v8::Handle PrepareClusterCommResultForJS( ClusterCommResult const* res) { v8::HandleScope scope; TRI_v8_global_t* v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData(); v8::Handle r = v8::Object::New(); if (0 == res) { r->Set(v8g->ErrorMessageKey, v8::String::New("out of memory")); } else if (res->dropped) { r->Set(v8g->ErrorMessageKey, v8::String::New("operation was dropped")); } else { r->Set(v8g->ClientTransactionIDKey, v8::String::New(res->clientTransactionID.c_str(), res->clientTransactionID.size())); // convert the ids to strings as uint64_t might be too big for JavaScript numbers std::string id = StringUtils::itoa(res->coordTransactionID); r->Set(v8g->CoordTransactionIDKey, v8::String::New(id.c_str(), id.size())); id = StringUtils::itoa(res->operationID); r->Set(v8g->OperationIDKey, v8::String::New(id.c_str(), id.size())); r->Set(v8g->ShardIDKey, v8::String::New(res->shardID.c_str(), res->shardID.size())); if (res->status == CL_COMM_SUBMITTED) { r->Set(v8g->StatusKey, v8::String::New("SUBMITTED")); } else if (res->status == CL_COMM_SENDING) { r->Set(v8g->StatusKey, v8::String::New("SENDING")); } else if (res->status == CL_COMM_SENT) { r->Set(v8g->StatusKey, v8::String::New("SENT")); // Maybe return the response of the initial request??? } else if (res->status == CL_COMM_TIMEOUT) { r->Set(v8g->StatusKey, v8::String::New("TIMEOUT")); r->Set(v8g->TimeoutKey,v8::BooleanObject::New(true)); } else if (res->status == CL_COMM_ERROR) { r->Set(v8g->StatusKey, v8::String::New("ERROR")); if (res->result && res->result->isComplete()) { v8::Handle details = v8::Object::New(); details->Set(v8::String::New("code"), v8::Number::New(res->result->getHttpReturnCode())); details->Set(v8::String::New("message"), v8::String::New(res->result->getHttpReturnMessage().c_str())); details->Set(v8::String::New("body"), v8::String::New(res->result->getBody().str().c_str(), res->result->getBody().str().length())); r->Set(v8::String::New("details"), details); r->Set(v8g->ErrorMessageKey, v8::String::New("got bad HTTP response")); } else { r->Set(v8g->ErrorMessageKey, v8::String::New("got no HTTP response, DBserver seems gone")); } } else if (res->status == CL_COMM_DROPPED) { r->Set(v8g->StatusKey, v8::String::New("DROPPED")); r->Set(v8g->ErrorMessageKey, v8::String::New("request dropped whilst waiting for answer")); } else { // Everything is OK assert(res->status == CL_COMM_RECEIVED); // The headers: v8::Handle h = v8::Object::New(); r->Set(v8g->StatusKey, v8::String::New("RECEIVED")); map headers = res->answer->headers(); map::iterator i; for (i = headers.begin(); i != headers.end(); ++i) { h->Set(v8::String::New(i->first.c_str()), v8::String::New(i->second.c_str())); } r->Set(v8::String::New("headers"), h); // The body: if (0 != res->answer->body()) { r->Set(v8::String::New("body"), v8::String::New(res->answer->body(), res->answer->bodySize())); } } } return scope.Close(r); } //////////////////////////////////////////////////////////////////////////////// /// @brief send an asynchronous request //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_AsyncRequest (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() < 4 || argv.Length() > 7) { TRI_V8_EXCEPTION_USAGE(scope, "asyncRequest(" "reqType, destination, dbname, path, body, headers, options)"); } // Possible options: // - clientTransactionID (string) // - coordTransactionID (number) // - timeout (number) if (ServerState::instance()->getRole() != ServerState::ROLE_COORDINATOR) { TRI_V8_EXCEPTION_INTERNAL(scope,"request works only in coordinator role"); } ClusterComm* cc = ClusterComm::instance(); if (cc == 0) { TRI_V8_EXCEPTION_MESSAGE(scope, TRI_ERROR_INTERNAL, "clustercomm object not found"); } triagens::rest::HttpRequest::HttpRequestType reqType; string destination; string path; string *body = new string(); map* headerFields = new map; ClientTransactionID clientTransactionID; CoordTransactionID coordTransactionID; double timeout; PrepareClusterCommRequest(argv, reqType, destination, path,*body,headerFields, clientTransactionID, coordTransactionID, timeout); ClusterCommResult const* res; res = cc->asyncRequest(clientTransactionID, coordTransactionID, destination, reqType, path, body, true, headerFields, 0, timeout); if (res == 0) { TRI_V8_EXCEPTION_MESSAGE(scope, TRI_ERROR_INTERNAL, "couldn't queue async request"); } LOG_DEBUG("JS_AsyncRequest: request has been submitted"); v8::Handle result = PrepareClusterCommResultForJS(res); delete res; return scope.Close(result); } //////////////////////////////////////////////////////////////////////////////// /// @brief send a synchronous request //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_SyncRequest (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() < 4 || argv.Length() > 7) { TRI_V8_EXCEPTION_USAGE(scope, "syncRequest(" "reqType, destination, dbname, path, body, headers, options)"); } // Possible options: // - clientTransactionID (string) // - coordTransactionID (number) // - timeout (number) if (ServerState::instance()->getRole() != ServerState::ROLE_COORDINATOR) { TRI_V8_EXCEPTION_INTERNAL(scope,"request works only in coordinator role"); } ClusterComm* cc = ClusterComm::instance(); if (cc == 0) { TRI_V8_EXCEPTION_MESSAGE(scope, TRI_ERROR_INTERNAL, "clustercomm object not found"); } triagens::rest::HttpRequest::HttpRequestType reqType; string destination; string path; string body; map* headerFields = new map; ClientTransactionID clientTransactionID; CoordTransactionID coordTransactionID; double timeout; PrepareClusterCommRequest(argv, reqType, destination, path, body, headerFields, clientTransactionID, coordTransactionID, timeout); ClusterCommResult const* res; res = cc->syncRequest(clientTransactionID, coordTransactionID, destination, reqType, path, body, *headerFields, timeout); delete headerFields; if (res == 0) { TRI_V8_EXCEPTION_MESSAGE(scope, TRI_ERROR_INTERNAL, "couldn't do sync request"); } LOG_DEBUG("JS_SyncRequest: request has been done"); v8::Handle result = PrepareClusterCommResultForJS(res); delete res; return scope.Close(result); } //////////////////////////////////////////////////////////////////////////////// /// @brief enquire information about an asynchronous request //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_Enquire (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 1) { TRI_V8_EXCEPTION_USAGE(scope, "enquire(operationID)"); } if (ServerState::instance()->getRole() != ServerState::ROLE_COORDINATOR) { TRI_V8_EXCEPTION_INTERNAL(scope,"request works only in coordinator role"); } ClusterComm* cc = ClusterComm::instance(); if (cc == 0) { TRI_V8_EXCEPTION_MESSAGE(scope, TRI_ERROR_INTERNAL, "clustercomm object not found"); } OperationID operationID = TRI_ObjectToUInt64(argv[0], true); ClusterCommResult const* res; LOG_DEBUG("JS_Enquire: calling ClusterComm::enquire()"); res = cc->enquire(operationID); v8::Handle result = PrepareClusterCommResultForJS(res); delete res; return scope.Close(result); } //////////////////////////////////////////////////////////////////////////////// /// @brief wait for the result of an asynchronous request //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_Wait (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 1) { TRI_V8_EXCEPTION_USAGE(scope, "wait(obj)"); } // Possible options: // - clientTransactionID (string) // - coordTransactionID (number) // - operationID (number) // - shardID (string) // - timeout (number) if (ServerState::instance()->getRole() != ServerState::ROLE_COORDINATOR) { TRI_V8_EXCEPTION_INTERNAL(scope,"request works only in coordinator role"); } ClusterComm* cc = ClusterComm::instance(); if (cc == 0) { TRI_V8_EXCEPTION_MESSAGE(scope, TRI_ERROR_INTERNAL, "clustercomm object not found"); } ClientTransactionID clientTransactionID = ""; CoordTransactionID coordTransactionID = 0; OperationID operationID = 0; ShardID shardID = ""; double timeout = 24*3600.0; TRI_v8_global_t* v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData(); if (argv[0]->IsObject()) { v8::Handle obj = argv[0].As(); if (obj->Has(v8g->ClientTransactionIDKey)) { clientTransactionID = TRI_ObjectToString(obj->Get(v8g->ClientTransactionIDKey)); } if (obj->Has(v8g->CoordTransactionIDKey)) { coordTransactionID = TRI_ObjectToUInt64(obj->Get(v8g->CoordTransactionIDKey), true); } if (obj->Has(v8g->OperationIDKey)) { operationID = TRI_ObjectToUInt64(obj->Get(v8g->OperationIDKey), true); } if (obj->Has(v8g->ShardIDKey)) { shardID = TRI_ObjectToString(obj->Get(v8g->ShardIDKey)); } if (obj->Has(v8g->TimeoutKey)) { timeout = TRI_ObjectToDouble(obj->Get(v8g->TimeoutKey)); if (timeout == 0.0) { timeout = 24*3600.0; } } } ClusterCommResult const* res; LOG_DEBUG("JS_Wait: calling ClusterComm::wait()"); res = cc->wait(clientTransactionID, coordTransactionID, operationID, shardID, timeout); v8::Handle result = PrepareClusterCommResultForJS(res); delete res; return scope.Close(result); } //////////////////////////////////////////////////////////////////////////////// /// @brief drop the result of an asynchronous request //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_Drop (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 1) { TRI_V8_EXCEPTION_USAGE(scope, "wait(obj)"); } // Possible options: // - clientTransactionID (string) // - coordTransactionID (number) // - operationID (number) // - shardID (string) if (ServerState::instance()->getRole() != ServerState::ROLE_COORDINATOR) { TRI_V8_EXCEPTION_INTERNAL(scope,"request works only in coordinator role"); } ClusterComm* cc = ClusterComm::instance(); if (cc == 0) { TRI_V8_EXCEPTION_MESSAGE(scope, TRI_ERROR_INTERNAL, "clustercomm object not found"); } ClientTransactionID clientTransactionID = ""; CoordTransactionID coordTransactionID = 0; OperationID operationID = 0; ShardID shardID = ""; TRI_v8_global_t* v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData(); if (argv[0]->IsObject()) { v8::Handle obj = argv[0].As(); if (obj->Has(v8g->ClientTransactionIDKey)) { clientTransactionID = TRI_ObjectToString(obj->Get(v8g->ClientTransactionIDKey)); } if (obj->Has(v8g->CoordTransactionIDKey)) { coordTransactionID = TRI_ObjectToUInt64(obj->Get(v8g->CoordTransactionIDKey), true); } if (obj->Has(v8g->OperationIDKey)) { operationID = TRI_ObjectToUInt64(obj->Get(v8g->OperationIDKey), true); } if (obj->Has(v8g->ShardIDKey)) { shardID = TRI_ObjectToString(obj->Get(v8g->ShardIDKey)); } } LOG_DEBUG("JS_Drop: calling ClusterComm::drop()"); cc->drop(clientTransactionID, coordTransactionID, operationID, shardID); return scope.Close(v8::Undefined()); } // ----------------------------------------------------------------------------- // --SECTION-- public functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief creates a global cluster context //////////////////////////////////////////////////////////////////////////////// void TRI_InitV8Cluster (v8::Handle context) { v8::HandleScope scope; // check the isolate v8::Isolate* isolate = v8::Isolate::GetCurrent(); TRI_v8_global_t* v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData(); assert(v8g != 0); v8::Handle rt; v8::Handle ft; // ........................................................................... // generate the agency template // ........................................................................... ft = v8::FunctionTemplate::New(); ft->SetClassName(TRI_V8_SYMBOL("ArangoAgency")); rt = ft->InstanceTemplate(); rt->SetInternalFieldCount(2); TRI_AddMethodVocbase(rt, "cas", JS_CasAgency); TRI_AddMethodVocbase(rt, "createDirectory", JS_CreateDirectoryAgency); TRI_AddMethodVocbase(rt, "get", JS_GetAgency); TRI_AddMethodVocbase(rt, "isEnabled", JS_IsEnabledAgency); TRI_AddMethodVocbase(rt, "increaseVersion", JS_IncreaseVersionAgency); TRI_AddMethodVocbase(rt, "list", JS_ListAgency); TRI_AddMethodVocbase(rt, "lockRead", JS_LockReadAgency); TRI_AddMethodVocbase(rt, "lockWrite", JS_LockWriteAgency); TRI_AddMethodVocbase(rt, "remove", JS_RemoveAgency); TRI_AddMethodVocbase(rt, "set", JS_SetAgency); TRI_AddMethodVocbase(rt, "watch", JS_WatchAgency); TRI_AddMethodVocbase(rt, "endpoints", JS_EndpointsAgency); TRI_AddMethodVocbase(rt, "prefix", JS_PrefixAgency); TRI_AddMethodVocbase(rt, "setPrefix", JS_SetPrefixAgency, true); TRI_AddMethodVocbase(rt, "uniqid", JS_UniqidAgency); TRI_AddMethodVocbase(rt, "unlockRead", JS_UnlockReadAgency); TRI_AddMethodVocbase(rt, "unlockWrite", JS_UnlockWriteAgency); TRI_AddMethodVocbase(rt, "version", JS_VersionAgency); v8g->AgencyTempl = v8::Persistent::New(isolate, rt); TRI_AddGlobalFunctionVocbase(context, "ArangoAgencyCtor", ft->GetFunction(), true); // register the global object v8::Handle aa = v8g->AgencyTempl->NewInstance(); if (! aa.IsEmpty()) { TRI_AddGlobalVariableVocbase(context, "ArangoAgency", aa); } // ............................................................................. // generate the cluster info template // ............................................................................. ft = v8::FunctionTemplate::New(); ft->SetClassName(TRI_V8_SYMBOL("ArangoClusterInfo")); rt = ft->InstanceTemplate(); rt->SetInternalFieldCount(2); TRI_AddMethodVocbase(rt, "doesDatabaseExist", JS_DoesDatabaseExistClusterInfo); TRI_AddMethodVocbase(rt, "listDatabases", JS_ListDatabases); TRI_AddMethodVocbase(rt, "flush", JS_FlushClusterInfo, true); TRI_AddMethodVocbase(rt, "getCollectionInfo", JS_GetCollectionInfoClusterInfo); TRI_AddMethodVocbase(rt, "getCollectionInfoCurrent", JS_GetCollectionInfoCurrentClusterInfo); TRI_AddMethodVocbase(rt, "getResponsibleServer", JS_GetResponsibleServerClusterInfo); TRI_AddMethodVocbase(rt, "getServerEndpoint", JS_GetServerEndpointClusterInfo); TRI_AddMethodVocbase(rt, "getDBServers", JS_GetDBServers); TRI_AddMethodVocbase(rt, "reloadDBServers", JS_ReloadDBServers); TRI_AddMethodVocbase(rt, "uniqid", JS_UniqidClusterInfo); v8g->ClusterInfoTempl = v8::Persistent::New(isolate, rt); TRI_AddGlobalFunctionVocbase(context, "ArangoClusterInfoCtor", ft->GetFunction(), true); // register the global object v8::Handle ci = v8g->ClusterInfoTempl->NewInstance(); if (! ci.IsEmpty()) { TRI_AddGlobalVariableVocbase(context, "ArangoClusterInfo", ci); } // ............................................................................. // generate the server state template // ........................................................................... ft = v8::FunctionTemplate::New(); ft->SetClassName(TRI_V8_SYMBOL("ArangoServerState")); rt = ft->InstanceTemplate(); rt->SetInternalFieldCount(2); TRI_AddMethodVocbase(rt, "address", JS_AddressServerState); TRI_AddMethodVocbase(rt, "flush", JS_FlushServerState, true); TRI_AddMethodVocbase(rt, "id", JS_IdServerState); TRI_AddMethodVocbase(rt, "dataPath", JS_DataPathServerState); TRI_AddMethodVocbase(rt, "logPath", JS_LogPathServerState); TRI_AddMethodVocbase(rt, "agentPath", JS_AgentPathServerState); TRI_AddMethodVocbase(rt, "arangodPath", JS_ArangodPathServerState); TRI_AddMethodVocbase(rt, "javaScriptPath", JS_JavaScriptPathServerState); TRI_AddMethodVocbase(rt, "dbserverConfig", JS_DBserverConfigServerState); TRI_AddMethodVocbase(rt, "coordinatorConfig", JS_CoordinatorConfigServerState); TRI_AddMethodVocbase(rt, "disableDispatcherFrontend", JS_DisableDipatcherFrontendServerState); TRI_AddMethodVocbase(rt, "disableDispatcherKickstarter", JS_DisableDipatcherKickstarterServerState); TRI_AddMethodVocbase(rt, "initialised", JS_InitialisedServerState); TRI_AddMethodVocbase(rt, "isCoordinator", JS_IsCoordinatorServerState); TRI_AddMethodVocbase(rt, "role", JS_RoleServerState); TRI_AddMethodVocbase(rt, "setId", JS_SetIdServerState, true); TRI_AddMethodVocbase(rt, "setRole", JS_SetRoleServerState, true); TRI_AddMethodVocbase(rt, "status", JS_StatusServerState); v8g->ServerStateTempl = v8::Persistent::New(isolate, rt); TRI_AddGlobalFunctionVocbase(context, "ArangoServerStateCtor", ft->GetFunction(), true); // register the global object v8::Handle ss = v8g->ServerStateTempl->NewInstance(); if (! ss.IsEmpty()) { TRI_AddGlobalVariableVocbase(context, "ArangoServerState", ss); } // ........................................................................... // generate the cluster comm template // ........................................................................... ft = v8::FunctionTemplate::New(); ft->SetClassName(TRI_V8_SYMBOL("ArangoClusterComm")); rt = ft->InstanceTemplate(); rt->SetInternalFieldCount(2); TRI_AddMethodVocbase(rt, "asyncRequest", JS_AsyncRequest); TRI_AddMethodVocbase(rt, "syncRequest", JS_SyncRequest); TRI_AddMethodVocbase(rt, "enquire", JS_Enquire); TRI_AddMethodVocbase(rt, "wait", JS_Wait); TRI_AddMethodVocbase(rt, "drop", JS_Drop); v8g->ClusterCommTempl = v8::Persistent::New(isolate, rt); TRI_AddGlobalFunctionVocbase(context, "ArangoClusterCommCtor", ft->GetFunction(), true); // register the global object ss = v8g->ClusterCommTempl->NewInstance(); if (! ss.IsEmpty()) { TRI_AddGlobalVariableVocbase(context, "ArangoClusterComm", ss); } } // Local Variables: // mode: outline-minor // outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|/// @page\\|// --SECTION--\\|/// @\\}" // End: