[1/8] qpid-dispatch git commit: Add token parser

classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

[1/8] qpid-dispatch git commit: Add token parser

kgiusti
Repository: qpid-dispatch
Updated Branches:
  refs/heads/master 4dd8764b3 -> 043c5ccf8


Add token parser


Project: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/commit/f1735eee
Tree: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/tree/f1735eee
Diff: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/diff/f1735eee

Branch: refs/heads/master
Commit: f1735eee37a498d62b14a27788a6b61755ad4737
Parents: 41fc945
Author: Kenneth Giusti <[hidden email]>
Authored: Mon Jul 3 14:52:46 2017 -0400
Committer: Kenneth Giusti <[hidden email]>
Committed: Mon Jul 3 14:52:46 2017 -0400

----------------------------------------------------------------------
 src/CMakeLists.txt |   1 +
 src/parse_tree.c   | 524 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/parse_tree.h   |  49 +++++
 3 files changed, 574 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/f1735eee/src/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 10cd7c6..a672f13 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -68,6 +68,7 @@ set(qpid_dispatch_SOURCES
   log.c
   message.c
   parse.c
+  parse_tree.c
   policy.c
   posix/threading.c
   python_embedded.c

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/f1735eee/src/parse_tree.c
----------------------------------------------------------------------
diff --git a/src/parse_tree.c b/src/parse_tree.c
new file mode 100644
index 0000000..07097b2
--- /dev/null
+++ b/src/parse_tree.c
@@ -0,0 +1,524 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 "parse_tree.h"
+
+
+// Wildcard pattern matching:
+// '*' - match exactly one single token
+// '#' - match zero or more tokens
+// * has higher precedence than #: "a/b" will match pattern "a/*" before "a/#"
+static const char STAR = '*';
+static const char HASH = '#';
+
+
+// token parsing
+// parse a string of tokens separated by a boundary character.
+//
+// The format of a pattern string is a series of tokens. A
+// token can contain any characters but '#', '*', or the boundary character.
+//
+// TODO(kgiusti): really should create a token parsing qd_iterator_t view.
+// Then all this token/token_iterator stuff can be removed...
+//
+typedef struct token {
+    const char *begin;
+    const char *end;
+} token_t;
+#define TOKEN_LEN(t) ((t).end - (t).begin)
+
+// for iterating over a string of tokens:
+typedef struct token_iterator {
+    token_t token;           // token at head of string
+    const char *terminator;  // end of entire string
+    char separator;          // delimits tokens
+} token_iterator_t;
+
+
+static void token_iterator_init(token_iterator_t *t, const char *str,
+                                char separator)
+{
+    const char *tend = strchr(str, separator);
+    t->terminator = str + strlen(str);
+    t->token.begin = str;
+    t->token.end = (tend) ? tend : t->terminator;
+    t->separator = separator;
+}
+
+
+static void token_iterator_next(token_iterator_t *t)
+{
+    if (t->token.end == t->terminator) {
+        t->token.begin = t->terminator;
+    } else {
+        const char *tend;
+        t->token.begin = t->token.end + 1;
+        tend = strchr(t->token.begin, t->separator);
+        t->token.end = (tend) ? tend : t->terminator;
+    }
+}
+
+
+static bool token_iterator_done(const token_iterator_t *t)
+{
+    return t->token.begin == t->terminator;
+}
+
+
+static void token_iterator_pop(token_iterator_t *t, token_t *head)
+{
+    if (head) *head = t->token;
+    token_iterator_next(t);
+}
+
+
+static bool token_match_str(const token_t *t, const char *str)
+{
+    return !strncmp(t->begin, str, TOKEN_LEN(*t));
+}
+
+// True if token matches the given char value
+static bool token_iterator_match_char(const token_iterator_t *t,
+                                      const char c)
+{
+    return TOKEN_LEN(t->token) == 1 && *t->token.begin == c;
+}
+
+
+// Optimize the pattern match code by performing the following
+// transformations to the pattern:
+// #.* ---> *.#
+// #.# ---> #
+//
+static char *normalize_pattern(char *pattern, char token_sep)
+{
+    token_iterator_t t;
+
+    token_iterator_init(&t, pattern, token_sep);
+    while (!token_iterator_done(&t)) {
+        if (token_iterator_match_char(&t, HASH)) {
+            token_t last_token;
+            token_iterator_pop(&t, &last_token);
+            if (token_iterator_done(&t)) break;
+            if (token_iterator_match_char(&t, HASH)) {  // #.# --> #
+                char *src = (char *)t.token.begin;
+                char *dest = (char *)last_token.begin;
+                // note: overlapping strings, can't strcpy
+                while (*src)
+                    *dest++ = *src++;
+                *dest = (char)0;
+                t.terminator = dest;
+                t.token = last_token;
+            } else if (token_iterator_match_char(&t, STAR)) { // #.* --> *.#
+                *(char *)t.token.begin = HASH;
+                *(char *)last_token.begin = STAR;
+            } else {
+                token_iterator_next(&t);
+            }
+        } else {
+            token_iterator_next(&t);
+        }
+    }
+    return pattern;
+}
+
+
+// Parse Tree
+//
+// The pattern is broken up into tokens delimited by a separation character and
+// stored in a directed tree graph.  Common pattern prefixes are merged.  This
+// allows the lookup alogrithm to quickly isolate those sub-trees that match a
+// given input string.
+// For example, given the patterns:
+//     a.b.c.<...>
+//     a.b.d.<...>
+//     a.x.y.<...>
+// The resulting parse tree would be:
+//    a-->b-->c-->...
+//    |   +-->d-->...
+//    +-->x-->y-->...
+//
+
+DEQ_DECLARE(parse_node_t, parse_node_list_t);
+struct parse_node {
+    DEQ_LINKS(parse_node_t);    // siblings
+    char *token;                // portion of pattern represented by this node
+    char token_sep;             // boundary character between tokens
+    bool is_star;
+    bool is_hash;
+    char *pattern;        // entire normalized pattern matching this node
+    parse_node_list_t  children;
+    struct parse_node  *star_child;
+    struct parse_node  *hash_child;
+    void *payload;      // data returned on match against this node
+};
+ALLOC_DECLARE(parse_node_t);
+ALLOC_DEFINE(parse_node_t);
+
+
+static parse_node_t *new_parse_node(const token_t *t, char token_sep)
+{
+    parse_node_t *n = new_parse_node_t();
+    if (n) {
+        DEQ_ITEM_INIT(n);
+        DEQ_INIT(n->children);
+        n->payload = NULL;
+        n->pattern = NULL;
+        n->star_child = n->hash_child = NULL;
+        n->token_sep = token_sep;
+
+        if (t) {
+            const size_t tlen = TOKEN_LEN(*t);
+            n->token = malloc(tlen + 1);
+            strncpy(n->token, t->begin, tlen);
+            n->token[tlen] = 0;
+            n->is_star = (tlen == 1 && *t->begin == STAR);
+            n->is_hash = (tlen == 1 && *t->begin == HASH);
+        } else {  // root
+            n->token = NULL;
+            n->is_star = n->is_hash = false;
+        }
+    }
+    return n;
+}
+
+
+// return count of child nodes
+static int parse_node_child_count(const parse_node_t *n)
+{
+    return DEQ_SIZE(n->children)
+        + n->star_child ? 1 : 0
+        + n->hash_child ? 1 : 0;
+}
+
+
+static void free_parse_node(parse_node_t *n)
+{
+    assert(parse_node_child_count(n) == 0);
+    free(n->token);
+    free(n->pattern);
+    free_parse_node_t(n);
+}
+
+
+// find immediate child node matching token
+static parse_node_t *parse_node_find_child(const parse_node_t *node, const token_t *token)
+{
+    parse_node_t *child = DEQ_HEAD(node->children);
+    while (child && !token_match_str(token, child->token))
+        child = DEQ_NEXT(child);
+    return child;
+}
+
+
+// Add a new pattern and associated data to the tree.  Returns the old payload
+// if the pattern has already been added to the tree.
+//
+static void *parse_node_add_pattern(parse_node_t *node,
+                                    token_iterator_t *key,
+                                    const char *pattern,
+                                    void *payload)
+{
+    if (token_iterator_done(key)) {
+        // this node's pattern
+        void *old;
+        if (!node->pattern) {
+            node->pattern = strdup(pattern);
+        }
+        assert(strcmp(node->pattern, pattern) == 0);
+        old = node->payload;
+        node->payload = payload;
+        return old;
+    }
+
+    if (token_iterator_match_char(key, STAR)) {
+        if (!node->star_child) {
+            node->star_child = new_parse_node(&key->token,
+                                              node->token_sep);
+        }
+        token_iterator_next(key);
+        return parse_node_add_pattern(node->star_child,
+                                      key,
+                                      pattern,
+                                      payload);
+    } else if (token_iterator_match_char(key, HASH)) {
+        if (!node->hash_child) {
+            node->hash_child = new_parse_node(&key->token,
+                                              node->token_sep);
+        }
+        token_iterator_next(key);
+        return parse_node_add_pattern(node->hash_child,
+                                      key,
+                                      pattern,
+                                      payload);
+    } else {
+        // check the children nodes
+        token_t current;
+        token_iterator_pop(key, &current);
+
+        parse_node_t *child = parse_node_find_child(node, &current);
+        if (child) {
+            return parse_node_add_pattern(child,
+                                          key,
+                                          pattern,
+                                          payload);
+        } else {
+            child = new_parse_node(&current, node->token_sep);
+            DEQ_INSERT_TAIL(node->children, child);
+            return parse_node_add_pattern(child,
+                                          key,
+                                          pattern,
+                                          payload);
+        }
+    }
+}
+
+// remove pattern from the tree.
+// return the payload of the removed pattern
+static void *parse_node_remove_pattern(parse_node_t *node,
+                                       token_iterator_t *key,
+                                       const char *pattern)
+{
+    void *old = NULL;
+    if (token_iterator_done(key)) {
+        // this node's pattern
+        if (node->pattern) {
+            assert(strcmp(node->pattern, pattern) == 0);
+            free(node->pattern);
+            node->pattern = NULL;
+        }
+        old = node->payload;
+        node->payload = NULL;
+    } else if (token_iterator_match_char(key, STAR)) {
+        assert(node->star_child);
+        token_iterator_next(key);
+        old = parse_node_remove_pattern(node->star_child, key, pattern);
+        if (node->star_child->pattern == NULL
+            && parse_node_child_count(node->star_child) == 0) {
+            free_parse_node(node->star_child);
+            node->star_child = NULL;
+        }
+    } else if (token_iterator_match_char(key, HASH)) {
+        assert(node->hash_child);
+        token_iterator_next(key);
+        old = parse_node_remove_pattern(node->hash_child, key, pattern);
+        if (node->hash_child->pattern == NULL
+            && parse_node_child_count(node->hash_child) == 0) {
+            free_parse_node(node->hash_child);
+            node->hash_child = NULL;
+        }
+    } else {
+        token_t current;
+        token_iterator_pop(key, &current);
+        parse_node_t *child = parse_node_find_child(node, &current);
+        if (child) {
+            old = parse_node_remove_pattern(child, key, pattern);
+            if (child->pattern == NULL
+                && parse_node_child_count(child) == 0) {
+                DEQ_REMOVE(node->children, child);
+                free_parse_node(child);
+            }
+        }
+    }
+    return old;
+}
+
+
+static bool parse_node_find(parse_node_t *, token_iterator_t *,
+                            parse_tree_visit_t *, void *);
+
+
+// search the sub-trees of this node.
+// This function returns false to stop the search
+static bool parse_node_find_children(parse_node_t *node, token_iterator_t *value,
+                                     parse_tree_visit_t *callback, void *handle)
+{
+    if (!token_iterator_done(value)) {
+
+        // check exact match first (precedence)
+        if (DEQ_SIZE(node->children) > 0) {
+            token_iterator_t tmp = *value;
+            token_t child_token;
+            token_iterator_pop(&tmp, &child_token);
+
+            parse_node_t *child = parse_node_find_child(node, &child_token);
+            if (child) {
+                if (!parse_node_find(child, &tmp, callback, handle))
+                    return false;
+            }
+        }
+
+        if (node->star_child) {
+            token_iterator_t tmp = *value;
+            if (!parse_node_find(node->star_child, &tmp, callback, handle))
+                return false;
+        }
+    }
+
+    // always try glob - even if empty (it can match empty keys)
+    if (node->hash_child) {
+        token_iterator_t tmp = *value;
+        if (!parse_node_find(node->hash_child, &tmp, callback, handle))
+            return false;
+    }
+
+    return true; // continue search
+}
+
+
+// This node matched the last token.
+// This function returns false to stop the search
+static bool parse_node_find_token(parse_node_t *node, token_iterator_t *value,
+                                  parse_tree_visit_t *callback, void *handle)
+{
+    if (token_iterator_done(value) && node->pattern) {
+        // exact match current node
+        return callback(handle, node->pattern, node->payload);
+    }
+
+    // no payload or more tokens.  Continue to lower sub-trees. Even if no more
+    // tokens (may have a # match)
+    return parse_node_find_children(node, value, callback, handle);
+}
+
+
+// this node matches any one token
+// returns false to stop the search
+static bool parse_node_find_star(parse_node_t *node, token_iterator_t *value,
+                                 parse_tree_visit_t *callback, void *handle)
+{
+    // must match exactly one token:
+    if (token_iterator_done(value))
+        return true;  // no match here, but continue searching
+
+    // pop the topmost token (matched)
+    token_iterator_next(value);
+
+    if (token_iterator_done(value) && node->pattern) {
+        // exact match current node
+        if (!callback(handle, node->pattern, node->payload))
+            return false;
+    }
+
+    // no payload or more tokens: continue to lower sub-trees
+    return parse_node_find_children(node, value, callback, handle);
+}
+
+
+// current node is hash, match the remainder of the string
+// return false to stop search
+static bool parse_node_find_hash(parse_node_t *node, token_iterator_t *value,
+                                 parse_tree_visit_t *callback, void *handle)
+{
+    // consume each token and look for a match on the
+    // remaining key.
+    while (!token_iterator_done(value)) {
+        if (!parse_node_find_children(node, value, callback, handle))
+            return false;
+        token_iterator_next(value);
+    }
+
+    // this node matches
+    if (node->pattern)
+        return callback(handle, node->pattern, node->payload);
+
+    return true;
+}
+
+
+// find the payload associated with the input string 'value'
+static bool parse_node_find(parse_node_t *node, token_iterator_t *value,
+                            parse_tree_visit_t *callback, void *handle)
+{
+    if (node->is_star)
+        return parse_node_find_star(node, value, callback, handle);
+    else if (node->is_hash)
+        return parse_node_find_hash(node, value, callback, handle);
+    else
+        return parse_node_find_token(node, value, callback, handle);
+}
+
+
+parse_node_t *parse_tree_new(char token_sep)
+{
+    return new_parse_node(NULL, token_sep);
+}
+
+
+void parse_tree_free(parse_node_t *node)
+{
+    if (node) {
+        if (node->star_child) parse_tree_free(node->star_child);
+        if (node->hash_child) parse_tree_free(node->hash_child);
+        node->star_child = node->hash_child = NULL;
+        while (!DEQ_IS_EMPTY(node->children)) {
+            parse_node_t *child = DEQ_HEAD(node->children);
+            DEQ_REMOVE_HEAD(node->children);
+            parse_tree_free(child);
+        }
+
+        free_parse_node(node);
+    }
+}
+
+
+// Invoke callback for each pattern that matches 'value
+void parse_tree_find(parse_node_t *node, const char *value,
+                     parse_tree_visit_t *callback, void *handle)
+{
+    token_iterator_t t_iter;
+
+    token_iterator_init(&t_iter, value, node->token_sep);
+    parse_node_find(node, &t_iter, callback, handle);
+}
+
+
+// returns old payload or NULL if new
+void *parse_tree_add_pattern(parse_node_t *node,
+                             const char *pattern,
+                             void *payload)
+{
+    token_iterator_t key;
+    void *rc = NULL;
+    char *norm = normalize_pattern(strdup(pattern),
+                                   node->token_sep);
+
+    token_iterator_init(&key, norm, node->token_sep);
+    rc = parse_node_add_pattern(node, &key, pattern, payload);
+    free(norm);
+    return rc;
+}
+
+
+// returns the payload void *
+void *parse_tree_remove_pattern(parse_node_t *node,
+                                const char *pattern)
+{
+    token_iterator_t key;
+    void *rc = NULL;
+    char *norm = normalize_pattern(strdup(pattern),
+                                   node->token_sep);
+
+    token_iterator_init(&key, norm, node->token_sep);
+    rc = parse_node_remove_pattern(node, &key, pattern);
+    free(norm);
+    return rc;
+}
+

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/f1735eee/src/parse_tree.h
----------------------------------------------------------------------
diff --git a/src/parse_tree.h b/src/parse_tree.h
new file mode 100644
index 0000000..62917bc
--- /dev/null
+++ b/src/parse_tree.h
@@ -0,0 +1,49 @@
+#ifndef PARSE_TREE_H
+#define PARSE_TREE_H 1
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 <stdbool.h>
+
+#include <qpid/dispatch/ctools.h>
+#include "alloc.h"
+
+
+typedef struct parse_node parse_node_t;
+
+parse_node_t *parse_tree_new(char token_sep);
+void parse_tree_free(parse_node_t *node);
+
+// return false to stop searching
+
+typedef bool parse_tree_visit_t(void *handle,
+                                const char *pattern,
+                                void *payload);
+
+void parse_tree_find(parse_node_t *node, const char *value,
+                     parse_tree_visit_t *callback, void *handle);
+
+void *parse_tree_add_pattern(parse_node_t *node,
+                             const char *pattern,
+                             void *payload);
+
+// returns the payload void *
+void *parse_tree_remove_pattern(parse_node_t *node,
+                                const char *pattern);
+#endif /* parse_tree.h */


---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

[2/8] qpid-dispatch git commit: Merge branch 'master' into DISPATCH-731

kgiusti
Merge branch 'master' into DISPATCH-731


Project: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/commit/704fc281
Tree: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/tree/704fc281
Diff: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/diff/704fc281

Branch: refs/heads/master
Commit: 704fc281d8cb44d0d8145fca8e9310b88239aa7f
Parents: f1735ee 2def0a9
Author: Kenneth Giusti <[hidden email]>
Authored: Thu Jul 13 11:54:43 2017 -0400
Committer: Kenneth Giusti <[hidden email]>
Committed: Thu Jul 13 11:54:43 2017 -0400

----------------------------------------------------------------------
 include/qpid/dispatch/amqp.h        |   3 +
 include/qpid/dispatch/compose.h     |  16 ++
 include/qpid/dispatch/iterator.h    |  30 +++
 include/qpid/dispatch/message.h     |  46 +++-
 include/qpid/dispatch/parse.h       |  72 +++++
 include/qpid/dispatch/server.h      |   5 +
 src/amqp.c                          |   3 +
 src/compose.c                       |  19 ++
 src/container.c                     |   4 +-
 src/iterator.c                      |  99 +++++--
 src/message.c                       | 295 ++++++++++++++-------
 src/message_private.h               |  16 +-
 src/parse.c                         | 252 +++++++++++++++++-
 src/python_embedded.c               |   9 +
 src/router_core/management_agent.c  |  20 +-
 src/router_core/transfer.c          |  20 +-
 src/router_node.c                   |  83 +++---
 src/server.c                        |   4 +
 src/server_private.h                |   2 +
 tests/message_test.c                |   9 +-
 tests/system_tests_three_routers.py | 437 ++++++++++++++++++++++++-------
 tests/system_tests_two_routers.py   |  68 ++---
 22 files changed, 1207 insertions(+), 305 deletions(-)
----------------------------------------------------------------------



---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

[3/8] qpid-dispatch git commit: DISPATCH-731: support wildcard configured addresses

kgiusti
In reply to this post by kgiusti
DISPATCH-731: support wildcard configured addresses


Project: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/commit/492ef7d4
Tree: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/tree/492ef7d4
Diff: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/diff/492ef7d4

Branch: refs/heads/master
Commit: 492ef7d4f3b22a62adf93614aa666383d0c5178b
Parents: 704fc28
Author: Kenneth Giusti <[hidden email]>
Authored: Thu Jul 13 11:55:41 2017 -0400
Committer: Kenneth Giusti <[hidden email]>
Committed: Tue Jul 25 16:00:38 2017 -0400

----------------------------------------------------------------------
 include/qpid/dispatch/compose.h                 |   1 +
 python/qpid_dispatch/management/qdrouter.json   |  10 +-
 .../qpid_dispatch_internal/management/config.py |  17 +-
 src/compose.c                                   |  10 +-
 src/parse_tree.c                                | 314 ++++++++++++++-----
 src/parse_tree.h                                |  64 +++-
 src/router_config.c                             |  25 +-
 src/router_core/agent_config_address.c          |  83 ++++-
 src/router_core/agent_config_address.h          |   2 +-
 src/router_core/connections.c                   |  21 +-
 src/router_core/route_tables.c                  |   1 +
 src/router_core/router_core.c                   |  13 +-
 src/router_core/router_core_private.h           |   4 +-
 src/router_private.h                            |   1 +
 tests/system_tests_qdmanage.py                  |   9 +-
 15 files changed, 434 insertions(+), 141 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/492ef7d4/include/qpid/dispatch/compose.h
----------------------------------------------------------------------
diff --git a/include/qpid/dispatch/compose.h b/include/qpid/dispatch/compose.h
index e808ea2..78bb8d2 100644
--- a/include/qpid/dispatch/compose.h
+++ b/include/qpid/dispatch/compose.h
@@ -183,6 +183,7 @@ void qd_compose_insert_binary_buffers(qd_composed_field_t *field, qd_buffer_list
  */
 void qd_compose_insert_string(qd_composed_field_t *field, const char *value);
 void qd_compose_insert_string2(qd_composed_field_t *field, const char *value1, const char *value2);
+void qd_compose_insert_string_n(qd_composed_field_t *field, const char *value, size_t len);
 
 /**
  * Insert a utf8-encoded string into the field from an iterator

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/492ef7d4/python/qpid_dispatch/management/qdrouter.json
----------------------------------------------------------------------
diff --git a/python/qpid_dispatch/management/qdrouter.json b/python/qpid_dispatch/management/qdrouter.json
index e80359f..1101961 100644
--- a/python/qpid_dispatch/management/qdrouter.json
+++ b/python/qpid_dispatch/management/qdrouter.json
@@ -951,9 +951,15 @@
             "attributes": {
                 "prefix": {
                     "type": "string",
-                    "description": "The address prefix for the configured settings",
+                    "description": "The address prefix for the configured settings. Cannot be used with a pattern attribute.",
                     "create": true,
-                    "required": true
+                    "required": false
+                },
+                "pattern": {
+                    "type": "string",
+                    "description": "A wildcarded pattern for address matching. Incoming addresses are matched against this pattern. Matching addresses use the configured settings. The pattern consists of one or more tokens separated by a forward slash '/'. A token can be one of the following: a * character, a # character, or a sequence of characters that do not include /, *, or #.  The * token matches any single token.  The # token matches zero or more tokens. * has higher precedence than #, and exact match has the highest precedence. Cannot be used with a prefix attribute.",
+                    "create": true,
+                    "required": false
                 },
                 "distribution": {
                     "type": ["multicast", "closest", "balanced"],

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/492ef7d4/python/qpid_dispatch_internal/management/config.py
----------------------------------------------------------------------
diff --git a/python/qpid_dispatch_internal/management/config.py b/python/qpid_dispatch_internal/management/config.py
index 881394b..b8766fb 100644
--- a/python/qpid_dispatch_internal/management/config.py
+++ b/python/qpid_dispatch_internal/management/config.py
@@ -59,13 +59,22 @@ class Config(object):
         begin = re.compile(r'([\w-]+)[ \t]*{') # WORD {
         end = re.compile(r'}')                 # }
         attr = re.compile(r'([\w-]+)[ \t]*:[ \t]*(.+)') # WORD1: VALUE
+        pattern = re.compile(r'([\w-]+)[ \t]*:[ \t]*([\S]+).*')
 
         def sub(line):
             """Do substitutions to make line json-friendly"""
-            line = line.split('#')[0].strip() # Strip comments
-            line = re.sub(begin, r'["\1", {', line)
-            line = re.sub(end, r'}],', line)
-            line = re.sub(attr, r'"\1": "\2",', line)
+            line = line.strip()
+            if line.startswith("#"):
+                return ""
+            # 'pattern:' is a special snowflake.  It allows '#' characters in
+            # its value, so they cannot be treated as comment delimiters
+            if line.split(':')[0].strip().lower() == "pattern":
+                line = re.sub(pattern, r'"\1": "\2",', line)
+            else:
+                line = line.split('#')[0].strip()
+                line = re.sub(begin, r'["\1", {', line)
+                line = re.sub(end, r'}],', line)
+                line = re.sub(attr, r'"\1": "\2",', line)
             return line
 
         js_text = "[%s]"%("\n".join([sub(l) for l in lines]))

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/492ef7d4/src/compose.c
----------------------------------------------------------------------
diff --git a/src/compose.c b/src/compose.c
index 01dccbb..2581241 100644
--- a/src/compose.c
+++ b/src/compose.c
@@ -425,10 +425,8 @@ void qd_compose_insert_binary_buffers(qd_composed_field_t *field, qd_buffer_list
 }
 
 
-void qd_compose_insert_string(qd_composed_field_t *field, const char *value)
+void qd_compose_insert_string_n(qd_composed_field_t *field, const char *value, size_t len)
 {
-    uint32_t len = strlen(value);
-
     if (len < 256) {
         qd_insert_8(field, QD_AMQP_STR8_UTF8);
         qd_insert_8(field, (uint8_t) len);
@@ -441,6 +439,12 @@ void qd_compose_insert_string(qd_composed_field_t *field, const char *value)
 }
 
 
+void qd_compose_insert_string(qd_composed_field_t *field, const char *value)
+{
+    qd_compose_insert_string_n(field, value, strlen(value));
+}
+
+
 void qd_compose_insert_string2(qd_composed_field_t *field, const char *value1, const char *value2)
 {
     uint32_t len1 = strlen(value1);

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/492ef7d4/src/parse_tree.c
----------------------------------------------------------------------
diff --git a/src/parse_tree.c b/src/parse_tree.c
index 07097b2..bd93ed9 100644
--- a/src/parse_tree.c
+++ b/src/parse_tree.c
@@ -19,7 +19,7 @@
 
 
 #include "parse_tree.h"
-
+#include <qpid/dispatch/log.h>
 
 // Wildcard pattern matching:
 // '*' - match exactly one single token
@@ -30,7 +30,7 @@ static const char HASH = '#';
 
 
 // token parsing
-// parse a string of tokens separated by a boundary character.
+// parse a string of tokens separated by a boundary character (. or /).
 //
 // The format of a pattern string is a series of tokens. A
 // token can contain any characters but '#', '*', or the boundary character.
@@ -38,6 +38,7 @@ static const char HASH = '#';
 // TODO(kgiusti): really should create a token parsing qd_iterator_t view.
 // Then all this token/token_iterator stuff can be removed...
 //
+const char * const QD_PARSE_TREE_TOKEN_SEP = "./";
 typedef struct token {
     const char *begin;
     const char *end;
@@ -48,18 +49,17 @@ typedef struct token {
 typedef struct token_iterator {
     token_t token;           // token at head of string
     const char *terminator;  // end of entire string
-    char separator;          // delimits tokens
 } token_iterator_t;
 
 
-static void token_iterator_init(token_iterator_t *t, const char *str,
-                                char separator)
+static void token_iterator_init(token_iterator_t *t, const char *str)
 {
-    const char *tend = strchr(str, separator);
+    while (*str && strchr(QD_PARSE_TREE_TOKEN_SEP, *str))
+        str++;  // skip any leading separators
+    const char *tend = strpbrk(str, QD_PARSE_TREE_TOKEN_SEP);
     t->terminator = str + strlen(str);
     t->token.begin = str;
     t->token.end = (tend) ? tend : t->terminator;
-    t->separator = separator;
 }
 
 
@@ -70,7 +70,7 @@ static void token_iterator_next(token_iterator_t *t)
     } else {
         const char *tend;
         t->token.begin = t->token.end + 1;
-        tend = strchr(t->token.begin, t->separator);
+        tend = strpbrk(t->token.begin, QD_PARSE_TREE_TOKEN_SEP);
         t->token.end = (tend) ? tend : t->terminator;
     }
 }
@@ -107,17 +107,21 @@ static bool token_iterator_match_char(const token_iterator_t *t,
 // #.* ---> *.#
 // #.# ---> #
 //
-static char *normalize_pattern(char *pattern, char token_sep)
+static bool normalize_pattern(char *pattern)
 {
     token_iterator_t t;
+    bool modified = false;
+    char *original = NULL;
 
-    token_iterator_init(&t, pattern, token_sep);
+    token_iterator_init(&t, pattern);
     while (!token_iterator_done(&t)) {
         if (token_iterator_match_char(&t, HASH)) {
             token_t last_token;
             token_iterator_pop(&t, &last_token);
             if (token_iterator_done(&t)) break;
             if (token_iterator_match_char(&t, HASH)) {  // #.# --> #
+                if (!modified) original = strdup(pattern);
+                modified = true;
                 char *src = (char *)t.token.begin;
                 char *dest = (char *)last_token.begin;
                 // note: overlapping strings, can't strcpy
@@ -127,6 +131,8 @@ static char *normalize_pattern(char *pattern, char token_sep)
                 t.terminator = dest;
                 t.token = last_token;
             } else if (token_iterator_match_char(&t, STAR)) { // #.* --> *.#
+                if (!modified) original = strdup(pattern);
+                modified = true;
                 *(char *)t.token.begin = HASH;
                 *(char *)last_token.begin = STAR;
             } else {
@@ -136,7 +142,15 @@ static char *normalize_pattern(char *pattern, char token_sep)
             token_iterator_next(&t);
         }
     }
-    return pattern;
+
+    if (original) {
+        qd_log(qd_log_source("DEFAULT"), QD_LOG_NOTICE,
+               "configured address '%s' optimized and re-written to '%s'",
+               original, pattern);
+        free(original);
+    }
+
+    return modified;
 }
 
 
@@ -156,33 +170,31 @@ static char *normalize_pattern(char *pattern, char token_sep)
 //    +-->x-->y-->...
 //
 
-DEQ_DECLARE(parse_node_t, parse_node_list_t);
-struct parse_node {
-    DEQ_LINKS(parse_node_t);    // siblings
+DEQ_DECLARE(qd_parse_node_t, qd_parse_node_list_t);
+struct qd_parse_node {
+    DEQ_LINKS(qd_parse_node_t); // siblings
     char *token;                // portion of pattern represented by this node
-    char token_sep;             // boundary character between tokens
     bool is_star;
     bool is_hash;
     char *pattern;        // entire normalized pattern matching this node
-    parse_node_list_t  children;
-    struct parse_node  *star_child;
-    struct parse_node  *hash_child;
+    qd_parse_node_list_t  children;
+    struct qd_parse_node  *star_child;
+    struct qd_parse_node  *hash_child;
     void *payload;      // data returned on match against this node
 };
-ALLOC_DECLARE(parse_node_t);
-ALLOC_DEFINE(parse_node_t);
+ALLOC_DECLARE(qd_parse_node_t);
+ALLOC_DEFINE(qd_parse_node_t);
 
 
-static parse_node_t *new_parse_node(const token_t *t, char token_sep)
+static qd_parse_node_t *new_parse_node(const token_t *t)
 {
-    parse_node_t *n = new_parse_node_t();
+    qd_parse_node_t *n = new_qd_parse_node_t();
     if (n) {
         DEQ_ITEM_INIT(n);
         DEQ_INIT(n->children);
         n->payload = NULL;
         n->pattern = NULL;
         n->star_child = n->hash_child = NULL;
-        n->token_sep = token_sep;
 
         if (t) {
             const size_t tlen = TOKEN_LEN(*t);
@@ -201,27 +213,27 @@ static parse_node_t *new_parse_node(const token_t *t, char token_sep)
 
 
 // return count of child nodes
-static int parse_node_child_count(const parse_node_t *n)
+static int parse_node_child_count(const qd_parse_node_t *n)
 {
     return DEQ_SIZE(n->children)
-        + n->star_child ? 1 : 0
-        + n->hash_child ? 1 : 0;
+        + (n->star_child ? 1 : 0)
+        + (n->hash_child ? 1 : 0);
 }
 
 
-static void free_parse_node(parse_node_t *n)
+static void free_parse_node(qd_parse_node_t *n)
 {
     assert(parse_node_child_count(n) == 0);
     free(n->token);
     free(n->pattern);
-    free_parse_node_t(n);
+    free_qd_parse_node_t(n);
 }
 
 
 // find immediate child node matching token
-static parse_node_t *parse_node_find_child(const parse_node_t *node, const token_t *token)
+static qd_parse_node_t *parse_node_find_child(const qd_parse_node_t *node, const token_t *token)
 {
-    parse_node_t *child = DEQ_HEAD(node->children);
+    qd_parse_node_t *child = DEQ_HEAD(node->children);
     while (child && !token_match_str(token, child->token))
         child = DEQ_NEXT(child);
     return child;
@@ -231,7 +243,7 @@ static parse_node_t *parse_node_find_child(const parse_node_t *node, const token
 // Add a new pattern and associated data to the tree.  Returns the old payload
 // if the pattern has already been added to the tree.
 //
-static void *parse_node_add_pattern(parse_node_t *node,
+static void *parse_node_add_pattern(qd_parse_node_t *node,
                                     token_iterator_t *key,
                                     const char *pattern,
                                     void *payload)
@@ -250,8 +262,7 @@ static void *parse_node_add_pattern(parse_node_t *node,
 
     if (token_iterator_match_char(key, STAR)) {
         if (!node->star_child) {
-            node->star_child = new_parse_node(&key->token,
-                                              node->token_sep);
+            node->star_child = new_parse_node(&key->token);
         }
         token_iterator_next(key);
         return parse_node_add_pattern(node->star_child,
@@ -260,8 +271,7 @@ static void *parse_node_add_pattern(parse_node_t *node,
                                       payload);
     } else if (token_iterator_match_char(key, HASH)) {
         if (!node->hash_child) {
-            node->hash_child = new_parse_node(&key->token,
-                                              node->token_sep);
+            node->hash_child = new_parse_node(&key->token);
         }
         token_iterator_next(key);
         return parse_node_add_pattern(node->hash_child,
@@ -273,14 +283,14 @@ static void *parse_node_add_pattern(parse_node_t *node,
         token_t current;
         token_iterator_pop(key, &current);
 
-        parse_node_t *child = parse_node_find_child(node, &current);
+        qd_parse_node_t *child = parse_node_find_child(node, &current);
         if (child) {
             return parse_node_add_pattern(child,
                                           key,
                                           pattern,
                                           payload);
         } else {
-            child = new_parse_node(&current, node->token_sep);
+            child = new_parse_node(&current);
             DEQ_INSERT_TAIL(node->children, child);
             return parse_node_add_pattern(child,
                                           key,
@@ -292,7 +302,7 @@ static void *parse_node_add_pattern(parse_node_t *node,
 
 // remove pattern from the tree.
 // return the payload of the removed pattern
-static void *parse_node_remove_pattern(parse_node_t *node,
+static void *parse_node_remove_pattern(qd_parse_node_t *node,
                                        token_iterator_t *key,
                                        const char *pattern)
 {
@@ -327,7 +337,7 @@ static void *parse_node_remove_pattern(parse_node_t *node,
     } else {
         token_t current;
         token_iterator_pop(key, &current);
-        parse_node_t *child = parse_node_find_child(node, &current);
+        qd_parse_node_t *child = parse_node_find_child(node, &current);
         if (child) {
             old = parse_node_remove_pattern(child, key, pattern);
             if (child->pattern == NULL
@@ -341,14 +351,53 @@ static void *parse_node_remove_pattern(parse_node_t *node,
 }
 
 
-static bool parse_node_find(parse_node_t *, token_iterator_t *,
-                            parse_tree_visit_t *, void *);
+// Find the pattern in the tree, return the payload pointer or NULL if pattern
+// is not in the tree
+static qd_parse_node_t *parse_node_get_pattern(qd_parse_node_t *node,
+                                               token_iterator_t *key,
+                                               const char *pattern)
+{
+    if (!node)
+        return NULL;
+
+    if (token_iterator_done(key))
+        return node;
+
+    if (token_iterator_match_char(key, STAR)) {
+        token_iterator_next(key);
+        return parse_node_get_pattern(node->star_child,
+                                      key,
+                                      pattern);
+    } else if (token_iterator_match_char(key, HASH)) {
+        token_iterator_next(key);
+        return parse_node_get_pattern(node->hash_child,
+                                      key,
+                                      pattern);
+    } else {
+        // check the children nodes
+        token_t current;
+        token_iterator_pop(key, &current);
+
+        qd_parse_node_t *child = parse_node_find_child(node, &current);
+        if (child) {
+            return parse_node_get_pattern(child,
+                                          key,
+                                          pattern);
+        }
+    }
+
+    return NULL;  // not found
+}
+
+
+static bool parse_node_find(qd_parse_node_t *, token_iterator_t *,
+                            qd_parse_tree_visit_t *, void *);
 
 
 // search the sub-trees of this node.
 // This function returns false to stop the search
-static bool parse_node_find_children(parse_node_t *node, token_iterator_t *value,
-                                     parse_tree_visit_t *callback, void *handle)
+static bool parse_node_find_children(qd_parse_node_t *node, token_iterator_t *value,
+                                     qd_parse_tree_visit_t *callback, void *handle)
 {
     if (!token_iterator_done(value)) {
 
@@ -358,7 +407,7 @@ static bool parse_node_find_children(parse_node_t *node, token_iterator_t *value
             token_t child_token;
             token_iterator_pop(&tmp, &child_token);
 
-            parse_node_t *child = parse_node_find_child(node, &child_token);
+            qd_parse_node_t *child = parse_node_find_child(node, &child_token);
             if (child) {
                 if (!parse_node_find(child, &tmp, callback, handle))
                     return false;
@@ -385,8 +434,8 @@ static bool parse_node_find_children(parse_node_t *node, token_iterator_t *value
 
 // This node matched the last token.
 // This function returns false to stop the search
-static bool parse_node_find_token(parse_node_t *node, token_iterator_t *value,
-                                  parse_tree_visit_t *callback, void *handle)
+static bool parse_node_find_token(qd_parse_node_t *node, token_iterator_t *value,
+                                  qd_parse_tree_visit_t *callback, void *handle)
 {
     if (token_iterator_done(value) && node->pattern) {
         // exact match current node
@@ -401,8 +450,8 @@ static bool parse_node_find_token(parse_node_t *node, token_iterator_t *value,
 
 // this node matches any one token
 // returns false to stop the search
-static bool parse_node_find_star(parse_node_t *node, token_iterator_t *value,
-                                 parse_tree_visit_t *callback, void *handle)
+static bool parse_node_find_star(qd_parse_node_t *node, token_iterator_t *value,
+                                 qd_parse_tree_visit_t *callback, void *handle)
 {
     // must match exactly one token:
     if (token_iterator_done(value))
@@ -424,8 +473,8 @@ static bool parse_node_find_star(parse_node_t *node, token_iterator_t *value,
 
 // current node is hash, match the remainder of the string
 // return false to stop search
-static bool parse_node_find_hash(parse_node_t *node, token_iterator_t *value,
-                                 parse_tree_visit_t *callback, void *handle)
+static bool parse_node_find_hash(qd_parse_node_t *node, token_iterator_t *value,
+                                 qd_parse_tree_visit_t *callback, void *handle)
 {
     // consume each token and look for a match on the
     // remaining key.
@@ -444,8 +493,8 @@ static bool parse_node_find_hash(parse_node_t *node, token_iterator_t *value,
 
 
 // find the payload associated with the input string 'value'
-static bool parse_node_find(parse_node_t *node, token_iterator_t *value,
-                            parse_tree_visit_t *callback, void *handle)
+static bool parse_node_find(qd_parse_node_t *node, token_iterator_t *value,
+                            qd_parse_tree_visit_t *callback, void *handle)
 {
     if (node->is_star)
         return parse_node_find_star(node, value, callback, handle);
@@ -456,22 +505,16 @@ static bool parse_node_find(parse_node_t *node, token_iterator_t *value,
 }
 
 
-parse_node_t *parse_tree_new(char token_sep)
-{
-    return new_parse_node(NULL, token_sep);
-}
-
-
-void parse_tree_free(parse_node_t *node)
+static void parse_node_free(qd_parse_node_t *node)
 {
     if (node) {
-        if (node->star_child) parse_tree_free(node->star_child);
-        if (node->hash_child) parse_tree_free(node->hash_child);
+        if (node->star_child) parse_node_free(node->star_child);
+        if (node->hash_child) parse_node_free(node->hash_child);
         node->star_child = node->hash_child = NULL;
         while (!DEQ_IS_EMPTY(node->children)) {
-            parse_node_t *child = DEQ_HEAD(node->children);
+            qd_parse_node_t *child = DEQ_HEAD(node->children);
             DEQ_REMOVE_HEAD(node->children);
-            parse_tree_free(child);
+            parse_node_free(child);
         }
 
         free_parse_node(node);
@@ -479,46 +522,151 @@ void parse_tree_free(parse_node_t *node)
 }
 
 
-// Invoke callback for each pattern that matches 'value
-void parse_tree_find(parse_node_t *node, const char *value,
-                     parse_tree_visit_t *callback, void *handle)
+qd_parse_node_t *qd_parse_tree_new()
+{
+    return new_parse_node(NULL);
+}
+
+
+// find best match for value
+//
+static bool get_first(void *handle, const char *pattern, void *payload)
+{
+    *(void **)handle = payload;
+    return false;
+}
+
+bool qd_parse_tree_retrieve_match(qd_parse_node_t *node,
+                                  const qd_iterator_t *value,
+                                  void **payload)
+{
+    *payload = NULL;
+    qd_parse_tree_search(node, value, get_first, payload);
+    return *payload != NULL;
+}
+
+
+// Invoke callback for each pattern that matches 'value'
+void qd_parse_tree_search(qd_parse_node_t *node,
+                          const qd_iterator_t *value,
+                          qd_parse_tree_visit_t *callback, void *handle)
 {
     token_iterator_t t_iter;
+    // @TODO(kgiusti) for now:
+    qd_iterator_t *dup = qd_iterator_dup(value);
+    char *str = (char *)qd_iterator_copy(dup);
 
-    token_iterator_init(&t_iter, value, node->token_sep);
+    normalize_pattern(str);
+
+    token_iterator_init(&t_iter, str);
     parse_node_find(node, &t_iter, callback, handle);
+
+    free(str);
+    qd_iterator_free(dup);
 }
 
 
 // returns old payload or NULL if new
-void *parse_tree_add_pattern(parse_node_t *node,
-                             const char *pattern,
-                             void *payload)
+void *qd_parse_tree_add_pattern(qd_parse_node_t *node,
+                                const qd_iterator_t *pattern,
+                                void *payload)
 {
     token_iterator_t key;
     void *rc = NULL;
-    char *norm = normalize_pattern(strdup(pattern),
-                                   node->token_sep);
-
-    token_iterator_init(&key, norm, node->token_sep);
-    rc = parse_node_add_pattern(node, &key, pattern, payload);
-    free(norm);
+    // @TODO(kgiusti) for now:
+    qd_iterator_t *dup = qd_iterator_dup(pattern);
+    char *str = (char *)qd_iterator_copy(dup);
+
+    normalize_pattern(str);
+    qd_log(qd_log_source("DEFAULT"), QD_LOG_DEBUG,
+           "Parse tree add address pattern '%s'", str);
+
+    token_iterator_init(&key, str);
+    rc = parse_node_add_pattern(node, &key, str, payload);
+    free(str);
+    qd_iterator_free(dup);
     return rc;
 }
 
 
+// returns true if pattern exists in tree
+bool qd_parse_tree_get_pattern(qd_parse_node_t *node,
+                               const qd_iterator_t *pattern,
+                               void **payload)
+{
+    token_iterator_t key;
+    qd_parse_node_t *found = NULL;
+    // @TODO(kgiusti) for now:
+    qd_iterator_t *dup = qd_iterator_dup(pattern);
+    char *str = (char *)qd_iterator_copy(dup);
+
+    normalize_pattern((char *)str);
+    qd_log(qd_log_source("DEFAULT"), QD_LOG_DEBUG,
+           "Parse tree get address pattern '%s'", str);
+
+    token_iterator_init(&key, str);
+    found = parse_node_get_pattern(node, &key, str);
+    free(str);
+    qd_iterator_free(dup);
+    *payload = found ? found->payload : NULL;
+    return *payload != NULL;
+}
+
+
 // returns the payload void *
-void *parse_tree_remove_pattern(parse_node_t *node,
-                                const char *pattern)
+void *qd_parse_tree_remove_pattern(qd_parse_node_t *node,
+                                   const qd_iterator_t *pattern)
 {
     token_iterator_t key;
     void *rc = NULL;
-    char *norm = normalize_pattern(strdup(pattern),
-                                   node->token_sep);
+    // @TODO(kgiusti) for now:
+    qd_iterator_t *dup = qd_iterator_dup(pattern);
+    char *str = (char *)qd_iterator_copy(dup);
 
-    token_iterator_init(&key, norm, node->token_sep);
-    rc = parse_node_remove_pattern(node, &key, pattern);
-    free(norm);
+    normalize_pattern(str);
+    qd_log(qd_log_source("DEFAULT"), QD_LOG_DEBUG,
+           "Parse tree remove address pattern '%s'", str);
+
+    token_iterator_init(&key, str);
+    rc = parse_node_remove_pattern(node, &key, str);
+    free(str);
     return rc;
 }
 
+
+#if 0
+#include <stdio.h>
+void qd_parse_tree_dump(qd_parse_node_t *node, int depth)
+{
+    for (int i = 0; i < depth; i++)
+        fprintf(stdout, "  ");
+    fprintf(stdout, "token: %s pattern: %s is star: %s is hash: %s # childs: %d\n",
+            node->token ? node->token : "*NONE*",
+            node->pattern ? node->pattern: "*NONE*",
+            node->is_star ? "yes" : "no",
+            node->is_hash ? "yes" : "no",
+            parse_node_child_count(node));
+    if (node->star_child) qd_parse_tree_dump(node->star_child, depth + 1);
+    if (node->hash_child) qd_parse_tree_dump(node->hash_child, depth + 1);
+    qd_parse_node_t *child = DEQ_HEAD(node->children);
+    while (child) {
+        qd_parse_tree_dump(child, depth + 1);
+        child = DEQ_NEXT(child);
+    }
+}
+
+void qd_parse_tree_free(qd_parse_node_t *node)
+{
+    fprintf(stdout, "PARSE TREE DUMP\n");
+    qd_parse_tree_dump(node, 4);
+    parse_node_free(node);
+}
+
+#else
+
+void qd_parse_tree_free(qd_parse_node_t *node)
+{
+    parse_node_free(node);
+}
+#endif
+

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/492ef7d4/src/parse_tree.h
----------------------------------------------------------------------
diff --git a/src/parse_tree.h b/src/parse_tree.h
index 62917bc..d616e48 100644
--- a/src/parse_tree.h
+++ b/src/parse_tree.h
@@ -22,28 +22,64 @@
 #include <stdbool.h>
 
 #include <qpid/dispatch/ctools.h>
+#include <qpid/dispatch/iterator.h>
 #include "alloc.h"
 
 
-typedef struct parse_node parse_node_t;
+typedef struct qd_parse_node qd_parse_node_t;
+extern const char * const QD_PARSE_TREE_TOKEN_SEP;
 
-parse_node_t *parse_tree_new(char token_sep);
-void parse_tree_free(parse_node_t *node);
+qd_parse_node_t *qd_parse_tree_new();
+void qd_parse_tree_free(qd_parse_node_t *node);
 
-// return false to stop searching
 
-typedef bool parse_tree_visit_t(void *handle,
-                                const char *pattern,
+// returns old payload or NULL if new
+void *qd_parse_tree_add_pattern(qd_parse_node_t *node,
+                                const qd_iterator_t *pattern,
                                 void *payload);
 
-void parse_tree_find(parse_node_t *node, const char *value,
-                     parse_tree_visit_t *callback, void *handle);
+// returns old payload or NULL if not present
+void *qd_parse_tree_remove_pattern(qd_parse_node_t *node,
+                                   const qd_iterator_t *pattern);
 
-void *parse_tree_add_pattern(parse_node_t *node,
-                             const char *pattern,
-                             void *payload);
+// retrieves the payload pointer
+// returns true if pattern found
+bool qd_parse_tree_get_pattern(qd_parse_node_t *node,
+                               const qd_iterator_t *pattern,
+                               void **payload);
+
+// find the 'best' match to 'value', using the following precedence (highest
+// first):
+//
+// 1) exact token match
+// 2) * wildcard match
+// 3) # wildcard match
+//
+// example:
+//   given patterns
+//   1) 'a.b.c'
+//   2) 'a.b.*'
+//   3)'a.b.#'
+//
+//  'a.b.c' will match 1
+//  'a.b.x' will match 2
+//  'a.b' and 'a.b.c.x' will match 3
+//
+// returns true on match and sets *payload
+bool qd_parse_tree_retrieve_match(qd_parse_node_t *node,
+                                  const qd_iterator_t *value,
+                                  void **payload);
+
+// parse tree traversal
+// visit each matching pattern that matches value in the order based on the
+// above precedence rules
+
+// return false to stop tree transversal
+typedef bool qd_parse_tree_visit_t(void *handle,
+                                   const char *pattern,
+                                   void *payload);
+
+void qd_parse_tree_search(qd_parse_node_t *node, const qd_iterator_t *value,
+                          qd_parse_tree_visit_t *callback, void *handle);
 
-// returns the payload void *
-void *parse_tree_remove_pattern(parse_node_t *node,
-                                const char *pattern);
 #endif /* parse_tree.h */

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/492ef7d4/src/router_config.c
----------------------------------------------------------------------
diff --git a/src/router_config.c b/src/router_config.c
index a0b68c4..fd1fd7b 100644
--- a/src/router_config.c
+++ b/src/router_config.c
@@ -180,14 +180,29 @@ qd_error_t qd_router_configure_lrp(qd_router_t *router, qd_entity_t *entity)
 qd_error_t qd_router_configure_address(qd_router_t *router, qd_entity_t *entity)
 {
     char *name    = 0;
-    char *prefix  = 0;
+    char *pattern = 0;
     char *distrib = 0;
+    char *prefix  = 0;
 
     do {
         name = qd_entity_opt_string(entity, "name", 0);             QD_ERROR_BREAK();
-        prefix = qd_entity_get_string(entity, "prefix");            QD_ERROR_BREAK();
         distrib = qd_entity_opt_string(entity, "distribution", 0);  QD_ERROR_BREAK();
 
+        pattern = qd_entity_opt_string(entity, "pattern", 0);
+        prefix = qd_entity_opt_string(entity, "prefix", 0);
+
+        if (prefix && pattern) {
+            qd_log(router->log_source, QD_LOG_WARNING,
+                   "Cannot set both 'prefix' and 'pattern': ignoring %s, %s",
+                   prefix, pattern);
+            break;
+        } else if (!prefix && !pattern) {
+            qd_log(router->log_source, QD_LOG_WARNING,
+                   "Must set either 'prefix' or 'pattern' attribute:"
+                   " ignoring configured address");
+            break;
+        }
+
         bool  waypoint  = qd_entity_opt_bool(entity, "waypoint", false);
         long  in_phase  = qd_entity_opt_long(entity, "ingressPhase", -1);
         long  out_phase = qd_entity_opt_long(entity, "egressPhase", -1);
@@ -208,6 +223,11 @@ qd_error_t qd_router_configure_address(qd_router_t *router, qd_entity_t *entity)
             qd_compose_insert_string(body, prefix);
         }
 
+        if (pattern) {
+            qd_compose_insert_string(body, "pattern");
+            qd_compose_insert_string(body, pattern);
+        }
+
         if (distrib) {
             qd_compose_insert_string(body, "distribution");
             qd_compose_insert_string(body, distrib);
@@ -235,6 +255,7 @@ qd_error_t qd_router_configure_address(qd_router_t *router, qd_entity_t *entity)
     free(name);
     free(prefix);
     free(distrib);
+    free(pattern);
 
     return qd_error_code();
 }

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/492ef7d4/src/router_core/agent_config_address.c
----------------------------------------------------------------------
diff --git a/src/router_core/agent_config_address.c b/src/router_core/agent_config_address.c
index c10f80e..f73ecf3 100644
--- a/src/router_core/agent_config_address.c
+++ b/src/router_core/agent_config_address.c
@@ -30,6 +30,7 @@
 #define QDR_CONFIG_ADDRESS_WAYPOINT      5
 #define QDR_CONFIG_ADDRESS_IN_PHASE      6
 #define QDR_CONFIG_ADDRESS_OUT_PHASE     7
+#define QDR_CONFIG_ADDRESS_PATTERN       8
 
 const char *qdr_config_address_columns[] =
     {"name",
@@ -40,6 +41,7 @@ const char *qdr_config_address_columns[] =
      "waypoint",
      "ingressPhase",
      "egressPhase",
+     "pattern",
      0};
 
 const char *CONFIG_ADDRESS_TYPE = "org.apache.qpid.dispatch.router.config.address";
@@ -47,7 +49,6 @@ const char *CONFIG_ADDRESS_TYPE = "org.apache.qpid.dispatch.router.config.addres
 static void qdr_config_address_insert_column_CT(qdr_address_config_t *addr, int col, qd_composed_field_t *body, bool as_map)
 {
     const char *text = 0;
-    const char *key;
 
     if (as_map)
         qd_compose_insert_string(body, qdr_config_address_columns[col]);
@@ -72,9 +73,20 @@ static void qdr_config_address_insert_column_CT(qdr_address_config_t *addr, int
         break;
 
     case QDR_CONFIG_ADDRESS_PREFIX:
-        key = (const char*) qd_hash_key_by_handle(addr->hash_handle);
-        if (key && key[0] == 'Z')
-            qd_compose_insert_string(body, &key[1]);
+        if (addr->is_prefix && addr->pattern) {
+            // Note (kgiusti): internally we prepend a '/#' to the configured
+            // prefix and treat it like a pattern.  Remove trailing '/#' to put
+            // it back into its original form
+            const size_t len = strlen(addr->pattern);
+            assert(len > 1);
+            qd_compose_insert_string_n(body, addr->pattern, len - 2);
+        } else
+            qd_compose_insert_null(body);
+        break;
+
+    case QDR_CONFIG_ADDRESS_PATTERN:
+        if (!addr->is_prefix && addr->pattern)
+            qd_compose_insert_string(body, addr->pattern);
         else
             qd_compose_insert_null(body);
         break;
@@ -284,11 +296,16 @@ void qdra_config_address_delete_CT(qdr_core_t    *core,
     qdr_agent_enqueue_response_CT(core, query);
 }
 
+void qd_parse_tree_dump(qd_parse_node_t *node, int depth);
+
 void qdra_config_address_create_CT(qdr_core_t         *core,
                                    qd_iterator_t      *name,
                                    qdr_query_t        *query,
                                    qd_parsed_field_t  *in_body)
 {
+    qd_iterator_t *iter = NULL;
+    char *buf = NULL;
+
     while (true) {
         //
         // Ensure there isn't a duplicate name and that the body is a map
@@ -318,36 +335,63 @@ void qdra_config_address_create_CT(qdr_core_t         *core,
         // Extract the fields from the request
         //
         qd_parsed_field_t *prefix_field    = qd_parse_value_by_key(in_body, qdr_config_address_columns[QDR_CONFIG_ADDRESS_PREFIX]);
+        qd_parsed_field_t *pattern_field   = qd_parse_value_by_key(in_body, qdr_config_address_columns[QDR_CONFIG_ADDRESS_PATTERN]);
         qd_parsed_field_t *distrib_field   = qd_parse_value_by_key(in_body, qdr_config_address_columns[QDR_CONFIG_ADDRESS_DISTRIBUTION]);
         qd_parsed_field_t *waypoint_field  = qd_parse_value_by_key(in_body, qdr_config_address_columns[QDR_CONFIG_ADDRESS_WAYPOINT]);
         qd_parsed_field_t *in_phase_field  = qd_parse_value_by_key(in_body, qdr_config_address_columns[QDR_CONFIG_ADDRESS_IN_PHASE]);
         qd_parsed_field_t *out_phase_field = qd_parse_value_by_key(in_body, qdr_config_address_columns[QDR_CONFIG_ADDRESS_OUT_PHASE]);
 
         //
-        // Prefix field is mandatory.  Fail if it is not here.
+        // Either a prefix or a pattern field is mandatory.  Prefix and pattern
+        // are mutually exclusive. Fail if either both or none are given.
         //
-        if (!prefix_field) {
+        if ((!prefix_field && !pattern_field) || (prefix_field && pattern_field)) {
+            query->status = QD_AMQP_BAD_REQUEST;
+            query->status.description = prefix_field ? "cannot specify both prefix and pattern"
+                : "a prefix or pattern field is mandatory";
+            qd_log(core->agent_log, QD_LOG_ERROR, "Error performing CREATE of %s: %s", CONFIG_ADDRESS_TYPE, query->status.description);
+            break;
+        }
+
+        // strip leading and trailing token separators
+        qd_iterator_t *tmp = qd_iterator_dup(qd_parse_raw(prefix_field ? prefix_field : pattern_field));
+        const int len = qd_iterator_length(tmp);
+        buf = malloc(len + 3);   // +'/#' if needed
+        char *pattern = qd_iterator_strncpy(tmp, buf, len + 1);
+        qd_iterator_free(tmp);
+
+        while (*pattern && strchr(QD_PARSE_TREE_TOKEN_SEP, *pattern))
+            pattern++;
+        while (*pattern && strchr(QD_PARSE_TREE_TOKEN_SEP, pattern[strlen(pattern) - 1]))
+            pattern[strlen(pattern) - 1] = '\0';
+
+        if (!*pattern) {
             query->status = QD_AMQP_BAD_REQUEST;
-            query->status.description = "prefix field is mandatory";
+            query->status.description = "invalid address pattern";
             qd_log(core->agent_log, QD_LOG_ERROR, "Error performing CREATE of %s: %s", CONFIG_ADDRESS_TYPE, query->status.description);
             break;
         }
 
+        if (prefix_field) {
+            // convert the old prefix value into a pattern by appending '/#'
+            // (match zero or more tokens)
+            strcat(pattern, "/#");
+        }
+
+        iter = qd_iterator_string(pattern, ITER_VIEW_ALL);
+
         //
-        // Ensure that there isn't another configured address with the same prefix
+        // Ensure that there isn't another configured address with the same pattern
         //
-        qd_iterator_t *iter = qd_parse_raw(prefix_field);
-        qd_iterator_reset_view(iter, ITER_VIEW_ADDRESS_HASH);
-        qd_iterator_annotate_prefix(iter, 'Z');
-        addr = 0;
-        qd_hash_retrieve(core->addr_hash, iter, (void**) &addr);
-        if (!!addr) {
+
+        if (qd_parse_tree_get_pattern(core->addr_parse_tree, iter, (void **)&addr)) {
             query->status = QD_AMQP_BAD_REQUEST;
             query->status.description = "Address prefix conflicts with an existing entity";
             qd_log(core->agent_log, QD_LOG_ERROR, "Error performing CREATE of %s: %s", CONFIG_ADDRESS_TYPE, query->status.description);
             break;
         }
 
+
         bool waypoint  = waypoint_field  ? qd_parse_as_bool(waypoint_field) : false;
         int  in_phase  = in_phase_field  ? qd_parse_as_int(in_phase_field)  : -1;
         int  out_phase = out_phase_field ? qd_parse_as_int(out_phase_field) : -1;
@@ -374,6 +418,7 @@ void qdra_config_address_create_CT(qdr_core_t         *core,
         //
         // The request is good.  Create the entity and insert it into the hash index and list.
         //
+
         addr = new_qdr_address_config_t();
         DEQ_ITEM_INIT(addr);
         addr->name      = name ? (char*) qd_iterator_copy(name) : 0;
@@ -381,8 +426,11 @@ void qdra_config_address_create_CT(qdr_core_t         *core,
         addr->treatment = qdra_address_treatment_CT(distrib_field);
         addr->in_phase  = in_phase;
         addr->out_phase = out_phase;
+        addr->pattern   = (char *) qd_iterator_copy(iter);
+        addr->is_prefix = !!prefix_field;
 
-        qd_hash_insert(core->addr_hash, iter, addr, &addr->hash_handle);
+        qd_iterator_reset_view(iter, ITER_VIEW_ALL);
+        qd_parse_tree_add_pattern(core->addr_parse_tree, iter, addr);
         DEQ_INSERT_TAIL(core->addr_config, addr);
 
         //
@@ -412,6 +460,11 @@ void qdra_config_address_create_CT(qdr_core_t         *core,
         qdr_agent_enqueue_response_CT(core, query);
     } else
         qdr_query_free(query);
+
+    if (buf) {
+        free(buf);
+        qd_iterator_free(iter);
+    }
 }
 
 

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/492ef7d4/src/router_core/agent_config_address.h
----------------------------------------------------------------------
diff --git a/src/router_core/agent_config_address.h b/src/router_core/agent_config_address.h
index f4caf27..9b4331a 100644
--- a/src/router_core/agent_config_address.h
+++ b/src/router_core/agent_config_address.h
@@ -33,7 +33,7 @@ void qdra_config_address_get_CT(qdr_core_t    *core,
                                 qdr_query_t   *query,
                                 const char    *qdr_config_address_columns[]);
 
-#define QDR_CONFIG_ADDRESS_COLUMN_COUNT 8
+#define QDR_CONFIG_ADDRESS_COLUMN_COUNT 9
 
 const char *qdr_config_address_columns[QDR_CONFIG_ADDRESS_COLUMN_COUNT + 1];
 

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/492ef7d4/src/router_core/connections.c
----------------------------------------------------------------------
diff --git a/src/router_core/connections.c b/src/router_core/connections.c
index df2f69e..7db6ecb 100644
--- a/src/router_core/connections.c
+++ b/src/router_core/connections.c
@@ -857,20 +857,19 @@ static char qdr_prefix_for_dir(qd_direction_t dir)
     return (dir == QD_INCOMING) ? 'C' : 'D';
 }
 
-
 qd_address_treatment_t qdr_treatment_for_address_CT(qdr_core_t *core, qdr_connection_t *conn, qd_iterator_t *iter, int *in_phase, int *out_phase)
 {
     qdr_address_config_t *addr = 0;
+    qd_iterator_view_t old_view = qd_iterator_get_view(iter);
 
-    //
-    // Set the prefix to 'Z' for configuration and do a prefix-retrieve to get the most
-    // specific match
-    //
-    qd_iterator_annotate_prefix(iter, 'Z');
+    // @TODO(kgiusti) - why not lookup exact match in has first??
+    qd_iterator_reset_view(iter, ITER_VIEW_ADDRESS_WITH_SPACE);
     if (conn && conn->tenant_space)
         qd_iterator_annotate_space(iter, conn->tenant_space, conn->tenant_space_len);
-    qd_hash_retrieve_prefix(core->addr_hash, iter, (void**) &addr);
+    qd_parse_tree_retrieve_match(core->addr_parse_tree, iter, (void **) &addr);
     qd_iterator_annotate_prefix(iter, '\0');
+    qd_iterator_reset_view(iter, old_view);
+
     if (in_phase)  *in_phase  = addr ? addr->in_phase  : 0;
     if (out_phase) *out_phase = addr ? addr->out_phase : 0;
 
@@ -905,11 +904,11 @@ qd_address_treatment_t qdr_treatment_for_address_hash_CT(qdr_core_t *core, qd_it
         //
         // Handle the mobile address case
         //
-        copy[1] = 'Z';
-        qd_iterator_t  *config_iter = qd_iterator_string(&copy[1], ITER_VIEW_ALL);
-        qdr_address_config_t *addr = 0;
 
-        qd_hash_retrieve_prefix(core->addr_hash, config_iter, (void**) &addr);
+        // @TODO(kgiusti) ask ted if tenant needs to be added first!
+        qd_iterator_t *config_iter = qd_iterator_string(&copy[2], ITER_VIEW_ADDRESS_WITH_SPACE);
+        qdr_address_config_t *addr = 0;
+        qd_parse_tree_retrieve_match(core->addr_parse_tree, config_iter, (void **) &addr);
         if (addr)
             trt = addr->treatment;
         qd_iterator_free(config_iter);

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/492ef7d4/src/router_core/route_tables.c
----------------------------------------------------------------------
diff --git a/src/router_core/route_tables.c b/src/router_core/route_tables.c
index 1110985..c42aad0 100644
--- a/src/router_core/route_tables.c
+++ b/src/router_core/route_tables.c
@@ -220,6 +220,7 @@ void qdr_route_table_setup_CT(qdr_core_t *core)
     core->addr_hash    = qd_hash(12, 32, 0);
     core->conn_id_hash = qd_hash(6, 4, 0);
     core->cost_epoch   = 1;
+    core->addr_parse_tree = qd_parse_tree_new("/");
 
     if (core->router_mode == QD_ROUTER_MODE_INTERIOR) {
         core->hello_addr      = qdr_add_local_address_CT(core, 'L', "qdhello",     QD_TREATMENT_MULTICAST_FLOOD);

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/492ef7d4/src/router_core/router_core.c
----------------------------------------------------------------------
diff --git a/src/router_core/router_core.c b/src/router_core/router_core.c
index 76e2270..13e0e62 100644
--- a/src/router_core/router_core.c
+++ b/src/router_core/router_core.c
@@ -138,7 +138,7 @@ void qdr_core_free(qdr_core_t *core)
         qdr_core_remove_address_config(core, addr_config);
     }
     qd_hash_free(core->addr_hash);
-
+    qd_parse_tree_free(core->addr_parse_tree);
     qd_hash_free(core->conn_id_hash);
     //TODO what about the actual connection identifier objects?
 
@@ -324,15 +324,20 @@ void qdr_core_remove_address(qdr_core_t *core, qdr_address_t *addr)
 
 void qdr_core_remove_address_config(qdr_core_t *core, qdr_address_config_t *addr)
 {
-    // Remove the address from the list and the hash index.
-    qd_hash_remove_by_handle(core->addr_hash, addr->hash_handle);
+    qdr_address_config_t *tmp;
+    qd_iterator_t *pattern = qd_iterator_string(addr->pattern, ITER_VIEW_ALL);
+
+    // Remove the address from the list and the parse tree
     DEQ_REMOVE(core->addr_config, addr);
+    qd_parse_tree_get_pattern(core->addr_parse_tree, pattern, (void **) &tmp);
+    assert(tmp == addr);
 
     // Free resources associated with this address.
     if (addr->name) {
         free(addr->name);
     }
-    qd_hash_handle_free(addr->hash_handle);
+    qd_iterator_free(pattern);
+    free(addr->pattern);
     free_qdr_address_config_t(addr);
 }
 

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/492ef7d4/src/router_core/router_core_private.h
----------------------------------------------------------------------
diff --git a/src/router_core/router_core_private.h b/src/router_core/router_core_private.h
index 61e5afc..da36b14 100644
--- a/src/router_core/router_core_private.h
+++ b/src/router_core/router_core_private.h
@@ -477,9 +477,10 @@ void qdr_core_remove_address(qdr_core_t *core, qdr_address_t *addr);
 
 struct qdr_address_config_t {
     DEQ_LINKS(qdr_address_config_t);
-    qd_hash_handle_t       *hash_handle;
     char                   *name;
     uint64_t                identity;
+    char                   *pattern;
+    bool                    is_prefix;
     qd_address_treatment_t  treatment;
     int                     in_phase;
     int                     out_phase;
@@ -661,6 +662,7 @@ struct qdr_core_t {
     qd_hash_t                 *conn_id_hash;
     qdr_address_list_t         addrs;
     qd_hash_t                 *addr_hash;
+    qd_parse_node_t           *addr_parse_tree;
     qdr_address_t             *hello_addr;
     qdr_address_t             *router_addr_L;
     qdr_address_t             *routerma_addr_L;

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/492ef7d4/src/router_private.h
----------------------------------------------------------------------
diff --git a/src/router_private.h b/src/router_private.h
index 533955a..4e9ac9c 100644
--- a/src/router_private.h
+++ b/src/router_private.h
@@ -32,6 +32,7 @@
 #include <qpid/dispatch/trace_mask.h>
 #include <qpid/dispatch/hash.h>
 #include <qpid/dispatch/log.h>
+#include "parse_tree.h"
 #include "dispatch_private.h"
 #include "entity.h"
 

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/492ef7d4/tests/system_tests_qdmanage.py
----------------------------------------------------------------------
diff --git a/tests/system_tests_qdmanage.py b/tests/system_tests_qdmanage.py
index 17304f1..91b38c0 100644
--- a/tests/system_tests_qdmanage.py
+++ b/tests/system_tests_qdmanage.py
@@ -49,7 +49,8 @@ class QdmanageTest(TestCase):
             ('address', {'name': 'test-address', 'prefix': 'abcd', 'distribution': 'multicast'}),
             ('linkRoute', {'name': 'test-link-route', 'prefix': 'xyz', 'dir': 'in'}),
             ('autoLink', {'name': 'test-auto-link', 'addr': 'mnop', 'dir': 'out'}),
-            ('listener', {'port': cls.tester.get_port(), 'sslProfile': 'server-ssl'})
+            ('listener', {'port': cls.tester.get_port(), 'sslProfile': 'server-ssl'}),
+            ('address', {'name': 'pattern-address', 'pattern': 'a/*/b/#/c', 'distribution': 'closest'})
         ])
 
         config_2 = Qdrouterd.Config([
@@ -239,9 +240,15 @@ class QdmanageTest(TestCase):
         long_type = 'org.apache.qpid.dispatch.router.config.address'
         query_command = 'QUERY --type=' + long_type
         output = json.loads(self.run_qdmanage(query_command))
+        self.assertEqual(len(output), 2)
         self.assertEqual(output[0]['name'], "test-address")
         self.assertEqual(output[0]['distribution'], "multicast")
         self.assertEqual(output[0]['prefix'], "abcd")
+        self.assertTrue('pattern' not in output[0])
+        self.assertEqual(output[1]['name'], "pattern-address")
+        self.assertEqual(output[1]['distribution'], "closest")
+        self.assertEqual(output[1]['pattern'], "a/*/b/#/c")
+        self.assertTrue('prefix' not in output[1])
 
     def test_check_link_route_name(self):
         long_type = 'org.apache.qpid.dispatch.router.config.linkRoute'


---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

[4/8] qpid-dispatch git commit: Merge branch 'master' into DISPATCH-731

kgiusti
In reply to this post by kgiusti
Merge branch 'master' into DISPATCH-731


Project: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/commit/778a9dd1
Tree: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/tree/778a9dd1
Diff: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/diff/778a9dd1

Branch: refs/heads/master
Commit: 778a9dd1f07389ca682469d31ad6834ecbea6612
Parents: 492ef7d ee4285f
Author: Kenneth Giusti <[hidden email]>
Authored: Tue Jul 25 16:08:48 2017 -0400
Committer: Kenneth Giusti <[hidden email]>
Committed: Tue Jul 25 16:08:48 2017 -0400

----------------------------------------------------------------------
 doc/man/qdmanage.8.adoc                        |  29 +--
 doc/man/qdrouterd.8.adoc                       |   4 +-
 doc/man/qdstat.8.adoc                          | 202 ++++++++++----------
 python/qpid_dispatch/management/client.py      |   8 +-
 python/qpid_dispatch_internal/tools/command.py |   4 +-
 tests/system_tests_qdstat.py                   |   1 +
 tests/system_tests_three_routers.py            | 188 +++++++++++++++++-
 tests/system_tests_user_id.py                  |   3 +-
 8 files changed, 319 insertions(+), 120 deletions(-)
----------------------------------------------------------------------



---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

[5/8] qpid-dispatch git commit: DISPATCH-731: add parse tree unit tests

kgiusti
In reply to this post by kgiusti
DISPATCH-731: add parse tree unit tests


Project: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/commit/f6dab30f
Tree: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/tree/f6dab30f
Diff: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/diff/f6dab30f

Branch: refs/heads/master
Commit: f6dab30fbeba609b227c1ed8eab5911428f10257
Parents: 778a9dd
Author: Kenneth Giusti <[hidden email]>
Authored: Tue Aug 1 15:07:05 2017 -0400
Committer: Kenneth Giusti <[hidden email]>
Committed: Wed Aug 2 10:28:13 2017 -0400

----------------------------------------------------------------------
 src/parse_tree.c         |  67 +++++-
 src/parse_tree.h         |   7 +-
 tests/CMakeLists.txt     |   1 +
 tests/parse_tree_tests.c | 470 ++++++++++++++++++++++++++++++++++++++++++
 tests/run_unit_tests.c   |   3 +
 5 files changed, 542 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/f6dab30f/src/parse_tree.c
----------------------------------------------------------------------
diff --git a/src/parse_tree.c b/src/parse_tree.c
index bd93ed9..3fe877a 100644
--- a/src/parse_tree.c
+++ b/src/parse_tree.c
@@ -181,6 +181,7 @@ struct qd_parse_node {
     struct qd_parse_node  *star_child;
     struct qd_parse_node  *hash_child;
     void *payload;      // data returned on match against this node
+    qd_log_source_t *log_source;
 };
 ALLOC_DECLARE(qd_parse_node_t);
 ALLOC_DEFINE(qd_parse_node_t);
@@ -195,6 +196,7 @@ static qd_parse_node_t *new_parse_node(const token_t *t)
         n->payload = NULL;
         n->pattern = NULL;
         n->star_child = n->hash_child = NULL;
+        n->log_source = qd_log_source("DEFAULT");
 
         if (t) {
             const size_t tlen = TOKEN_LEN(*t);
@@ -399,6 +401,12 @@ static bool parse_node_find(qd_parse_node_t *, token_iterator_t *,
 static bool parse_node_find_children(qd_parse_node_t *node, token_iterator_t *value,
                                      qd_parse_tree_visit_t *callback, void *handle)
 {
+    qd_log(node->log_source, QD_LOG_DEBUG,
+           "find_children token=%s pattern=%s input=%s",
+           node->token ? node->token : "NULL",
+           node->pattern ? node->pattern : "NULL",
+           value->token);
+
     if (!token_iterator_done(value)) {
 
         // check exact match first (precedence)
@@ -437,9 +445,16 @@ static bool parse_node_find_children(qd_parse_node_t *node, token_iterator_t *va
 static bool parse_node_find_token(qd_parse_node_t *node, token_iterator_t *value,
                                   qd_parse_tree_visit_t *callback, void *handle)
 {
+    qd_log(node->log_source, QD_LOG_DEBUG,
+           "find_token token=%s pattern=%s input=%s",
+           node->token ? node->token : "NULL",
+           node->pattern ? node->pattern : "NULL",
+           value->token);
+
     if (token_iterator_done(value) && node->pattern) {
         // exact match current node
-        return callback(handle, node->pattern, node->payload);
+        if (!callback(handle, node->pattern, node->payload))
+            return false;
     }
 
     // no payload or more tokens.  Continue to lower sub-trees. Even if no more
@@ -453,6 +468,12 @@ static bool parse_node_find_token(qd_parse_node_t *node, token_iterator_t *value
 static bool parse_node_find_star(qd_parse_node_t *node, token_iterator_t *value,
                                  qd_parse_tree_visit_t *callback, void *handle)
 {
+    qd_log(node->log_source, QD_LOG_DEBUG,
+           "find_star token=%s pattern=%s input=%s",
+           node->token ? node->token : "NULL",
+           node->pattern ? node->pattern : "NULL",
+           value->token);
+
     // must match exactly one token:
     if (token_iterator_done(value))
         return true;  // no match here, but continue searching
@@ -476,6 +497,12 @@ static bool parse_node_find_star(qd_parse_node_t *node, token_iterator_t *value,
 static bool parse_node_find_hash(qd_parse_node_t *node, token_iterator_t *value,
                                  qd_parse_tree_visit_t *callback, void *handle)
 {
+    qd_log(node->log_source, QD_LOG_DEBUG,
+           "find_hash token=%s pattern=%s input=%s",
+           node->token ? node->token : "NULL",
+           node->pattern ? node->pattern : "NULL",
+           value->token);
+
     // consume each token and look for a match on the
     // remaining key.
     while (!token_iterator_done(value)) {
@@ -496,6 +523,12 @@ static bool parse_node_find_hash(qd_parse_node_t *node, token_iterator_t *value,
 static bool parse_node_find(qd_parse_node_t *node, token_iterator_t *value,
                             qd_parse_tree_visit_t *callback, void *handle)
 {
+    qd_log(node->log_source, QD_LOG_DEBUG,
+           "node_find token=%s pattern=%s input=%s",
+           node->token ? node->token : "NULL",
+           node->pattern ? node->pattern : "NULL",
+           value->token);
+
     if (node->is_star)
         return parse_node_find_star(node, value, callback, handle);
     else if (node->is_hash)
@@ -578,7 +611,7 @@ void *qd_parse_tree_add_pattern(qd_parse_node_t *node,
     char *str = (char *)qd_iterator_copy(dup);
 
     normalize_pattern(str);
-    qd_log(qd_log_source("DEFAULT"), QD_LOG_DEBUG,
+    qd_log(node->log_source, QD_LOG_DEBUG,
            "Parse tree add address pattern '%s'", str);
 
     token_iterator_init(&key, str);
@@ -601,7 +634,7 @@ bool qd_parse_tree_get_pattern(qd_parse_node_t *node,
     char *str = (char *)qd_iterator_copy(dup);
 
     normalize_pattern((char *)str);
-    qd_log(qd_log_source("DEFAULT"), QD_LOG_DEBUG,
+    qd_log(node->log_source, QD_LOG_DEBUG,
            "Parse tree get address pattern '%s'", str);
 
     token_iterator_init(&key, str);
@@ -624,7 +657,7 @@ void *qd_parse_tree_remove_pattern(qd_parse_node_t *node,
     char *str = (char *)qd_iterator_copy(dup);
 
     normalize_pattern(str);
-    qd_log(qd_log_source("DEFAULT"), QD_LOG_DEBUG,
+    qd_log(node->log_source, QD_LOG_DEBUG,
            "Parse tree remove address pattern '%s'", str);
 
     token_iterator_init(&key, str);
@@ -634,6 +667,32 @@ void *qd_parse_tree_remove_pattern(qd_parse_node_t *node,
 }
 
 
+bool qd_parse_tree_walk(qd_parse_node_t *node, qd_parse_tree_visit_t *callback, void *handle)
+{
+    if (node->pattern) {  // terminal node for pattern
+        if (!callback(handle, node->pattern, node->payload))
+            return false;
+    }
+
+    qd_parse_node_t *child = DEQ_HEAD(node->children);
+    while (child) {
+        if (!qd_parse_tree_walk(child, callback, handle))
+            return false;
+        child = DEQ_NEXT(child);
+    }
+
+    if (node->star_child)
+        if (!qd_parse_tree_walk(node->star_child, callback, handle))
+            return false;
+
+    if (node->hash_child)
+        if (!qd_parse_tree_walk(node->hash_child, callback, handle))
+            return false;
+
+    return true;
+}
+
+
 #if 0
 #include <stdio.h>
 void qd_parse_tree_dump(qd_parse_node_t *node, int depth)

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/f6dab30f/src/parse_tree.h
----------------------------------------------------------------------
diff --git a/src/parse_tree.h b/src/parse_tree.h
index d616e48..2e53269 100644
--- a/src/parse_tree.h
+++ b/src/parse_tree.h
@@ -71,15 +71,18 @@ bool qd_parse_tree_retrieve_match(qd_parse_node_t *node,
                                   void **payload);
 
 // parse tree traversal
-// visit each matching pattern that matches value in the order based on the
-// above precedence rules
 
 // return false to stop tree transversal
 typedef bool qd_parse_tree_visit_t(void *handle,
                                    const char *pattern,
                                    void *payload);
 
+// visit each matching pattern that matches value in the order based on the
+// above precedence rules
 void qd_parse_tree_search(qd_parse_node_t *node, const qd_iterator_t *value,
                           qd_parse_tree_visit_t *callback, void *handle);
 
+// visit each terminal node on the tree, returns last value returned by callback
+bool qd_parse_tree_walk(qd_parse_node_t *node, qd_parse_tree_visit_t *callback, void *handle);
+
 #endif /* parse_tree.h */

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/f6dab30f/tests/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index bc62232..8a5cbd1 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -32,6 +32,7 @@ set(unit_test_SOURCES
     tool_test.c
     failoverlist_test.c
     timer_test.c
+    parse_tree_tests
     )
 if (USE_MEMORY_POOL)
   list(APPEND unit_test_SOURCES alloc_test.c)

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/f6dab30f/tests/parse_tree_tests.c
----------------------------------------------------------------------
diff --git a/tests/parse_tree_tests.c b/tests/parse_tree_tests.c
new file mode 100644
index 0000000..347f6df
--- /dev/null
+++ b/tests/parse_tree_tests.c
@@ -0,0 +1,470 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 "test_case.h"
+#include <stdio.h>
+#include <string.h>
+#include "parse_tree.h"
+
+
+static char *test_add_remove(void *context)
+{
+    qd_iterator_t *piter = qd_iterator_string("I.am.Sam", ITER_VIEW_ALL);
+    qd_iterator_t *piter2 = qd_iterator_string("Sam.I.Am", ITER_VIEW_ALL);
+    qd_parse_node_t *node = qd_parse_tree_new();
+    void *payload;
+
+    if (qd_parse_tree_remove_pattern(node, piter))
+        return "Failed to remove a non-existing pattern";
+
+    if (qd_parse_tree_get_pattern(node, piter, &payload))
+        return "Got a non-existing pattern";
+
+    if (qd_parse_tree_add_pattern(node, piter, "Hi Sam"))
+        return "Add returned existing value";
+
+    if (qd_parse_tree_add_pattern(node, piter2, "Bye Sam"))
+        return "Add returned existing value";
+
+    if (!qd_parse_tree_get_pattern(node, piter, &payload))
+        return "Could not get pattern";
+
+    if (!payload || strcmp("Hi Sam", (char *)payload))
+        return "Got bad pattern";
+
+    if (!qd_parse_tree_get_pattern(node, piter2, &payload))
+        return "Could not get pattern";
+
+    if (!payload || strcmp("Bye Sam", (char *)payload))
+        return "Got bad pattern";
+
+    if (!qd_parse_tree_remove_pattern(node, piter))
+        return "Failed to remove an existing pattern";
+
+    if (!qd_parse_tree_remove_pattern(node, piter2))
+        return "Failed to remove an existing pattern";
+
+    qd_parse_tree_free(node);
+    qd_iterator_free(piter);
+    qd_iterator_free(piter2);
+    return NULL;
+}
+
+// for pattern match callback
+typedef struct {
+    int count;
+    const char **patterns;
+    void **payloads;
+} visit_handle_t;
+
+
+// callback to visit all matching patterns in tree
+static bool visit_all(void *handle,
+                      const char *pattern,
+                      void *payload)
+{
+    visit_handle_t *h = (visit_handle_t *)handle;
+    h->patterns[h->count] = pattern;
+    h->payloads[h->count] = payload;
+    h->count++;
+    return true;
+}
+
+
+// callback to return first (best) match
+static bool find_best(void *handle,
+                      const char *pattern,
+                      void *payload)
+{
+    visit_handle_t *h = (visit_handle_t *)handle;
+    h->patterns[0] = pattern;
+    h->payloads[0] = payload;
+    h->count = 1;
+    return false;
+}
+
+
+// check if input patterns are correctly "normalized" (see parse_tree.c)
+static char *check_normalize(const char *input,
+                             const char *expected)
+{
+    const char *patterns[1];
+    void *payloads[1];
+    visit_handle_t vh = {0, patterns, payloads};
+    qd_parse_node_t *node = qd_parse_tree_new();
+    qd_iterator_t *iter = qd_iterator_string(input, ITER_VIEW_ALL);
+    void *payload;
+
+    if (qd_parse_tree_add_pattern(node, iter, (void *)input) != NULL)
+        return "Unexpected duplicate pattern";
+    if (!qd_parse_tree_get_pattern(node, iter, &payload))
+        return "Could not find added pattern";
+    if (!payload || strcmp((const char *)payload, input))
+        return "Failed to find pattern";
+
+    qd_parse_tree_walk(node, visit_all, &vh);
+    if (vh.count != 1)
+        return "Did not find expected pattern";
+    if (strcmp(vh.payloads[0], input))
+        return "Unexpected payload!";
+    if (strcmp(vh.patterns[0], expected)) {
+        fprintf(stderr, "%s %s\n", vh.patterns[0], expected);
+        return "Incorrect normalization";
+    }
+
+    payload = qd_parse_tree_remove_pattern(node, iter);
+    if (!payload || strcmp((const char *)payload, input))
+        return "Failed to remove pattern";
+
+    qd_parse_tree_free(node);
+    qd_iterator_free(iter);
+    return NULL;
+}
+
+
+static char *test_normalization(void *context)
+{
+    char *rc = NULL;
+    char *patterns[][2] = {
+        // normalized  raw
+        {"",          ""},
+        {"a.b.c",     "a.b.c"},
+        {"a.*.c",     "a.*.c"},
+        {"#",         "#"},
+        {"#",         "#.#.#.#"},
+        {"*.*.*.#",   "#.*.#.*.#.#.*"},
+        {"a.*.*.*.#", "a.*.#.*.#.*.#"},
+        {"a.*.*.*.#", "a.*.#.*.#.*"},
+        {"*.*.*.#",   "*.#.#.*.*.#"},
+        {NULL, NULL}
+    };
+
+    for (int i = 0; !rc && patterns[i][0]; i++)
+        rc = check_normalize(patterns[i][1], patterns[i][0]);
+
+    return rc;
+}
+
+
+typedef struct {
+    const char *address;
+    bool match;
+} match_test_t;
+
+static char *match_test(const char *pattern,
+                        const match_test_t *tests)
+{
+    char *rc = NULL;
+    qd_iterator_t *piter = qd_iterator_string(pattern, ITER_VIEW_ALL);
+    qd_parse_node_t *node = qd_parse_tree_new();
+    void *payload = (void *)"found";
+
+    if (qd_parse_tree_add_pattern(node, piter, payload))
+        return "Unexpected payload when adding pattern";
+
+    for (int i = 0; tests[i].address && !rc; i++) {
+        qd_iterator_t *iter = qd_iterator_string(tests[i].address, ITER_VIEW_ALL);
+        bool match = (int)qd_parse_tree_retrieve_match(node, iter, &payload);
+        if (match != tests[i].match) {
+            printf("match address '%s' to pattern '%s': expected %d got %d\n",
+                   tests[i].address, pattern, (int)tests[i].match, (int)match);
+            return "Match test failed";
+        }
+        qd_iterator_free(iter);
+    }
+
+    qd_parse_tree_free(node);
+    qd_iterator_free(piter);
+    return NULL;
+}
+
+
+// check various pattern matches
+static char *test_matches(void *context)
+{
+    match_test_t test1[] = {
+        { "ab.cd.e",   true},
+        { "abx.cd.e",  false},
+        { "ab.cd",     false},
+        { "ab.cd.ef.", false},
+        { "ab.cd.E",   false},
+        { "x.ab.cd.e", false},
+        {NULL, false}
+    };
+
+    char *rc = match_test("ab.cd.e", test1);
+    if (rc) return rc;
+
+    match_test_t test2[] = {
+        {"", true},
+        {NULL, false},
+    };
+    rc = match_test("", test2);
+    if (rc) return rc;
+
+    match_test_t test3[] = {
+        {".", true},
+        {NULL, false},
+    };
+    rc = match_test(".", test3);
+    if (rc) return rc;
+
+    match_test_t test4[] = {
+        {"a.xx.b", true},
+        {"a.b", false},
+        {NULL, false}
+    };
+    rc = match_test("a.*.b", test4);
+    if (rc) return rc;
+
+    match_test_t test5[] = {
+        {"y.x", true},
+        {"x",   false},
+        {"x.y", false},
+        {NULL,  false}
+    };
+    rc = match_test("*.x", test5);
+    if (rc) return rc;
+
+    match_test_t test6[] = {
+        {"x.x.y", true},
+        {"x.x",   false},
+        {"y.x.x", false},
+        {"q.x.y", false},
+        {NULL, false}
+    };
+    rc = match_test("x.x.*", test6);
+    if (rc) return rc;
+
+
+    match_test_t test7[] = {
+        {"a.b", true},
+        {"a.x.b", true},
+        {"a..x.y.zz.b", true},
+        {"a.b.z", false},
+        {"z.a.b", false},
+        {"q.x.b", false},
+        {NULL, false}
+    };
+    rc = match_test("a.#.b", test7);
+    if (rc) return rc;
+
+    match_test_t test8[] = {
+        {"a", true},
+        {"a.b", true},
+        {"a.b.c", true},
+        {"b.a", false},
+        {NULL, false}
+    };
+    rc = match_test("a.#", test8);
+    if (rc) return rc;
+
+    match_test_t test9[] = {
+        {"a", true},
+        {"x.y.a", true},
+        {"a.b", false},
+        {NULL, false}
+    };
+    rc = match_test("#.a", test9);
+    if (rc) return rc;
+
+    match_test_t test10[] = {
+        {"a.b.c", true},
+        {"a.x.b.y.c", true},
+        {"a.x.x.b.y.y.c", true},
+        {"a.b", false},
+        {NULL, false}
+    };
+    rc = match_test("a.#.b.#.c", test10);
+    if (rc) return rc;
+
+    match_test_t test11[] = {
+        {"a.x.y", true},
+        {"a.x.p.qq.y", true},
+        {"a.a.x.y", false},
+        {"aa.x.b.c", false},
+        {"x.p.qq.y", false},
+        {"a.x.p.qq.y.b", false},
+        {NULL, false}
+    };
+    rc = match_test("*.x.#.y", test11);
+    if (rc) return rc;
+
+    match_test_t test12[] = {
+        {"a.b.x", true},
+        {"a.x.x.x.b.x", true},
+        {"a.b.b.b.b.y", true},
+        {"a.b.b.b.b", true},
+        {"a.b", false},
+        {NULL, false}
+    };
+    rc = match_test("a.#.b.*", test12);
+    if (rc) return rc;
+
+    match_test_t test13[] = {
+        {"x/y/z", true},
+        {"x.y.z/a.b.c", true},
+        {"x.y/z", true},
+        {"x.y", false},
+        {"x/y", false},
+        {"x", false},
+        {NULL, false}
+    };
+    rc = match_test("*.*.*.#", test13);
+    if (rc) return rc;
+
+    match_test_t test14[] = {
+        {"x", false},
+        {"x.y", true},
+        {"x.y.z", true},
+        {"x.y.z.y.z.y.z", true},
+        {NULL, false}
+    };
+    rc = match_test("*/#/*", test14);
+
+    return rc;
+}
+
+// For debug (see parse_tree.c)
+// void qd_parse_tree_dump(qd_parse_node_t *node, int depth);
+
+// search a full parse tree for multiple and best matches
+static char *test_multiple_matches(void *context)
+{
+#define PCOUNT 17
+    const char *patterns[PCOUNT] =
+      { "alpha",
+        "bravo",
+        "alpha.bravo",
+        "bravo.charlie",
+        "alpha.bravo.charlie.delta",
+        "bravo.charlie.delta.echo",
+        "alpha.*",
+        "alpha.#",
+        "alpha.*.#",
+        "#.bravo",
+        "*.bravo",
+        "*.#.bravo",
+        "alpha.*.bravo",
+        "alpha.#.bravo",
+        "alpha.*.#.bravo",
+        "*.bravo.*",
+        "#.bravo.#",
+      };
+    const char *_patterns[PCOUNT] = {NULL};
+    void *_payloads[PCOUNT] = {NULL};
+    visit_handle_t vh = {0, _patterns, _payloads};
+    qd_parse_node_t *node = qd_parse_tree_new();
+
+    // build the tree
+    for (int i = 0; i < PCOUNT; i++) {
+        qd_iterator_t *pattern = qd_iterator_string(patterns[i], ITER_VIEW_ALL);
+        if (qd_parse_tree_add_pattern(node, pattern, (void *)patterns[i])) {
+            printf("Failed to add pattern %s to parse tree\n", patterns[i]);
+            return "failed adding pattern to tree";
+        }
+        qd_iterator_free(pattern);
+    }
+
+    {
+        // read all patterns and verify all are present
+        qd_parse_tree_walk(node, visit_all, &vh);
+        if (vh.count != PCOUNT)
+            return "Not all patterns in tree";
+        for (int i = 0; i < PCOUNT; i++) {
+            bool found = false;
+            for (int j = 0; j < PCOUNT; j++) {
+                if (strcmp(patterns[i], vh.patterns[j]) == 0)
+                    found = true;
+            }
+            if (!found)
+                return "All patterns not visited";
+        }
+    }
+
+    // matches are listed in order of best->least best match
+    struct {
+        const char *address;
+        const int   count;
+        const char *matches[PCOUNT];
+    } tests[] = {
+        {"alpha",       2, {"alpha", "alpha.#"}},
+        {"alpha.zulu",  3, { "alpha.*", "alpha.*.#", "alpha.#"}},
+        {"alpha.bravo", 9, {"alpha.bravo", "alpha.*", "alpha.*.#", "alpha.#.bravo", "alpha.#", "*.bravo", "*.#.bravo", "#.bravo", "#.bravo.#"}},
+        {"bravo",       3, {"bravo", "#.bravo",  "#.bravo.#"}},
+        {"xray.bravo",  4, {"*.bravo", "*.#.bravo", "#.bravo", "#.bravo.#"}},
+        {"alpha.bravo.charlie",         4, {"alpha.*.#", "alpha.#", "*.bravo.*", "#.bravo.#"}},
+        {"xray.yankee.zulu.bravo",      3, {"*.#.bravo", "#.bravo", "#.bravo.#"}},
+        {"alpha.bravo.charlie.delta",   4, {"alpha.bravo.charlie.delta", "alpha.*.#","alpha.#", "#.bravo.#"}},
+        {"alpha.charlie.charlie.bravo", 7, {"alpha.*.#.bravo", "alpha.*.#", "alpha.#.bravo", "alpha.#", "*.#.bravo", "#.bravo", "#.bravo.#"}},
+        {"xray.yankeee.zulu.bravo.alpha.bravo.charlie", 2, {"#.bravo.#", "#.bravo.#"}},
+        {"I.match.nothing", 0, {NULL}},
+        {NULL, 0, {NULL}}
+    };
+
+    // verify all matching patterns are hit and in the correct order
+    for (int k = 0; tests[k].address; k++) {
+        qd_iterator_t *find_me = qd_iterator_string(tests[k].address, ITER_VIEW_ALL);
+        vh.count = 0;
+        qd_parse_tree_search(node, find_me, visit_all, (void *)&vh);
+        // printf("Matches for %s:\n", tests[k].address);
+        // for (int i = 0; i < vh.count; i++)
+        //    printf("%s, ", vh.patterns[i]);
+        // printf("count = %d\n", vh.count);
+        if (vh.count != tests[k].count)
+            return "Unexpected match count";
+        for (int i = 0; i < tests[k].count; i++) {
+            if (strcmp(vh.patterns[i], tests[k].matches[i]))
+                return "Unexpected pattern match";
+        }
+        qd_iterator_free(find_me);
+    }
+
+    // verify 'best' match is found
+    for (int k = 0; tests[k].address; k++) {
+        qd_iterator_t *find_me = qd_iterator_string(tests[k].address, ITER_VIEW_ALL);
+        vh.count = 0;
+        qd_parse_tree_search(node, find_me, find_best, (void *)&vh);
+        // printf("best match for %s: %s\n", tests[k].address, vh.patterns[0]);
+        if (tests[k].count == 0) {
+            if (vh.count != 0) {
+                return "Did not expect to find a best match!";
+            }
+        } else if (vh.count == 0 || strcmp(vh.patterns[0], tests[k].matches[0]))
+                return "Unexpected best pattern match";
+        qd_iterator_free(find_me);
+    }
+
+    qd_parse_tree_free(node);
+
+    return NULL;
+}
+
+
+int parse_tree_tests(void)
+{
+    int result = 0;
+    char *test_group = "parse_tree_tests";
+
+    TEST_CASE(test_add_remove, 0);
+    TEST_CASE(test_normalization, 0);
+    TEST_CASE(test_matches, 0);
+    TEST_CASE(test_multiple_matches, 0);
+    return result;
+}

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/f6dab30f/tests/run_unit_tests.c
----------------------------------------------------------------------
diff --git a/tests/run_unit_tests.c b/tests/run_unit_tests.c
index c8c4ef3..4c2b469 100644
--- a/tests/run_unit_tests.c
+++ b/tests/run_unit_tests.c
@@ -29,6 +29,7 @@ int alloc_tests(void);
 int compose_tests(void);
 int policy_tests(void);
 int failoverlist_tests(void);
+int parse_tree_tests(void);
 
 int main(int argc, char** argv)
 {
@@ -60,6 +61,8 @@ int main(int argc, char** argv)
 #endif
     result += policy_tests();
     result += failoverlist_tests();
+    result += parse_tree_tests();
+
     qd_dispatch_free(qd);       // dispatch_free last.
 
     return result;


---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

[6/8] qpid-dispatch git commit: DISPATCH-731: add functional tests

kgiusti
In reply to this post by kgiusti
DISPATCH-731: add functional tests


Project: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/commit/2c80f108
Tree: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/tree/2c80f108
Diff: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/diff/2c80f108

Branch: refs/heads/master
Commit: 2c80f10877c2837af48244ff0315a12f7042eda3
Parents: f6dab30
Author: Kenneth Giusti <[hidden email]>
Authored: Wed Aug 2 12:21:33 2017 -0400
Committer: Kenneth Giusti <[hidden email]>
Committed: Thu Aug 3 10:06:04 2017 -0400

----------------------------------------------------------------------
 tests/system_tests_qdmanage.py    | 54 +++++++++++++++++++++++-
 tests/system_tests_two_routers.py | 77 ++++++++++++++++++++++++++++++++++
 2 files changed, 130 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/2c80f108/tests/system_tests_qdmanage.py
----------------------------------------------------------------------
diff --git a/tests/system_tests_qdmanage.py b/tests/system_tests_qdmanage.py
index 91b38c0..9b92dbc 100644
--- a/tests/system_tests_qdmanage.py
+++ b/tests/system_tests_qdmanage.py
@@ -351,7 +351,59 @@ class QdmanageTest(TestCase):
                          ' name=' + ssl_profile_name + ' certDb=' + self.ssl_file('ca-certificate.pem')
         output = json.loads(self.run_qdmanage(ssl_create_command))
         self.assertEqual(output['name'], ssl_profile_name)
-        self.run_qdmanage('DELETE --type=sslProfile --name=' + ssl_profile_name)
+        self.run_qdmanage('DELETE --type=sslProfile --name=' +
+        ssl_profile_name)
+
+    def test_create_delete_address_pattern(self):
+        config = [('mercury.*.earth.#', 'closest'),
+                  ('*/mars/*/#', 'multicast'),
+                  ('*.mercury', 'closest'),
+                  ('*/#/pluto', 'multicast')]
+        long_type = 'org.apache.qpid.dispatch.router.config.address'
+
+        # add patterns:
+        pcount = 0
+        for p in config:
+            query_command = 'CREATE --type=' + long_type + \
+                                             ' pattern=' + p[0] + \
+                                             ' distribution=' + p[1] + \
+                                             ' name=Pattern' + str(pcount)
+            self.run_qdmanage(query_command)
+            pcount += 1
+
+        # verify correctly added:
+        query_command = 'QUERY --type=' + long_type
+        output = json.loads(self.run_qdmanage(query_command))
+        total = len(output)
+
+        pcount = 0
+        for o in output:
+            pattern = o.get('pattern')
+            if pattern is not None:
+                for p in config:
+                    if p[0] == pattern:
+                        pcount += 1
+                        self.assertEqual(p[1], o.get('distribution'))
+        self.assertEqual(pcount, len(config))
+
+        # delete
+        pcount = 0
+        for p in config:
+            query_command = 'DELETE --type=' + long_type + \
+                                             ' --name=Pattern' + str(pcount)
+            self.run_qdmanage(query_command)
+            pcount += 1
+
+        # verify deleted:
+        query_command = 'QUERY --type=' + long_type
+        output = json.loads(self.run_qdmanage(query_command))
+        self.assertEqual(len(output), total - len(config))
+        for o in output:
+            pattern = o.get('pattern')
+            if pattern is not None:
+                for p in config:
+                    self.assertNotEqual(p[0], pattern)
+
 
 if __name__ == '__main__':
     unittest.main(main_module())

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/2c80f108/tests/system_tests_two_routers.py
----------------------------------------------------------------------
diff --git a/tests/system_tests_two_routers.py b/tests/system_tests_two_routers.py
index db2b20b..cebc74a 100644
--- a/tests/system_tests_two_routers.py
+++ b/tests/system_tests_two_routers.py
@@ -57,6 +57,20 @@ class RouterTest(TestCase):
                 ('address', {'prefix': 'closest', 'distribution': 'closest'}),
                 ('address', {'prefix': 'spread', 'distribution': 'balanced'}),
                 ('address', {'prefix': 'multicast', 'distribution': 'multicast'}),
+
+                # for testing pattern matching
+                ('address', {'pattern': 'a.b.c.d',
+                             'distribution': 'closest'}),
+                ('address', {'pattern': '#.b.c.d',
+                             'distribution': 'multicast'}),
+                ('address', {'pattern': 'a/*/#/d',
+                             'distribution': 'closest'}),
+                ('address', {'pattern': '*/b/c/d',
+                             'distribution': 'multicast'}),
+                ('address', {'pattern': 'a.x.d',
+                             'distribution': 'closest'}),
+                ('address', {'pattern': 'a.*.d',
+                             'distribution': 'multicast'}),
                 connection
             ]
 
@@ -1090,6 +1104,69 @@ class RouterTest(TestCase):
         M1.stop()
         M2.stop()
 
+    def test_17_address_wildcard(self):
+        # verify proper distribution is selected by wildcard
+        addresses = [
+            # (address, count of messages expected to be received)
+            ('a.b.c.d',   1), # closest 'a.b.c.d'
+            ('b.c.d',     2), # multi   '#.b.c.d'
+            ('f.a.b.c.d', 2), # multi   '#.b.c.d
+            ('a.c.d',     2), # multi   'a.*.d'
+            ('a/c/c/d',   1), # closest 'a/*/#.d
+            ('a/x/z/z/d', 1), # closest 'a/*/#.d
+            ('a/x/d',     1), # closest 'a.x.d'
+            ('a.x.e',     1), # balanced  ----
+            ('m.b.c.d',   2)  # multi   '*/b/c/d'
+        ]
+
+        # two receivers per address - one for each router
+        receivers = []
+        for a in addresses:
+            for x in range(2):
+                M = self.messenger(timeout=0.1)
+                M.route("amqp:/*", self.routers[x].addresses[0]+"/$1")
+                M.start()
+                M.subscribe('amqp:/' + a[0])
+                receivers.append(M)
+            self.routers[0].wait_address(a[0], 1, 1)
+            self.routers[1].wait_address(a[0], 1, 1)
+
+        # single sender sends one message to each address
+        M1 = self.messenger()
+        M1.route("amqp:/*", self.routers[0].addresses[0]+"/$1")
+        M1.start()
+        for a in addresses:
+            tm = Message()
+            tm.address = 'amqp:/' + a[0]
+            tm.body = {'address': a[0]}
+            M1.put(tm)
+            M1.send()
+
+        # gather all received messages
+        msgs_recvd = {}
+        rm = Message()
+        for M in receivers:
+            try:
+                while True:
+                    M.recv(1)
+                    M.get(rm)
+                    index = rm.body.get('address', "ERROR")
+                    if index not in msgs_recvd:
+                        msgs_recvd[index] = 0
+                    msgs_recvd[index] += 1
+            except Exception as exc:
+                self.assertTrue("None" in str(exc))
+
+        # verify expected count == actual count
+        self.assertTrue("ERROR" not in msgs_recvd)
+        for a in addresses:
+            self.assertTrue(a[0] in msgs_recvd)
+            self.assertEqual(a[1], msgs_recvd[a[0]])
+
+        M1.stop()
+        for M in receivers:
+            M.stop()
+
 
 class Timeout(object):
     def __init__(self, parent):


---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

[7/8] qpid-dispatch git commit: DISPATCH-731: fix an iterator leak

kgiusti
In reply to this post by kgiusti
DISPATCH-731: fix an iterator leak


Project: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/commit/e87c87a0
Tree: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/tree/e87c87a0
Diff: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/diff/e87c87a0

Branch: refs/heads/master
Commit: e87c87a087a17ef3d3cc4fddb7f58bfe7ed6853a
Parents: 2c80f10
Author: Kenneth Giusti <[hidden email]>
Authored: Tue Aug 8 13:24:36 2017 -0400
Committer: Kenneth Giusti <[hidden email]>
Committed: Tue Aug 8 13:24:36 2017 -0400

----------------------------------------------------------------------
 src/parse_tree.c | 1 +
 1 file changed, 1 insertion(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/e87c87a0/src/parse_tree.c
----------------------------------------------------------------------
diff --git a/src/parse_tree.c b/src/parse_tree.c
index 3fe877a..9ba78c4 100644
--- a/src/parse_tree.c
+++ b/src/parse_tree.c
@@ -663,6 +663,7 @@ void *qd_parse_tree_remove_pattern(qd_parse_node_t *node,
     token_iterator_init(&key, str);
     rc = parse_node_remove_pattern(node, &key, str);
     free(str);
+    qd_iterator_free(dup);
     return rc;
 }
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

[8/8] qpid-dispatch git commit: Merge branch 'DISPATCH-731'

kgiusti
In reply to this post by kgiusti
Merge branch 'DISPATCH-731'

This closes #181


Project: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/commit/043c5ccf
Tree: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/tree/043c5ccf
Diff: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/diff/043c5ccf

Branch: refs/heads/master
Commit: 043c5ccf8c51786afb0baee6ca00e7816cfcdf9e
Parents: 4dd8764 e87c87a
Author: Kenneth Giusti <[hidden email]>
Authored: Wed Aug 9 13:19:31 2017 -0400
Committer: Kenneth Giusti <[hidden email]>
Committed: Wed Aug 9 13:51:29 2017 -0400

----------------------------------------------------------------------
 include/qpid/dispatch/compose.h                 |   1 +
 python/qpid_dispatch/management/qdrouter.json   |  10 +-
 .../qpid_dispatch_internal/management/config.py |  17 +-
 src/CMakeLists.txt                              |   1 +
 src/compose.c                                   |  10 +-
 src/parse_tree.c                                | 732 +++++++++++++++++++
 src/parse_tree.h                                |  88 +++
 src/router_config.c                             |  25 +-
 src/router_core/agent_config_address.c          |  83 ++-
 src/router_core/agent_config_address.h          |   2 +-
 src/router_core/connections.c                   |  21 +-
 src/router_core/route_tables.c                  |   1 +
 src/router_core/router_core.c                   |  13 +-
 src/router_core/router_core_private.h           |   4 +-
 src/router_private.h                            |   1 +
 tests/CMakeLists.txt                            |   1 +
 tests/parse_tree_tests.c                        | 470 ++++++++++++
 tests/run_unit_tests.c                          |   3 +
 tests/system_tests_qdmanage.py                  |  63 +-
 tests/system_tests_two_routers.py               |  77 ++
 20 files changed, 1578 insertions(+), 45 deletions(-)
----------------------------------------------------------------------



---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Loading...