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};
159 session.setHost(uri.getHost());
160 session.setPort(uri.getPort());
161
162 Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, path, Poco::Net::HTTPMessage::HTTP_1_1);
163 session.sendRequest(request);
164
165 // Close the request by requesting a response.
166 Poco::Net::HTTPResponse response;
167 // Store the response for use IF an error occurs (e.g. 404).
168 std::istream &responseStream = session.receiveResponse(response);
169
170 // Obtain the status returned by the server to verify if it was a success.
171 Poco::Net::HTTPResponse::HTTPStatus HTTPStatus = response.getStatus();
172 // The error message returned by the IDS (if one exists).
173 std::string IDSError = CatalogAlgorithmHelper().getIDSError(HTTPStatus, responseStream);
174 // Cancel the algorithm and display the message if it exists.
175 if (!IDSError.empty()) {
176 // As an error occurred we must cancel the algorithm to prevent success
177 // message.
178 this->cancel();
179 // Output an appropriate error message from the JSON object returned by
180 // the IDS.
181 g_log.error(IDSError);
182 return "";
183 }
184
185 // Save the file to local disk if no errors occurred on the IDS.
186 pathToDownloadedDatafile = saveFiletoDisk(responseStream, fileName);
187
188 clock_t end = clock();
189 float diff = float(end - start) / CLOCKS_PER_SEC;
190 g_log.information() << "Time taken to download file " << fileName << " is " << std::fixed << std::setprecision(2)
191 << diff << " seconds\n";
192
193 } catch (Poco::Net::SSLException &error) {
194 throw std::runtime_error(error.displayText());
195 }
196 // A strange error occurs (what returns: {I/O error}, while message returns: {
197 // 9: The BIO reported an error }.
198 // This bug has been fixed in POCO 1.4 and is noted -
199 // http://sourceforge.net/p/poco/bugs/403/
200 // I have opted to catch the exception and do nothing as this allows the
201 // load/download functionality to work.
202 // However, the port the user used to download the file will be left open.
203 catch (Poco::Exception &error) {
204 throw std::runtime_error(error.displayText());
205 }
206
207 return pathToDownloadedDatafile;
208}
209
217std::string CatalogDownloadDataFiles::saveFiletoDisk(std::istream &rs, const std::string &fileName) {
218 std::string filepath = Poco::Path(getPropertyValue("DownloadPath"), fileName).toString();
219 std::ios_base::openmode mode = isDataFile(fileName) ? std::ios_base::binary : std::ios_base::out;
220
221 std::ofstream ofs(filepath.c_str(), mode);
222 if (ofs.rdstate() & std::ios::failbit)
223 throw Exception::FileError("Error on creating File", fileName);
224 // copy the input stream to a file.
225 Poco::StreamCopier::copyStream(rs, ofs);
226
227 return filepath;
228}
229
242std::string CatalogDownloadDataFiles::testDownload(const std::string &URL, const std::string &fileName) {
243 return doDownloadandSavetoLocalDrive(URL, fileName);
244}
245} // namespace Mantid::ICat
#define DECLARE_ALGORITHM(classname)
Definition: Algorithm.h:576
double error
Definition: IndexPeaks.cpp:133
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
Definition: Algorithm.cpp:2026
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Definition: Algorithm.cpp:2076
Kernel::Logger & g_log
Definition: Algorithm.h:451
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
Definition: Algorithm.cpp:231
void cancel() override
Raises the cancel flag.
Definition: Algorithm.cpp:1644
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.
Definition: ArrayProperty.h:28
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:114
void error(const std::string &msg)
Logs at error level.
Definition: Logger.cpp:77
void information(const std::string &msg)
Logs at information level.
Definition: Logger.cpp:105
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
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