IMPALA-3223: Relocate squeasel and mustache directories This change moves the source and header files of squeasel and mustache to be/src/thirdparty. This is a step towards removing thirdparty as a preparation to move to ASF.
There is also corresponding change to Impala-lzo to update its include path. Change-Id: I782e493bc28086a1587274b3c474ea6b6f201855 Reviewed-on: http://gerrit.cloudera.org:8080/3206 Reviewed-by: Michael Ho <[email protected]> Tested-by: Michael Ho <[email protected]> Project: http://git-wip-us.apache.org/repos/asf/incubator-impala/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-impala/commit/0b7ae6e4 Tree: http://git-wip-us.apache.org/repos/asf/incubator-impala/tree/0b7ae6e4 Diff: http://git-wip-us.apache.org/repos/asf/incubator-impala/diff/0b7ae6e4 Branch: refs/heads/master Commit: 0b7ae6e4eb4e363d6d67ff98f1fe78d7f73a6fce Parents: a485d44 Author: Michael Ho <[email protected]> Authored: Wed May 25 18:26:14 2016 -0700 Committer: Tim Armstrong <[email protected]> Committed: Tue May 31 23:31:41 2016 -0700 ---------------------------------------------------------------------- CMakeLists.txt | 8 - be/src/thirdparty/mustache/README | 3 + be/src/thirdparty/mustache/mustache.cc | 366 ++ be/src/thirdparty/mustache/mustache.h | 27 + be/src/thirdparty/squeasel/LICENSE | 19 + be/src/thirdparty/squeasel/squeasel.c | 4914 +++++++++++++++++++++++++++ be/src/thirdparty/squeasel/squeasel.h | 432 +++ be/src/util/CMakeLists.txt | 3 + be/src/util/webserver.cc | 8 +- be/src/util/webserver.h | 6 +- cmake_modules/FindMustache.cmake | 26 - cmake_modules/FindSqueasel.cmake | 25 - 12 files changed, 5771 insertions(+), 66 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/0b7ae6e4/CMakeLists.txt ---------------------------------------------------------------------- diff --git a/CMakeLists.txt b/CMakeLists.txt index 6451371..dc0ec57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -191,14 +191,6 @@ else() set(HDFS_LIB HDFS_STATIC) endif() -# find the Squeasel webserver library -find_package(Squeasel REQUIRED) -include_directories(${SQUEASEL_INCLUDE_DIR}) - -# find the Mustache template library -find_package(Mustache REQUIRED) -include_directories(${MUSTACHE_INCLUDE_DIR}) - # find GLog headers and libs. Must include glog headers before the other # google libraries. They all have a config.h and we want glog's to be picked # up first. http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/0b7ae6e4/be/src/thirdparty/mustache/README ---------------------------------------------------------------------- diff --git a/be/src/thirdparty/mustache/README b/be/src/thirdparty/mustache/README new file mode 100644 index 0000000..9834079 --- /dev/null +++ b/be/src/thirdparty/mustache/README @@ -0,0 +1,3 @@ +This Mustache template library is taken from the Apache licensed version at https://github.com/henryr/cpp-mustache + +The current commit is: 80cbe5 http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/0b7ae6e4/be/src/thirdparty/mustache/mustache.cc ---------------------------------------------------------------------- diff --git a/be/src/thirdparty/mustache/mustache.cc b/be/src/thirdparty/mustache/mustache.cc new file mode 100644 index 0000000..59aeafc --- /dev/null +++ b/be/src/thirdparty/mustache/mustache.cc @@ -0,0 +1,366 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "mustache.h" + +#include "rapidjson/stringbuffer.h" +#include "rapidjson/writer.h" + +#include <iostream> +#include <fstream> +#include <vector> + +#include <boost/algorithm/string.hpp> +#include <boost/foreach.hpp> + +using namespace rapidjson; +using namespace std; +using namespace boost::algorithm; + +namespace mustache { + +// TODO: +// # Handle malformed templates better +// # Support array_tag.length? +// # Better support for reading templates from files + +enum TagOperator { + SUBSTITUTION, + SECTION_START, + NEGATED_SECTION_START, + PREDICATE_SECTION_START, + SECTION_END, + PARTIAL, + COMMENT, + LENGTH, + NONE +}; + +TagOperator GetOperator(const string& tag) { + if (tag.size() == 0) return SUBSTITUTION; + switch (tag[0]) { + case '#': return SECTION_START; + case '^': return NEGATED_SECTION_START; + case '?': return PREDICATE_SECTION_START; + case '/': return SECTION_END; + case '>': return PARTIAL; + case '!': return COMMENT; + case '%': return LENGTH; + default: return SUBSTITUTION; + } +} + +int EvaluateTag(const string& document, const string& document_root, int idx, + const Value* context, TagOperator tag, const string& tag_name, bool is_triple, + stringstream* out); + +void EscapeHtml(const string& in, stringstream *out) { + BOOST_FOREACH(const char& c, in) { + switch (c) { + case '&': (*out) << "&"; + break; + case '"': (*out) << """; + break; + case '\'': (*out) << "'"; + break; + case '<': (*out) << "<"; + break; + case '>': (*out) << ">"; + break; + default: (*out) << c; + break; + } + } +} + +// Breaks a dotted path into individual components. One wrinkle, which stops this from +// being a simple split() is that we allow path components to be quoted, e.g.: "foo".bar, +// and any '.' characters inside those quoted sections aren't considered to be +// delimiters. This is to allow Json keys that contain periods. +void FindJsonPathComponents(const string& path, vector<string>* components) { + bool in_quote = false; + bool escape_this_char = false; + int start = 0; + for (int i = start; i < path.size(); ++i) { + if (path[i] == '"' && !escape_this_char) in_quote = !in_quote; + if (path[i] == '.' && !escape_this_char && !in_quote) { + // Current char == delimiter and not escaped and not in a quote pair => found a + // component + if (i - start > 0) { + if (path[start] == '"' && path[(i - 1) - start] == '"') { + if (i - start > 3) { + components->push_back(path.substr(start + 1, i - (start + 2))); + } + } else { + components->push_back(path.substr(start, i - start)); + } + start = i + 1; + } + } + + escape_this_char = (path[i] == '\\' && !escape_this_char); + } + + if (path.size() - start > 0) { + if (path[start] == '"' && path[(path.size() - 1) - start] == '"') { + if (path.size() - start > 3) { + components->push_back(path.substr(start + 1, path.size() - (start + 2))); + } + } else { + components->push_back(path.substr(start, path.size() - start)); + } + } +} + +// Looks up the json entity at 'path' in 'parent_context', and places it in 'resolved'. If +// the entity does not exist (i.e. the path is invalid), 'resolved' will be set to NULL. +void ResolveJsonContext(const string& path, const Value& parent_context, + const Value** resolved) { + if (path == ".") { + *resolved = &parent_context; + return; + } + vector<string> components; + FindJsonPathComponents(path, &components); + const Value* cur = &parent_context; + BOOST_FOREACH(const string& c, components) { + if (cur->IsObject() && cur->HasMember(c.c_str())) { + cur = &(*cur)[c.c_str()]; + } else { + *resolved = NULL; + return; + } + } + + *resolved = cur; +} + +int FindNextTag(const string& document, int idx, TagOperator* tag_op, string* tag_name, + bool* is_triple, stringstream* out) { + *tag_op = NONE; + while (idx < document.size()) { + if (document[idx] == '{' && idx < (document.size() - 3) && document[idx + 1] == '{') { + if (document[idx + 2] == '{') { + idx += 3; + *is_triple = true; + } else { + *is_triple = false; + idx += 2; // Now at start of template expression + } + stringstream expr; + while (idx < document.size()) { + if (document[idx] != '}') { + expr << document[idx]; + ++idx; + } else { + if (!*is_triple && idx < document.size() - 1 && document[idx + 1] == '}') { + ++idx; + break; + } else if (*is_triple && idx < document.size() - 2 && document[idx + 1] == '}' + && document[idx + 2] == '}') { + idx += 2; + break; + } else { + expr << '}'; + } + } + } + + string key = expr.str(); + trim(key); + if (key != ".") trim_if(key, is_any_of(".")); + if (key.size() == 0) continue; + *tag_op = GetOperator(key); + if (*tag_op != SUBSTITUTION) { + key = key.substr(1); + trim(key); + } + if (key.size() == 0) continue; + *tag_name = key; + return ++idx; + } else { + if (out != NULL) (*out) << document[idx]; + } + ++idx; + } + return idx; +} + +// Evaluates a [PREDICATE_|NEGATED_]SECTION_START / SECTION_END pair by evaluating the tag +// in 'parent_context'. False or non-existant values cause the entire section to be +// skipped. True values cause the section to be evaluated as though it were a normal +// section, but with the parent context being the root context for that section. +// +// If 'is_negation' is true, the behaviour is the opposite of the above: false values +// cause the section to be normally evaluated etc. +int EvaluateSection(const string& document, const string& document_root, int idx, + const Value* parent_context, TagOperator op, const string& tag_name, + stringstream* out) { + // Precondition: idx is the immedate next character after an opening {{ #tag_name }} + const Value* context; + ResolveJsonContext(tag_name, *parent_context, &context); + + // If we a) cannot resolve the context from the tag name or b) the context evaluates to + // false, we should skip the contents of the template until a closing {{/tag_name}}. + bool skip_contents = (context == NULL || context->IsFalse()); + + // If the tag is a negative block (i.e. {{^tag_name}}), do the opposite: if the context + // exists and is true, skip the contents, else echo them. + if (op == NEGATED_SECTION_START) { + context = parent_context; + skip_contents = !skip_contents; + } else if (op == PREDICATE_SECTION_START) { + context = parent_context; + } + + vector<const Value*> values; + if (!skip_contents && context != NULL && context->IsArray()) { + for (int i = 0; i < context->Size(); ++i) { + values.push_back(&(*context)[i]); + } + } else { + values.push_back(skip_contents ? NULL : context); + } + if (values.size() == 0) { + skip_contents = true; + values.push_back(NULL); + } + + int start_idx = idx; + BOOST_FOREACH(const Value* v, values) { + idx = start_idx; + while (idx < document.size()) { + TagOperator tag_op; + string next_tag_name; + bool is_triple; + idx = FindNextTag(document, idx, &tag_op, &next_tag_name, &is_triple, + skip_contents ? NULL : out); + + if (idx > document.size()) return idx; + if (tag_op == SECTION_END && next_tag_name == tag_name) { + break; + } + + // Don't need to evaluate any templates if we're skipping the contents + if (!skip_contents) { + idx = EvaluateTag(document, document_root, idx, v, tag_op, next_tag_name, + is_triple, out); + } + } + } + return idx; +} + +// Evaluates a SUBSTITUTION tag, by replacing its contents with the value of the tag's +// name in 'parent_context'. +int EvaluateSubstitution(const string& document, const int idx, + const Value* parent_context, const string& tag_name, bool is_triple, + stringstream* out) { + const Value* context; + ResolveJsonContext(tag_name, *parent_context, &context); + if (context == NULL) return idx; + if (context->IsString()) { + if (!is_triple) { + EscapeHtml(context->GetString(), out); + } else { + // TODO: Triple {{{ means don't escape + (*out) << context->GetString(); + } + } else if (context->IsInt()) { + (*out) << context->GetInt(); + } else if (context->IsDouble()) { + (*out) << context->GetDouble(); + } else if (context->IsBool()) { + (*out) << boolalpha << context->GetBool(); + } + return idx; +} + +// Evaluates a LENGTH tag by replacing its contents with the type-dependent 'size' of the +// value. +int EvaluateLength(const string& document, const int idx, const Value* parent_context, + const string& tag_name, stringstream* out) { + const Value* context; + ResolveJsonContext(tag_name, *parent_context, &context); + if (context == NULL) return idx; + if (context->IsArray()) { + (*out) << context->Size(); + } else if (context->IsString()) { + (*out) << context->GetStringLength(); + }; + + return idx; +} + +// Evaluates a 'partial' template by reading it fully from disk, then rendering it +// directly into the current output with the current context. +// +// TODO: This could obviously be more efficient (and there are lots of file accesses in a +// long list context). +void EvaluatePartial(const string& tag_name, const string& document_root, + const Value* parent_context, stringstream* out) { + stringstream ss; + ss << document_root << tag_name; + ifstream tmpl(ss.str().c_str()); + if (!tmpl.is_open()) { + ss << ".mustache"; + tmpl.open(ss.str().c_str()); + if (!tmpl.is_open()) return; + } + stringstream file_ss; + file_ss << tmpl.rdbuf(); + RenderTemplate(file_ss.str(), document_root, *parent_context, out); +} + +// Given a tag name, and its operator, evaluate the tag in the given context and write the +// output to 'out'. The heavy-lifting is delegated to specific Evaluate*() +// methods. Returns the new cursor position within 'document', or -1 on error. +int EvaluateTag(const string& document, const string& document_root, int idx, + const Value* context, TagOperator tag, + const string& tag_name, bool is_triple, stringstream* out) { + if (idx == -1) return idx; + switch (tag) { + case SECTION_START: + case PREDICATE_SECTION_START: + case NEGATED_SECTION_START: + return EvaluateSection(document, document_root, idx, context, tag, tag_name, out); + case SUBSTITUTION: + return EvaluateSubstitution(document, idx, context, tag_name, is_triple, out); + case COMMENT: + return idx; // Ignored + case PARTIAL: + EvaluatePartial(tag_name, document_root, context, out); + return idx; + case LENGTH: + return EvaluateLength(document, idx, context, tag_name, out); + case NONE: + return idx; // No tag was found + default: + cout << "Unknown tag: " << tag << endl; + return -1; + } +} + +void RenderTemplate(const string& document, const string& document_root, + const Value& context, stringstream* out) { + int idx = 0; + while (idx < document.size() && idx != -1) { + string tag_name; + TagOperator tag_op; + bool is_triple; + idx = FindNextTag(document, idx, &tag_op, &tag_name, &is_triple, out); + idx = EvaluateTag(document, document_root, idx, &context, tag_op, tag_name, is_triple, + out); + } +} + +} http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/0b7ae6e4/be/src/thirdparty/mustache/mustache.h ---------------------------------------------------------------------- diff --git a/be/src/thirdparty/mustache/mustache.h b/be/src/thirdparty/mustache/mustache.h new file mode 100644 index 0000000..e4168d3 --- /dev/null +++ b/be/src/thirdparty/mustache/mustache.h @@ -0,0 +1,27 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "rapidjson/document.h" +#include <sstream> + +// Routines for rendering Mustache (http://mustache.github.io) templates with RapidJson +// (https://code.google.com/p/rapidjson/) documents. +namespace mustache { + +// Render a template contained in 'document' with respect to the json context +// 'context'. Alternately finds a tag and then evaluates it. Returns when an error is +// signalled (TODO: probably doesn't work in all paths), and evaluates that tag. Output is +// accumulated in 'out'. +void RenderTemplate(const std::string& document, const std::string& document_root, + const rapidjson::Value& context, std::stringstream* out); + +} http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/0b7ae6e4/be/src/thirdparty/squeasel/LICENSE ---------------------------------------------------------------------- diff --git a/be/src/thirdparty/squeasel/LICENSE b/be/src/thirdparty/squeasel/LICENSE new file mode 100644 index 0000000..edb1983 --- /dev/null +++ b/be/src/thirdparty/squeasel/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2013 Sergey Lyubka + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE.
