0
|
1 /**
|
|
2 * Transfers accelerator plugin for Orthanc
|
9
|
3 * Copyright (C) 2018-2019 Osimis S.A., Belgium
|
0
|
4 *
|
|
5 * This program is free software: you can redistribute it and/or
|
|
6 * modify it under the terms of the GNU Affero General Public License
|
|
7 * as published by the Free Software Foundation, either version 3 of
|
|
8 * the License, or (at your option) any later version.
|
|
9 *
|
|
10 * This program is distributed in the hope that it will be useful, but
|
|
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
13 * Affero General Public License for more details.
|
|
14 *
|
|
15 * You should have received a copy of the GNU Affero General Public License
|
|
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17 **/
|
|
18
|
|
19
|
|
20 #include "TransferBucket.h"
|
|
21
|
|
22 #include <Core/Logging.h>
|
|
23 #include <Core/OrthancException.h>
|
|
24
|
|
25
|
|
26 namespace OrthancPlugins
|
|
27 {
|
|
28 TransferBucket::TransferBucket() :
|
|
29 totalSize_(0),
|
|
30 extensible_(true)
|
|
31 {
|
|
32 }
|
|
33
|
|
34
|
|
35 TransferBucket::TransferBucket(const Json::Value& serialized) :
|
|
36 totalSize_(0),
|
|
37 extensible_(false)
|
|
38 {
|
|
39 if (serialized.type() != Json::arrayValue)
|
|
40 {
|
|
41 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
|
|
42 }
|
|
43
|
|
44 chunks_.reserve(serialized.size());
|
|
45
|
|
46 for (Json::Value::ArrayIndex i = 0; i < serialized.size(); i++)
|
|
47 {
|
|
48 if (serialized[i].type() != Json::objectValue ||
|
|
49 !serialized[i].isMember(KEY_ID) ||
|
|
50 !serialized[i].isMember(KEY_OFFSET) ||
|
|
51 !serialized[i].isMember(KEY_SIZE) ||
|
|
52 serialized[i][KEY_ID].type() != Json::stringValue ||
|
|
53 serialized[i][KEY_OFFSET].type() != Json::stringValue ||
|
|
54 serialized[i][KEY_SIZE].type() != Json::stringValue)
|
|
55 {
|
|
56 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
|
|
57 }
|
|
58 else
|
|
59 {
|
|
60 try
|
|
61 {
|
|
62 Chunk chunk;
|
|
63 chunk.instanceId_ = serialized[i][KEY_ID].asString();
|
|
64 chunk.offset_ = boost::lexical_cast<size_t>(serialized[i][KEY_OFFSET].asString());
|
|
65 chunk.size_ = boost::lexical_cast<size_t>(serialized[i][KEY_SIZE].asString());
|
|
66
|
|
67 chunks_.push_back(chunk);
|
|
68 totalSize_ += chunk.size_;
|
|
69 }
|
|
70 catch (boost::bad_lexical_cast&)
|
|
71 {
|
|
72 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
|
|
73 }
|
|
74 }
|
|
75 }
|
|
76 }
|
|
77
|
|
78
|
|
79 void TransferBucket::Serialize(Json::Value& target) const
|
|
80 {
|
|
81 target = Json::arrayValue;
|
|
82
|
|
83 for (size_t i = 0; i < chunks_.size(); i++)
|
|
84 {
|
|
85 Json::Value item = Json::objectValue;
|
|
86 item[KEY_ID] = chunks_[i].instanceId_;
|
|
87 item[KEY_OFFSET] = boost::lexical_cast<std::string>(chunks_[i].offset_);
|
|
88 item[KEY_SIZE] = boost::lexical_cast<std::string>(chunks_[i].size_);
|
|
89 target.append(item);
|
|
90 }
|
|
91 }
|
|
92
|
|
93 void TransferBucket::Clear()
|
|
94 {
|
|
95 chunks_.clear();
|
|
96 totalSize_ = 0;
|
|
97 extensible_ = true;
|
|
98 }
|
|
99
|
|
100
|
|
101 void TransferBucket::AddChunk(const DicomInstanceInfo& instance,
|
|
102 size_t chunkOffset,
|
|
103 size_t chunkSize)
|
|
104 {
|
|
105 if (chunkOffset + chunkSize > instance.GetSize())
|
|
106 {
|
|
107 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
|
|
108 }
|
|
109
|
|
110 if (!extensible_)
|
|
111 {
|
|
112 LOG(ERROR) << "Cannot add a new chunk after a truncated instance";
|
|
113 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
|
|
114 }
|
|
115
|
|
116 if (!chunks_.empty() &&
|
|
117 chunkOffset != 0)
|
|
118 {
|
|
119 LOG(ERROR) << "Only the first chunk can have non-zero offset in a transfer bucket";
|
|
120 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
|
|
121 }
|
|
122
|
|
123 if (chunkSize == 0)
|
|
124 {
|
|
125 // Ignore empty chunks
|
|
126 return;
|
|
127 }
|
|
128
|
|
129 if (!chunks_.empty() &&
|
|
130 chunkSize != instance.GetSize())
|
|
131 {
|
|
132 // Prevents adding new chunk after an incomplete instance
|
|
133 extensible_ = false;
|
|
134 }
|
|
135
|
|
136 Chunk chunk;
|
|
137 chunk.instanceId_ = instance.GetId();
|
|
138 chunk.offset_ = chunkOffset;
|
|
139 chunk.size_ = chunkSize;
|
|
140
|
|
141 chunks_.push_back(chunk);
|
|
142 totalSize_ += chunkSize;
|
|
143 }
|
|
144
|
|
145
|
|
146 const std::string& TransferBucket::GetChunkInstanceId(size_t index) const
|
|
147 {
|
|
148 if (index >= chunks_.size())
|
|
149 {
|
|
150 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
|
|
151 }
|
|
152 else
|
|
153 {
|
|
154 return chunks_[index].instanceId_;
|
|
155 }
|
|
156 }
|
|
157
|
|
158
|
|
159 size_t TransferBucket::GetChunkOffset(size_t index) const
|
|
160 {
|
|
161 if (index >= chunks_.size())
|
|
162 {
|
|
163 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
|
|
164 }
|
|
165 else
|
|
166 {
|
|
167 return chunks_[index].offset_;
|
|
168 }
|
|
169 }
|
|
170
|
|
171
|
|
172 size_t TransferBucket::GetChunkSize(size_t index) const
|
|
173 {
|
|
174 if (index >= chunks_.size())
|
|
175 {
|
|
176 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
|
|
177 }
|
|
178 else
|
|
179 {
|
|
180 return chunks_[index].size_;
|
|
181 }
|
|
182 }
|
|
183
|
|
184
|
|
185 void TransferBucket::ComputePullUri(std::string& uri,
|
|
186 BucketCompression compression) const
|
|
187 {
|
|
188 if (chunks_.empty())
|
|
189 {
|
|
190 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
|
|
191 }
|
|
192
|
|
193 bool first = true;
|
|
194 uri = std::string(URI_CHUNKS) + "/";
|
|
195
|
|
196 for (size_t i = 0; i < chunks_.size(); i++)
|
|
197 {
|
|
198 if (first)
|
|
199 {
|
|
200 first = false;
|
|
201 }
|
|
202 else
|
|
203 {
|
|
204 uri += ".";
|
|
205 }
|
|
206
|
|
207 uri += chunks_[i].instanceId_;
|
|
208
|
|
209 assert(i == 0 || chunks_[i].offset_ == 0);
|
|
210 }
|
|
211
|
|
212 uri += ("?offset=" + boost::lexical_cast<std::string>(chunks_[0].offset_) +
|
|
213 "&size=" + boost::lexical_cast<std::string>(totalSize_));
|
|
214
|
|
215 switch (compression)
|
|
216 {
|
|
217 case BucketCompression_None:
|
|
218 uri += "&compression=none";
|
|
219 break;
|
|
220
|
|
221 case BucketCompression_Gzip:
|
|
222 uri += "&compression=gzip";
|
|
223 break;
|
|
224
|
|
225 default:
|
|
226 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
|
|
227 }
|
|
228 }
|
|
229 }
|