1/********************************************************************
2 * Copyright (c) 2013 - 2014, Pivotal Inc.
3 * All rights reserved.
4 *
5 * Author: Zhanwei Wang
6 ********************************************************************/
7/********************************************************************
8 * 2014 -
9 * open source under Apache License Version 2.0
10 ********************************************************************/
11/**
12 * Licensed to the Apache Software Foundation (ASF) under one
13 * or more contributor license agreements. See the NOTICE file
14 * distributed with this work for additional information
15 * regarding copyright ownership. The ASF licenses this file
16 * to you under the Apache License, Version 2.0 (the
17 * "License"); you may not use this file except in compliance
18 * with the License. You may obtain a copy of the License at
19 *
20 * http://www.apache.org/licenses/LICENSE-2.0
21 *
22 * Unless required by applicable law or agreed to in writing, software
23 * distributed under the License is distributed on an "AS IS" BASIS,
24 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
25 * See the License for the specific language governing permissions and
26 * limitations under the License.
27 */
28#include "Exception.h"
29#include "ExceptionInternal.h"
30#include "XmlConfig.h"
31#include "Hash.h"
32
33#include <cassert>
34#include <errno.h>
35#include <fstream>
36#include <libxml/parser.h>
37#include <libxml/tree.h>
38#include <limits>
39#include <string.h>
40#include <unistd.h>
41
42using namespace Hdfs::Internal;
43
44namespace Hdfs {
45
46typedef std::map<std::string, std::string>::const_iterator Iterator;
47typedef std::map<std::string, std::string> Map;
48
49static int32_t StrToInt32(const char * str) {
50 long retval;
51 char * end = NULL;
52 errno = 0;
53 retval = strtol(str, &end, 0);
54
55 if (EINVAL == errno || 0 != *end) {
56 THROW(HdfsBadNumFoumat, "Invalid int32_t type: %s", str);
57 }
58
59 if (ERANGE == errno || retval > std::numeric_limits<int32_t>::max()
60 || retval < std::numeric_limits<int32_t>::min()) {
61 THROW(HdfsBadNumFoumat, "Underflow/Overflow int32_t type: %s", str);
62 }
63
64 return retval;
65}
66
67static int64_t StrToInt64(const char * str) {
68 long long retval;
69 char * end = NULL;
70 errno = 0;
71 retval = strtoll(str, &end, 0);
72
73 if (EINVAL == errno || 0 != *end) {
74 THROW(HdfsBadNumFoumat, "Invalid int64_t type: %s", str);
75 }
76
77 if (ERANGE == errno || retval > std::numeric_limits<int64_t>::max()
78 || retval < std::numeric_limits<int64_t>::min()) {
79 THROW(HdfsBadNumFoumat, "Underflow/Overflow int64_t type: %s", str);
80 }
81
82 return retval;
83}
84
85static bool StrToBool(const char * str) {
86 bool retval = false;
87
88 if (!strcasecmp(str, "true") || !strcmp(str, "1")) {
89 retval = true;
90 } else if (!strcasecmp(str, "false") || !strcmp(str, "0")) {
91 retval = false;
92 } else {
93 THROW(HdfsBadBoolFoumat, "Invalid bool type: %s", str);
94 }
95
96 return retval;
97}
98
99static double StrToDouble(const char * str) {
100 double retval;
101 char * end = NULL;
102 errno = 0;
103 retval = strtod(str, &end);
104
105 if (EINVAL == errno || 0 != *end) {
106 THROW(HdfsBadNumFoumat, "Invalid double type: %s", str);
107 }
108
109 if (ERANGE == errno || retval > std::numeric_limits<double>::max()
110 || retval < std::numeric_limits<double>::min()) {
111 THROW(HdfsBadNumFoumat, "Underflow/Overflow int64_t type: %s", str);
112 }
113
114 return retval;
115}
116
117static void readConfigItem(xmlNodePtr root, Map & kv, const char * path) {
118 std::string key, value;
119 xmlNodePtr curNode;
120 bool hasname = false, hasvalue = false;
121
122 for (curNode = root; NULL != curNode; curNode = curNode->next) {
123 if (curNode->type != XML_ELEMENT_NODE) {
124 continue;
125 }
126
127 if (!hasname && !strcmp((const char *) curNode->name, "name")) {
128 if (NULL != curNode->children
129 && XML_TEXT_NODE == curNode->children->type) {
130 key = (const char *) curNode->children->content;
131 hasname = true;
132 }
133 } else if (!hasvalue
134 && !strcmp((const char *) curNode->name, "value")) {
135 if (NULL != curNode->children
136 && XML_TEXT_NODE == curNode->children->type) {
137 value = (const char *) curNode->children->content;
138 hasvalue = true;
139 }
140 } else {
141 continue;
142 }
143 }
144
145 if (hasname && hasvalue) {
146 kv[key] = value;
147 return;
148 } else if (hasname) {
149 kv[key] = "";
150 return;
151 }
152
153 THROW(HdfsBadConfigFoumat, "Config cannot parse configure file: \"%s\"",
154 path);
155}
156
157static void readConfigItems(xmlDocPtr doc, Map & kv, const char * path) {
158 xmlNodePtr root, curNode;
159 root = xmlDocGetRootElement(doc);
160
161 if (NULL == root || strcmp((const char *) root->name, "configuration")) {
162 THROW(HdfsBadConfigFoumat, "Config cannot parse configure file: \"%s\"",
163 path);
164 }
165
166 /*
167 * for each property
168 */
169 for (curNode = root->children; NULL != curNode; curNode = curNode->next) {
170 if (curNode->type != XML_ELEMENT_NODE) {
171 continue;
172 }
173
174 if (strcmp((const char *) curNode->name, "property")) {
175 THROW(HdfsBadConfigFoumat,
176 "Config cannot parse configure file: \"%s\"", path);
177 }
178
179 readConfigItem(curNode->children, kv, path);
180 }
181}
182
183Config::Config(const char * p) :
184 path(p) {
185 update(p);
186}
187
188void Config::update(const char * p) {
189 xmlDocPtr doc; /* the resulting document tree */
190 LIBXML_TEST_VERSION
191 kv.clear();
192 path = p;
193
194 if (access(path.c_str(), R_OK)) {
195 THROW(HdfsBadConfigFoumat, "Cannot read configure file: \"%s\", %s",
196 path.c_str(), GetSystemErrorInfo(errno));
197 }
198
199 /* parse the file */
200 doc = xmlReadFile(path.c_str(), NULL, 0);
201
202 try {
203 /* check if parsing succeeded */
204 if (doc == NULL) {
205 THROW(HdfsBadConfigFoumat,
206 "Config cannot parse configure file: \"%s\"", path.c_str());
207 } else {
208 readConfigItems(doc, kv, path.c_str());
209 /* free up the resulting document */
210 xmlFreeDoc(doc);
211 }
212 } catch (...) {
213 xmlFreeDoc(doc);
214 throw;
215 }
216}
217
218const char * Config::getString(const char * key) const {
219 Iterator it = kv.find(key);
220
221 if (kv.end() == it) {
222 THROW(HdfsConfigNotFound, "Config key: %s not found", key);
223 }
224
225 return it->second.c_str();
226}
227
228const char * Config::getString(const char * key, const char * def) const {
229 Iterator it = kv.find(key);
230
231 if (kv.end() == it) {
232 return def;
233 } else {
234 return it->second.c_str();
235 }
236}
237
238const char * Config::getString(const std::string & key) const {
239 return getString(key.c_str());
240}
241
242const char * Config::getString(const std::string & key,
243 const std::string & def) const {
244 return getString(key.c_str(), def.c_str());
245}
246
247int64_t Config::getInt64(const char * key) const {
248 int64_t retval;
249 Iterator it = kv.find(key);
250
251 if (kv.end() == it) {
252 THROW(HdfsConfigNotFound, "Config key: %s not found", key);
253 }
254
255 try {
256 retval = StrToInt64(it->second.c_str());
257 } catch (const HdfsBadNumFoumat & e) {
258 NESTED_THROW(HdfsConfigNotFound, "Config key: %s not found", key);
259 }
260
261 return retval;
262}
263
264int64_t Config::getInt64(const char * key, int64_t def) const {
265 int64_t retval;
266 Iterator it = kv.find(key);
267
268 if (kv.end() == it) {
269 return def;
270 }
271
272 try {
273 retval = StrToInt64(it->second.c_str());
274 } catch (const HdfsBadNumFoumat & e) {
275 NESTED_THROW(HdfsConfigNotFound, "Config key: %s not found", key);
276 }
277
278 return retval;
279}
280
281int32_t Config::getInt32(const char * key) const {
282 int32_t retval;
283 Iterator it = kv.find(key);
284
285 if (kv.end() == it) {
286 THROW(HdfsConfigNotFound, "Config key: %s not found", key);
287 }
288
289 try {
290 retval = StrToInt32(it->second.c_str());
291 } catch (const HdfsBadNumFoumat & e) {
292 NESTED_THROW(HdfsConfigNotFound, "Config key: %s not found", key);
293 }
294
295 return retval;
296}
297
298int32_t Config::getInt32(const char * key, int32_t def) const {
299 int32_t retval;
300 Iterator it = kv.find(key);
301
302 if (kv.end() == it) {
303 return def;
304 }
305
306 try {
307 retval = StrToInt32(it->second.c_str());
308 } catch (const HdfsBadNumFoumat & e) {
309 NESTED_THROW(HdfsConfigNotFound, "Config key: %s not found", key);
310 }
311
312 return retval;
313}
314
315double Config::getDouble(const char * key) const {
316 double retval;
317 Iterator it = kv.find(key);
318
319 if (kv.end() == it) {
320 THROW(HdfsConfigNotFound, "Config key: %s not found", key);
321 }
322
323 try {
324 retval = StrToDouble(it->second.c_str());
325 } catch (const HdfsBadNumFoumat & e) {
326 NESTED_THROW(HdfsConfigNotFound, "Config key: %s not found", key);
327 }
328
329 return retval;
330}
331
332double Config::getDouble(const char * key, double def) const {
333 double retval;
334 Iterator it = kv.find(key);
335
336 if (kv.end() == it) {
337 return def;
338 }
339
340 try {
341 retval = StrToDouble(it->second.c_str());
342 } catch (const HdfsBadNumFoumat & e) {
343 NESTED_THROW(HdfsConfigNotFound, "Config key: %s not found", key);
344 }
345
346 return retval;
347}
348
349bool Config::getBool(const char * key) const {
350 bool retval;
351 Iterator it = kv.find(key);
352
353 if (kv.end() == it) {
354 THROW(HdfsConfigNotFound, "Config key: %s not found", key);
355 }
356
357 try {
358 retval = StrToBool(it->second.c_str());
359 } catch (const HdfsBadBoolFoumat & e) {
360 NESTED_THROW(HdfsConfigNotFound, "Config key: %s not found", key);
361 }
362
363 return retval;
364}
365
366bool Config::getBool(const char * key, bool def) const {
367 bool retval;
368 Iterator it = kv.find(key);
369
370 if (kv.end() == it) {
371 return def;
372 }
373
374 try {
375 retval = StrToBool(it->second.c_str());
376 } catch (const HdfsBadNumFoumat & e) {
377 NESTED_THROW(HdfsConfigNotFound, "Config key: %s not found", key);
378 }
379
380 return retval;
381}
382
383size_t Config::hash_value() const {
384 std::vector<size_t> values;
385 std::map<std::string, std::string>::const_iterator s, e;
386 e = kv.end();
387
388 for (s = kv.begin(); s != e; ++s) {
389 values.push_back(StringHasher(s->first));
390 values.push_back(StringHasher(s->second));
391 }
392
393 return CombineHasher(&values[0], values.size());
394}
395
396}
397
398