summaryrefslogtreecommitdiff
path: root/platform/default/src/mbgl/storage/database_file_source.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'platform/default/src/mbgl/storage/database_file_source.cpp')
-rw-r--r--platform/default/src/mbgl/storage/database_file_source.cpp280
1 files changed, 280 insertions, 0 deletions
diff --git a/platform/default/src/mbgl/storage/database_file_source.cpp b/platform/default/src/mbgl/storage/database_file_source.cpp
new file mode 100644
index 0000000000..ba8ba7b31d
--- /dev/null
+++ b/platform/default/src/mbgl/storage/database_file_source.cpp
@@ -0,0 +1,280 @@
+#include <mbgl/storage/database_file_source.hpp>
+#include <mbgl/storage/file_source_manager.hpp>
+#include <mbgl/storage/file_source_request.hpp>
+#include <mbgl/storage/offline_database.hpp>
+#include <mbgl/storage/offline_download.hpp>
+#include <mbgl/storage/resource_options.hpp>
+#include <mbgl/storage/response.hpp>
+#include <mbgl/util/constants.hpp>
+#include <mbgl/util/logging.hpp>
+#include <mbgl/util/thread.hpp>
+
+namespace mbgl {
+
+// For testing use only
+constexpr const char* READ_ONLY_MODE_KEY = "read-only-mode";
+
+class DatabaseFileSourceThread {
+public:
+ DatabaseFileSourceThread(std::shared_ptr<FileSource> onlineFileSource_, std::string cachePath)
+ : db(std::make_unique<OfflineDatabase>(cachePath)), onlineFileSource(onlineFileSource_) {}
+
+ void request(const Resource& resource, ActorRef<FileSourceRequest> req) {
+ auto offlineResponse = db->get(resource);
+ if (!offlineResponse) {
+ offlineResponse.emplace();
+ offlineResponse->noContent = true;
+ offlineResponse->error =
+ std::make_unique<Response::Error>(Response::Error::Reason::NotFound, "Not found in offline database");
+ } else if (!offlineResponse->isUsable()) {
+ offlineResponse->error =
+ std::make_unique<Response::Error>(Response::Error::Reason::NotFound, "Cached resource is unusable");
+ }
+ req.invoke(&FileSourceRequest::setResponse, *offlineResponse);
+ }
+
+ void setDatabasePath(const std::string& path, std::function<void()> callback) {
+ db->changePath(path);
+ if (callback) {
+ callback();
+ }
+ }
+
+ void forward(const Resource& resource, const Response& response, std::function<void()> callback) {
+ db->put(resource, response);
+ if (callback) {
+ callback();
+ }
+ }
+
+ void resetDatabase(std::function<void(std::exception_ptr)> callback) { callback(db->resetDatabase()); }
+
+ void packDatabase(std::function<void(std::exception_ptr)> callback) { callback(db->pack()); }
+
+ void runPackDatabaseAutomatically(bool autopack) { db->runPackDatabaseAutomatically(autopack); }
+
+ void put(const Resource& resource, const Response& response) { db->put(resource, response); }
+
+ void invalidateAmbientCache(std::function<void(std::exception_ptr)> callback) {
+ callback(db->invalidateAmbientCache());
+ }
+
+ void clearAmbientCache(std::function<void(std::exception_ptr)> callback) { callback(db->clearAmbientCache()); }
+
+ void setMaximumAmbientCacheSize(uint64_t size, std::function<void(std::exception_ptr)> callback) {
+ callback(db->setMaximumAmbientCacheSize(size));
+ }
+
+ void listRegions(std::function<void(expected<OfflineRegions, std::exception_ptr>)> callback) {
+ callback(db->listRegions());
+ }
+
+ void createRegion(const OfflineRegionDefinition& definition,
+ const OfflineRegionMetadata& metadata,
+ std::function<void(expected<OfflineRegion, std::exception_ptr>)> callback) {
+ callback(db->createRegion(definition, metadata));
+ }
+
+ void mergeOfflineRegions(const std::string& sideDatabasePath,
+ std::function<void(expected<OfflineRegions, std::exception_ptr>)> callback) {
+ callback(db->mergeDatabase(sideDatabasePath));
+ }
+
+ void updateMetadata(const int64_t regionID,
+ const OfflineRegionMetadata& metadata,
+ std::function<void(expected<OfflineRegionMetadata, std::exception_ptr>)> callback) {
+ callback(db->updateMetadata(regionID, metadata));
+ }
+
+ void getRegionStatus(int64_t regionID,
+ std::function<void(expected<OfflineRegionStatus, std::exception_ptr>)> callback) {
+ if (auto download = getDownload(regionID)) {
+ callback(download.value()->getStatus());
+ } else {
+ callback(unexpected<std::exception_ptr>(download.error()));
+ }
+ }
+
+ void deleteRegion(OfflineRegion region, std::function<void(std::exception_ptr)> callback) {
+ downloads.erase(region.getID());
+ callback(db->deleteRegion(std::move(region)));
+ }
+
+ void invalidateRegion(int64_t regionID, std::function<void(std::exception_ptr)> callback) {
+ callback(db->invalidateRegion(regionID));
+ }
+
+ void setRegionObserver(int64_t regionID, std::unique_ptr<OfflineRegionObserver> observer) {
+ if (auto download = getDownload(regionID)) {
+ download.value()->setObserver(std::move(observer));
+ }
+ }
+
+ void setRegionDownloadState(int64_t regionID, OfflineRegionDownloadState state) {
+ if (auto download = getDownload(regionID)) {
+ download.value()->setState(state);
+ }
+ }
+
+ void setOfflineMapboxTileCountLimit(uint64_t limit) { db->setOfflineMapboxTileCountLimit(limit); }
+
+ void reopenDatabaseReadOnlyForTesting() { db->reopenDatabaseReadOnlyForTesting(); }
+
+private:
+ expected<OfflineDownload*, std::exception_ptr> getDownload(int64_t regionID) {
+ if (!onlineFileSource) {
+ return unexpected<std::exception_ptr>(
+ std::make_exception_ptr(std::runtime_error("Network file source unavailable.")));
+ }
+
+ auto it = downloads.find(regionID);
+ if (it != downloads.end()) {
+ return it->second.get();
+ }
+ auto definition = db->getRegionDefinition(regionID);
+ if (!definition) {
+ return unexpected<std::exception_ptr>(definition.error());
+ }
+ auto download =
+ std::make_unique<OfflineDownload>(regionID, std::move(definition.value()), *db, *onlineFileSource);
+ return downloads.emplace(regionID, std::move(download)).first->second.get();
+ }
+
+ std::unique_ptr<OfflineDatabase> db;
+ std::unordered_map<int64_t, std::unique_ptr<OfflineDownload>> downloads;
+ std::shared_ptr<FileSource> onlineFileSource;
+};
+
+class DatabaseFileSource::Impl {
+public:
+ Impl(std::shared_ptr<FileSource> onlineFileSource, const std::string& cachePath)
+ : thread(std::make_unique<util::Thread<DatabaseFileSourceThread>>(
+ "DatabaseFileSource", std::move(onlineFileSource), cachePath)) {}
+
+ ActorRef<DatabaseFileSourceThread> actor() const { return thread->actor(); }
+
+private:
+ const std::unique_ptr<util::Thread<DatabaseFileSourceThread>> thread;
+};
+
+DatabaseFileSource::DatabaseFileSource(const ResourceOptions& options)
+ : impl(std::make_unique<Impl>(FileSourceManager::get()->getFileSource(FileSourceType::Network, options),
+ options.cachePath())) {}
+
+DatabaseFileSource::~DatabaseFileSource() = default;
+
+std::unique_ptr<AsyncRequest> DatabaseFileSource::request(const Resource& resource, Callback callback) {
+ auto req = std::make_unique<FileSourceRequest>(std::move(callback));
+ impl->actor().invoke(&DatabaseFileSourceThread::request, resource, req->actor());
+ return std::move(req);
+}
+
+void DatabaseFileSource::forward(const Resource& res, const Response& response, std::function<void()> callback) {
+ std::function<void()> wrapper;
+ if (callback) {
+ wrapper = Scheduler::GetCurrent()->bindOnce(std::move(callback));
+ }
+ impl->actor().invoke(&DatabaseFileSourceThread::forward, res, response, std::move(wrapper));
+}
+
+bool DatabaseFileSource::canRequest(const Resource& resource) const {
+ return resource.hasLoadingMethod(Resource::LoadingMethod::Cache) &&
+ resource.url.rfind(mbgl::util::ASSET_PROTOCOL, 0) == std::string::npos &&
+ resource.url.rfind(mbgl::util::FILE_PROTOCOL, 0) == std::string::npos;
+}
+
+void DatabaseFileSource::setDatabasePath(const std::string& path, std::function<void()> callback) {
+ impl->actor().invoke(&DatabaseFileSourceThread::setDatabasePath, path, std::move(callback));
+}
+
+void DatabaseFileSource::resetDatabase(std::function<void(std::exception_ptr)> callback) {
+ impl->actor().invoke(&DatabaseFileSourceThread::resetDatabase, std::move(callback));
+}
+
+void DatabaseFileSource::packDatabase(std::function<void(std::exception_ptr)> callback) {
+ impl->actor().invoke(&DatabaseFileSourceThread::packDatabase, std::move(callback));
+}
+
+void DatabaseFileSource::runPackDatabaseAutomatically(bool autopack) {
+ impl->actor().invoke(&DatabaseFileSourceThread::runPackDatabaseAutomatically, autopack);
+}
+
+void DatabaseFileSource::put(const Resource& resource, const Response& response) {
+ impl->actor().invoke(&DatabaseFileSourceThread::put, resource, response);
+}
+
+void DatabaseFileSource::invalidateAmbientCache(std::function<void(std::exception_ptr)> callback) {
+ impl->actor().invoke(&DatabaseFileSourceThread::invalidateAmbientCache, std::move(callback));
+}
+
+void DatabaseFileSource::clearAmbientCache(std::function<void(std::exception_ptr)> callback) {
+ impl->actor().invoke(&DatabaseFileSourceThread::clearAmbientCache, std::move(callback));
+}
+
+void DatabaseFileSource::setMaximumAmbientCacheSize(uint64_t size, std::function<void(std::exception_ptr)> callback) {
+ impl->actor().invoke(&DatabaseFileSourceThread::setMaximumAmbientCacheSize, size, std::move(callback));
+}
+
+void DatabaseFileSource::listOfflineRegions(
+ std::function<void(expected<OfflineRegions, std::exception_ptr>)> callback) {
+ impl->actor().invoke(&DatabaseFileSourceThread::listRegions, callback);
+}
+
+void DatabaseFileSource::createOfflineRegion(
+ const OfflineRegionDefinition& definition,
+ const OfflineRegionMetadata& metadata,
+ std::function<void(expected<OfflineRegion, std::exception_ptr>)> callback) {
+ impl->actor().invoke(&DatabaseFileSourceThread::createRegion, definition, metadata, callback);
+}
+
+void DatabaseFileSource::mergeOfflineRegions(
+ const std::string& sideDatabasePath, std::function<void(expected<OfflineRegions, std::exception_ptr>)> callback) {
+ impl->actor().invoke(&DatabaseFileSourceThread::mergeOfflineRegions, sideDatabasePath, callback);
+}
+
+void DatabaseFileSource::updateOfflineMetadata(
+ const int64_t regionID,
+ const OfflineRegionMetadata& metadata,
+ std::function<void(expected<OfflineRegionMetadata, std::exception_ptr>)> callback) {
+ impl->actor().invoke(&DatabaseFileSourceThread::updateMetadata, regionID, metadata, callback);
+}
+
+void DatabaseFileSource::deleteOfflineRegion(OfflineRegion region, std::function<void(std::exception_ptr)> callback) {
+ impl->actor().invoke(&DatabaseFileSourceThread::deleteRegion, std::move(region), callback);
+}
+
+void DatabaseFileSource::invalidateOfflineRegion(OfflineRegion& region,
+ std::function<void(std::exception_ptr)> callback) {
+ impl->actor().invoke(&DatabaseFileSourceThread::invalidateRegion, region.getID(), callback);
+}
+
+void DatabaseFileSource::setOfflineRegionObserver(OfflineRegion& region,
+ std::unique_ptr<OfflineRegionObserver> observer) {
+ impl->actor().invoke(&DatabaseFileSourceThread::setRegionObserver, region.getID(), std::move(observer));
+}
+
+void DatabaseFileSource::setOfflineRegionDownloadState(OfflineRegion& region, OfflineRegionDownloadState state) {
+ impl->actor().invoke(&DatabaseFileSourceThread::setRegionDownloadState, region.getID(), state);
+}
+
+void DatabaseFileSource::getOfflineRegionStatus(
+ OfflineRegion& region, std::function<void(expected<OfflineRegionStatus, std::exception_ptr>)> callback) const {
+ impl->actor().invoke(&DatabaseFileSourceThread::getRegionStatus, region.getID(), callback);
+}
+
+void DatabaseFileSource::setOfflineMapboxTileCountLimit(uint64_t limit) const {
+ impl->actor().invoke(&DatabaseFileSourceThread::setOfflineMapboxTileCountLimit, limit);
+}
+
+void DatabaseFileSource::setProperty(const std::string& key, const mapbox::base::Value& value) {
+ if (key == READ_ONLY_MODE_KEY && value.getBool()) {
+ if (*value.getBool()) {
+ impl->actor().invoke(&DatabaseFileSourceThread::reopenDatabaseReadOnlyForTesting);
+ }
+ } else {
+ std::string message = "Resource provider does not support property " + key;
+ Log::Error(Event::General, message.c_str());
+ }
+}
+
+} // namespace mbgl