Mercurial > hg > orthanc
comparison OrthancServer/Plugins/Samples/AdvancedStorage/Plugin.cpp @ 5081:c673997507ea attach-custom-data
advanced storage cont
author | Alain Mazy <am@osimis.io> |
---|---|
date | Tue, 13 Sep 2022 11:02:43 +0200 |
parents | d7274e43ea7c |
children | 4af5f496a0dd |
comparison
equal
deleted
inserted
replaced
5080:d7274e43ea7c | 5081:c673997507ea |
---|---|
42 #include <list> | 42 #include <list> |
43 #include <time.h> | 43 #include <time.h> |
44 | 44 |
45 namespace fs = boost::filesystem; | 45 namespace fs = boost::filesystem; |
46 | 46 |
47 fs::path absoluteRootPath_; | 47 fs::path rootPath_; |
48 bool multipleStoragesEnabled_ = false; | |
49 std::map<std::string, fs::path> rootPaths_; | |
50 std::string currentStorageId_; | |
51 std::string namingScheme_; | |
48 bool fsyncOnWrite_ = true; | 52 bool fsyncOnWrite_ = true; |
53 size_t maxPathLength_ = 256; | |
54 size_t legacyPathLength = 39; // ex "/00/f7/00f7fd8b-47bd8c3a-ff917804-d180cdbc-40cf9527" | |
55 | |
56 fs::path GetRootPath() | |
57 { | |
58 if (multipleStoragesEnabled_) | |
59 { | |
60 return rootPaths_[currentStorageId_]; | |
61 } | |
62 | |
63 return rootPath_; | |
64 } | |
65 | |
66 fs::path GetRootPath(const std::string& storageId) | |
67 { | |
68 if (multipleStoragesEnabled_) | |
69 { | |
70 if (rootPaths_.find(storageId) == rootPaths_.end()) | |
71 { | |
72 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange, std::string("Advanced Storage - storage '" + storageId + "' is not defined in configuration")); | |
73 } | |
74 return rootPaths_[storageId]; | |
75 } | |
76 | |
77 return rootPath_; | |
78 } | |
49 | 79 |
50 | 80 |
51 fs::path GetLegacyRelativePath(const std::string& uuid) | 81 fs::path GetLegacyRelativePath(const std::string& uuid) |
52 { | 82 { |
53 | |
54 if (!Orthanc::Toolbox::IsUuid(uuid)) | 83 if (!Orthanc::Toolbox::IsUuid(uuid)) |
55 { | 84 { |
56 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | 85 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); |
57 } | 86 } |
58 | 87 |
59 fs::path path = absoluteRootPath_; | 88 fs::path path; |
60 | 89 |
61 path /= std::string(&uuid[0], &uuid[2]); | 90 path /= std::string(&uuid[0], &uuid[2]); |
62 path /= std::string(&uuid[2], &uuid[4]); | 91 path /= std::string(&uuid[2], &uuid[4]); |
63 path /= uuid; | 92 path /= uuid; |
64 | 93 |
67 #endif | 96 #endif |
68 | 97 |
69 return path; | 98 return path; |
70 } | 99 } |
71 | 100 |
72 fs::path GetAbsolutePath(const std::string& uuid, const std::string& customData) | 101 fs::path GetPath(const std::string& uuid, const std::string& customDataString) |
73 { | 102 { |
74 fs::path path = absoluteRootPath_; | 103 fs::path path; |
75 | 104 |
76 if (!customData.empty()) | 105 if (!customDataString.empty()) |
77 { | 106 { |
78 if (customData.substr(0, 2) == "1.") // version 1 | 107 Json::Value customData; |
79 { | 108 Orthanc::Toolbox::ReadJson(customData, customDataString); |
80 path /= customData.substr(2); | 109 |
110 if (customData["Version"].asInt() == 1) | |
111 { | |
112 if (customData.isMember("StorageId")) | |
113 { | |
114 path = GetRootPath(customData["StorageId"].asString()); | |
115 } | |
116 else | |
117 { | |
118 path = GetRootPath(); | |
119 } | |
120 | |
121 if (customData.isMember("Path")) | |
122 { | |
123 path /= customData["Path"].asString(); | |
124 } | |
125 else | |
126 { // we are in "legacy mode" for the path part | |
127 path /= GetLegacyRelativePath(uuid); | |
128 } | |
81 } | 129 } |
82 else | 130 else |
83 { | 131 { |
84 throw "TODO: unknown version"; | 132 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange, std::string("Advanced Storage - unknown version for custom data '" + boost::lexical_cast<std::string>(customData["Version"].asInt()) + "'")); |
85 } | 133 } |
86 | 134 } |
87 } | 135 else // we are in "legacy mode" |
88 else | 136 { |
89 { | 137 path = GetRootPath(); |
90 path /= GetLegacyRelativePath(uuid); | 138 path /= GetLegacyRelativePath(uuid); |
91 } | 139 } |
92 | 140 |
93 path.make_preferred(); | 141 path.make_preferred(); |
94 return path; | 142 return path; |
95 } | 143 } |
96 | 144 |
97 std::string GetCustomData(const fs::path& path) | 145 void GetCustomData(std::string& output, const fs::path& path) |
98 { | 146 { |
99 return std::string("1.") + path.string(); // prefix the relative path with a version | 147 // if we use defaults, non need to store anything in the metadata, the plugin has the same behavior as the core of Orthanc |
100 } | 148 if (namingScheme_ == "OrthancDefault" && !multipleStoragesEnabled_) |
101 | 149 { |
102 void AddDateDicomTagToPath(fs::path& path, const Json::Value& tags, const char* tagName, const char* defaultValue = NULL) | 150 return; |
151 } | |
152 | |
153 Json::Value customDataJson; | |
154 customDataJson["Version"] = 1; | |
155 | |
156 if (namingScheme_ != "OrthancDefault") | |
157 { // no need to store the pathc since we are in the default mode | |
158 customDataJson["Path"] = path.string(); | |
159 } | |
160 | |
161 if (multipleStoragesEnabled_) | |
162 { | |
163 customDataJson["StorageId"] = currentStorageId_; | |
164 } | |
165 | |
166 return Orthanc::Toolbox::WriteFastJson(output, customDataJson); | |
167 } | |
168 | |
169 void AddSplitDateDicomTagToPath(fs::path& path, const Json::Value& tags, const char* tagName, const char* defaultValue = NULL) | |
103 { | 170 { |
104 if (tags.isMember(tagName) && tags[tagName].asString().size() == 8) | 171 if (tags.isMember(tagName) && tags[tagName].asString().size() == 8) |
105 { | 172 { |
106 std::string date = tags[tagName].asString(); | 173 std::string date = tags[tagName].asString(); |
107 path /= date.substr(0, 4); | 174 path /= date.substr(0, 4); |
112 { | 179 { |
113 path /= defaultValue; | 180 path /= defaultValue; |
114 } | 181 } |
115 } | 182 } |
116 | 183 |
117 void AddSringDicomTagToPath(fs::path& path, const Json::Value& tags, const char* tagName, const char* defaultValue = NULL) | 184 void AddStringDicomTagToPath(fs::path& path, const Json::Value& tags, const char* tagName, const char* defaultValue = NULL) |
118 { | 185 { |
119 if (tags.isMember(tagName) && tags[tagName].isString() && tags[tagName].asString().size() > 0) | 186 if (tags.isMember(tagName) && tags[tagName].isString() && tags[tagName].asString().size() > 0) |
120 { | 187 { |
121 path /= tags[tagName].asString(); | 188 path /= tags[tagName].asString(); |
122 } | 189 } |
145 { | 212 { |
146 path /= defaultValue; | 213 path /= defaultValue; |
147 } | 214 } |
148 } | 215 } |
149 | 216 |
150 fs::path GetRelativePathFromTags(const Json::Value& tags, const char* uuid, OrthancPluginContentType type, bool isCompressed) | 217 std::string GetExtension(OrthancPluginContentType type, bool isCompressed) |
151 { | 218 { |
152 fs::path path; | |
153 | |
154 if (type == OrthancPluginContentType_Dicom || type == OrthancPluginContentType_DicomUntilPixelData) | |
155 { | |
156 // TODO: allow customization ... note: right now, we always need the uuid in the path !! | |
157 | |
158 AddDateDicomTagToPath(path, tags, "StudyDate", "NO_STUDY_DATE"); | |
159 AddSringDicomTagToPath(path, tags, "PatientID"); // no default value, tag is always present if the instance is accepted by Orthanc | |
160 AddSringDicomTagToPath(path, tags, "StudyInstanceUID"); | |
161 AddSringDicomTagToPath(path, tags, "SeriesInstanceUID"); | |
162 //AddIntDicomTagToPath(path, tags, "InstanceNumber", 8, uuid); | |
163 path /= uuid; | |
164 } | |
165 else | |
166 { | |
167 path = GetLegacyRelativePath(uuid); | |
168 } | |
169 | |
170 std::string extension; | 219 std::string extension; |
171 | 220 |
172 switch (type) | 221 switch (type) |
173 { | 222 { |
174 case OrthancPluginContentType_Dicom: | 223 case OrthancPluginContentType_Dicom: |
183 if (isCompressed) | 232 if (isCompressed) |
184 { | 233 { |
185 extension = extension + ".cmp"; // compression is zlib + size -> we can not use the .zip extension | 234 extension = extension + ".cmp"; // compression is zlib + size -> we can not use the .zip extension |
186 } | 235 } |
187 | 236 |
188 path += extension; | 237 return extension; |
189 | 238 } |
190 return path; | 239 |
240 fs::path GetRelativePathFromTags(const Json::Value& tags, const char* uuid, OrthancPluginContentType type, bool isCompressed) | |
241 { | |
242 fs::path path; | |
243 | |
244 if (!tags.isNull()) | |
245 { | |
246 if (namingScheme_ == "Preset1-StudyDatePatientID") | |
247 { | |
248 if (!tags.isMember("StudyDate")) | |
249 { | |
250 LOG(WARNING) << "AdvancedStorage - No 'StudyDate' in attachment " << uuid << ". Attachment will be stored in NO_STUDY_DATE folder"; | |
251 } | |
252 | |
253 AddSplitDateDicomTagToPath(path, tags, "StudyDate", "NO_STUDY_DATE"); | |
254 AddStringDicomTagToPath(path, tags, "PatientID"); // no default value, tag is always present if the instance is accepted by Orthanc | |
255 | |
256 if (tags.isMember("PatientName") && tags["PatientName"].isString() && !tags["PatientName"].asString().empty()) | |
257 { | |
258 path += std::string(" - ") + tags["PatientName"].asString(); | |
259 } | |
260 | |
261 AddStringDicomTagToPath(path, tags, "StudyDescription"); | |
262 AddStringDicomTagToPath(path, tags, "SeriesInstanceUID"); | |
263 | |
264 path /= uuid; | |
265 path += GetExtension(type, isCompressed); | |
266 return path; | |
267 } | |
268 } | |
269 | |
270 return GetLegacyRelativePath(uuid); | |
191 } | 271 } |
192 | 272 |
193 | 273 |
194 OrthancPluginErrorCode StorageCreate(OrthancPluginMemoryBuffer* customData, | 274 OrthancPluginErrorCode StorageCreate(OrthancPluginMemoryBuffer* customData, |
195 const char* uuid, | 275 const char* uuid, |
198 int64_t size, | 278 int64_t size, |
199 OrthancPluginContentType type, | 279 OrthancPluginContentType type, |
200 bool isCompressed) | 280 bool isCompressed) |
201 { | 281 { |
202 fs::path relativePath = GetRelativePathFromTags(tags, uuid, type, isCompressed); | 282 fs::path relativePath = GetRelativePathFromTags(tags, uuid, type, isCompressed); |
203 std::string customDataString = GetCustomData(relativePath); | 283 std::string customDataString; |
204 | 284 GetCustomData(customDataString, relativePath); |
205 fs::path absolutePath = absoluteRootPath_ / relativePath; | 285 |
206 | 286 fs::path rootPath = GetRootPath(); |
207 if (fs::exists(absolutePath)) | 287 fs::path path = rootPath / relativePath; |
288 | |
289 LOG(INFO) << "Advanced Storage - creating attachment \"" << uuid << "\" of type " << static_cast<int>(type) << " (path = " + path.string() + ")"; | |
290 | |
291 // check that the final path is not 'above' the root path (this could happen if e.g., a PatientName is ../../../../toto) | |
292 std::string canonicalPath = fs::canonical(path).string(); | |
293 if (!Orthanc::Toolbox::StartsWith(canonicalPath, rootPath.string())) | |
294 { | |
295 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, std::string("Advanced Storage - final path is above root: '") + canonicalPath + "' - '" + rootPath.string() + "'") ; | |
296 } | |
297 | |
298 // check path length !!!!!, if too long, go back to legacy path and issue a warning | |
299 if (path.string().size() > maxPathLength_) | |
300 { | |
301 fs::path legacyPath = rootPath / GetLegacyRelativePath(uuid); | |
302 LOG(WARNING) << "Advanced Storage - WAS01 - Path is too long: '" << path.string() << "' will be stored in '" << legacyPath << "'"; | |
303 path = legacyPath; | |
304 } | |
305 | |
306 if (fs::exists(path)) | |
208 { | 307 { |
209 // Extremely unlikely case if uuid is included in the path: This Uuid has already been created | 308 // Extremely unlikely case if uuid is included in the path: This Uuid has already been created |
210 // in the past. | 309 // in the past. |
211 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | 310 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Advanced Storage - path already exists"); |
212 | 311 |
213 // TODO for the future: handle duplicates path (e.g: there's no uuid in the path and we are uploading the same file again) | 312 // TODO for the future: handle duplicates path (e.g: there's no uuid in the path and we are uploading the same file again) |
214 // OrthancPlugins::LogWarning(std::string("Overwriting file \"") + path.string() + "\" (" + uuid + ")"); | 313 } |
215 } | 314 |
216 | 315 if (fs::exists(path.parent_path())) |
217 if (fs::exists(absolutePath.parent_path())) | 316 { |
218 { | 317 if (!fs::is_directory(path.parent_path())) |
219 if (!fs::is_directory(absolutePath.parent_path())) | |
220 { | 318 { |
221 throw Orthanc::OrthancException(Orthanc::ErrorCode_DirectoryOverFile); | 319 throw Orthanc::OrthancException(Orthanc::ErrorCode_DirectoryOverFile); |
222 } | 320 } |
223 } | 321 } |
224 else | 322 else |
225 { | 323 { |
226 if (!fs::create_directories(absolutePath.parent_path())) | 324 if (!fs::create_directories(path.parent_path())) |
227 { | 325 { |
228 throw Orthanc::OrthancException(Orthanc::ErrorCode_FileStorageCannotWrite); | 326 throw Orthanc::OrthancException(Orthanc::ErrorCode_FileStorageCannotWrite); |
229 } | 327 } |
230 } | 328 } |
231 | 329 |
232 Orthanc::SystemToolbox::WriteFile(content, size, absolutePath.string(), fsyncOnWrite_); | 330 Orthanc::SystemToolbox::WriteFile(content, size, path.string(), fsyncOnWrite_); |
233 | 331 |
234 OrthancPluginCreateMemoryBuffer(OrthancPlugins::GetGlobalContext(), customData, customDataString.size()); | 332 OrthancPluginCreateMemoryBuffer(OrthancPlugins::GetGlobalContext(), customData, customDataString.size()); |
235 memcpy(customData->data, customDataString.data(), customDataString.size()); | 333 memcpy(customData->data, customDataString.data(), customDataString.size()); |
236 | 334 |
237 return OrthancPluginErrorCode_Success; | 335 return OrthancPluginErrorCode_Success; |
246 OrthancPluginContentType type, | 344 OrthancPluginContentType type, |
247 bool isCompressed) | 345 bool isCompressed) |
248 { | 346 { |
249 try | 347 try |
250 { | 348 { |
251 OrthancPlugins::LogInfo(std::string("Creating instance attachment \"") + uuid + "\""); | |
252 | |
253 OrthancPlugins::DicomInstance dicomInstance(instance); | 349 OrthancPlugins::DicomInstance dicomInstance(instance); |
254 Json::Value tags; | 350 Json::Value tags; |
255 dicomInstance.GetSimplifiedJson(tags); | 351 dicomInstance.GetSimplifiedJson(tags); |
256 | 352 |
257 return StorageCreate(customData, uuid, tags, content, size, type, isCompressed); | 353 return StorageCreate(customData, uuid, tags, content, size, type, isCompressed); |
302 OrthancPluginErrorCode StorageReadWhole(OrthancPluginMemoryBuffer64* target, | 398 OrthancPluginErrorCode StorageReadWhole(OrthancPluginMemoryBuffer64* target, |
303 const char* uuid, | 399 const char* uuid, |
304 const char* customData, | 400 const char* customData, |
305 OrthancPluginContentType type) | 401 OrthancPluginContentType type) |
306 { | 402 { |
307 OrthancPlugins::LogInfo(std::string("Reading attachment \"") + uuid + "\""); | 403 std::string path = GetPath(uuid, customData).string(); |
308 | 404 |
309 std::string path = GetAbsolutePath(uuid, customData).string(); | 405 LOG(INFO) << "Advanced Storage - Reading whole attachment \"" << uuid << "\" of type " << static_cast<int>(type) << " (path = " + path + ")"; |
310 | 406 |
311 if (!Orthanc::SystemToolbox::IsRegularFile(path)) | 407 if (!Orthanc::SystemToolbox::IsRegularFile(path)) |
312 { | 408 { |
313 OrthancPlugins::LogError(std::string("The path does not point to a regular file: ") + path); | 409 OrthancPlugins::LogError(std::string("The path does not point to a regular file: ") + path); |
314 return OrthancPluginErrorCode_InexistentFile; | 410 return OrthancPluginErrorCode_InexistentFile; |
357 const char* uuid, | 453 const char* uuid, |
358 const char* customData, | 454 const char* customData, |
359 OrthancPluginContentType type, | 455 OrthancPluginContentType type, |
360 uint64_t rangeStart) | 456 uint64_t rangeStart) |
361 { | 457 { |
362 OrthancPlugins::LogInfo(std::string("Reading attachment \"") + uuid + "\""); | 458 std::string path = GetPath(uuid, customData).string(); |
363 | 459 |
364 std::string path = GetAbsolutePath(uuid, customData).string(); | 460 LOG(INFO) << "Advanced Storage - Reading range of attachment \"" << uuid << "\" of type " << static_cast<int>(type) << " (path = " + path + ")"; |
365 | 461 |
366 if (!Orthanc::SystemToolbox::IsRegularFile(path)) | 462 if (!Orthanc::SystemToolbox::IsRegularFile(path)) |
367 { | 463 { |
368 OrthancPlugins::LogError(std::string("The path does not point to a regular file: ") + path); | 464 OrthancPlugins::LogError(std::string("The path does not point to a regular file: ") + path); |
369 return OrthancPluginErrorCode_InexistentFile; | 465 return OrthancPluginErrorCode_InexistentFile; |
398 | 494 |
399 OrthancPluginErrorCode StorageRemove (const char* uuid, | 495 OrthancPluginErrorCode StorageRemove (const char* uuid, |
400 const char* customData, | 496 const char* customData, |
401 OrthancPluginContentType type) | 497 OrthancPluginContentType type) |
402 { | 498 { |
403 // LOG(INFO) << "Deleting attachment \"" << uuid << "\" of type " << static_cast<int>(type); | 499 fs::path path = GetPath(uuid, customData); |
404 | 500 |
405 fs::path p = GetAbsolutePath(uuid, customData); | 501 LOG(INFO) << "Advanced Storage - Deleting attachment \"" << uuid << "\" of type " << static_cast<int>(type) << " (path = " + path.string() + ")"; |
406 | 502 |
407 try | 503 try |
408 { | 504 { |
409 fs::remove(p); | 505 fs::remove(path); |
410 } | 506 } |
411 catch (...) | 507 catch (...) |
412 { | 508 { |
413 // Ignore the error | 509 // Ignore the error |
414 } | 510 } |
415 | 511 |
416 // Remove the empty parent directories, (ignoring the error code if these directories are not empty) | 512 // Remove the empty parent directories, (ignoring the error code if these directories are not empty) |
417 | 513 |
418 try | 514 try |
419 { | 515 { |
420 fs::path parent = p.parent_path(); | 516 fs::path parent = path.parent_path(); |
421 | 517 |
422 while (parent != absoluteRootPath_) | 518 while (parent != GetRootPath()) |
423 { | 519 { |
424 fs::remove(parent); | 520 fs::remove(parent); |
425 parent = parent.parent_path(); | 521 parent = parent.parent_path(); |
426 } | 522 } |
427 } | 523 } |
465 { | 561 { |
466 "AdvancedStorage": { | 562 "AdvancedStorage": { |
467 | 563 |
468 // Enables/disables the plugin | 564 // Enables/disables the plugin |
469 "Enable": false, | 565 "Enable": false, |
566 | |
567 // Enables/disables support for multiple StorageDirectories | |
568 "MultipleStorages" : { | |
569 "Storages" : { | |
570 // The storgae ids below may never change since they are stored in DB | |
571 // The storage path may change in case you move your data from one place to the other | |
572 "1" : "/var/lib/orthanc/db", | |
573 "2" : "/mnt/disk2/orthanc" | |
574 }, | |
575 | |
576 // the storage on which new data is stored. | |
577 // There's currently no automatic changes of disks | |
578 "CurrentStorage" : "2", | |
579 }, | |
580 | |
581 // Defines the storage structure and file namings. Right now, | |
582 // only the "OrthancDefault" value shall be used in a production environment. | |
583 // All other values are currently experimental | |
584 // "OrthancDefault" = same structure and file naming as default orthanc, | |
585 // "Preset1-StudyDatePatientID" = split(StudyDate)/PatientID - PatientName/StudyDescription/SeriesInstanceUID/uuid.ext | |
586 "NamingScheme" : "OrthancDefault", | |
587 | |
588 // Defines the maximum length for path used in the storage. If a file is longer | |
589 // than this limit, it is stored with the default orthanc naming scheme | |
590 // (and a warning is issued). | |
591 // Note, on Windows, the maximum path length is 260 bytes by default but can be increased | |
592 // through a configuration. | |
593 "MaxPathLength" : 256 | |
470 } | 594 } |
471 } | 595 } |
472 */ | 596 */ |
473 | 597 |
474 absoluteRootPath_ = fs::absolute(fs::path(orthancConfiguration.GetStringValue("StorageDirectory", "OrthancStorage"))); | 598 fsyncOnWrite_ = orthancConfiguration.GetBooleanValue("SyncStorageArea", true); |
475 LOG(WARNING) << "AdvancedStorage - Path to the storage area: " << absoluteRootPath_.string(); | 599 |
600 const Json::Value& pluginJson = advancedStorage.GetJson(); | |
601 | |
602 namingScheme_ = advancedStorage.GetStringValue("NamingScheme", "OrthancDefault"); | |
603 | |
604 // if we have enabled multiple storage after files have been saved without this plugin, we still need the default StorageDirectory | |
605 rootPath_ = fs::path(orthancConfiguration.GetStringValue("StorageDirectory", "OrthancStorage")); | |
606 LOG(WARNING) << "AdvancedStorage - Path to the default storage area: " << rootPath_.string(); | |
607 | |
608 maxPathLength_ = orthancConfiguration.GetIntegerValue("MaxPathLength", 256); | |
609 LOG(WARNING) << "AdvancedStorage - Maximum path length: " << maxPathLength_; | |
610 | |
611 if (!rootPath_.is_absolute()) | |
612 { | |
613 LOG(ERROR) << "AdvancedStorage - Path to the default storage area should be an absolute path"; | |
614 return -1; | |
615 } | |
616 | |
617 if (rootPath_.size() > (maxPathLength_ - legacyPathLength)) | |
618 { | |
619 LOG(ERROR) << "AdvancedStorage - Path to the default storage is too long"; | |
620 return -1; | |
621 } | |
622 | |
623 if (pluginJson.isMember("MultipleStorages")) | |
624 { | |
625 multipleStoragesEnabled_ = true; | |
626 const Json::Value& multipleStoragesJson = pluginJson["MultipleStorages"]; | |
627 | |
628 if (multipleStoragesJson.isMember("Storages") && multipleStoragesJson.isObject() && multipleStoragesJson.isMember("CurrentStorage") && multipleStoragesJson["CurrentStorage"].isString()) | |
629 { | |
630 const Json::Value& storagesJson = multipleStoragesJson["Storages"]; | |
631 Json::Value::Members storageIds = storagesJson.getMemberNames(); | |
632 | |
633 for (Json::Value::Members::const_iterator it = storageIds.begin(); it != storageIds.end(); ++it) | |
634 { | |
635 const Json::Value& storagePath = storagesJson[*it]; | |
636 if (!storagePath.isString()) | |
637 { | |
638 LOG(ERROR) << "AdvancedStorage - Storage path is not a string " << *it; | |
639 return -1; | |
640 } | |
641 | |
642 rootPaths_[*it] = storagePath.asString(); | |
643 | |
644 if (!rootPaths_[*it].is_absolute()) | |
645 { | |
646 LOG(ERROR) << "AdvancedStorage - Storage path shall be absolute path '" << storagePath.asString() << "'"; | |
647 return -1; | |
648 } | |
649 | |
650 if (storagePath.asString().size() > (maxPathLength_ - legacyPathLength)) | |
651 { | |
652 LOG(ERROR) << "AdvancedStorage - Storage path is too long '" << storagePath.asString() << "'"; | |
653 return -1; | |
654 } | |
655 } | |
656 | |
657 currentStorageId_ = multipleStoragesJson["CurrentStorage"].asString(); | |
658 | |
659 if (rootPaths_.find(currentStorageId_) == rootPaths_.end()) | |
660 { | |
661 LOG(ERROR) << "AdvancedStorage - CurrentStorage is not defined in Storages list: " << currentStorageId_; | |
662 return -1; | |
663 } | |
664 | |
665 LOG(WARNING) << "AdvancedStorage - multiple storages enabled. Current storage : " << rootPaths_[currentStorageId_].string(); | |
666 } | |
667 } | |
476 | 668 |
477 OrthancPluginRegisterStorageArea3(context, StorageCreateInstance, StorageCreateAttachment, StorageReadWhole, StorageReadRange, StorageRemove); | 669 OrthancPluginRegisterStorageArea3(context, StorageCreateInstance, StorageCreateAttachment, StorageReadWhole, StorageReadRange, StorageRemove); |
478 } | 670 } |
479 else | 671 else |
480 { | 672 { |