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 <kgiu...@apache.org>
Authored: Thu Jul 13 11:55:41 2017 -0400
Committer: Kenneth Giusti <kgiu...@apache.org>
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: commits-unsubscr...@qpid.apache.org
For additional commands, e-mail: commits-h...@qpid.apache.org

Reply via email to