bes  Updated for version 3.20.13
build_dmrpp.cc
1 // -*- mode: c++; c-basic-offset:4 -*-
2 
3 // This file is part of the Hyrax data server.
4 
5 // Copyright (c) 2018 OPeNDAP, Inc.
6 // Author: James Gallagher <jgallagher@opendap.org>
7 //
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Lesser General Public
10 // License as published by the Free Software Foundation; either
11 // version 2.1 of the License, or (at your option) any later version.
12 //
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Lesser General Public License for more details.
17 //
18 // You should have received a copy of the GNU Lesser General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 //
22 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
23 
24 #include "config.h"
25 
26 
27 #include <iostream>
28 #include <sstream>
29 #include <memory>
30 #include <iterator>
31 
32 #include <unistd.h>
33 #include <cstdlib>
34 #include <libgen.h>
35 
36 #include <libdap/Array.h>
37 #include <libdap/util.h>
38 #include <libdap/D4Attributes.h>
39 #include <libdap/D4ParserSax2.h>
40 
41 #include <TheBESKeys.h>
42 #include <BESUtil.h>
43 #include <BESDebug.h>
44 #include <BESError.h>
45 
46 #include "DMRpp.h"
47 #include "DmrppTypeFactory.h"
48 #include "DmrppD4Group.h"
49 #include "DmrppMetadataStore.h"
50 
51 #include "build_dmrpp_util.h"
52 
53 using namespace std;
54 using namespace libdap;
55 using namespace dmrpp;
56 using namespace build_dmrpp_util;
57 
58 #define DEBUG_KEY "metadata_store,dmrpp_store,dmrpp"
59 #define ROOT_DIRECTORY "BES.Catalog.catalog.RootDirectory"
60 
67 static string cmdln(int argc, char *argv[])
68 {
69  stringstream ss;
70  for(int i=0; i<argc; i++) {
71  if (i > 0)
72  ss << " ";
73  ss << argv[i];
74  }
75  return ss.str();
76 }
77 
85 void inject_version_and_configuration(int argc, char **argv, DMRpp *dmrpp)
86 {
87  dmrpp->set_version(CVER);
88 
89  // Build the version attributes for the DMR++
90  auto version = new D4Attribute("build_dmrpp_metadata", StringToD4AttributeType("container"));
91 
92  auto build_dmrpp_version = new D4Attribute("build_dmrpp", StringToD4AttributeType("string"));
93  build_dmrpp_version->add_value(CVER);
94  version->attributes()->add_attribute_nocopy(build_dmrpp_version);
95 
96  auto bes_version = new D4Attribute("bes", StringToD4AttributeType("string"));
97  bes_version->add_value(CVER);
98  version->attributes()->add_attribute_nocopy(bes_version);
99 
100  stringstream ldv;
101  ldv << libdap_name() << "-" << libdap_version();
102  auto libdap4_version = new D4Attribute("libdap", StringToD4AttributeType("string"));
103  libdap4_version->add_value(ldv.str());
104  version->attributes()->add_attribute_nocopy(libdap4_version);
105 
106  if(!TheBESKeys::ConfigFile.empty()) {
107  // What is the BES configuration in play?
108  auto config = new D4Attribute("configuration", StringToD4AttributeType("string"));
109  config->add_value(TheBESKeys::TheKeys()->get_as_config());
110  version->attributes()->add_attribute_nocopy(config);
111  }
112 
113  // How was build_dmrpp invoked?
114  auto invoke = new D4Attribute("invocation", StringToD4AttributeType("string"));
115  invoke->add_value(cmdln(argc, argv));
116  version->attributes()->add_attribute_nocopy(invoke);
117 
118  // Inject version and configuration attributes into DMR here.
119  dmrpp->root()->attributes()->add_attribute_nocopy(version);
120 }
121 
122 void usage() {
123  const char *help = R"(
124  build_dmrpp -h: Show this help
125 
126  build_dmrpp -V: Show build versions for components that make up the program
127 
128  build_dmrpp -c <bes.conf> -f <data file> [-u <href url>]: Build the DMR++ using the <bes.conf>
129  options to initialize the software for the <data file>. Optionally substitute the <href url>.
130  Builds the DMR using the HDF5 handler as configured using the options in the <bes.conf>.
131 
132  build_dmrpp build_dmrpp -f <data file> -r <dmr file> [-u <href url>]: As above, but uses the DMR
133  read from the given file (so it does not run the HDF5 handler code and does not require the
134  Metadata Store - MDS - be configured). Note that the get_dmrpp command appears to use this
135  option and thus, the configuration options listed in the built DMR++ files lack the MDS setup.
136 
137  Other options:
138  -v: Verbose
139  -d: Turn on BES software debugging output
140  -M: Add information about the build_dmrpp software, incl versions, to the built DMR++)";
141 
142  cerr << help << endl;
143 }
144 
145 int main(int argc, char *argv[]) {
146  string h5_file_name;
147  string h5_dset_path;
148  string dmr_name;
149  string url_name;
150  bool add_production_metadata = false;
151 
152  int option_char;
153  while ((option_char = getopt(argc, argv, "c:f:r:u:dhvVM")) != -1) {
154  switch (option_char) {
155  case 'V':
156  cerr << basename(argv[0]) << "-" << CVER << " (bes-"<< CVER << ", " << libdap_name() << "-"
157  << libdap_version() << ")" << endl;
158  return 0;
159 
160  case 'v':
161  build_dmrpp_util::verbose = true; // verbose hdf5 errors
162  break;
163 
164  case 'd':
165  BESDebug::SetUp(string("cerr,").append(DEBUG_KEY));
166  break;
167 
168  case 'f':
169  h5_file_name = optarg;
170  break;
171 
172  case 'r':
173  dmr_name = optarg;
174  break;
175 
176  case 'u':
177  url_name = optarg;
178  break;
179 
180  case 'c':
181  TheBESKeys::ConfigFile = optarg;
182  break;
183 
184  case 'M':
185  add_production_metadata = true;
186  break;
187 
188  case 'h':
189  usage();
190  exit(EXIT_FAILURE);
191 
192  default:
193  break;
194  }
195  }
196 
197  if (h5_file_name.empty()) {
198  cerr << "HDF5 file name must be given (-f <input>)." << endl;
199  return EXIT_FAILURE;
200  }
201 
202  try {
203  // Turn off automatic hdf5 error printing.
204  // See: https://support.hdfgroup.org/HDF5/doc1.8/RM/RM_H5E.html#Error-SetAuto2
205 
206  // For a given HDF5, get info for all the HDF5 datasets in a DMR or for a
207  // given HDF5 dataset
208  if (!dmr_name.empty()) {
209  // Get dmr:
210  DMRpp dmrpp;
211  DmrppTypeFactory dtf;
212  dmrpp.set_factory(&dtf);
213 
214  ifstream in(dmr_name.c_str());
215  D4ParserSax2 parser;
216  parser.intern(in, &dmrpp, false);
217 
218  add_chunk_information(h5_file_name, &dmrpp);
219 
220  if (add_production_metadata) {
221  inject_version_and_configuration(argc, argv, &dmrpp);
222  }
223 
224  XMLWriter writer;
225  dmrpp.print_dmrpp(writer, url_name);
226 
227  cout << writer.get_doc();
228  } else {
229  string bes_data_root = TheBESKeys::TheKeys()->read_string_key(ROOT_DIRECTORY, "");
230  if (bes_data_root.empty()) {
231  cerr << "Could not find the data directory." << endl;
232  return EXIT_FAILURE;
233  }
234 
235  // Use the values from the bes.conf file... jhrg 5/21/18
237  if (!mds) {
238  cerr << "The Metadata Store (MDS) must be configured for this command to work (but see the -r option)." << endl;
239  return EXIT_FAILURE;
240  }
241 
242  // Use the full path to open the file, but use the 'name' (which is the
243  // path relative to the BES Data Root) with the MDS.
244  // Changed this to utilize assemblePath() because simply concatenating the strings
245  // is fragile. - ndp 6/6/18
246  string h5_file_path = BESUtil::assemblePath(bes_data_root, h5_file_name);
247 
248  bes::DmrppMetadataStore::MDSReadLock lock = mds->is_dmr_available(h5_file_path, h5_file_name, "h5");
249  if (lock()) {
250  // parse the DMR into a DMRpp (that uses the DmrppTypes)
251  unique_ptr<DMRpp> dmrpp(dynamic_cast<DMRpp *>(mds->get_dmr_object(h5_file_name /*h5_file_path*/)));
252  if (!dmrpp) {
253  cerr << "Expected a DMR++ object from the DmrppMetadataStore." << endl;
254  return EXIT_FAILURE;
255  }
256 
257  add_chunk_information(h5_file_path, dmrpp.get());
258 
259  dmrpp->set_href(url_name);
260 
261  mds->add_dmrpp_response(dmrpp.get(), h5_file_name /*h5_file_path*/);
262 
263  XMLWriter writer;
264  dmrpp->set_print_chunks(true);
265  dmrpp->print_dap4(writer);
266 
267  cout << writer.get_doc();
268  } else {
269  cerr << "Error: Could not get a lock on the DMR for '" + h5_file_path + "'." << endl;
270  return EXIT_FAILURE;
271  }
272  }
273  }
274  catch (const BESError &e) {
275  cerr << "Error: " << e.get_message() << endl;
276  return EXIT_FAILURE;
277  }
278  catch (const std::exception &e) {
279  cerr << "std::exception: " << e.what() << endl;
280  return EXIT_FAILURE;
281  }
282  catch (...) {
283  cerr << "Unknown error." << endl;
284  return EXIT_FAILURE;
285  }
286 
287  return EXIT_SUCCESS;
288 }
static void SetUp(const std::string &values)
Sets up debugging for the bes.
Definition: BESDebug.cc:98
Base exception class for the BES with basic string message.
Definition: BESError.h:59
std::string get_message() const
get the error message for this exception
Definition: BESError.h:111
static std::string assemblePath(const std::string &firstPart, const std::string &secondPart, bool leadingSlash=false, bool trailingSlash=false)
Assemble path fragments making sure that they are separated by a single '/' character.
Definition: BESUtil.cc:801
static TheBESKeys * TheKeys()
Definition: TheBESKeys.cc:71
static std::string ConfigFile
Definition: TheBESKeys.h:185
std::string read_string_key(const std::string &key, const std::string &default_value)
Read a string-valued key from the bes.conf file.
Definition: TheBESKeys.cc:423
Store the DAP DMR++ metadata responses.
virtual libdap::DMR * get_dmr_object(const string &name)
Use the DMR response to build a DMR with Dmrpp Types.
static DmrppMetadataStore * get_instance()
virtual MDSReadLock is_dmr_available(const std::string &name)
Is the DMR response for.
Provide a way to print the DMR++ response.
Definition: DMRpp.h:44
virtual void print_dmrpp(libdap::XMLWriter &xml, const std::string &href="", bool constrained=false, bool print_chunks=true)
Print the DMR++ response.
Definition: DMRpp.cc:71
void print_dap4(libdap::XMLWriter &xml, bool constrained=false)
override DMR::print_dap4() so the chunk info will print too.
Definition: DMRpp.cc:140
Unlock and close the MDS item when the ReadLock goes out of scope.