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 +
19
20#include <Poco/Net/AcceptCertificateHandler.h>
21#include <Poco/Net/HTTPRequest.h>
22#include <Poco/Net/HTTPResponse.h>
23#include <Poco/Net/HTTPSClientSession.h>
24#include <Poco/Net/PrivateKeyPassphraseHandler.h>
25#include <Poco/Net/SSLException.h>
26#include <Poco/Net/SSLManager.h>
27#include <Poco/Net/SecureStreamSocket.h>
28#include <Poco/Path.h>
29#include <Poco/StreamCopier.h>
30#include <Poco/URI.h>
31
32#include <fstream>
33#include <iomanip>
34#include <memory>
35
36namespace Mantid::ICat {
37using namespace Kernel;
38using namespace API;
39
40DECLARE_ALGORITHM(CatalogDownloadDataFiles)
41
42
44 declareProperty(std::make_unique<ArrayProperty<int64_t>>("FileIds"),
45 "List of fileids to download from the data server");
46 declareProperty(std::make_unique<ArrayProperty<std::string>>("FileNames"),
47 "List of filenames to download from the data server");
48 declareProperty("DownloadPath", "", "The path to save the downloaded files.");
49 declareProperty("Session", "", "The session information of the catalog to use.");
50 declareProperty(std::make_unique<ArrayProperty<std::string>>("FileLocations", std::vector<std::string>(),
51 std::make_shared<NullValidator>(), Direction::Output),
52 "A list of file locations to the catalog datafiles.");
53}
54
57 // Cast a catalog to a catalogInfoService to access downloading functionality.
58 auto catalogInfoService = std::dynamic_pointer_cast<API::ICatalogInfoService>(
59 API::CatalogManager::Instance().getCatalog(getPropertyValue("Session")));
60 // Check if the catalog created supports publishing functionality.
61 if (!catalogInfoService)
62 throw std::runtime_error("The catalog that you are using does not support "
63 "external downloading.");
64
65 std::unique_ptr<CatalogConfigService> catConfigService(makeCatalogConfigServiceAdapter(ConfigService::Instance()));
66 UserCatalogInfo catalogInfo(ConfigService::Instance().getFacility().catalogInfo(), *catConfigService);
67
68 std::vector<int64_t> fileIDs = getProperty("FileIds");
69 std::vector<std::string> fileNames = getProperty("FileNames");
70
71 // Stores the paths to the related files located in the archives (if user has
72 // access).
73 // Otherwise, stores the path to the downloaded file.
74 std::vector<std::string> fileLocations;
75
76 m_prog = 0.0;
77
78 std::vector<int64_t>::const_iterator fileID = fileIDs.begin();
79 std::vector<std::string>::const_iterator fileName = fileNames.begin();
80
81 // For every file with the given ID.
82 for (; fileID != fileIDs.end(); ++fileID, ++fileName) {
83 m_prog += 0.1;
84 double prog = m_prog / (double(fileIDs.size()) / 10);
85
86 progress(prog, "getting location string...");
87
88 // The location of the file (on the server) stored in the archives.
89 std::string fileLocation = catalogInfoService->getFileLocation(*fileID);
90
91 g_log.debug() << "CatalogDownloadDataFiles -> File location before transform is: " << fileLocation
92 << " mac path is : " << catalogInfo.macPrefix() << '\n';
93 // Transform the archive path to the path of the user's operating system.
94 fileLocation = catalogInfo.transformArchivePath(fileLocation);
95 g_log.debug() << "CatalogDownloadDataFiles -> File location after transform is: " << fileLocation << '\n';
96
97 // Can we open the file (Hence, have access to the archives?)
98 std::ifstream hasAccessToArchives(fileLocation.c_str());
99 if (hasAccessToArchives) {
100 g_log.information() << "File (" << *fileName << ") located in archives (" << fileLocation << ").\n";
101 fileLocations.emplace_back(fileLocation);
102 } else {
103 g_log.information() << "Unable to open file (" << *fileName
104 << ") from archive. Beginning to download over Internet.\n";
105 progress(prog / 2, "getting the url ....");
106 // Obtain URL for related file to download from net.
107 const std::string url = catalogInfoService->getDownloadURL(*fileID);
108 progress(prog, "downloading over internet...");
109 const std::string fullPathDownloadedFile = doDownloadandSavetoLocalDrive(url, *fileName);
110 fileLocations.emplace_back(fullPathDownloadedFile);
111 }
112 }
113
114 // Set the fileLocations property
115 setProperty("FileLocations", fileLocations);
116}
117
123bool CatalogDownloadDataFiles::isDataFile(const std::string &fileName) {
124 std::string extension = Poco::Path(fileName).getExtension();
125 std::transform(extension.begin(), extension.end(), extension.begin(), tolower);
126 return (extension == "raw" || extension == "nxs");
127}
128
137 const std::string &fileName) {
138 std::string pathToDownloadedDatafile;
139
140 try {
141 Poco::URI uri(URL);
142
143 std::string path(uri.getPathAndQuery());
144 clock_t start = clock();
145
146 Poco::SharedPtr<Poco::Net::InvalidCertificateHandler> certificateHandler =
147 new Poco::Net::AcceptCertificateHandler(true);
148 // Currently do not use any means of authentication. This should be updated
149 // IDS has signed certificate.
150 const Poco::Net::Context::Ptr context =
151 new Poco::Net::Context(Poco::Net::Context::CLIENT_USE, "", "", "", Poco::Net::Context::VERIFY_NONE);
152 // Create a singleton for holding the default context. E.g. any future
153 // requests to publish are made to this certificate and context.
154 Poco::Net::SSLManager::instance().initializeClient(nullptr, certificateHandler, context);
155
156 // Session takes ownership of socket
157 Poco::Net::SecureStreamSocket socket{context};
158 Poco::Net::HTTPSClientSession session{socket, uri.getHost(), uri.getPort()};
159
160 Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, path, Poco::Net::HTTPMessage::HTTP_1_1);
161 session.sendRequest(request);
162
163 // Close the request by requesting a response.
164 Poco::Net::HTTPResponse response;
165 // Store the response for use IF an error occurs (e.g. 404).
166 std::istream &responseStream = session.receiveResponse(response);
167
168 // Obtain the status returned by the server to verify if it was a success.
169 Poco::Net::HTTPResponse::HTTPStatus HTTPStatus = response.getStatus();
170 // The error message returned by the IDS (if one exists).
171 std::string IDSError = CatalogAlgorithmHelper().getIDSError(HTTPStatus, responseStream);
172 // Cancel the algorithm and display the message if it exists.
173 if (!IDSError.empty()) {
174 // As an error occurred we must cancel the algorithm to prevent success
175 // message.
176 this->cancel();
177 // Output an appropriate error message from the JSON object returned by
178 // the IDS.
179 g_log.error(IDSError);
180 return "";
181 }
182
183 // Save the file to local disk if no errors occurred on the IDS.
184 pathToDownloadedDatafile = saveFiletoDisk(responseStream, fileName);
185
186 clock_t end = clock();
187 float diff = float(end - start) / CLOCKS_PER_SEC;
188 g_log.information() << "Time taken to download file " << fileName << " is " << std::fixed << std::setprecision(2)
189 << diff << " seconds\n";
190
191 } catch (Poco::Net::SSLException &error) {
192 throw std::runtime_error(error.displayText());
193 }
194 // A strange error occurs (what returns: {I/O error}, while message returns: {
195 // 9: The BIO reported an error }.
196 // This bug has been fixed in POCO 1.4 and is noted -
197 // http://sourceforge.net/p/poco/bugs/403/
198 // I have opted to catch the exception and do nothing as this allows the
199 // load/download functionality to work.
200 // However, the port the user used to download the file will be left open.
201 catch (Poco::Exception &error) {
202 throw std::runtime_error(error.displayText());
203 }
204
205 return pathToDownloadedDatafile;
206}
207
215std::string CatalogDownloadDataFiles::saveFiletoDisk(std::istream &rs, const std::string &fileName) {
216 std::string filepath = Poco::Path(getPropertyValue("DownloadPath"), fileName).toString();
217 std::ios_base::openmode mode = isDataFile(fileName) ? std::ios_base::binary : std::ios_base::out;
218
219 std::ofstream ofs(filepath.c_str(), mode);
220 if (ofs.rdstate() & std::ios::failbit)
221 throw Exception::FileError("Error on creating File", fileName);
222 // copy the input stream to a file.
223 Poco::StreamCopier::copyStream(rs, ofs);
224
225 return filepath;
226}
227
240std::string CatalogDownloadDataFiles::testDownload(const std::string &URL, const std::string &fileName) {
241 return doDownloadandSavetoLocalDrive(URL, fileName);
242}
243} // namespace Mantid::ICat
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
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:422
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:54