Mantid
Loading...
Searching...
No Matches
CatalogDownloadDataFiles.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
20
21#include <Poco/Net/AcceptCertificateHandler.h>
22#include <Poco/Net/HTTPRequest.h>
23#include <Poco/Net/HTTPResponse.h>
24#include <Poco/Net/HTTPSClientSession.h>
25#include <Poco/Net/PrivateKeyPassphraseHandler.h>
26#include <Poco/Net/SSLException.h>
27#include <Poco/Net/SSLManager.h>
28#include <Poco/Net/SecureStreamSocket.h>
29#include <Poco/StreamCopier.h>
30#include <Poco/URI.h>
31
32#include <filesystem>
33#include <fstream>
34#include <iomanip>
35#include <memory>
36
37namespace Mantid::ICat {
38using namespace Kernel;
39using namespace API;
40
41DECLARE_ALGORITHM(CatalogDownloadDataFiles)
42
43
45 declareProperty(std::make_unique<ArrayProperty<int64_t>>("FileIds"),
46 "List of fileids to download from the data server");
47 declareProperty(std::make_unique<ArrayProperty<std::string>>("FileNames"),
48 "List of filenames to download from the data server");
49 declareProperty("DownloadPath", "", "The path to save the downloaded files.");
50 declareProperty("Session", "", "The session information of the catalog to use.");
51 declareProperty(std::make_unique<ArrayProperty<std::string>>("FileLocations", std::vector<std::string>(),
52 std::make_shared<NullValidator>(), Direction::Output),
53 "A list of file locations to the catalog datafiles.");
54}
55
58 // Cast a catalog to a catalogInfoService to access downloading functionality.
59 auto catalogInfoService = std::dynamic_pointer_cast<API::ICatalogInfoService>(
60 API::CatalogManager::Instance().getCatalog(getPropertyValue("Session")));
61 // Check if the catalog created supports publishing functionality.
62 if (!catalogInfoService)
63 throw std::runtime_error("The catalog that you are using does not support "
64 "external downloading.");
65
66 std::unique_ptr<CatalogConfigService> catConfigService(makeCatalogConfigServiceAdapter(ConfigService::Instance()));
67 UserCatalogInfo catalogInfo(ConfigService::Instance().getFacility().catalogInfo(), *catConfigService);
68
69 std::vector<int64_t> fileIDs = getProperty("FileIds");
70 std::vector<std::string> fileNames = getProperty("FileNames");
71
72 // Stores the paths to the related files located in the archives (if user has
73 // access).
74 // Otherwise, stores the path to the downloaded file.
75 std::vector<std::string> fileLocations;
76
77 m_prog = 0.0;
78
79 std::vector<int64_t>::const_iterator fileID = fileIDs.begin();
80 std::vector<std::string>::const_iterator fileName = fileNames.begin();
81
82 // For every file with the given ID.
83 for (; fileID != fileIDs.end(); ++fileID, ++fileName) {
84 m_prog += 0.1;
85 double prog = m_prog / (double(fileIDs.size()) / 10);
86
87 progress(prog, "getting location string...");
88
89 // The location of the file (on the server) stored in the archives.
90 std::string fileLocation = catalogInfoService->getFileLocation(*fileID);
91
92 g_log.debug() << "CatalogDownloadDataFiles -> File location before transform is: " << fileLocation
93 << " mac path is : " << catalogInfo.macPrefix() << '\n';
94 // Transform the archive path to the path of the user's operating system.
95 fileLocation = catalogInfo.transformArchivePath(fileLocation);
96 g_log.debug() << "CatalogDownloadDataFiles -> File location after transform is: " << fileLocation << '\n';
97
98 // Can we open the file (Hence, have access to the archives?)
99 std::ifstream hasAccessToArchives(fileLocation.c_str());
100 if (hasAccessToArchives) {
101 g_log.information() << "File (" << *fileName << ") located in archives (" << fileLocation << ").\n";
102 fileLocations.emplace_back(fileLocation);
103 } else {
104 g_log.information() << "Unable to open file (" << *fileName
105 << ") from archive. Beginning to download over Internet.\n";
106 progress(prog / 2, "getting the url ....");
107 // Obtain URL for related file to download from net.
108 const std::string url = catalogInfoService->getDownloadURL(*fileID);
109 progress(prog, "downloading over internet...");
110 const std::string fullPathDownloadedFile = doDownloadandSavetoLocalDrive(url, *fileName);
111 fileLocations.emplace_back(fullPathDownloadedFile);
112 }
113 }
114
115 // Set the fileLocations property
116 setProperty("FileLocations", fileLocations);
117}
118
124bool CatalogDownloadDataFiles::isDataFile(const std::string &fileName) {
125 std::filesystem::path path(fileName);
126 std::string extension = path.extension().string();
127 if (!extension.empty() && extension[0] == '.') {
128 extension = extension.substr(1); // remove leading dot
129 }
130 std::transform(extension.begin(), extension.end(), extension.begin(), tolower);
131 return (extension == "raw" || extension == "nxs");
132}
133
142 const std::string &fileName) {
143 std::string pathToDownloadedDatafile;
144
145 try {
146 Poco::URI uri(URL);
147
148 std::string path(uri.getPathAndQuery());
149 clock_t start = clock();
150
151 Poco::SharedPtr<Poco::Net::InvalidCertificateHandler> certificateHandler =
152 new Poco::Net::AcceptCertificateHandler(true);
153 // Currently do not use any means of authentication. This should be updated
154 // IDS has signed certificate.
155 const Poco::Net::Context::Ptr context =
156 new Poco::Net::Context(Poco::Net::Context::CLIENT_USE, "", "", "", Poco::Net::Context::VERIFY_NONE);
157 // Create a singleton for holding the default context. E.g. any future
158 // requests to publish are made to this certificate and context.
159 Poco::Net::SSLManager::instance().initializeClient(nullptr, certificateHandler, context);
160
161 // Session takes ownership of socket
162 Poco::Net::SecureStreamSocket socket{context};
163 Poco::Net::HTTPSClientSession session{socket, uri.getHost(), uri.getPort()};
164
165 Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, path, Poco::Net::HTTPMessage::HTTP_1_1);
166 session.sendRequest(request);
167
168 // Close the request by requesting a response.
169 Poco::Net::HTTPResponse response;
170 // Store the response for use IF an error occurs (e.g. 404).
171 std::istream &responseStream = session.receiveResponse(response);
172
173 // Obtain the status returned by the server to verify if it was a success.
174 Poco::Net::HTTPResponse::HTTPStatus HTTPStatus = response.getStatus();
175 // The error message returned by the IDS (if one exists).
176 std::string IDSError = CatalogAlgorithmHelper().getIDSError(HTTPStatus, responseStream);
177 // Cancel the algorithm and display the message if it exists.
178 if (!IDSError.empty()) {
179 // As an error occurred we must cancel the algorithm to prevent success
180 // message.
181 this->cancel();
182 // Output an appropriate error message from the JSON object returned by
183 // the IDS.
184 g_log.error(IDSError);
185 return "";
186 }
187
188 // Save the file to local disk if no errors occurred on the IDS.
189 pathToDownloadedDatafile = saveFiletoDisk(responseStream, fileName);
190
191 clock_t end = clock();
192 float diff = float(end - start) / CLOCKS_PER_SEC;
193 g_log.information() << "Time taken to download file " << fileName << " is " << std::fixed << std::setprecision(2)
194 << diff << " seconds\n";
195
196 } catch (Poco::Net::SSLException &error) {
197 throw std::runtime_error(error.displayText());
198 }
199 // A strange error occurs (what returns: {I/O error}, while message returns: {
200 // 9: The BIO reported an error }.
201 // This bug has been fixed in POCO 1.4 and is noted -
202 // http://sourceforge.net/p/poco/bugs/403/
203 // I have opted to catch the exception and do nothing as this allows the
204 // load/download functionality to work.
205 // However, the port the user used to download the file will be left open.
206 catch (Poco::Exception &error) {
207 throw std::runtime_error(error.displayText());
208 }
209
210 return pathToDownloadedDatafile;
211}
212
220std::string CatalogDownloadDataFiles::saveFiletoDisk(std::istream &rs, const std::string &fileName) {
221 std::filesystem::path downloadPath(getPropertyValue("DownloadPath"));
222 std::filesystem::path filepath = downloadPath / fileName;
223 std::ios_base::openmode mode = isDataFile(fileName) ? std::ios_base::binary : std::ios_base::out;
224
225 std::ofstream ofs(filepath, mode);
226 if (ofs.rdstate() & std::ios::failbit)
227 throw Exception::FileError("Error on creating File", fileName);
228 // copy the input stream to a file.
229 Poco::StreamCopier::copyStream(rs, ofs);
230
231 return filepath.string();
232}
233
246std::string CatalogDownloadDataFiles::testDownload(const std::string &URL, const std::string &fileName) {
247 return doDownloadandSavetoLocalDrive(URL, fileName);
248}
249} // namespace Mantid::ICat
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:547
double error
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Kernel::Logger & g_log
Definition Algorithm.h:426
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
void cancel() override
Raises the cancel flag.
const std::string getIDSError(const Poco::Net::HTTPResponse::HTTPStatus &HTTPStatus, std::istream &responseStream)
Obtain the error message returned by the IDS.
CatalogDownloadDataFiles is responsible for downloading datafiles from a catalog.
std::string testDownload(const std::string &URL, const std::string &fileName)
This method is used for unit testing purpose.
std::string doDownloadandSavetoLocalDrive(const std::string &URL, const std::string &fileName)
Saves downloaded file to local disk.
std::string saveFiletoDisk(std::istream &rs, const std::string &fileName)
Saves the downloaded file to disc.
bool isDataFile(const std::string &fileName)
True if the extension of the file is a datafile.
void exec() override
Overwrites Algorithm method.
Support for a property that holds an array of values.
Records the filename and the description of failure.
Definition Exception.h:98
virtual std::string transformArchivePath(const std::string &path) const
Transform's the archive path based on operating system used.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp:145
void error(const std::string &msg)
Logs at error level.
Definition Logger.cpp:108
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
UserCatalogInfo : Takes catalog info from the facility (via CatalogInfo), but provides the ability to...
const std::string macPrefix() const override
Obtain Macintosh prefix from facility file.
CatalogConfigService * makeCatalogConfigServiceAdapter(const T &adaptee, const std::string &key="icatDownload.mountPoint")
@ Output
An output workspace.
Definition Property.h:55