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 | |
42 | using namespace Hdfs::Internal; |
43 | |
44 | namespace Hdfs { |
45 | |
46 | typedef std::map<std::string, std::string>::const_iterator Iterator; |
47 | typedef std::map<std::string, std::string> Map; |
48 | |
49 | static 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 | |
67 | static 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 | |
85 | static 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 | |
99 | static 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 | |
117 | static 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 | |
157 | static 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 | |
183 | Config::Config(const char * p) : |
184 | path(p) { |
185 | update(p); |
186 | } |
187 | |
188 | void 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 | |
218 | const 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 | |
228 | const 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 | |
238 | const char * Config::getString(const std::string & key) const { |
239 | return getString(key.c_str()); |
240 | } |
241 | |
242 | const char * Config::getString(const std::string & key, |
243 | const std::string & def) const { |
244 | return getString(key.c_str(), def.c_str()); |
245 | } |
246 | |
247 | int64_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 | |
264 | int64_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 | |
281 | int32_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 | |
298 | int32_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 | |
315 | double 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 | |
332 | double 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 | |
349 | bool 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 | |
366 | bool 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 | |
383 | size_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 | |