libfoedus-core
FOEDUS Core Library
externalizable.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014-2015, Hewlett-Packard Development Company, LP.
3  * This program is free software; you can redistribute it and/or modify it
4  * under the terms of the GNU General Public License as published by the Free
5  * Software Foundation; either version 2 of the License, or (at your option)
6  * any later version.
7  *
8  * This program is distributed in the hope that it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11  * more details. You should have received a copy of the GNU General Public
12  * License along with this program; if not, write to the Free Software
13  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14  *
15  * HP designates this particular file as subject to the "Classpath" exception
16  * as provided by HP in the LICENSE.txt file that accompanied this code.
17  */
19 
20 #include <tinyxml2.h>
21 
22 #include <cstring>
23 #include <ostream>
24 #include <sstream>
25 #include <string>
26 #include <vector>
27 
31 #include "foedus/fs/filesystem.hpp"
32 #include "foedus/fs/path.hpp"
34 
35 namespace foedus {
36 namespace externalize {
37 
39  tinyxml2::XMLDocument document;
40  tinyxml2::XMLError load_error = document.Parse(xml.data(), xml.size());
41  if (load_error != tinyxml2::XML_SUCCESS) {
42  std::stringstream custom_message;
43  custom_message << "xml=" << xml << ", tinyxml2 error=" << load_error
44  << ", GetErrorStr1()=" << document.GetErrorStr1()
45  << ", GetErrorStr2()=" << document.GetErrorStr2();
46  return ERROR_STACK_MSG(kErrorCodeConfParseFailed, custom_message.str().c_str());
47  } else if (!document.RootElement()) {
48  return ERROR_STACK_MSG(kErrorCodeConfEmptyXml, xml.c_str());
49  } else {
50  CHECK_ERROR(load(document.RootElement()));
51  }
52  return kRetOk;
53 }
54 
55 void Externalizable::save_to_stream(std::ostream* ptr) const {
56  std::ostream &o = *ptr;
57  tinyxml2::XMLDocument doc;
58  // root element name is class name.
59  tinyxml2::XMLElement* element = doc.NewElement(get_tag_name());
60  if (!element) {
61  o << "Out-of-memory during Externalizable::save_to_stream()";
62  return;
63  }
64  doc.InsertFirstChild(element);
65  ErrorStack error_stack = save(element);
66  if (error_stack.is_error()) {
67  o << "Failed during Externalizable::save_to_stream(): " << error_stack;
68  return;
69  }
70  tinyxml2::XMLPrinter printer;
71  doc.Print(&printer);
72  o << printer.CStr();
73 }
74 
76  tinyxml2::XMLDocument document;
77  if (!fs::exists(path)) {
79  }
80 
81  tinyxml2::XMLError load_error = document.LoadFile(path.c_str());
82  if (load_error != tinyxml2::XML_SUCCESS) {
83  std::stringstream custom_message;
84  custom_message << "problemtic file=" << path << ", tinyxml2 error=" << load_error
85  << ", GetErrorStr1()=" << document.GetErrorStr1()
86  << ", GetErrorStr2()=" << document.GetErrorStr2();
87  return ERROR_STACK_MSG(kErrorCodeConfParseFailed, custom_message.str().c_str());
88  } else if (!document.RootElement()) {
90  } else {
91  CHECK_ERROR(load(document.RootElement()));
92  }
93  return kRetOk;
94 }
95 
97  // construct the XML in memory
98  tinyxml2::XMLDocument document;
99  tinyxml2::XMLElement* root = document.NewElement(get_tag_name());
100  CHECK_OUTOFMEMORY(root);
101  document.InsertFirstChild(root);
102  CHECK_ERROR(save(root));
103 
104  fs::Path folder = path.parent_path();
105  // create the folder if not exists
106  if (!fs::exists(folder)) {
107  if (!fs::create_directories(folder, true)) {
108  std::stringstream custom_message;
109  custom_message << "file=" << path << ", folder=" << folder
110  << ", err=" << assorted::os_error();
111  return ERROR_STACK_MSG(kErrorCodeConfMkdirsFailed, custom_message.str().c_str());
112  }
113  }
114 
115  // Retrieve an XML representation. We initially used document.SaveFile(), but
116  // it is not ideal as it uses non-aligned non-O_DIRECT APIs.
117  // This caused a data loss in our NVDIMM environment.
118  tinyxml2::XMLPrinter xml_stream;
119  document.Print(&xml_stream);
120  const uint32_t non_aligned_size = xml_stream.CStrSize();
121  const uint32_t aligned_size = assorted::align<uint32_t, 1U << 12>(non_aligned_size);
122  memory::AlignedMemory xml_memory;
123  xml_memory.alloc(aligned_size, 1U << 12, memory::AlignedMemory::kPosixMemalign, 0);
124  if (xml_memory.is_null()) {
125  return ERROR_STACK_MSG(kErrorCodeConfCouldNotWrite, "Out of memory in posix_memalign");
126  }
127  char* buffer = reinterpret_cast<char*>(xml_memory.get_block());
128  std::memcpy(buffer, xml_stream.CStr(), non_aligned_size);
129  std::memset(buffer + non_aligned_size, '\n', aligned_size - non_aligned_size); // LF padding
130 
131  // To atomically save a file, we write to a temporary file and call sync, then use POSIX rename.
132  fs::Path tmp_path(path);
133  tmp_path += ".tmp_";
134  tmp_path += fs::unique_name("%%%%%%%%");
135 
136  {
137  // Use Direct I/O to save this file. For all critical files, we always use
138  // open/write with O_DIRECT.
139  fs::DirectIoFile tmp_file(tmp_path);
140  WRAP_ERROR_CODE(tmp_file.open(false, true, true, true));
141  WRAP_ERROR_CODE(tmp_file.write(aligned_size, xml_memory));
142  tmp_file.close();
143  // TASK(Hideaki) We should not need the following fsync. Let's test this later.
144  // For now, do a safer thing.
145  fs::fsync(tmp_path, true);
146  }
147 
148  if (!fs::durable_atomic_rename(tmp_path, path)) {
149  std::stringstream custom_message;
150  custom_message << "dest file=" << path << ", src file=" << tmp_path
151  << ", err=" << assorted::os_error();
152  return ERROR_STACK_MSG(kErrorCodeConfCouldNotRename, custom_message.str().c_str());
153  }
154 
155  return kRetOk;
156 }
157 
158 
159 ErrorStack insert_comment_impl(tinyxml2::XMLElement* element, const std::string& comment) {
160  if (comment.size() > 0) {
161  tinyxml2::XMLComment* cm = element->GetDocument()->NewComment(comment.c_str());
162  CHECK_OUTOFMEMORY(cm);
163  tinyxml2::XMLNode* parent = element->Parent();
164  if (!parent) {
165  element->GetDocument()->InsertFirstChild(cm);
166  } else {
167  tinyxml2::XMLNode* previous = element->PreviousSibling();
168  if (previous) {
169  parent->InsertAfterChild(previous, cm);
170  } else {
171  parent->InsertFirstChild(cm);
172  }
173  }
174  }
175  return kRetOk;
176 }
177 ErrorStack Externalizable::insert_comment(tinyxml2::XMLElement* element,
178  const std::string& comment) {
179  return insert_comment_impl(element, comment);
180 }
181 ErrorStack Externalizable::append_comment(tinyxml2::XMLElement* parent,
182  const std::string& comment) {
183  if (comment.size() > 0) {
184  tinyxml2::XMLComment* cm = parent->GetDocument()->NewComment(comment.c_str());
185  CHECK_OUTOFMEMORY(cm);
186  parent->InsertEndChild(cm);
187  }
188  return kRetOk;
189 }
190 
191 ErrorStack Externalizable::create_element(tinyxml2::XMLElement* parent, const std::string& name,
192  tinyxml2::XMLElement** out) {
193  *out = parent->GetDocument()->NewElement(name.c_str());
194  CHECK_OUTOFMEMORY(*out);
195  parent->InsertEndChild(*out);
196  return kRetOk;
197 }
198 
199 template <typename T>
200 ErrorStack Externalizable::add_element(tinyxml2::XMLElement* parent,
201  const std::string& tag, const std::string& comment, T value) {
202  tinyxml2::XMLElement* element = parent->GetDocument()->NewElement(tag.c_str());
203  CHECK_OUTOFMEMORY(element);
204  TinyxmlSetter<T> tinyxml_setter;
205  tinyxml_setter(element, value);
206  parent->InsertEndChild(element);
207  if (comment.size() > 0) {
209  tag + " (type=" + assorted::get_pretty_type_name<T>() + "): " + comment));
210  }
211  return kRetOk;
212 }
213 
214 // Explicit instantiations for each type
215 // @cond DOXYGEN_IGNORE
216 #define EXPLICIT_INSTANTIATION_ADD(x) template ErrorStack Externalizable::add_element< x > \
217  (tinyxml2::XMLElement* parent, const std::string& tag, const std::string& comment, x value)
218 INSTANTIATE_ALL_TYPES(EXPLICIT_INSTANTIATION_ADD);
219 // @endcond
220 
221 ErrorStack Externalizable::add_child_element(tinyxml2::XMLElement* parent, const std::string& tag,
222  const std::string& comment, const Externalizable& child) {
223  tinyxml2::XMLElement* element = parent->GetDocument()->NewElement(tag.c_str());
224  CHECK_OUTOFMEMORY(element);
225  parent->InsertEndChild(element);
226  CHECK_ERROR(insert_comment_impl(element, comment));
227  CHECK_ERROR(child.save(element));
228  return kRetOk;
229 }
230 
231 template <typename T>
232 ErrorStack Externalizable::get_element(tinyxml2::XMLElement* parent, const std::string& tag,
233  T* out, bool optional, T default_value) {
234  TinyxmlGetter<T> tinyxml_getter;
235  tinyxml2::XMLElement* element = parent->FirstChildElement(tag.c_str());
236  if (element) {
237  tinyxml2::XMLError xml_error = tinyxml_getter(element, out);
238  if (xml_error == tinyxml2::XML_SUCCESS) {
239  return kRetOk;
240  } else {
241  return ERROR_STACK_MSG(kErrorCodeConfInvalidElement, tag.c_str());
242  }
243  } else {
244  if (optional) {
245  *out = default_value;
246  return kRetOk;
247  } else {
248  return ERROR_STACK_MSG(kErrorCodeConfMissingElement, tag.c_str());
249  }
250  }
251 }
252 
253 // Explicit instantiations for each type
254 // @cond DOXYGEN_IGNORE
255 #define EXPLICIT_INSTANTIATION_GET(x) template ErrorStack Externalizable::get_element< x > \
256  (tinyxml2::XMLElement* parent, const std::string& tag, x * out, bool optional, x default_value)
257 INSTANTIATE_ALL_TYPES(EXPLICIT_INSTANTIATION_GET);
258 // @endcond
259 
260 ErrorStack Externalizable::get_element(tinyxml2::XMLElement* parent, const std::string& tag,
261  std::string* out, bool optional, const char* default_value) {
262  return get_element<std::string>(parent, tag, out, optional, std::string(default_value));
263 }
264 
265 template <typename T>
266 ErrorStack Externalizable::get_element(tinyxml2::XMLElement* parent, const std::string& tag,
267  std::vector<T> * out, bool optional) {
268  out->clear();
269  TinyxmlGetter<T> tinyxml_getter;
270  for (tinyxml2::XMLElement* element = parent->FirstChildElement(tag.c_str());
271  element; element = element->NextSiblingElement(tag.c_str())) {
272  T tmp;
273  tinyxml2::XMLError xml_error = tinyxml_getter(element, &tmp);
274  if (xml_error == tinyxml2::XML_SUCCESS) {
275  out->push_back(tmp); // vector<bool> doesn't support emplace_back!
276  } else {
277  return ERROR_STACK_MSG(kErrorCodeConfInvalidElement, tag.c_str());
278  }
279  }
280  if (out->size() == 0 && !optional) {
281  return ERROR_STACK_MSG(kErrorCodeConfMissingElement, tag.c_str());
282  }
283  return kRetOk;
284 }
285 
286 // Explicit instantiations for each type
287 // @cond DOXYGEN_IGNORE
288 #define EXPLICIT_INSTANTIATION_GET_VECTOR(x) template ErrorStack Externalizable::get_element< x > \
289  (tinyxml2::XMLElement* parent, const std::string& tag, std::vector< x > * out, bool optional)
290 INSTANTIATE_ALL_TYPES(EXPLICIT_INSTANTIATION_GET_VECTOR);
291 // @endcond
292 
293 ErrorStack Externalizable::get_child_element(tinyxml2::XMLElement* parent, const std::string& tag,
294  Externalizable* child, bool optional) {
295  tinyxml2::XMLElement* element = parent->FirstChildElement(tag.c_str());
296  if (element) {
297  return child->load(element);
298  } else {
299  if (optional) {
300  return kRetOk;
301  } else {
302  return ERROR_STACK_MSG(kErrorCodeConfMissingElement, tag.c_str());
303  }
304  }
305 }
306 
307 } // namespace externalize
308 } // namespace foedus
Functor to help use tinyxml2's Element SetText().
ErrorStack load_from_string(const std::string &xml)
Load the content of this object from the given XML string.
void save_to_stream(std::ostream *ptr) const
Invokes save() and directs the resulting XML text to the given stream.
ErrorStack insert_comment_impl(tinyxml2::XMLElement *element, const std::string &comment)
static ErrorStack get_child_element(tinyxml2::XMLElement *parent, const std::string &tag, Externalizable *child, bool optional=false)
child Externalizable version
Represents an object that can be written to and read from files/bytes in XML format.
ErrorCode write(uint64_t desired_bytes, const foedus::memory::AlignedMemory &buffer)
Sequentially write the given amount of contents from the current position.
Root package of FOEDUS (Fast Optimistic Engine for Data Unification Services).
Definition: assert_nd.hpp:44
static ErrorStack add_child_element(tinyxml2::XMLElement *parent, const std::string &tag, const std::string &comment, const Externalizable &child)
child Externalizable version
0x0408 : "CONFIG : Failed to atomically rename the temporary file to destination." ...
Definition: error_code.hpp:154
bool close()
Close the file if not yet closed.
static ErrorStack append_comment(tinyxml2::XMLElement *parent, const std::string &comment)
Brings error stacktrace information as return value of functions.
Definition: error_stack.hpp:81
ErrorStack save_to_file(const fs::Path &path) const
Atomically and durably writes out this object to the specified XML file.
void alloc(uint64_t size, uint64_t alignment, AllocType alloc_type, int numa_node) noexcept
Allocate a memory, releasing the current memory if exists.
ErrorCode open(bool read, bool write, bool append, bool create)
Tries to open the file for the specified volume.
static ErrorStack insert_comment(tinyxml2::XMLElement *element, const std::string &comment)
#define CHECK_OUTOFMEMORY(ptr)
This macro checks if ptr is nullptr, and if so exists with kErrorCodeOutofmemory error stack...
#define INSTANTIATE_ALL_TYPES(M)
A macro to explicitly instantiate the given template for all types we care.
0x0409 : "CONFIG : Failed to create a directory. Check permissions." .
Definition: error_code.hpp:155
0x0406 : "CONFIG : The file doesn't exist." .
Definition: error_code.hpp:152
0x0404 : "CONFIG : An invalid valud in the xml element." .
Definition: error_code.hpp:150
0x0401 : "CONFIG : Config file parsing failed. This usually means an invalid XML file." .
Definition: error_code.hpp:147
Functor to help use tinyxml2's Element QueryXxxText().
0x0407 : "CONFIG : Failed to write a configuration file." .
Definition: error_code.hpp:153
Analogue of boost::filesystem::path.
Definition: path.hpp:37
0x0403 : "CONFIG : The xml element is missing in the config file." .
Definition: error_code.hpp:149
static ErrorStack create_element(tinyxml2::XMLElement *parent, const std::string &name, tinyxml2::XMLElement **out)
bool durable_atomic_rename(const Path &old_path, const Path &new_path)
fsync() on source file before rename, then fsync() on the parent folder after rename.
Definition: filesystem.cpp:236
bool create_directories(const Path &p, bool sync=false)
Recursive mkdir (mkdirs).
Definition: filesystem.cpp:89
bool exists(const Path &p)
Returns if the file exists.
Definition: filesystem.hpp:128
virtual const char * get_tag_name() const =0
Returns an XML tag name for this object as a root element.
Path parent_path() const
Definition: path.cpp:55
const char * c_str() const
Definition: path.hpp:64
std::string os_error()
Thread-safe strerror(errno).
virtual ErrorStack save(tinyxml2::XMLElement *element) const =0
Writes the content of this object to the given XML element.
#define CHECK_ERROR(x)
This macro calls x and checks its returned value.
Represents an I/O stream on one file without filesystem caching.
Represents one memory block aligned to actual OS/hardware pages.
std::string unique_name(uint64_t differentiator=0)
Equivalent to unique_path("%%%%-%%%%-%%%%-%%%%").
Definition: filesystem.cpp:174
ErrorStack load_from_file(const fs::Path &path)
Load the content of this object from the specified XML file.
const ErrorStack kRetOk
Normal return value for no-error case.
0x0405 : "CONFIG : No root element in the xml." .
Definition: error_code.hpp:151
static ErrorStack get_element(tinyxml2::XMLElement *parent, const std::string &tag, T *out, bool optional=false, T value=0)
Only declaration in header.
#define ERROR_STACK_MSG(e, m)
Overload of ERROR_STACK(e) to receive a custom error message.
virtual ErrorStack load(tinyxml2::XMLElement *element)=0
Reads the content of this object from the given XML element.
#define WRAP_ERROR_CODE(x)
Same as CHECK_ERROR(x) except it receives only an error code, thus more efficient.
bool fsync(const Path &path, bool sync_parent_directory=false)
Makes the content and metadata of the file durable all the way up to devices.
Definition: filesystem.cpp:203
static ErrorStack add_element(tinyxml2::XMLElement *parent, const std::string &tag, const std::string &comment, T value)
Only declaration in header.
bool is_error() const
Returns if this return code is not kErrorCodeOk.