1 /**
2  * Orthanc - A Lightweight, RESTful DICOM Store
3  * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4  * Department, University Hospital of Liege, Belgium
5  * Copyright (C) 2017-2020 Osimis S.A., Belgium
6  *
7  * This program is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * In addition, as a special exception, the copyright holders of this
13  * program give permission to link the code of its release with the
14  * OpenSSL project's "OpenSSL" library (or with modified versions of it
15  * that use the same license as the "OpenSSL" library), and distribute
16  * the linked executables. You must obey the GNU General Public License
17  * in all respects for all of the code used other than "OpenSSL". If you
18  * modify file(s) with this exception, you may extend this exception to
19  * your version of the file(s), but you are not obligated to do so. If
20  * you do not wish to do so, delete this exception statement from your
21  * version. If you delete this exception statement from all source files
22  * in the program, then also delete it here.
23  *
24  * This program is distributed in the hope that it will be useful, but
25  * WITHOUT ANY WARRANTY; without even the implied warranty of
26  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27  * General Public License for more details.
28  *
29  * You should have received a copy of the GNU General Public License
30  * along with this program. If not, see <http://www.gnu.org/licenses/>.
31  **/
32 
33 
34 #include "PrecompiledHeadersServer.h"
35 #include "StorageCommitmentReports.h"
36 
37 #include "../../OrthancFramework/Sources/OrthancException.h"
38 
39 namespace Orthanc
40 {
MarkAsComplete()41   void StorageCommitmentReports::Report::MarkAsComplete()
42   {
43     if (isComplete_)
44     {
45       throw OrthancException(ErrorCode_BadSequenceOfCalls);
46     }
47     else
48     {
49       isComplete_ = true;
50     }
51   }
52 
AddSuccess(const std::string & sopClassUid,const std::string & sopInstanceUid)53   void StorageCommitmentReports::Report::AddSuccess(const std::string& sopClassUid,
54                                                     const std::string& sopInstanceUid)
55   {
56     if (isComplete_)
57     {
58       throw OrthancException(ErrorCode_BadSequenceOfCalls);
59     }
60     else
61     {
62       Success success;
63       success.sopClassUid_ = sopClassUid;
64       success.sopInstanceUid_ = sopInstanceUid;
65       success_.push_back(success);
66     }
67   }
68 
AddFailure(const std::string & sopClassUid,const std::string & sopInstanceUid,StorageCommitmentFailureReason reason)69   void StorageCommitmentReports::Report::AddFailure(const std::string& sopClassUid,
70                                                     const std::string& sopInstanceUid,
71                                                     StorageCommitmentFailureReason reason)
72   {
73     if (isComplete_)
74     {
75       throw OrthancException(ErrorCode_BadSequenceOfCalls);
76     }
77     else
78     {
79       Failure failure;
80       failure.sopClassUid_ = sopClassUid;
81       failure.sopInstanceUid_ = sopInstanceUid;
82       failure.reason_ = reason;
83       failures_.push_back(failure);
84     }
85   }
86 
87 
GetStatus() const88   StorageCommitmentReports::Report::Status StorageCommitmentReports::Report::GetStatus() const
89   {
90     if (!isComplete_)
91     {
92       return Status_Pending;
93     }
94     else if (failures_.empty())
95     {
96       return Status_Success;
97     }
98     else
99     {
100       return Status_Failure;
101     }
102   }
103 
104 
Format(Json::Value & json) const105   void StorageCommitmentReports::Report::Format(Json::Value& json) const
106   {
107     static const char* const FIELD_STATUS = "Status";
108     static const char* const FIELD_SOP_CLASS_UID = "SOPClassUID";
109     static const char* const FIELD_SOP_INSTANCE_UID = "SOPInstanceUID";
110     static const char* const FIELD_FAILURE_REASON = "FailureReason";
111     static const char* const FIELD_DESCRIPTION = "Description";
112     static const char* const FIELD_REMOTE_AET = "RemoteAET";
113     static const char* const FIELD_SUCCESS = "Success";
114     static const char* const FIELD_FAILURES = "Failures";
115 
116 
117     json = Json::objectValue;
118     json[FIELD_REMOTE_AET] = remoteAet_;
119 
120     bool pending;
121 
122     switch (GetStatus())
123     {
124       case Status_Pending:
125         json[FIELD_STATUS] = "Pending";
126         pending = true;
127         break;
128 
129       case Status_Success:
130         json[FIELD_STATUS] = "Success";
131         pending = false;
132         break;
133 
134       case Status_Failure:
135         json[FIELD_STATUS] = "Failure";
136         pending = false;
137         break;
138 
139       default:
140         throw OrthancException(ErrorCode_InternalError);
141     }
142 
143     if (!pending)
144     {
145       {
146         Json::Value success = Json::arrayValue;
147         for (std::list<Success>::const_iterator
148                it = success_.begin(); it != success_.end(); ++it)
149         {
150           Json::Value item = Json::objectValue;
151           item[FIELD_SOP_CLASS_UID] = it->sopClassUid_;
152           item[FIELD_SOP_INSTANCE_UID] = it->sopInstanceUid_;
153           success.append(item);
154         }
155 
156         json[FIELD_SUCCESS] = success;
157       }
158 
159       {
160         Json::Value failures = Json::arrayValue;
161         for (std::list<Failure>::const_iterator
162                it = failures_.begin(); it != failures_.end(); ++it)
163         {
164           Json::Value item = Json::objectValue;
165           item[FIELD_SOP_CLASS_UID] = it->sopClassUid_;
166           item[FIELD_SOP_INSTANCE_UID] = it->sopInstanceUid_;
167           item[FIELD_FAILURE_REASON] = it->reason_;
168           item[FIELD_DESCRIPTION] = EnumerationToString(it->reason_);
169           failures.append(item);
170         }
171 
172         json[FIELD_FAILURES] = failures;
173       }
174     }
175   }
176 
177 
GetSuccessSopInstanceUids(std::vector<std::string> & target) const178   void StorageCommitmentReports::Report::GetSuccessSopInstanceUids(
179     std::vector<std::string>& target) const
180   {
181     target.clear();
182     target.reserve(success_.size());
183 
184     for (std::list<Success>::const_iterator
185            it = success_.begin(); it != success_.end(); ++it)
186     {
187       target.push_back(it->sopInstanceUid_);
188     }
189   }
190 
191 
~StorageCommitmentReports()192   StorageCommitmentReports::~StorageCommitmentReports()
193   {
194     while (!content_.IsEmpty())
195     {
196       Report* report = NULL;
197       content_.RemoveOldest(report);
198 
199       assert(report != NULL);
200       delete report;
201     }
202   }
203 
204 
Store(const std::string & transactionUid,Report * report)205   void StorageCommitmentReports::Store(const std::string& transactionUid,
206                                        Report* report)
207   {
208     std::unique_ptr<Report> protection(report);
209 
210     boost::mutex::scoped_lock lock(mutex_);
211 
212     {
213       Report* previous = NULL;
214       if (content_.Contains(transactionUid, previous))
215       {
216         assert(previous != NULL);
217         delete previous;
218 
219         content_.Invalidate(transactionUid);
220       }
221     }
222 
223     assert(maxSize_ == 0 ||
224            content_.GetSize() <= maxSize_);
225 
226     if (maxSize_ != 0 &&
227         content_.GetSize() == maxSize_)
228     {
229       assert(!content_.IsEmpty());
230 
231       Report* oldest = NULL;
232       content_.RemoveOldest(oldest);
233 
234       assert(oldest != NULL);
235       delete oldest;
236     }
237 
238     assert(maxSize_ == 0 ||
239            content_.GetSize() < maxSize_);
240 
241     content_.Add(transactionUid, protection.release());
242   }
243 
244 
Accessor(StorageCommitmentReports & that,const std::string & transactionUid)245   StorageCommitmentReports::Accessor::Accessor(StorageCommitmentReports& that,
246                                                const std::string& transactionUid) :
247     lock_(that.mutex_),
248     transactionUid_(transactionUid)
249   {
250     if (that.content_.Contains(transactionUid, report_))
251     {
252       that.content_.MakeMostRecent(transactionUid);
253     }
254     else
255     {
256       report_ = NULL;
257     }
258   }
259 
260   const StorageCommitmentReports::Report&
GetReport() const261   StorageCommitmentReports::Accessor::GetReport() const
262   {
263     if (report_ == NULL)
264     {
265       throw OrthancException(ErrorCode_BadSequenceOfCalls);
266     }
267     else
268     {
269       return *report_;
270     }
271   }
272 }
273