comparison OrthancServer/ServerJobs/ArchiveJob.cpp @ 2976:cb5d75143da0

Asynchronous generation of ZIP archives and DICOM medias
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 06 Dec 2018 12:23:46 +0100
parents 10c610e80b15
children 4e43e67f8ecf
comparison
equal deleted inserted replaced
2970:eea66afed0db 2976:cb5d75143da0
45 #define snprintf _snprintf 45 #define snprintf _snprintf
46 #endif 46 #endif
47 47
48 static const uint64_t MEGA_BYTES = 1024 * 1024; 48 static const uint64_t MEGA_BYTES = 1024 * 1024;
49 static const uint64_t GIGA_BYTES = 1024 * 1024 * 1024; 49 static const uint64_t GIGA_BYTES = 1024 * 1024 * 1024;
50 static const char* MEDIA_IMAGES_FOLDER = "IMAGES"; 50
51 static const char* const MEDIA_IMAGES_FOLDER = "IMAGES";
52 static const char* const KEY_DESCRIPTION = "Description";
53 static const char* const KEY_INSTANCES_COUNT = "InstancesCount";
54 static const char* const KEY_UNCOMPRESSED_SIZE_MB = "UncompressedSizeMB";
55
51 56
52 namespace Orthanc 57 namespace Orthanc
53 { 58 {
54 static bool IsZip64Required(uint64_t uncompressedSize, 59 static bool IsZip64Required(uint64_t uncompressedSize,
55 unsigned int countInstances) 60 unsigned int countInstances)
789 instancesCount_(0), 794 instancesCount_(0),
790 uncompressedSize_(0) 795 uncompressedSize_(0)
791 { 796 {
792 } 797 }
793 798
799
800 ArchiveJob::~ArchiveJob()
801 {
802 if (!mediaArchiveId_.empty())
803 {
804 context_.GetMediaArchive().Remove(mediaArchiveId_);
805 }
806 }
807
794 808
795 void ArchiveJob::SetSynchronousTarget(boost::shared_ptr<TemporaryFile>& target) 809 void ArchiveJob::SetSynchronousTarget(boost::shared_ptr<TemporaryFile>& target)
796 { 810 {
797 if (target.get() == NULL) 811 if (target.get() == NULL)
798 { 812 {
799 throw OrthancException(ErrorCode_NullPointer); 813 throw OrthancException(ErrorCode_NullPointer);
800 } 814 }
801 else if (synchronousTarget_.get() != NULL) 815 else if (writer_.get() != NULL || // Already started
816 synchronousTarget_.get() != NULL ||
817 asynchronousTarget_.get() != NULL)
802 { 818 {
803 throw OrthancException(ErrorCode_BadSequenceOfCalls); 819 throw OrthancException(ErrorCode_BadSequenceOfCalls);
804 } 820 }
805 else 821 else
806 { 822 {
807 synchronousTarget_ = target; 823 synchronousTarget_ = target;
808 } 824 }
809 } 825 }
810 826
811 827
828 void ArchiveJob::SetDescription(const std::string& description)
829 {
830 if (writer_.get() != NULL) // Already started
831 {
832 throw OrthancException(ErrorCode_BadSequenceOfCalls);
833 }
834 else
835 {
836 description_ = description;
837 }
838 }
839
840
812 void ArchiveJob::AddResource(const std::string& publicId) 841 void ArchiveJob::AddResource(const std::string& publicId)
813 { 842 {
814 if (writer_.get() != NULL) // Already started 843 if (writer_.get() != NULL) // Already started
815 { 844 {
816 throw OrthancException(ErrorCode_BadSequenceOfCalls); 845 throw OrthancException(ErrorCode_BadSequenceOfCalls);
817 } 846 }
818 847 else
819 ResourceIdentifiers resource(context_.GetIndex(), publicId); 848 {
820 archive_->Add(context_.GetIndex(), resource); 849 ResourceIdentifiers resource(context_.GetIndex(), publicId);
850 archive_->Add(context_.GetIndex(), resource);
851 }
821 } 852 }
822 853
823 854
824 void ArchiveJob::Reset() 855 void ArchiveJob::Reset()
825 { 856 {
828 } 859 }
829 860
830 861
831 void ArchiveJob::Start() 862 void ArchiveJob::Start()
832 { 863 {
864 TemporaryFile* target = NULL;
865
833 if (synchronousTarget_.get() == NULL) 866 if (synchronousTarget_.get() == NULL)
834 { 867 {
835 throw OrthancException(ErrorCode_BadSequenceOfCalls); 868 asynchronousTarget_.reset(new TemporaryFile);
869 target = asynchronousTarget_.get();
870 }
871 else
872 {
873 target = synchronousTarget_.get();
836 } 874 }
837 875
838 if (writer_.get() != NULL) 876 if (writer_.get() != NULL)
839 { 877 {
840 throw OrthancException(ErrorCode_BadSequenceOfCalls); 878 throw OrthancException(ErrorCode_BadSequenceOfCalls);
841 } 879 }
842 880
843 writer_.reset(new ZipWriterIterator(*synchronousTarget_, context_, *archive_, 881 writer_.reset(new ZipWriterIterator(*target, context_, *archive_,
844 isMedia_, enableExtendedSopClass_)); 882 isMedia_, enableExtendedSopClass_));
845 883
846 instancesCount_ = writer_->GetInstancesCount(); 884 instancesCount_ = writer_->GetInstancesCount();
847 uncompressedSize_ = writer_->GetUncompressedSize(); 885 uncompressedSize_ = writer_->GetUncompressedSize();
848 } 886 }
849 887
888
889
890 namespace
891 {
892 class DynamicTemporaryFile : public IDynamicObject
893 {
894 private:
895 std::auto_ptr<TemporaryFile> file_;
896
897 public:
898 DynamicTemporaryFile(TemporaryFile* f) : file_(f)
899 {
900 if (f == NULL)
901 {
902 throw OrthancException(ErrorCode_NullPointer);
903 }
904 }
905
906 const TemporaryFile& GetFile() const
907 {
908 assert(file_.get() != NULL);
909 return *file_;
910 }
911 };
912 }
850 913
914
915 void ArchiveJob::FinalizeTarget()
916 {
917 writer_.reset(); // Flush all the results
918
919 if (asynchronousTarget_.get() != NULL)
920 {
921 // Asynchronous behavior: Move the resulting file into the media archive
922 mediaArchiveId_ = context_.GetMediaArchive().Add(
923 new DynamicTemporaryFile(asynchronousTarget_.release()));
924 }
925 }
926
927
851 JobStepResult ArchiveJob::Step() 928 JobStepResult ArchiveJob::Step()
852 { 929 {
853 assert(writer_.get() != NULL); 930 assert(writer_.get() != NULL);
854 931
855 if (synchronousTarget_.unique()) 932 if (synchronousTarget_.get() != NULL &&
933 synchronousTarget_.unique())
856 { 934 {
857 LOG(WARNING) << "A client has disconnected while creating an archive"; 935 LOG(WARNING) << "A client has disconnected while creating an archive";
858 return JobStepResult::Failure(ErrorCode_NetworkProtocol); 936 return JobStepResult::Failure(ErrorCode_NetworkProtocol);
859 } 937 }
860 938
861 if (writer_->GetStepsCount() == 0) 939 if (writer_->GetStepsCount() == 0)
862 { 940 {
863 writer_.reset(); // Flush all the results 941 FinalizeTarget();
864 return JobStepResult::Success(); 942 return JobStepResult::Success();
865 } 943 }
866 else 944 else
867 { 945 {
868 writer_->RunStep(currentStep_); 946 writer_->RunStep(currentStep_);
869 947
870 currentStep_ ++; 948 currentStep_ ++;
871 949
872 if (currentStep_ == writer_->GetStepsCount()) 950 if (currentStep_ == writer_->GetStepsCount())
873 { 951 {
874 writer_.reset(); // Flush all the results 952 FinalizeTarget();
875 return JobStepResult::Success(); 953 return JobStepResult::Success();
876 } 954 }
877 else 955 else
878 { 956 {
879 return JobStepResult::Continue(); 957 return JobStepResult::Continue();
907 { 985 {
908 target = "Archive"; 986 target = "Archive";
909 } 987 }
910 } 988 }
911 989
912 990
913 void ArchiveJob::GetPublicContent(Json::Value& value) 991 void ArchiveJob::GetPublicContent(Json::Value& value)
914 { 992 {
915 value["Description"] = description_; 993 value = Json::objectValue;
916 value["InstancesCount"] = instancesCount_; 994 value[KEY_DESCRIPTION] = description_;
917 value["UncompressedSizeMB"] = 995 value[KEY_INSTANCES_COUNT] = instancesCount_;
996 value[KEY_UNCOMPRESSED_SIZE_MB] =
918 static_cast<unsigned int>(uncompressedSize_ / MEGA_BYTES); 997 static_cast<unsigned int>(uncompressedSize_ / MEGA_BYTES);
919 } 998 }
999
1000
1001 bool ArchiveJob::GetOutput(std::string& output,
1002 MimeType& mime,
1003 const std::string& key)
1004 {
1005 if (key == "archive" &&
1006 !mediaArchiveId_.empty())
1007 {
1008 SharedArchive::Accessor accessor(context_.GetMediaArchive(), mediaArchiveId_);
1009
1010 if (accessor.IsValid())
1011 {
1012 const DynamicTemporaryFile& f = dynamic_cast<DynamicTemporaryFile&>(accessor.GetItem());
1013 f.GetFile().Read(output);
1014 mime = MimeType_Zip;
1015 return true;
1016 }
1017 else
1018 {
1019 return false;
1020 }
1021 }
1022 else
1023 {
1024 return false;
1025 }
1026 }
920 } 1027 }