tomcat-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rj...@apache.org
Subject svn commit: r479316 [2/2] - /tomcat/connectors/trunk/jk/native/common/jk_status.c
Date Sun, 26 Nov 2006 09:17:17 GMT

Modified: tomcat/connectors/trunk/jk/native/common/jk_status.c
URL: http://svn.apache.org/viewvc/tomcat/connectors/trunk/jk/native/common/jk_status.c?view=diff&rev=479316&r1=479315&r2=479316
==============================================================================
--- tomcat/connectors/trunk/jk/native/common/jk_status.c (original)
+++ tomcat/connectors/trunk/jk/native/common/jk_status.c Sun Nov 26 01:17:17 2006
@@ -38,6 +38,82 @@
 
 #define HUGE_BUFFER_SIZE (8*1024)
 
+/**
+ * Command line reference:
+ * cmd=list (default) display configuration
+ * cmd=show display detailed configuration
+ * cmd=edit form to change configuration
+ * cmd=update commit update configuration
+ * cmd=reset reset lb runtime states, or lb member runtime states
+ * Query arguments:
+ * re=n (refresh time in seconds, n=0: disabled)
+ * w=worker (cmd should be executed for worker "worker")
+ * sw=sub_worker (cmd should be executed for "sub_worker" of worker "worker")
+ * from=lastcmd (the last viewing command was "lastcmd")
+ */
+
+#define JK_STATUS_ARG_CMD                  ("cmd")
+#define JK_STATUS_ARG_MIME                 ("mime")
+#define JK_STATUS_ARG_FROM                 ("from")
+#define JK_STATUS_ARG_REFRESH              ("re")
+#define JK_STATUS_ARG_WORKER               ("w")
+#define JK_STATUS_ARG_WORKER_MEMBER        ("sw")
+#define JK_STATUS_ARG_LB_MEMBER_ATT        ("att")
+
+#define JK_STATUS_ARG_LB_RETRIES           ("lr")
+#define JK_STATUS_ARG_LB_RECOVER_TIME      ("lt")
+#define JK_STATUS_ARG_LB_STICKY            ("ls")
+#define JK_STATUS_ARG_LB_STICKY_FORCE      ("lf")
+#define JK_STATUS_ARG_LB_METHOD            ("lm")
+#define JK_STATUS_ARG_LB_LOCK              ("ll")
+
+#define JK_STATUS_ARG_LB_TEXT_RETRIES      ("Retries")
+#define JK_STATUS_ARG_LB_TEXT_RECOVER_TIME ("Recover Wait Time")
+#define JK_STATUS_ARG_LB_TEXT_STICKY       ("Sticky Sessions")
+#define JK_STATUS_ARG_LB_TEXT_STICKY_FORCE ("Force Sticky Sessions")
+#define JK_STATUS_ARG_LB_TEXT_METHOD       ("LB Method")
+#define JK_STATUS_ARG_LB_TEXT_LOCK         ("Locking")
+
+#define JK_STATUS_ARG_LBM_ACTIVATION       ("wa")
+#define JK_STATUS_ARG_LBM_FACTOR           ("wf")
+#define JK_STATUS_ARG_LBM_ROUTE            ("wn")
+#define JK_STATUS_ARG_LBM_REDIRECT         ("wr")
+#define JK_STATUS_ARG_LBM_DOMAIN           ("wc")
+#define JK_STATUS_ARG_LBM_DISTANCE         ("wd")
+
+#define JK_STATUS_ARG_LBM_TEXT_ACTIVATION  ("Activation")
+#define JK_STATUS_ARG_LBM_TEXT_FACTOR      ("LB Factor")
+#define JK_STATUS_ARG_LBM_TEXT_ROUTE       ("Route")
+#define JK_STATUS_ARG_LBM_TEXT_REDIRECT    ("Redirect Route")
+#define JK_STATUS_ARG_LBM_TEXT_DOMAIN      ("Cluster Domain")
+#define JK_STATUS_ARG_LBM_TEXT_DISTANCE    ("Distance")
+
+#define JK_STATUS_CMD_LIST                 (1)
+#define JK_STATUS_CMD_SHOW                 (2)
+#define JK_STATUS_CMD_EDIT                 (3)
+#define JK_STATUS_CMD_UPDATE               (4)
+#define JK_STATUS_CMD_RESET                (5)
+#define JK_STATUS_CMD_DEF                  (JK_STATUS_CMD_LIST)
+#define JK_STATUS_CMD_MAX                  (JK_STATUS_CMD_RESET)
+#define JK_STATUS_CMD_TEXT_LIST            ("list")
+#define JK_STATUS_CMD_TEXT_SHOW            ("show")
+#define JK_STATUS_CMD_TEXT_EDIT            ("edit")
+#define JK_STATUS_CMD_TEXT_UPDATE          ("update")
+#define JK_STATUS_CMD_TEXT_RESET           ("reset")
+#define JK_STATUS_CMD_TEXT_DEF             (JK_STATUS_CMD_TEXT_LIST)
+
+#define JK_STATUS_MIME_HTML                (1)
+#define JK_STATUS_MIME_XML                 (2)
+#define JK_STATUS_MIME_TXT                 (3)
+#define JK_STATUS_MIME_DEF                 (JK_STATUS_MIME_HTML)
+#define JK_STATUS_MIME_MAX                 (JK_STATUS_MIME_TXT)
+#define JK_STATUS_MIME_TEXT_HTML           ("html")
+#define JK_STATUS_MIME_TEXT_XML            ("xml")
+#define JK_STATUS_MIME_TEXT_TXT            ("txt")
+#define JK_STATUS_MIME_TEXT_DEF            (JK_STATUS_MIME_TEXT_HTML)
+
+#define JK_STATUS_ESC_CHARS                ("<>?&")
+
 #define JK_STATUS_HEAD "<!DOCTYPE HTML PUBLIC \"-//W3C//" \
                        "DTD HTML 3.2 Final//EN\">\n"      \
                        "<html><head><title>JK Status Manager</title>"
@@ -50,8 +126,6 @@
 
 #define JK_STATUS_XMLE "</jk:status>\n"
 
-#define JK_STATUS_TEXTUPDATE_RESPONSE "OK - jk status worker updated\n"
-
 typedef struct status_worker status_worker_t;
 
 struct status_endpoint
@@ -69,6 +143,9 @@
     jk_pool_atom_t    buf[TINY_POOL_SIZE];
     const char        *name;
     const char        *css;
+    int               read_only;
+    char              **user_names;
+    int               num_of_users;
     jk_worker_t       worker;
     status_endpoint_t ep;
     jk_worker_env_t   *we;
@@ -92,6 +169,24 @@
     NULL
 };
 
+static const char *cmd_type[] = {
+    "unknown",
+    JK_STATUS_CMD_TEXT_LIST,
+    JK_STATUS_CMD_TEXT_SHOW,
+    JK_STATUS_CMD_TEXT_EDIT,
+    JK_STATUS_CMD_TEXT_UPDATE,
+    JK_STATUS_CMD_TEXT_RESET,
+    NULL
+};
+
+static const char *mime_type[] = {
+    "unknown",
+    JK_STATUS_MIME_TEXT_HTML,
+    JK_STATUS_MIME_TEXT_XML,
+    JK_STATUS_MIME_TEXT_TXT,
+    NULL
+};
+
 #define HEADERS_NO_CACHE "no-cache", "no-cache", NULL
 
 static const char *headers_vhtml[] = {
@@ -131,6 +226,29 @@
 }
 #endif
 
+static void jk_puts(jk_ws_service_t *s, const char *str)
+{
+    if (str)
+        s->write(s, str, (unsigned int)strlen(str));
+    else
+        s->write(s, "(null)", 6);
+}
+
+static void jk_putv(jk_ws_service_t *s, ...)
+{
+    va_list va;
+    const char *str;
+
+    va_start(va, s);
+    while (1) {
+        str = va_arg(va, const char *);
+        if (str == NULL)
+            break;
+        s->write(s, str, (unsigned int)strlen(str));
+    }
+    va_end(va);
+}
+
 static int jk_printf(jk_ws_service_t *s, const char *fmt, ...)
 {
     int rc = 0;
@@ -218,30 +336,7 @@
         return "True";
 }
 
-static void jk_puts(jk_ws_service_t *s, const char *str)
-{
-    if (str)
-        s->write(s, str, (unsigned int)strlen(str));
-    else
-        s->write(s, "(null)", 6);
-}
-
-static void jk_putv(jk_ws_service_t *s, ...)
-{
-    va_list va;
-    const char *str;
-
-    va_start(va, s);
-    while (1) {
-        str = va_arg(va, const char *);
-        if (str == NULL)
-            break;
-        s->write(s, str, (unsigned int)strlen(str));
-    }
-    va_end(va);
-}
-
-static const char *status_cmd(const char *param, const char *req, char *buf, size_t len)
+static char *status_get_arg_raw(const char *param, const char *req, char *buf, size_t len)
 {
     char ps[32];
     char *p;
@@ -266,7 +361,7 @@
                 buf[l++] = *p;
             else
                 break;
-            if (l + 2 > len)
+            if (l >= len-1)
                 break;
             p++;
         }
@@ -280,44 +375,203 @@
         return NULL;
 }
 
-static int status_int(const char *param, const char *req, int def)
+static const char *status_get_arg(const char *param, const char *req, char *buf, size_t len)
+{
+    if (status_get_arg_raw(param, req, buf, len)) {
+        char *off = buf;
+        while (off=strpbrk(off, JK_STATUS_ESC_CHARS))
+            off[0] = '@';
+        return buf;
+    }
+    return NULL;
+}
+
+static int status_get_int(const char *param, const char *req, int def)
 {
-    const char *v;
     char buf[32];
     int rv = def;
 
-    if ((v = status_cmd(param, req, buf, sizeof(buf) -1))) {
-        rv = atoi(v);
+    if (status_get_arg_raw(param, req, buf, sizeof(buf) -1)) {
+        rv = atoi(buf);
     }
     return rv;
 }
 
-static int status_bool(const char *param, const char *req, int def)
+static int status_get_bool(const char *param, const char *req, int def)
 {
-    const char *v;
     char buf[32];
     int rv = def;
 
-    if ((v = status_cmd(param, req, buf, sizeof(buf)))) {
-        if (strcasecmp(v, "on") == 0 ||
-            strcasecmp(v, "true") == 0 ||
-            strcasecmp(v, "1") == 0)
+    if (status_get_arg_raw(param, req, buf, sizeof(buf))) {
+        if (strcasecmp(buf, "on") == 0 ||
+            strcasecmp(buf, "true") == 0 ||
+            strcasecmp(buf, "1") == 0)
             rv = 1;
-        else if (strcasecmp(v, "off") == 0 ||
-            strcasecmp(v, "false") == 0 ||
-            strcasecmp(v, "0") == 0)
+        else if (strcasecmp(buf, "off") == 0 ||
+            strcasecmp(buf, "false") == 0 ||
+            strcasecmp(buf, "0") == 0)
             rv = 0;
     }
     return rv;
 }
 
+const char *status_cmd_text(int cmd)
+{
+    return cmd_type[cmd];
+}
+
+static int status_cmd_int(const char *cmd)
+{
+    if (!cmd)
+        return JK_STATUS_CMD_DEF;
+    if (!strcmp(cmd, JK_STATUS_CMD_TEXT_LIST))
+        return JK_STATUS_CMD_LIST;
+    else if (!strcmp(cmd, JK_STATUS_CMD_TEXT_SHOW))
+        return JK_STATUS_CMD_SHOW;
+    else if (!strcmp(cmd, JK_STATUS_CMD_TEXT_EDIT))
+        return JK_STATUS_CMD_EDIT;
+    else if (!strcmp(cmd, JK_STATUS_CMD_TEXT_UPDATE))
+        return JK_STATUS_CMD_UPDATE;
+    else if (!strcmp(cmd, JK_STATUS_CMD_TEXT_RESET))
+        return JK_STATUS_CMD_RESET;
+    return JK_STATUS_CMD_DEF;
+}
+
+const char *status_mime_text(int mime)
+{
+    return mime_type[mime];
+}
+
+static int status_mime_int(const char *mime)
+{
+    if (!mime)
+        return JK_STATUS_MIME_DEF;
+    if (!strcmp(mime, JK_STATUS_MIME_TEXT_HTML))
+        return JK_STATUS_MIME_HTML;
+    else if (!strcmp(mime, JK_STATUS_MIME_TEXT_XML))
+        return JK_STATUS_MIME_XML;
+    else if (!strcmp(mime, JK_STATUS_MIME_TEXT_TXT))
+        return JK_STATUS_MIME_TXT;
+    return JK_STATUS_MIME_DEF;
+}
+
+static void status_start_form(jk_ws_service_t *s, const char *method,
+                              int cmd, int mime,
+                              int from, int refresh,
+                              const char *worker, const char *sub_worker)
+{
+    if (method)
+        jk_putv(s, "<form method=\"", method,
+        "\" action=\"", s->req_uri, "\">\n", NULL);
+    else
+        return;
+    if (cmd) {
+        jk_putv(s, "<input type=\"hidden\" name=\"",
+                JK_STATUS_ARG_CMD, "\" ", NULL);
+        jk_putv(s, "value=\"", status_cmd_text(cmd), "\"/>\n", NULL);
+        if (mime) {
+            jk_putv(s, "<input type=\"hidden\" name=\"",
+                JK_STATUS_ARG_MIME, "\" ", NULL);
+            jk_putv(s, "value=\"", status_mime_text(mime), "\"/>\n", NULL);
+        }
+        if (from) {
+            jk_putv(s, "<input type=\"hidden\" name=\"",
+                JK_STATUS_ARG_FROM, "\" ", NULL);
+            jk_putv(s, "value=\"", status_cmd_text(from), "\"/>\n", NULL);
+        }
+        if (refresh) {
+            jk_putv(s, "<input type=\"hidden\" name=\"",
+                JK_STATUS_ARG_REFRESH, "\" ", NULL);
+            jk_printf(s, "value=\"%d\"/>\n", refresh);
+        }
+        if (worker) {
+            jk_putv(s, "<input type=\"hidden\" name=\"",
+                JK_STATUS_ARG_WORKER, "\" ", NULL);
+            jk_putv(s, "value=\"", worker, "\"/>\n", NULL);
+        }
+        if (sub_worker) {
+            jk_putv(s, "<input type=\"hidden\" name=\"",
+                JK_STATUS_ARG_WORKER_MEMBER, "\" ", NULL);
+            jk_putv(s, "value=\"", sub_worker, "\"/>\n", NULL);
+        }
+    }
+}
+
+static void status_write_uri(jk_ws_service_t *s, const char *text,
+                             int cmd, int mime,
+                             int from, int refresh,
+                             const char *worker, const char *sub_worker)
+{
+    if (text)
+        jk_puts(s, "<a href=\"");
+    if (cmd) {
+        jk_putv(s, s->req_uri, "?", JK_STATUS_ARG_CMD, "=",
+                status_cmd_text(cmd), NULL);
+        if (mime)
+            jk_putv(s, "&", JK_STATUS_ARG_MIME, "=",
+                    status_mime_text(mime), NULL);
+        if (from)
+            jk_putv(s, "&", JK_STATUS_ARG_FROM, "=",
+                    status_cmd_text(from), NULL);
+        if (refresh)
+            jk_printf(s, "&%s=%d", JK_STATUS_ARG_REFRESH, refresh);
+        if (worker)
+            jk_putv(s, "&", JK_STATUS_ARG_WORKER, "=",
+                    worker, NULL);
+        if (sub_worker)
+            jk_putv(s, "&", JK_STATUS_ARG_WORKER_MEMBER, "=",
+                    sub_worker, NULL);
+    }
+    if (text)
+        jk_putv(s, "\">", text, "</a>", NULL);
+}
+
+static void status_parse_uri(jk_ws_service_t *s,
+                             int *cmd, int *mime,
+                             int *from, int *refresh,
+                             char *worker, char *sub_worker,
+                             jk_logger_t *l)
+{
+    char buf[128];
+
+    JK_TRACE_ENTER(l);
+    *cmd = JK_STATUS_CMD_DEF;
+    *mime = JK_STATUS_MIME_DEF;
+    *from = JK_STATUS_CMD_DEF;
+    worker[0] = '\0';
+    sub_worker[0] = '\0';
+    *refresh = 0;
+    if (!s->query_string)
+        return;
+    if (status_get_arg_raw(JK_STATUS_ARG_CMD, s->query_string, buf, sizeof(buf)))
+        *cmd = status_cmd_int(buf);
+    if (status_get_arg_raw(JK_STATUS_ARG_MIME, s->query_string, buf, sizeof(buf)))
+        *mime = status_mime_int(buf);
+    if (status_get_arg_raw(JK_STATUS_ARG_FROM, s->query_string, buf, sizeof(buf)))
+        *from = status_cmd_int(buf);
+    *refresh = status_get_int(JK_STATUS_ARG_REFRESH, s->query_string, 0);
+    if (status_get_arg(JK_STATUS_ARG_WORKER, s->query_string, buf, sizeof(buf)))
+        strncpy(worker, buf, JK_SHM_STR_SIZ);
+    if (status_get_arg(JK_STATUS_ARG_WORKER_MEMBER, s->query_string, buf, sizeof(buf)))
+        strncpy(sub_worker, buf, JK_SHM_STR_SIZ);
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG,
+               "standard request params cmd='%s' mime='%s' from='%s' refresh=%d "
+               "worker='%s' sub worker='%s%'",
+               status_cmd_text(*cmd), status_mime_text(*mime),
+               status_cmd_text(*from), refresh, worker, sub_worker);
+    JK_TRACE_EXIT(l);
+}
+
 static void display_maps(jk_ws_service_t *s,
                          jk_uri_worker_map_t *uwmap,
                          const char *worker, jk_logger_t *l)
 {
     char buf[64];
     unsigned int i;
+    int count=0;
 
+    JK_TRACE_ENTER(l);
     jk_putv(s, "<hr/><h3>URI Mappings for ", worker, "</h3>\n", NULL);
     jk_puts(s, "<table>\n<tr><th>Match Type</th><th>Uri</th>"
                "<th>Source</th></tr>\n");
@@ -326,6 +580,7 @@
         if (!worker || strcmp(uwr->worker_name, worker)) {
             continue;
         }
+        count++;
         jk_putv(s, "<tr><td>",
                 uri_worker_map_get_match(uwr, buf, l),
                 "</td><td>", NULL);
@@ -335,27 +590,66 @@
                 "</td></tr>\n", NULL);
     }
     jk_puts(s, "</table>\n");
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG,
+               "displayed %d maps for worker '%s'",
+               count, worker);
+    JK_TRACE_EXIT(l);
 }
 
-static void dump_maps(jk_ws_service_t *s,
-                      jk_uri_worker_map_t *uwmap,
-                      const char *worker, jk_logger_t *l)
+static void display_maps_xml(jk_ws_service_t *s,
+                             jk_uri_worker_map_t *uwmap,
+                             const char *worker, jk_logger_t *l)
 {
     char buf[64];
     unsigned int i;
+    int count=0;
 
+    JK_TRACE_ENTER(l);
     for (i = 0; i < uwmap->size; i++) {
         uri_worker_record_t *uwr = uwmap->maps[i];
         if (!worker || strcmp(uwr->worker_name, worker)) {
             continue;
         }
-        jk_printf(s, "    <jk:map type=\"%s\" uri=\"%s\" source=\"%s\" />\n",
-                  uri_worker_map_get_match(uwr, buf, l),
-                  uwr->uri,
-                  uri_worker_map_get_source(uwr, l));
-    }
+        count++;
+        jk_printf(s, "      <jk:map\n");
+        jk_printf(s, "        type=\"%s\"\n", uri_worker_map_get_match(uwr, buf, l));
+        jk_printf(s, "        uri=\"%s\"\n", uwr->uri);
+        jk_printf(s, "        source=\"%s\"/>\n", uri_worker_map_get_source(uwr, l));
+    }
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG,
+               "dumped %d maps for worker '%s'",
+               count, worker);
+    JK_TRACE_EXIT(l);
 }
 
+static void display_maps_txt(jk_ws_service_t *s,
+                             jk_uri_worker_map_t *uwmap,
+                             const char *worker, jk_logger_t *l)
+{
+    char buf[64];
+    unsigned int i;
+    int count=0;
+
+    JK_TRACE_ENTER(l);
+    for (i = 0; i < uwmap->size; i++) {
+        uri_worker_record_t *uwr = uwmap->maps[i];
+        if (!worker || strcmp(uwr->worker_name, worker)) {
+            continue;
+        }
+        count++;
+        jk_printf(s, "      JK Map:");
+        jk_printf(s, " type=\"%s\"", uri_worker_map_get_match(uwr, buf, l));
+        jk_printf(s, " uri=\"%s\"", uwr->uri);
+        jk_printf(s, " source=\"%s\"\n", uri_worker_map_get_source(uwr, l));
+    }
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG,
+               "dumped %d maps for worker '%s'",
+               count, worker);
+    JK_TRACE_EXIT(l);
+}
 
 static void display_worker(jk_ws_service_t *s, jk_worker_t *w,
                           int refresh, int single,
@@ -363,27 +657,48 @@
 {
     char buf[32];
     const char *name = NULL;
-    const char *from = NULL;
+    int from = JK_STATUS_CMD_LIST;
     ajp_worker_t *aw = NULL;
     lb_worker_t *lb = NULL;
 
+    JK_TRACE_ENTER(l);
     if (w->type == JK_LB_WORKER_TYPE) {
         lb = (lb_worker_t *)w->worker_private;
+        name = lb->s->name;
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "%s lb worker '%s'",
+                   single ? "showing" : "listing", name);
     }
     else if (w->type == JK_AJP13_WORKER_TYPE ||
              w->type == JK_AJP14_WORKER_TYPE) {
         aw = (ajp_worker_t *)w->worker_private;
+        name = aw->name;
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "%s ajp worker '%s'",
+                   single ? "showing" : "listing", name);
     }
-    if (single) {
-        from = "show";
-        jk_putv(s, "<hr/>\n<a href=\"", s->req_uri, "?cmd=list\">Back to full worker list</a><br/>\n", NULL);
+    else {
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "worker type not implemented");
+        JK_TRACE_EXIT(l);
+        return;
     }
-    else{
-        from = "list";
+    if (single) {
+        jk_puts(s, "<hr/>\n");
+        status_write_uri(s, "Back to full worker list", JK_STATUS_CMD_LIST,
+                         0, 0, refresh, NULL, NULL); 
+        jk_puts(s, "<br/>\n");
+        from = JK_STATUS_CMD_SHOW;
     }
 
-    if (lb != NULL) {
+    if (lb) {
         time_t now = time(NULL);
+        unsigned int good = 0;
+        unsigned int degraded = 0;
+        unsigned int bad = 0;
         unsigned int j;
 
         jk_shm_lock();
@@ -391,31 +706,30 @@
             jk_lb_pull(lb, l);
         jk_shm_unlock();
 
-        name = lb->s->name;
         jk_puts(s, "<hr/><h3>[");
         if (single) {
             jk_puts(s, "S");
         }
-        else{
-            jk_printf(s, "<a href=\"%s?cmd=show&w=%s&refresh=%d\">S</a>",
-                      s->req_uri, name, refresh);
+        else {
+            status_write_uri(s, "S", JK_STATUS_CMD_SHOW,
+                             0, 0, refresh, name, NULL); 
         }
         jk_puts(s, "|");
-        jk_printf(s, "<a href=\"%s?cmd=edit&w=%s&from=%s&refresh=%d\">E</a>",
-                  s->req_uri, name, from, refresh);
+        status_write_uri(s, "E", JK_STATUS_CMD_EDIT,
+                         0, from, refresh, name, NULL); 
         jk_puts(s, "|");
-        jk_printf(s, "<a href=\"%s?cmd=reset&w=%s&from=%s&refresh=%d\">R</a>",
-                  s->req_uri, name, from, refresh);
+        status_write_uri(s, "R", JK_STATUS_CMD_RESET,
+                         0, from, refresh, name, NULL); 
         jk_puts(s, "]&nbsp;&nbsp;");
         jk_putv(s, "Worker Status for ", name, "<h3/>\n", NULL);
-        jk_puts(s, "<table><tr>"
-                "<th>Type</th><th>Sticky session</th>"
-                "<th>Force Sticky session</th>"
-                "<th>Retries</th>"
-                "<th>Method</th>"
-                "<th>Lock</th>"
-                "<th>Recovery timeout</th>"
-                "</tr>\n<tr>");
+        jk_putv(s, "<table><tr>"
+                "<th>Type</th><th>", JK_STATUS_ARG_LB_TEXT_STICKY, "</th>"
+                "<th>", JK_STATUS_ARG_LB_TEXT_STICKY_FORCE, "</th>"
+                "<th>", JK_STATUS_ARG_LB_TEXT_RETRIES, "</th>"
+                "<th>", JK_STATUS_ARG_LB_TEXT_METHOD, "</th>"
+                "<th>", JK_STATUS_ARG_LB_TEXT_LOCK, "</th>"
+                "<th>", JK_STATUS_ARG_LB_TEXT_RECOVER_TIME, "</th>"
+                "</tr>\n<tr>", NULL);
         jk_putv(s, "<td>", status_worker_type(w->type), "</td>", NULL);
         jk_putv(s, "<td>", status_val_bool(lb->sticky_session),
                 "</td>", NULL);
@@ -427,26 +741,43 @@
         jk_printf(s, "<td>%d</td>", lb->recover_wait_time);
         jk_puts(s, "</tr>\n</table>\n<br/>\n");
 
+        for (j = 0; j < lb->num_of_workers; j++) {
+            worker_record_t *wr = &(lb->lb_workers[j]);
+            if (wr->s->state == JK_LB_STATE_ERROR ||
+               wr->s->state == JK_LB_STATE_RECOVER ||
+               wr->s->activation == JK_LB_ACTIVATION_STOPPED)
+               bad++;
+            else if (wr->s->state == JK_LB_STATE_BUSY ||
+               wr->s->activation == JK_LB_ACTIVATION_DISABLED)
+               degraded++;
+            else
+               good++;
+        }
+
         jk_puts(s, "<table><tr>"
-                "<th>Busy</th><th>Max Busy</th>"
+                "<th>Good</th><th>Degraded</th><th>Bad/Stopped</th><th>Busy</th><th>Max Busy</th>"
                 "</tr>\n<tr>");
+        jk_printf(s, "<td>%d</td>", good);
+        jk_printf(s, "<td>%d</td>", degraded);
+        jk_printf(s, "<td>%d</td>", bad);
         jk_printf(s, "<td>%d</td>", lb->s->busy);
         jk_printf(s, "<td>%d</td>", lb->s->max_busy);
         jk_puts(s, "</tr>\n</table>\n<br/>\n");
 
-        jk_puts(s, "<table><tr>"
-                "<th>Name</th><th>Type</th><th>jvmRoute</th><th>Host</th><th>Addr</th>"
+        jk_putv(s, "<table><tr>"
+                "<th>Name</th><th>Type</th><th>", JK_STATUS_ARG_LBM_TEXT_ROUTE,
+                "</th><th>Host</th><th>Addr</th>"
                 "<th>Act</th><th>Stat</th><th>D</th><th>F</th><th>M</th><th>V</th><th>Acc</th><th>Err</th><th>CE</th>"
-                "<th>Wr</th><th>Rd</th><th>Busy</th><th>Max</th><th>RR</th><th>Cd</th><th>Rs</th></tr>\n");
+                "<th>Wr</th><th>Rd</th><th>Busy</th><th>Max</th><th>RR</th><th>Cd</th><th>Rs</th></tr>\n", NULL);
         for (j = 0; j < lb->num_of_workers; j++) {
             worker_record_t *wr = &(lb->lb_workers[j]);
             ajp_worker_t *a = (ajp_worker_t *)wr->w->worker_private;
             jk_puts(s, "<tr>\n<td>[");
-            jk_printf(s, "<a href=\"%s?cmd=edit&w=%s&lb=%s&from=%s&refresh=%d\">E</a>",
-                      s->req_uri, wr->s->name, name, from, refresh);
+            status_write_uri(s, "E", JK_STATUS_CMD_EDIT,
+                             0, from, refresh, name, wr->s->name); 
             jk_puts(s, "|");
-            jk_printf(s, "<a href=\"%s?cmd=reset&w=%s&lb=%s&from=%s&refresh=%d\">R</a>",
-                      s->req_uri, wr->s->name, name, from, refresh);
+            status_write_uri(s, "R", JK_STATUS_CMD_RESET,
+                             0, from, refresh, name, wr->s->name); 
             jk_putv(s, "]&nbsp;", wr->s->name, "</td>\n", NULL);
             jk_putv(s, "<td>", status_worker_type(wr->w->type), "</td>", NULL);
             jk_putv(s, "<td>", wr->s->jvm_route, "</td>", NULL);
@@ -491,14 +822,13 @@
         }
         jk_puts(s, "</table><br/>\n");
     }
-    else if (aw != NULL) {
-        name = aw->name;
+    else if (aw) {
         jk_puts(s, "<hr/><h3>[");
         if (single)
             jk_puts(s, "S");
         else
-            jk_printf(s, "<a href=\"%s?cmd=show&w=%s&refresh=%d\">S</a>",
-                      s->req_uri, name, refresh);
+            status_write_uri(s, "S", JK_STATUS_CMD_SHOW,
+                             0, from, refresh, name, NULL); 
         jk_puts(s, "]&nbsp;&nbsp;");
         jk_putv(s, "Worker Status for ", name, "<h3/>\n", NULL);
         jk_puts(s, "\n\n<table><tr>"
@@ -512,460 +842,1312 @@
     }
     if (name)
         display_maps(s, s->uw_map, name, l);
+    JK_TRACE_EXIT(l);
 }
 
-
-static void form_worker(jk_ws_service_t *s, jk_worker_t *w,
-                        const char *from, int refresh, jk_logger_t *l)
+static void display_worker_xml(jk_ws_service_t *s, jk_worker_t *w,
+                              int single,
+                              jk_logger_t *l)
 {
+    char buf[32];
     const char *name = NULL;
     ajp_worker_t *aw = NULL;
     lb_worker_t *lb = NULL;
 
+    JK_TRACE_ENTER(l);
     if (w->type == JK_LB_WORKER_TYPE) {
         lb = (lb_worker_t *)w->worker_private;
+        name = lb->s->name;
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "%s lb worker '%s' as xml",
+                   single ? "showing" : "listing", name);
     }
     else if (w->type == JK_AJP13_WORKER_TYPE ||
              w->type == JK_AJP14_WORKER_TYPE) {
         aw = (ajp_worker_t *)w->worker_private;
+        name = aw->name;
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "%s ajp worker '%s' as xml",
+                   single ? "showing" : "listing", name);
     }
-
-    if (lb != NULL) {
-        name = lb->s->name;
-        /* Edit Load balancer settings */
-        jk_putv(s, "<hr/><h3>Edit Load balancer settings for ",
-                name, "</h3>\n", NULL);
-        jk_putv(s, "<form method=\"GET\" action=\"",
-                s->req_uri, "\">\n", NULL);
-        jk_puts(s, "<input type=\"hidden\" name=\"cmd\" ");
-        jk_puts(s, "value=\"update\"/>\n");
-        if (from) {
-            jk_puts(s, "<input type=\"hidden\" name=\"from\" ");
-            jk_putv(s, "value=\"", from, "\"/>\n", NULL);
-        }
-        jk_puts(s, "<input type=\"hidden\" name=\"refresh\" ");
-        jk_printf(s, "value=\"%d\">\n", refresh);
-        jk_puts(s, "<input type=\"hidden\" name=\"w\" ");
-        jk_putv(s, "value=\"", name, "\"/>\n", NULL);
-
-        jk_puts(s, "<table>\n<tr><td>Retries:</td><td><input name=\"lr\" type=\"text\" ");
-        jk_printf(s, "value=\"%d\"/></td></tr>\n", lb->retries);
-        jk_puts(s, "<tr><td>Recover time:</td><td><input name=\"lt\" type=\"text\" ");
-        jk_printf(s, "value=\"%d\"/></td></tr>\n", lb->recover_wait_time);
-        jk_puts(s, "<tr><td>Sticky session:</td><td><input name=\"ls\" type=\"checkbox\"");
-        if (lb->sticky_session)
-            jk_puts(s, " checked=\"checked\"");
-        jk_puts(s, "/></td></tr>\n");
-        jk_puts(s, "<tr><td>Force Sticky session:</td><td><input name=\"lf\" type=\"checkbox\"");
-        if (lb->sticky_session_force)
-            jk_puts(s, " checked=\"checked\"");
-        jk_puts(s, "/></td></tr>\n");
-        jk_puts(s, "<tr><td>Method:</td><td></td></tr>\n");
-        jk_puts(s, "<tr><td>&nbsp;&nbsp;Requests</td><td><input name=\"lm\" type=\"radio\"");
-        jk_printf(s, " value=\"%d\"", JK_LB_METHOD_REQUESTS);
-        if (lb->lbmethod == JK_LB_METHOD_REQUESTS)
-            jk_puts(s, " checked=\"checked\"");
-        jk_puts(s, "/></td></tr>\n");
-        jk_puts(s, "<tr><td>&nbsp;&nbsp;Traffic</td><td><input name=\"lm\" type=\"radio\"");
-        jk_printf(s, " value=\"%d\"", JK_LB_METHOD_TRAFFIC);
-        if (lb->lbmethod == JK_LB_METHOD_TRAFFIC)
-            jk_puts(s, " checked=\"checked\"");
-        jk_puts(s, "/></td></tr>\n");
-        jk_puts(s, "<tr><td>&nbsp;&nbsp;Busyness</td><td><input name=\"lm\" type=\"radio\"");
-        jk_printf(s, " value=\"%d\"", JK_LB_METHOD_BUSYNESS);
-        if (lb->lbmethod == JK_LB_METHOD_BUSYNESS)
-            jk_puts(s, " checked=\"checked\"");
-        jk_puts(s, "/></td></tr>\n");
-        jk_puts(s, "<tr><td>&nbsp;&nbsp;Sessions</td><td><input name=\"lm\" type=\"radio\"");
-        jk_printf(s, " value=\"%d\"", JK_LB_METHOD_SESSIONS);
-        if (lb->lbmethod == JK_LB_METHOD_SESSIONS)
-            jk_puts(s, " checked=\"checked\"");
-        jk_puts(s, "/></td></tr>\n");
-        jk_puts(s, "<tr><td>Locking:</td><td></td></tr>\n");
-        jk_puts(s, "<tr><td>&nbsp;&nbsp;Optimistic</td><td><input name=\"ll\" type=\"radio\"");
-        jk_printf(s, " value=\"%d\"", JK_LB_LOCK_OPTIMISTIC);
-        if (lb->lblock == JK_LB_LOCK_OPTIMISTIC)
-            jk_puts(s, " checked=\"checked\"");
-        jk_puts(s, "/></td></tr>\n");
-        jk_puts(s, "<tr><td>&nbsp;&nbsp;Pessimistic</td><td><input name=\"ll\" type=\"radio\"");
-        jk_printf(s, " value=\"%d\"", JK_LB_LOCK_PESSIMISTIC);
-        if (lb->lblock == JK_LB_LOCK_PESSIMISTIC)
-            jk_puts(s, " checked=\"checked\"");
-        jk_puts(s, "/></td></tr>\n");
-        jk_puts(s, "</table>\n");
-        jk_puts(s, "<br/><input type=\"submit\" value=\"Update Balancer\"/></form>\n");
+    else {
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "worker type not implemented");
+        JK_TRACE_EXIT(l);
+        return;
     }
-}
 
+    if (lb) {
+        time_t now = time(NULL);
+        unsigned int good = 0;
+        unsigned int degraded = 0;
+        unsigned int bad = 0;
+        unsigned int j;
 
-static void form_member(jk_ws_service_t *s, worker_record_t *wr,
-                        const char *lb_name, const char *from,
-                        int refresh, jk_logger_t *l)
-{
-    jk_putv(s, "<hr/><h3>Edit worker settings for ",
-            wr->s->name, "</h3>\n", NULL);
-    jk_putv(s, "<form method=\"GET\" action=\"",
-            s->req_uri, "\">\n", NULL);
-    jk_puts(s, "<input type=\"hidden\" name=\"cmd\" ");
-    jk_puts(s, "value=\"update\"/>\n");
-    if (from) {
-        jk_puts(s, "<input type=\"hidden\" name=\"from\" ");
-        jk_putv(s, "value=\"", from, "\"/>\n", NULL);
-    }
-    jk_puts(s, "<input type=\"hidden\" name=\"refresh\" ");
-    jk_printf(s, "value=\"%d\">\n", refresh);
-    jk_puts(s, "<input type=\"hidden\" name=\"w\" ");
-    jk_putv(s, "value=\"", wr->s->name, "\"/>\n", NULL);
-    jk_puts(s, "<input type=\"hidden\" name=\"lb\" ");
-    jk_printf(s, "value=\"%s\">\n", lb_name);
+        jk_shm_lock();
+        if (lb->sequence != lb->s->sequence)
+            jk_lb_pull(lb, l);
+        jk_shm_unlock();
 
-    jk_puts(s, "<tr><td>Load factor:</td><td><input name=\"wf\" type=\"text\" ");
-    jk_printf(s, "value=\"%d\"/></td></tr>\n", wr->s->lb_factor);
-    jk_puts(s, "<table>\n<tr><td>Route:</td><td><input name=\"wn\" type=\"text\" ");
-    jk_printf(s, "value=\"%s\"/></td></tr>\n", wr->s->jvm_route);
-    jk_puts(s, "<tr><td>Route Redirect:</td><td><input name=\"wr\" type=\"text\" ");
-    jk_putv(s, "value=\"", wr->s->redirect, NULL);
-    jk_puts(s, "\"/></td></tr>\n");
-    jk_puts(s, "<tr><td>Cluster Domain:</td><td><input name=\"wc\" type=\"text\" ");
-    jk_putv(s, "value=\"", wr->s->domain, NULL);
-    jk_puts(s, "\"/></td></tr>\n");
-    jk_puts(s, "<tr><td>Distance:</td><td><input name=\"wx\" type=\"text\" ");
-    jk_printf(s, "value=\"%d\"/></td></tr>\n", wr->s->distance);
-    jk_puts(s, "<tr><td>Activation:</td><td></td></tr>\n");
-    jk_puts(s, "<tr><td>&nbsp;&nbsp;Active</td><td><input name=\"wa\" type=\"radio\"");
-    jk_printf(s, " value=\"%d\"", JK_LB_ACTIVATION_ACTIVE);
-    if (wr->s->activation == JK_LB_ACTIVATION_ACTIVE)
-        jk_puts(s, " checked=\"checked\"");
-    jk_puts(s, "/></td></tr>\n");
-    jk_puts(s, "<tr><td>&nbsp;&nbsp;Disabled</td><td><input name=\"wa\" type=\"radio\"");
-    jk_printf(s, " value=\"%d\"", JK_LB_ACTIVATION_DISABLED);
-    if (wr->s->activation == JK_LB_ACTIVATION_DISABLED)
-        jk_puts(s, " checked=\"checked\"");
-    jk_puts(s, "/></td></tr>\n");
-    jk_puts(s, "<tr><td>&nbsp;&nbsp;Stopped</td><td><input name=\"wa\" type=\"radio\"");
-    jk_printf(s, " value=\"%d\"", JK_LB_ACTIVATION_STOPPED);
-    if (wr->s->activation == JK_LB_ACTIVATION_STOPPED)
-        jk_puts(s, " checked=\"checked\"");
-    jk_puts(s, "/></td></tr>\n");
-    jk_puts(s, "</table>\n");
-    jk_puts(s, "<br/><input type=\"submit\" value=\"Update Worker\"/>\n</form>\n");
-}
+        for (j = 0; j < lb->num_of_workers; j++) {
+            worker_record_t *wr = &(lb->lb_workers[j]);
+            if (wr->s->state == JK_LB_STATE_ERROR ||
+               wr->s->state == JK_LB_STATE_RECOVER ||
+               wr->s->activation == JK_LB_ACTIVATION_STOPPED)
+               bad++;
+            else if (wr->s->state == JK_LB_STATE_BUSY ||
+               wr->s->activation == JK_LB_ACTIVATION_DISABLED)
+               degraded++;
+            else
+               good++;
+        }
 
+        jk_printf(s, "  <jk:balancer\n");
+        jk_printf(s, "    name=\"%s\"\n", name);
+        jk_printf(s, "    type=\"%s\"\n", status_worker_type(w->type));
+        jk_printf(s, "    sticky=\"%s\"\n", status_val_bool(lb->sticky_session));
+        jk_printf(s, "    stickyforce=\"%s\"\n", status_val_bool(lb->sticky_session_force));
+        jk_printf(s, "    retries=\"%d\"\n", lb->retries);
+        jk_printf(s, "    recover=\"%d\"\n", lb->recover_wait_time);
+        jk_printf(s, "    method=\"%s\"\n", jk_lb_get_method(lb, l));
+        jk_printf(s, "    lock=\"%s\"\n", jk_lb_get_lock(lb, l));
+        jk_printf(s, "    good=\"%d\"\n", good);
+        jk_printf(s, "    degraded=\"%d\"\n", degraded);
+        jk_printf(s, "    bad=\"%d\"\n", bad);
+        jk_printf(s, "    busy=\"%d\"\n", lb->s->busy);
+        jk_printf(s, "    max_busy=\"%d\">\n", lb->s->max_busy);
 
-static void display_legend(jk_ws_service_t *s, jk_logger_t *l)
-{
-    /* Display legend */
-    jk_puts(s, "<hr/><table>\n"
-            "<tbody valign=\"baseline\">\n"
-            "<tr><th>Name</th><td>Worker name</td></tr>\n"
-            "<tr><th>Type</th><td>Worker type</td></tr>\n"
-            "<tr><th>jvmRoute</th><td>Worker JVM route</td></tr>\n"
-            "<tr><th>Addr</th><td>Backend Address info</td></tr>\n"
-            "<tr><th>Act</th><td>Worker activation configuration</br>\n"
-            "ACT=Active, DIS=Disabled, STP=Stopped</td></tr>\n"
-            "<tr><th>Stat</th><td>Worker error status</br>\n"
-            "OK=OK, N/A=Not availabe, ERR=Error, REC=Recovering, BSY=Busy</td></tr>\n"
-            "<tr><th>D</th><td>Worker distance</td></tr>\n"
-            "<tr><th>F</th><td>Load Balancer factor</td></tr>\n"
-            "<tr><th>M</th><td>Load Balancer multiplicity</td></tr>\n"
-            "<tr><th>V</th><td>Load Balancer value</td></tr>\n"
-            "<tr><th>Acc</th><td>Number of requests</td></tr>\n"
-            "<tr><th>Err</th><td>Number of failed requests</td></tr>\n"
-            "<tr><th>CE</th><td>Number of client errors</td></tr>\n"
-            "<tr><th>Wr</th><td>Number of bytes transferred/min</td></tr>\n"
-            "<tr><th>Rd</th><td>Number of bytes read/min</td></tr>\n"
-            "<tr><th>Busy</th><td>Current number of busy connections</td></tr>\n"
-            "<tr><th>Max</th><td>Maximum number of busy connections</td></tr>\n"
-            "<tr><th>RR</th><td>Route redirect</td></tr>\n"
-            "<tr><th>Cd</th><td>Cluster domain</td></tr>\n"
-            "<tr><th>Rs</th><td>Recovery scheduled</td></tr>\n"
-            "</tbody>\n"
-            "</table>");
+        for (j = 0; j < lb->num_of_workers; j++) {
+            worker_record_t *wr = &(lb->lb_workers[j]);
+            ajp_worker_t *a = (ajp_worker_t *)wr->w->worker_private;
+            int rs = 0;
+            /* TODO: descriptive status */
+            jk_printf(s, "      <jk:member\n");
+            jk_printf(s, "        name=\"%s\"\n", wr->s->name);
+            jk_printf(s, "        type=\"%s\"\n", status_worker_type(wr->w->type));
+            jk_printf(s, "        host=\"%s\"\n", a->host);
+            jk_printf(s, "        port=\"%d\"\n", a->port);
+            jk_printf(s, "        address=\"%s\"\n", jk_dump_hinfo(&a->worker_inet_addr, buf));
+            jk_printf(s, "        activation=\"%s\"\n", jk_lb_get_activation(wr, l));
+            jk_printf(s, "        lbfactor=\"%d\"\n", wr->s->lb_factor);
+            jk_printf(s, "        jvm_route=\"%s\"\n", wr->s->jvm_route ? wr->s->jvm_route : "");
+            jk_printf(s, "        redirect=\"%s\"\n", wr->s->redirect ? wr->s->redirect : "");
+            jk_printf(s, "        domain=\"%s\"\n", wr->s->domain ? wr->s->domain : "");
+            jk_printf(s, "        distance=\"%d\"\n", wr->s->distance);
+            jk_printf(s, "        state=\"%s\"\n", jk_lb_get_state(wr, l));
+            jk_printf(s, "        lbmult=\"%" JK_UINT64_T_FMT "\"\n", wr->s->lb_mult);
+            jk_printf(s, "        lbvalue=\"%" JK_UINT64_T_FMT "\"\n", wr->s->lb_value);
+            jk_printf(s, "        elected=\"%" JK_UINT64_T_FMT "\"\n", wr->s->elected);
+            jk_printf(s, "        errors=\"%" JK_UINT32_T_FMT "\"\n", wr->s->errors);
+            jk_printf(s, "        clienterrors=\"%" JK_UINT32_T_FMT "\"\n", wr->s->client_errors);
+            jk_printf(s, "        transferred=\"%" JK_UINT64_T_FMT "\"\n", wr->s->transferred);
+            jk_printf(s, "        readed=\"%" JK_UINT64_T_FMT "\"\n", wr->s->readed);
+            jk_printf(s, "        busy=\"%u\"\n", wr->s->busy);
+            jk_printf(s, "        maxbusy=\"%u\"\n", wr->s->max_busy);
+            if (wr->s->state == JK_LB_STATE_ERROR) {
+                int rs = lb->maintain_time - (int)difftime(now, lb->s->last_maintain_time);
+                if (rs < lb->recover_wait_time - (int)difftime(now, wr->s->error_time))
+                    rs += lb->maintain_time;
+            }
+            jk_printf(s, "        time-to-recover=\"%u\"/>\n", rs < 0 ? 0 : rs);
+        }
+        if (name)
+            display_maps_xml(s, s->uw_map, name, l);
+        jk_puts(s, "  </jk:balancer>\n");
+    }
+    else if (aw) {
+        jk_printf(s, "<jk:ajp\n");
+        jk_printf(s, "  name=\"%s\"\n", name);
+        jk_printf(s, "  type=\"%s\"\n", status_worker_type(w->type));
+        jk_printf(s, "  host=\"%s\"\n", aw->host);
+        jk_printf(s, "  port=\"%d\"\n", aw->port);
+        jk_printf(s, "  address=\"%s\"/>\n", jk_dump_hinfo(&aw->worker_inet_addr, buf));
+        if (name)
+            display_maps_xml(s, s->uw_map, name, l);
+        jk_puts(s, "</jk:ajp>\n");
+    }
+    JK_TRACE_EXIT(l);
 }
 
-
-static int list_workers(jk_ws_service_t *s, status_worker_t *sw,
-                        int refresh, jk_logger_t *l)
+static void display_worker_txt(jk_ws_service_t *s, jk_worker_t *w,
+                              int single,
+                              jk_logger_t *l)
 {
-    unsigned int i;
-    jk_worker_t *w = NULL;
+    char buf[32];
     const char *name = NULL;
+    ajp_worker_t *aw = NULL;
+    lb_worker_t *lb = NULL;
 
-    for (i = 0; i < sw->we->num_of_workers; i++) {
-        name = sw->we->worker_list[i];
-        w = wc_get_worker_for_name(name, l);
-        if (!w)
-            continue;
-        if (w->type != JK_LB_WORKER_TYPE &&
-            w->type != JK_AJP13_WORKER_TYPE &&
-            w->type != JK_AJP14_WORKER_TYPE)
-            continue;
-        display_worker(s, w, refresh, 0, l);
+    JK_TRACE_ENTER(l);
+    if (w->type == JK_LB_WORKER_TYPE) {
+        lb = (lb_worker_t *)w->worker_private;
+        name = lb->s->name;
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "%s lb worker '%s' as txt",
+                   single ? "showing" : "listing", name);
     }
-    display_legend(s, l);
-    return JK_TRUE;
-}
-
-
-static int show_worker(jk_ws_service_t *s, status_worker_t *sw,
-                       const char *dworker, const char *lb_name,
-                       int refresh, jk_logger_t *l)
-{
-    unsigned int i;
-    jk_worker_t *w = NULL;
+    else if (w->type == JK_AJP13_WORKER_TYPE ||
+             w->type == JK_AJP14_WORKER_TYPE) {
+        aw = (ajp_worker_t *)w->worker_private;
+        name = aw->name;
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "%s ajp worker '%s' as txt",
+                   single ? "showing" : "listing", name);
+    }
+    else {
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "worker type not implemented");
+        JK_TRACE_EXIT(l);
+        return;
+    }
+
+    if (lb) {
+        time_t now = time(NULL);
+        unsigned int good = 0;
+        unsigned int degraded = 0;
+        unsigned int bad = 0;
+        unsigned int j;
+
+        jk_shm_lock();
+        if (lb->sequence != lb->s->sequence)
+            jk_lb_pull(lb, l);
+        jk_shm_unlock();
+
+        for (j = 0; j < lb->num_of_workers; j++) {
+            worker_record_t *wr = &(lb->lb_workers[j]);
+            if (wr->s->state == JK_LB_STATE_ERROR ||
+               wr->s->state == JK_LB_STATE_RECOVER ||
+               wr->s->activation == JK_LB_ACTIVATION_STOPPED)
+               bad++;
+            else if (wr->s->state == JK_LB_STATE_BUSY ||
+               wr->s->activation == JK_LB_ACTIVATION_DISABLED)
+               degraded++;
+            else
+               good++;
+        }
+
+        jk_printf(s, "  Balancer:");
+        jk_printf(s, " name=\"%s\"", name);
+        jk_printf(s, " type=\"%s\"", status_worker_type(w->type));
+        jk_printf(s, " sticky=\"%s\"", status_val_bool(lb->sticky_session));
+        jk_printf(s, " stickyforce=\"%s\"", status_val_bool(lb->sticky_session_force));
+        jk_printf(s, " retries=\"%d\"", lb->retries);
+        jk_printf(s, " recover=\"%d\"", lb->recover_wait_time);
+        jk_printf(s, " method=\"%s\"", jk_lb_get_method(lb, l));
+        jk_printf(s, " lock=\"%s\"", jk_lb_get_lock(lb, l));
+        jk_printf(s, " good=\"%d\"", good);
+        jk_printf(s, " degraded=\"%d\"", degraded);
+        jk_printf(s, " bad=\"%d\"", bad);
+        jk_printf(s, " busy=\"%d\"", lb->s->busy);
+        jk_printf(s, " max_busy=\"%d\"\n", lb->s->max_busy);
+
+        for (j = 0; j < lb->num_of_workers; j++) {
+            worker_record_t *wr = &(lb->lb_workers[j]);
+            ajp_worker_t *a = (ajp_worker_t *)wr->w->worker_private;
+            int rs = 0;
+            /* TODO: descriptive status */
+            jk_printf(s, "      Member");
+            jk_printf(s, " name=\"%s\"", wr->s->name);
+            jk_printf(s, " type=\"%s\"", status_worker_type(wr->w->type));
+            jk_printf(s, " host=\"%s\"", a->host);
+            jk_printf(s, " port=\"%d\"", a->port);
+            jk_printf(s, " address=\"%s\"", jk_dump_hinfo(&a->worker_inet_addr, buf));
+            jk_printf(s, " activation=\"%s\"", jk_lb_get_activation(wr, l));
+            jk_printf(s, " lbfactor=\"%d\"", wr->s->lb_factor);
+            jk_printf(s, " jvm_route=\"%s\"", wr->s->jvm_route ? wr->s->jvm_route : "");
+            jk_printf(s, " redirect=\"%s\"", wr->s->redirect ? wr->s->redirect : "");
+            jk_printf(s, " domain=\"%s\"", wr->s->domain ? wr->s->domain : "");
+            jk_printf(s, " distance=\"%d\"", wr->s->distance);
+            jk_printf(s, " state=\"%s\"", jk_lb_get_state(wr, l));
+            jk_printf(s, " lbmult=\"%" JK_UINT64_T_FMT "\"", wr->s->lb_mult);
+            jk_printf(s, " lbvalue=\"%" JK_UINT64_T_FMT "\"", wr->s->lb_value);
+            jk_printf(s, " elected=\"%" JK_UINT64_T_FMT "\"", wr->s->elected);
+            jk_printf(s, " errors=\"%" JK_UINT32_T_FMT "\"", wr->s->errors);
+            jk_printf(s, " clienterrors=\"%" JK_UINT32_T_FMT "\"", wr->s->client_errors);
+            jk_printf(s, " transferred=\"%" JK_UINT64_T_FMT "\"", wr->s->transferred);
+            jk_printf(s, " readed=\"%" JK_UINT64_T_FMT "\"", wr->s->readed);
+            jk_printf(s, " busy=\"%u\"", wr->s->busy);
+            jk_printf(s, " maxbusy=\"%u\"", wr->s->max_busy);
+            if (wr->s->state == JK_LB_STATE_ERROR) {
+                int rs = lb->maintain_time - (int)difftime(now, lb->s->last_maintain_time);
+                if (rs < lb->recover_wait_time - (int)difftime(now, wr->s->error_time))
+                    rs += lb->maintain_time;
+            }
+            jk_printf(s, " time-to-recover=\"%u\"\n", rs < 0 ? 0 : rs);
+        }
+        if (name)
+            display_maps_txt(s, s->uw_map, name, l);
+    }
+    else if (aw) {
+        jk_printf(s, "AJP Worker");
+        jk_printf(s, " name=\"%s\"", name);
+        jk_printf(s, " type=\"%s\"", status_worker_type(w->type));
+        jk_printf(s, " host=\"%s\"", aw->host);
+        jk_printf(s, " port=\"%d\"", aw->port);
+        jk_printf(s, " address=\"%s\"\n", jk_dump_hinfo(&aw->worker_inet_addr, buf));
+        if (name)
+            display_maps_txt(s, s->uw_map, name, l);
+    }
+    JK_TRACE_EXIT(l);
+}
+
+static void form_worker(jk_ws_service_t *s, jk_worker_t *w,
+                        int from, int refresh,
+                        jk_logger_t *l)
+{
+    const char *name = NULL;
+    lb_worker_t *lb = NULL;
+
+    JK_TRACE_ENTER(l);
+    if (w->type == JK_LB_WORKER_TYPE) {
+        lb = (lb_worker_t *)w->worker_private;
+        name = lb->s->name;
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "producing edit form for lb worker '%s'",
+                   name);
+    }
+    else {
+        jk_log(l, JK_LOG_WARNING,
+               "worker type not implemented");
+        JK_TRACE_EXIT(l);
+        return;
+    }
+
+    if (!lb) {
+        jk_log(l, JK_LOG_WARNING,
+               "lb structure is (NULL)");
+        JK_TRACE_EXIT(l);
+        return;
+    }
+
+    jk_putv(s, "<hr/><h3>Edit load balancer settings for ",
+            name, "</h3>\n", NULL);
+
+    status_start_form(s, "GET", JK_STATUS_CMD_UPDATE,
+                      0, from, refresh, name, NULL);
+
+    jk_putv(s, "<table>\n<tr><td>", JK_STATUS_ARG_LB_TEXT_RETRIES,
+            ":</td><td><input name=\"",
+            JK_STATUS_ARG_LB_RETRIES, "\" type=\"text\" ", NULL);
+    jk_printf(s, "value=\"%d\"/></td></tr>\n", lb->retries);
+    jk_putv(s, "<tr><td>", JK_STATUS_ARG_LB_TEXT_RECOVER_TIME,
+            ":</td><td><input name=\"",
+            JK_STATUS_ARG_LB_RECOVER_TIME, "\" type=\"text\" ", NULL);
+    jk_printf(s, "value=\"%d\"/></td></tr>\n", lb->recover_wait_time);
+    jk_putv(s, "<tr><td>", JK_STATUS_ARG_LB_TEXT_STICKY,
+            ":</td><td><input name=\"",
+            JK_STATUS_ARG_LB_STICKY, "\" type=\"checkbox\"", NULL);
+    if (lb->sticky_session)
+        jk_puts(s, " checked=\"checked\"");
+    jk_puts(s, "/></td></tr>\n");
+    jk_putv(s, "<tr><td>", JK_STATUS_ARG_LB_TEXT_STICKY_FORCE,
+            ":</td><td><input name=\"",
+            JK_STATUS_ARG_LB_STICKY_FORCE, "\" type=\"checkbox\"", NULL);
+    if (lb->sticky_session_force)
+        jk_puts(s, " checked=\"checked\"");
+    jk_puts(s, "/></td></tr>\n");
+    jk_putv(s, "<tr><td>", JK_STATUS_ARG_LB_TEXT_METHOD,
+            ":</td><td></td></tr>\n", NULL);
+    jk_putv(s, "<tr><td>&nbsp;&nbsp;Requests</td><td><input name=\"",
+            JK_STATUS_ARG_LB_METHOD, "\" type=\"radio\"", NULL);
+    jk_printf(s, " value=\"%d\"", JK_LB_METHOD_REQUESTS);
+    if (lb->lbmethod == JK_LB_METHOD_REQUESTS)
+        jk_puts(s, " checked=\"checked\"");
+    jk_puts(s, "/></td></tr>\n");
+    jk_putv(s, "<tr><td>&nbsp;&nbsp;Traffic</td><td><input name=\"",
+            JK_STATUS_ARG_LB_METHOD, "\" type=\"radio\"", NULL);
+    jk_printf(s, " value=\"%d\"", JK_LB_METHOD_TRAFFIC);
+    if (lb->lbmethod == JK_LB_METHOD_TRAFFIC)
+        jk_puts(s, " checked=\"checked\"");
+    jk_puts(s, "/></td></tr>\n");
+    jk_putv(s, "<tr><td>&nbsp;&nbsp;Busyness</td><td><input name=\"",
+            JK_STATUS_ARG_LB_METHOD, "\" type=\"radio\"", NULL);
+    jk_printf(s, " value=\"%d\"", JK_LB_METHOD_BUSYNESS);
+    if (lb->lbmethod == JK_LB_METHOD_BUSYNESS)
+        jk_puts(s, " checked=\"checked\"");
+    jk_puts(s, "/></td></tr>\n");
+    jk_putv(s, "<tr><td>&nbsp;&nbsp;Sessions</td><td><input name=\"",
+            JK_STATUS_ARG_LB_METHOD, "\" type=\"radio\"", NULL);
+    jk_printf(s, " value=\"%d\"", JK_LB_METHOD_SESSIONS);
+    if (lb->lbmethod == JK_LB_METHOD_SESSIONS)
+        jk_puts(s, " checked=\"checked\"");
+    jk_puts(s, "/></td></tr>\n");
+    jk_putv(s, "<tr><td>", JK_STATUS_ARG_LB_TEXT_LOCK,
+            ":</td><td></td></tr>\n", NULL);
+    jk_putv(s, "<tr><td>&nbsp;&nbsp;Optimistic</td><td><input name=\"",
+            JK_STATUS_ARG_LB_LOCK, "\" type=\"radio\"", NULL);
+    jk_printf(s, " value=\"%d\"", JK_LB_LOCK_OPTIMISTIC);
+    if (lb->lblock == JK_LB_LOCK_OPTIMISTIC)
+        jk_puts(s, " checked=\"checked\"");
+    jk_puts(s, "/></td></tr>\n");
+    jk_putv(s, "<tr><td>&nbsp;&nbsp;Pessimistic</td><td><input name=\"",
+            JK_STATUS_ARG_LB_LOCK, "\" type=\"radio\"", NULL);
+    jk_printf(s, " value=\"%d\"", JK_LB_LOCK_PESSIMISTIC);
+    if (lb->lblock == JK_LB_LOCK_PESSIMISTIC)
+        jk_puts(s, " checked=\"checked\"");
+    jk_puts(s, "/></td></tr>\n");
+    jk_puts(s, "</table>\n");
+    jk_puts(s, "<br/><input type=\"submit\" value=\"Update Balancer\"/></form>\n");
+
+    jk_putv(s, "<hr/><h3>Edit load balancer member settings by type for ",
+            name, "</h3>\n", NULL);
+    jk_puts(s, "[<a href=\"");
+    status_write_uri(s, NULL, JK_STATUS_CMD_EDIT,
+                     0, from, refresh, name, NULL); 
+    jk_putv(s, "&", JK_STATUS_ARG_LB_MEMBER_ATT, "=",
+            JK_STATUS_ARG_LBM_ACTIVATION, "\">",
+            JK_STATUS_ARG_LBM_TEXT_ACTIVATION, "</a>\n", NULL);
+    jk_puts(s, "|<a href=\"");
+    status_write_uri(s, NULL, JK_STATUS_CMD_EDIT,
+                     0, from, refresh, name, NULL); 
+    jk_putv(s, "&", JK_STATUS_ARG_LB_MEMBER_ATT, "=",
+            JK_STATUS_ARG_LBM_FACTOR, "\">",
+            JK_STATUS_ARG_LBM_TEXT_FACTOR, "</a>\n", NULL);
+    jk_puts(s, "|<a href=\"");
+    status_write_uri(s, NULL, JK_STATUS_CMD_EDIT,
+                     0, from, refresh, name, NULL); 
+    jk_putv(s, "&", JK_STATUS_ARG_LB_MEMBER_ATT, "=",
+            JK_STATUS_ARG_LBM_ROUTE, "\">",
+            JK_STATUS_ARG_LBM_TEXT_ROUTE, "</a>\n", NULL);
+    jk_puts(s, "|<a href=\"");
+    status_write_uri(s, NULL, JK_STATUS_CMD_EDIT,
+                     0, from, refresh, name, NULL); 
+    jk_putv(s, "&", JK_STATUS_ARG_LB_MEMBER_ATT, "=",
+            JK_STATUS_ARG_LBM_REDIRECT, "\">",
+            JK_STATUS_ARG_LBM_TEXT_REDIRECT, "</a>\n", NULL);
+    jk_puts(s, "|<a href=\"");
+    status_write_uri(s, NULL, JK_STATUS_CMD_EDIT,
+                     0, from, refresh, name, NULL); 
+    jk_putv(s, "&", JK_STATUS_ARG_LB_MEMBER_ATT, "=",
+            JK_STATUS_ARG_LBM_DOMAIN, "\">",
+            JK_STATUS_ARG_LBM_TEXT_DOMAIN, "</a>\n", NULL);
+    jk_puts(s, "|<a href=\"");
+    status_write_uri(s, NULL, JK_STATUS_CMD_EDIT,
+                     0, from, refresh, name, NULL); 
+    jk_putv(s, "&", JK_STATUS_ARG_LB_MEMBER_ATT, "=",
+            JK_STATUS_ARG_LBM_DISTANCE, "\">",
+            JK_STATUS_ARG_LBM_TEXT_DISTANCE, "</a>\n", NULL);
+    jk_puts(s, "]<br/>\n");
+
+    JK_TRACE_EXIT(l);
+}
+
+static void form_member(jk_ws_service_t *s, worker_record_t *wr,
+                        const char *worker,
+                        int from, int refresh,
+                        jk_logger_t *l)
+{
+    JK_TRACE_ENTER(l);
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG,
+               "producing edit form for sub worker '%s' of lb worker '%s'",
+               wr->s->name, worker);
+    jk_putv(s, "<hr/><h3>Edit worker settings for ",
+            wr->s->name, "</h3>\n", NULL);
+    status_start_form(s, "GET", JK_STATUS_CMD_UPDATE,
+                      0, from, refresh, worker, wr->s->name);
+
+    jk_putv(s, "<tr><td>", JK_STATUS_ARG_LBM_TEXT_ACTIVATION,
+            ":</td><td></td></tr>\n", NULL);
+    jk_putv(s, "<tr><td>&nbsp;&nbsp;Active</td><td><input name=\"",
+            JK_STATUS_ARG_LBM_ACTIVATION, "\" type=\"radio\"", NULL);
+    jk_printf(s, " value=\"%d\"", JK_LB_ACTIVATION_ACTIVE);
+    if (wr->s->activation == JK_LB_ACTIVATION_ACTIVE)
+        jk_puts(s, " checked=\"checked\"");
+    jk_puts(s, "/></td></tr>\n");
+    jk_putv(s, "<tr><td>&nbsp;&nbsp;Disabled</td><td><input name=\"",
+            JK_STATUS_ARG_LBM_ACTIVATION, "\" type=\"radio\"", NULL);
+    jk_printf(s, " value=\"%d\"", JK_LB_ACTIVATION_DISABLED);
+    if (wr->s->activation == JK_LB_ACTIVATION_DISABLED)
+        jk_puts(s, " checked=\"checked\"");
+    jk_puts(s, "/></td></tr>\n");
+    jk_putv(s, "<tr><td>&nbsp;&nbsp;Stopped</td><td><input name=\"",
+            JK_STATUS_ARG_LBM_ACTIVATION, "\" type=\"radio\"", NULL);
+    jk_printf(s, " value=\"%d\"", JK_LB_ACTIVATION_STOPPED);
+    if (wr->s->activation == JK_LB_ACTIVATION_STOPPED)
+        jk_puts(s, " checked=\"checked\"");
+    jk_puts(s, "/></td></tr>\n");
+    jk_putv(s, "<tr><td>", JK_STATUS_ARG_LBM_TEXT_FACTOR,
+            ":</td><td><input name=\"",
+            JK_STATUS_ARG_LBM_FACTOR, "\" type=\"text\" ", NULL);
+    jk_printf(s, "value=\"%d\"/></td></tr>\n", wr->s->lb_factor);
+    jk_putv(s, "<table>\n<tr><td>", JK_STATUS_ARG_LBM_TEXT_ROUTE,
+            ":</td><td><input name=\"",
+            JK_STATUS_ARG_LBM_ROUTE, "\" type=\"text\" ", NULL);
+    jk_printf(s, "value=\"%s\"/></td></tr>\n", wr->s->jvm_route);
+    jk_putv(s, "<tr><td>", JK_STATUS_ARG_LBM_TEXT_REDIRECT,
+            ":</td><td><input name=\"",
+            JK_STATUS_ARG_LBM_REDIRECT, "\" type=\"text\" ", NULL);
+    jk_putv(s, "value=\"", wr->s->redirect, NULL);
+    jk_puts(s, "\"/></td></tr>\n");
+    jk_putv(s, "<tr><td>", JK_STATUS_ARG_LBM_TEXT_DOMAIN,
+            ":</td><td><input name=\"",
+            JK_STATUS_ARG_LBM_DOMAIN, "\" type=\"text\" ", NULL);
+    jk_putv(s, "value=\"", wr->s->domain, NULL);
+    jk_puts(s, "\"/></td></tr>\n");
+    jk_putv(s, "<tr><td>", JK_STATUS_ARG_LBM_TEXT_DISTANCE,
+            ":</td><td><input name=\"",
+            JK_STATUS_ARG_LBM_DISTANCE, "\" type=\"text\" ", NULL);
+    jk_printf(s, "value=\"%d\"/></td></tr>\n", wr->s->distance);
+    jk_puts(s, "</table>\n");
+    jk_puts(s, "<br/><input type=\"submit\" value=\"Update Worker\"/>\n</form>\n");
+    JK_TRACE_EXIT(l);
+}
+
+static void form_all_members(jk_ws_service_t *s, jk_worker_t *w,
+                             const char *attribute,
+                             int from, int refresh,
+                             jk_logger_t *l)
+{
+    const char *name = NULL;
+    lb_worker_t *lb = NULL;
+    const char *aname;
+    unsigned int i;
+
+    JK_TRACE_ENTER(l);
+    if (!attribute) {
+        jk_log(l, JK_LOG_WARNING,
+               "missing request parameter '%s'",
+               JK_STATUS_ARG_LB_MEMBER_ATT);
+        JK_TRACE_EXIT(l);
+        return;
+    }
+    else {
+        if (!strcmp(attribute, JK_STATUS_ARG_LBM_ACTIVATION))
+            aname=JK_STATUS_ARG_LBM_TEXT_ACTIVATION;
+        else if (!strcmp(attribute, JK_STATUS_ARG_LBM_FACTOR))
+            aname=JK_STATUS_ARG_LBM_TEXT_FACTOR;
+        else if (!strcmp(attribute, JK_STATUS_ARG_LBM_ROUTE))
+            aname=JK_STATUS_ARG_LBM_TEXT_ROUTE;
+        else if (!strcmp(attribute, JK_STATUS_ARG_LBM_REDIRECT))
+            aname=JK_STATUS_ARG_LBM_TEXT_REDIRECT;
+        else if (!strcmp(attribute, JK_STATUS_ARG_LBM_DOMAIN))
+            aname=JK_STATUS_ARG_LBM_TEXT_DOMAIN;
+        else if (!strcmp(attribute, JK_STATUS_ARG_LBM_DISTANCE))
+            aname=JK_STATUS_ARG_LBM_TEXT_DISTANCE;
+        else {
+            jk_log(l, JK_LOG_WARNING,
+                   "unknown attribute '%s'",
+                   attribute);
+            JK_TRACE_EXIT(l);
+            return;
+        }
+    }
+    if (w->type == JK_LB_WORKER_TYPE) {
+        lb = (lb_worker_t *)w->worker_private;
+        name = lb->s->name;
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "producing edit form for attribute '%s' [%s] of all members of lb worker '%s'",
+                   attribute, aname, name);
+    }
+    else {
+        jk_log(l, JK_LOG_WARNING,
+               "worker type not implemented");
+        JK_TRACE_EXIT(l);
+        return;
+    }
+
+    if (lb) {
+        jk_putv(s, "<hr/><h3>Edit attribute '", aname,
+                "' for all members of load balancer ",
+                name, "</h3>\n", NULL);
+
+        status_start_form(s, "GET", JK_STATUS_CMD_UPDATE,
+                          0, from, refresh, name, NULL);
+
+        jk_putv(s, "<input type=\"hidden\" name=\"",
+                JK_STATUS_ARG_LB_MEMBER_ATT, "\" ", NULL);
+        jk_putv(s, "value=\"", attribute, "\"/>\n", NULL);
+
+        jk_putv(s, "<table><tr>"
+                "<th>Balanced Worker</th><th>", aname, "</th>"
+                "</tr>", NULL);
+
+        for (i = 0; i < lb->num_of_workers; i++) {
+            worker_record_t *wr = &(lb->lb_workers[i]);
+            ajp_worker_t *a = (ajp_worker_t *)wr->w->worker_private;
+
+            jk_putv(s, "<tr><td>", wr->s->name, "</td><td>\n", NULL);
+
+            if (!strcmp(attribute, JK_STATUS_ARG_LBM_ACTIVATION)) {
+
+                jk_printf(s, "Active:&nbsp;<input name=\"val%d\" type=\"radio\"", i);
+                jk_printf(s, " value=\"%d\"", JK_LB_ACTIVATION_ACTIVE);
+                if (wr->s->activation == JK_LB_ACTIVATION_ACTIVE)
+                    jk_puts(s, " checked=\"checked\"");
+                jk_puts(s, "/>&nbsp;|&nbsp;\n");
+                jk_printf(s, "Disabled:&nbsp;<input name=\"val%d\" type=\"radio\"", i);
+                jk_printf(s, " value=\"%d\"", JK_LB_ACTIVATION_DISABLED);
+                if (wr->s->activation == JK_LB_ACTIVATION_DISABLED)
+                    jk_puts(s, " checked=\"checked\"");
+                jk_puts(s, "/>&nbsp;|&nbsp;\n");
+                jk_printf(s, "Stopped:&nbsp;<input name=\"val%d\" type=\"radio\"", i);
+                jk_printf(s, " value=\"%d\"", JK_LB_ACTIVATION_STOPPED);
+                if (wr->s->activation == JK_LB_ACTIVATION_STOPPED)
+                    jk_puts(s, " checked=\"checked\"");
+                jk_puts(s, "/>\n");
+
+            }
+            else if (!strcmp(attribute, JK_STATUS_ARG_LBM_FACTOR)) {
+                jk_printf(s, "<input name=\"val%d\" type=\"text\"", i);
+                jk_printf(s, "value=\"%d\"/>\n", wr->s->lb_factor);
+            }
+            else if (!strcmp(attribute, JK_STATUS_ARG_LBM_ROUTE)) {
+                jk_printf(s, "<input name=\"val%d\" type=\"text\"", i);
+                jk_putv(s, "value=\"", wr->s->jvm_route, "\"/>\n", NULL);
+            }
+            else if (!strcmp(attribute, JK_STATUS_ARG_LBM_REDIRECT)) {
+                jk_printf(s, "<input name=\"val%d\" type=\"text\"", i);
+                jk_putv(s, "value=\"", wr->s->redirect, "\"/>\n", NULL);
+            }
+            else if (!strcmp(attribute, JK_STATUS_ARG_LBM_DOMAIN)) {
+                jk_printf(s, "<input name=\"val%d\" type=\"text\"", i);
+                jk_putv(s, "value=\"", wr->s->domain, "\"/>\n", NULL);
+            }
+            else if (!strcmp(attribute, JK_STATUS_ARG_LBM_DISTANCE)) {
+                jk_printf(s, "<input name=\"val%d\" type=\"text\"", i);
+                jk_printf(s, "value=\"%d\"/>\n", wr->s->distance);
+            }
+
+            jk_puts(s, "</td></tr>");
+        }
+
+        jk_puts(s, "</table>\n");
+        jk_puts(s, "<br/><input type=\"submit\" value=\"Update Balancer\"/></form>\n");
+    }
+    JK_TRACE_EXIT(l);
+}
+
+static void commit_worker(jk_ws_service_t *s, jk_worker_t *w,
+                          jk_logger_t *l)
+{
+    const char *name = NULL;
+    lb_worker_t *lb = NULL;
+    int i;
+
+    JK_TRACE_ENTER(l);
+    if (w->type == JK_LB_WORKER_TYPE) {
+        lb = (lb_worker_t *)w->worker_private;
+        name = lb->s->name;
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "committing changes for lb worker '%s'",
+                   name);
+    }
+    else {
+        jk_log(l, JK_LOG_WARNING,
+               "worker type not implemented");
+        JK_TRACE_EXIT(l);
+        return;
+    }
+
+    if (!lb) {
+        jk_log(l, JK_LOG_WARNING,
+               "lb structure is (NULL)");
+        JK_TRACE_EXIT(l);
+        return;
+    }
+
+    if (lb->sequence != lb->s->sequence)
+        jk_lb_pull(lb, l);
+
+    i = status_get_int(JK_STATUS_ARG_LB_RETRIES,
+                       s->query_string, lb->retries);
+    if (i != lb->retries && i > 0) {
+        jk_log(l, JK_LOG_INFO,
+               "setting 'retries' for lb worker '%s' to '%i'",
+               name, i);
+        lb->retries = i;
+    }
+    i = status_get_int(JK_STATUS_ARG_LB_RECOVER_TIME,
+                       s->query_string, lb->recover_wait_time);
+    if (i != lb->recover_wait_time && i > 0) {
+        jk_log(l, JK_LOG_INFO,
+               "setting 'recover_time' for lb worker '%s' to '%i'",
+               name, i);
+        lb->recover_wait_time = i;
+    }
+    i = status_get_bool(JK_STATUS_ARG_LB_STICKY,
+                        s->query_string, 0);
+    if (i != lb->sticky_session && i > 0) {
+        jk_log(l, JK_LOG_INFO,
+               "setting 'sticky_session' for lb worker '%s' to '%i'",
+               name, i);
+        lb->sticky_session = i;
+    }
+    i = status_get_bool(JK_STATUS_ARG_LB_STICKY_FORCE,
+                        s->query_string, 0);
+    if (i != lb->sticky_session_force && i > 0) {
+        jk_log(l, JK_LOG_INFO,
+               "setting 'sticky_session_force' for lb worker '%s' to '%i'",
+               name, i);
+        lb->sticky_session_force = i;
+    }
+    i = status_get_int(JK_STATUS_ARG_LB_METHOD,
+                       s->query_string, lb->lbmethod);
+    if (i != lb->lbmethod && i > 0 && i <= JK_LB_METHOD_MAX) {
+        jk_log(l, JK_LOG_INFO,
+               "setting 'method' for lb worker '%s' to '%i'",
+               name, i);
+        lb->lbmethod = i;
+    }
+    i = status_get_int(JK_STATUS_ARG_LB_LOCK,
+                       s->query_string, lb->lblock);
+    if (i != lb->lblock && i > 0 && i <= JK_LB_LOCK_MAX) {
+        jk_log(l, JK_LOG_INFO,
+               "setting 'lock' for lb worker '%s' to '%i'",
+               name, i);
+        lb->lblock = i;
+    }
+    lb->sequence++;
+    jk_lb_push(lb, l); 
+}
+
+static int commit_member(jk_ws_service_t *s, worker_record_t *wr,
+                          const char *worker,
+                          jk_logger_t *l)
+{
+    char buf[128];
+    int rc = 0;
+    int i;
+
+    JK_TRACE_ENTER(l);
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG,
+               "committing changes for sub worker '%s' of lb worker '%s'",
+               wr->s->name, worker);
+
+    i = status_get_int(JK_STATUS_ARG_LBM_ACTIVATION,
+                       s->query_string, wr->s->activation);
+    if (i != wr->s->activation && i > 0 && i<= JK_LB_ACTIVATION_MAX) {
+        jk_log(l, JK_LOG_INFO,
+               "setting 'activation' for sub worker '%s' of lb worker '%s' to '%s'",
+               wr->s->name, worker, jk_lb_get_activation(wr, l));
+        wr->s->activation = i;
+        rc |= 1;
+    }
+    i = status_get_int(JK_STATUS_ARG_LBM_FACTOR,
+                       s->query_string, wr->s->lb_factor);
+    if (i != wr->s->lb_factor && i > 0) {
+        jk_log(l, JK_LOG_INFO,
+               "setting 'lbfactor' for sub worker '%s' of lb worker '%s' to '%i'",
+               wr->s->name, worker, i);
+        wr->s->lb_factor = i;
+        /* Recalculate the load multiplicators wrt. lb_factor */
+        rc |= 2;
+    }
+    if (status_get_arg(JK_STATUS_ARG_LBM_ROUTE,
+                       s->query_string, buf, sizeof(buf))) {
+        if (strncmp(wr->s->jvm_route, buf, JK_SHM_STR_SIZ)) {
+            jk_log(l, JK_LOG_INFO,
+                   "setting 'jvm_route' for sub worker '%s' of lb worker '%s' to '%s'",
+                   wr->s->name, worker, buf);
+            strncpy(wr->s->jvm_route, buf, JK_SHM_STR_SIZ);
+            if (!wr->s->domain[0]) {
+                char * id_domain = strchr(wr->s->jvm_route, '.');
+                if (id_domain) {
+                    *id_domain = '\0';
+                    strcpy(wr->s->domain, wr->s->jvm_route);
+                    *id_domain = '.';
+                }
+            }
+        }
+    }
+    else {
+        jk_log(l, JK_LOG_INFO,
+               "resetting 'jvm_route' for sub worker '%s' of lb worker '%s'",
+               wr->s->name, worker);
+        memset(wr->s->jvm_route, 0, JK_SHM_STR_SIZ);
+    }
+    if (status_get_arg(JK_STATUS_ARG_LBM_REDIRECT,
+                       s->query_string, buf, sizeof(buf))) {
+        if (strncmp(wr->s->redirect, buf, JK_SHM_STR_SIZ)) {
+            jk_log(l, JK_LOG_INFO,
+                   "setting 'redirect' for sub worker '%s' of lb worker '%s' to '%s'",
+                   wr->s->name, worker, buf);
+            strncpy(wr->s->redirect, buf, JK_SHM_STR_SIZ);
+        }
+    }
+    else {
+        jk_log(l, JK_LOG_INFO,
+               "resetting 'redirect' for sub worker '%s' of lb worker '%s'",
+               wr->s->name, worker);
+        memset(wr->s->redirect, 0, JK_SHM_STR_SIZ);
+    }
+    if (status_get_arg(JK_STATUS_ARG_LBM_DOMAIN,
+                       s->query_string, buf, sizeof(buf))) {
+        if (strncmp(wr->s->domain, buf, JK_SHM_STR_SIZ)) {
+            jk_log(l, JK_LOG_INFO,
+                   "setting 'domain' for sub worker '%s' of lb worker '%s' to '%s'",
+                   wr->s->name, worker, buf);
+            strncpy(wr->s->domain, buf, JK_SHM_STR_SIZ);
+        }
+    }
+    else {
+        jk_log(l, JK_LOG_INFO,
+               "resetting 'domain' for sub worker '%s' of lb worker '%s'",
+               wr->s->name, worker);
+        memset(wr->s->domain, 0, JK_SHM_STR_SIZ);
+    }
+    i = status_get_int(JK_STATUS_ARG_LBM_DISTANCE,
+                       s->query_string, wr->s->distance);
+    if (i != wr->s->distance && i > 0) {
+        jk_log(l, JK_LOG_INFO,
+               "setting 'distance' for sub worker '%s' of lb worker '%s' to '%i'",
+               wr->s->name, worker, i);
+        wr->s->distance = i;
+    }
+    return rc;
+}
+
+static void commit_all_members(jk_ws_service_t *s, jk_worker_t *w,
+                               const char *attribute,
+                               jk_logger_t *l)
+{
+    char buf[128];
+    char vname[32];
+    const char *name = NULL;
+    lb_worker_t *lb = NULL;
+    const char *aname;
+    int i;
+    int rc = 0;
+    unsigned int j;
+
+    JK_TRACE_ENTER(l);
+    if (!attribute) {
+        jk_log(l, JK_LOG_WARNING,
+               "missing request parameter '%s'",
+               JK_STATUS_ARG_LB_MEMBER_ATT);
+        JK_TRACE_EXIT(l);
+        return;
+    }
+    else {
+        if (!strcmp(attribute, JK_STATUS_ARG_LBM_ACTIVATION))
+            aname=JK_STATUS_ARG_LBM_TEXT_ACTIVATION;
+        else if (!strcmp(attribute, JK_STATUS_ARG_LBM_FACTOR))
+            aname=JK_STATUS_ARG_LBM_TEXT_FACTOR;
+        else if (!strcmp(attribute, JK_STATUS_ARG_LBM_ROUTE))
+            aname=JK_STATUS_ARG_LBM_TEXT_ROUTE;
+        else if (!strcmp(attribute, JK_STATUS_ARG_LBM_REDIRECT))
+            aname=JK_STATUS_ARG_LBM_TEXT_REDIRECT;
+        else if (!strcmp(attribute, JK_STATUS_ARG_LBM_DOMAIN))
+            aname=JK_STATUS_ARG_LBM_TEXT_DOMAIN;
+        else if (!strcmp(attribute, JK_STATUS_ARG_LBM_DISTANCE))
+            aname=JK_STATUS_ARG_LBM_TEXT_DISTANCE;
+        else {
+            jk_log(l, JK_LOG_WARNING,
+                   "unknown attribute '%s'",
+                   attribute);
+            JK_TRACE_EXIT(l);
+            return;
+        }
+    }
+    if (w->type == JK_LB_WORKER_TYPE) {
+        lb = (lb_worker_t *)w->worker_private;
+        name = lb->s->name;
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "committing changes for attribute '%s' [%s] of all members of lb worker '%s'",
+                   attribute, aname, name);
+    }
+    else {
+        jk_log(l, JK_LOG_WARNING,
+               "worker type not implemented");
+        JK_TRACE_EXIT(l);
+        return;
+    }
+
+    if (lb) {
+        for (j = 0; j < lb->num_of_workers; j++) {
+            worker_record_t *wr = &(lb->lb_workers[j]);
+            snprintf(vname, 32-1, "val%d", j);
+
+            if (!strcmp(attribute, JK_STATUS_ARG_LBM_ACTIVATION)) {
+                i = status_get_int(vname, s->query_string, wr->s->activation);
+                if (i != wr->s->activation && i > 0 && i<= JK_LB_ACTIVATION_MAX) {
+                    jk_log(l, JK_LOG_INFO,
+                           "setting 'activation' for sub worker '%s' of lb worker '%s' to '%s'",
+                           wr->s->name, name, jk_lb_get_activation(wr, l));
+                    wr->s->activation = i;
+                    rc = 1;
+                }
+            }
+            else if (!strcmp(attribute, JK_STATUS_ARG_LBM_FACTOR)) {
+                i = status_get_int(vname, s->query_string, wr->s->lb_factor);
+                if (i != wr->s->lb_factor && i > 0) {
+                    jk_log(l, JK_LOG_INFO,
+                           "setting 'lbfactor' for sub worker '%s' of lb worker '%s' to '%i'",
+                           wr->s->name, name, i);
+                    wr->s->lb_factor = i;
+                    rc = 2;
+                }
+            }
+            else if (!strcmp(attribute, JK_STATUS_ARG_LBM_ROUTE)) {
+                if (status_get_arg(vname, s->query_string, buf, sizeof(buf))) {
+                    if (strncmp(wr->s->jvm_route, buf, JK_SHM_STR_SIZ)) {
+                        jk_log(l, JK_LOG_INFO,
+                               "setting 'jvm_route' for sub worker '%s' of lb worker '%s' to '%s'",
+                               wr->s->name, name, buf);
+                        strncpy(wr->s->jvm_route, buf, JK_SHM_STR_SIZ);
+                        if (!wr->s->domain[0]) {
+                            char * id_domain = strchr(wr->s->jvm_route, '.');
+                            if (id_domain) {
+                                *id_domain = '\0';
+                                strcpy(wr->s->domain, wr->s->jvm_route);
+                                *id_domain = '.';
+                            }
+                        }
+                    }
+                }
+                else {
+                    jk_log(l, JK_LOG_INFO,
+                           "resetting 'jvm_route' for sub worker '%s' of lb worker '%s'",
+                           wr->s->name, name);
+                    memset(wr->s->jvm_route, 0, JK_SHM_STR_SIZ);
+                }
+            }
+            else if (!strcmp(attribute, JK_STATUS_ARG_LBM_REDIRECT)) {
+                if (status_get_arg(vname, s->query_string, buf, sizeof(buf))) {
+                    if (strncmp(wr->s->redirect, buf, JK_SHM_STR_SIZ)) {
+                        jk_log(l, JK_LOG_INFO,
+                               "setting 'redirect' for sub worker '%s' of lb worker '%s' to '%s'",
+                               wr->s->name, name, buf);
+                        strncpy(wr->s->redirect, buf, JK_SHM_STR_SIZ);
+                    }
+                }
+                else {
+                    jk_log(l, JK_LOG_INFO,
+                           "resetting 'redirect' for sub worker '%s' of lb worker '%s'",
+                           wr->s->name, name);
+                    memset(wr->s->redirect, 0, JK_SHM_STR_SIZ);
+                }
+            }
+            else if (!strcmp(attribute, JK_STATUS_ARG_LBM_DOMAIN)) {
+                if (status_get_arg(vname, s->query_string, buf, sizeof(buf))) {
+                    if (strncmp(wr->s->domain, buf, JK_SHM_STR_SIZ)) {
+                        jk_log(l, JK_LOG_INFO,
+                               "setting 'domain' for sub worker '%s' of lb worker '%s' to '%s'",
+                               wr->s->name, name, buf);
+                        strncpy(wr->s->domain, buf, JK_SHM_STR_SIZ);
+                    }
+                }
+                else {
+                    jk_log(l, JK_LOG_INFO,
+                           "resetting 'domain' for sub worker '%s' of lb worker '%s'",
+                           wr->s->name, name);
+                    memset(wr->s->domain, 0, JK_SHM_STR_SIZ);
+                }
+
+            }
+            else if (!strcmp(attribute, JK_STATUS_ARG_LBM_DISTANCE)) {
+                i = status_get_int(vname, s->query_string, wr->s->distance);
+                if (i != wr->s->distance && i > 0) {
+                    jk_log(l, JK_LOG_INFO,
+                           "setting 'distance' for sub worker '%s' of lb worker '%s' to '%i'",
+                           wr->s->name, name, i);
+                    wr->s->lb_factor = i;
+                }
+
+            }
+        }
+        if (rc == 1)
+            reset_lb_values(lb, l);
+        else if (rc == 2)
+            /* Recalculate the load multiplicators wrt. lb_factor */
+            update_mult(lb, l);
+    }
+    JK_TRACE_EXIT(l);
+}
+
+
+
+
+static void display_legend(jk_ws_service_t *s, jk_logger_t *l)
+{
+    JK_TRACE_ENTER(l);
+    jk_puts(s, "<hr/><table>\n"
+            "<tbody valign=\"baseline\">\n"
+            "<tr><th>Name</th><td>Worker name</td></tr>\n"
+            "<tr><th>Type</th><td>Worker type</td></tr>\n"
+            "<tr><th>jvmRoute</th><td>Worker JVM route</td></tr>\n"
+            "<tr><th>Addr</th><td>Backend Address info</td></tr>\n"
+            "<tr><th>Act</th><td>Worker activation configuration</br>\n"
+            "ACT=Active, DIS=Disabled, STP=Stopped</td></tr>\n"
+            "<tr><th>Stat</th><td>Worker error status</br>\n"
+            "OK=OK, N/A=Not availabe, ERR=Error, REC=Recovering, BSY=Busy</td></tr>\n"
+            "<tr><th>D</th><td>Worker distance</td></tr>\n"
+            "<tr><th>F</th><td>Load Balancer factor</td></tr>\n"
+            "<tr><th>M</th><td>Load Balancer multiplicity</td></tr>\n"
+            "<tr><th>V</th><td>Load Balancer value</td></tr>\n"
+            "<tr><th>Acc</th><td>Number of requests</td></tr>\n"
+            "<tr><th>Err</th><td>Number of failed requests</td></tr>\n"
+            "<tr><th>CE</th><td>Number of client errors</td></tr>\n"
+            "<tr><th>Wr</th><td>Number of bytes transferred/min</td></tr>\n"
+            "<tr><th>Rd</th><td>Number of bytes read/min</td></tr>\n"
+            "<tr><th>Busy</th><td>Current number of busy connections</td></tr>\n"
+            "<tr><th>Max</th><td>Maximum number of busy connections</td></tr>\n"
+            "<tr><th>RR</th><td>Route redirect</td></tr>\n"
+            "<tr><th>Cd</th><td>Cluster domain</td></tr>\n"
+            "<tr><th>Rs</th><td>Recovery scheduled</td></tr>\n"
+            "</tbody>\n"
+            "</table>");
+    JK_TRACE_EXIT(l);
+}
+
+
+static int list_workers(jk_ws_service_t *s, status_worker_t *sw,
+                        int refresh, jk_logger_t *l)
+{
+    unsigned int i;
+    jk_worker_t *w = NULL;
+
+    JK_TRACE_ENTER(l);
+    for (i = 0; i < sw->we->num_of_workers; i++) {
+        w = wc_get_worker_for_name(sw->we->worker_list[i], l);
+        if (!w) {
+            jk_log(l, JK_LOG_WARNING,
+                   "could not find worker '%s'",
+                   sw->we->worker_list[i]);
+            continue;
+        }
+        display_worker(s, w, refresh, 0, l);
+    }
+    display_legend(s, l);
+    JK_TRACE_EXIT(l);
+    return JK_TRUE;
+}
+
+static int list_workers_xml(jk_ws_service_t *s, status_worker_t *sw,
+                            jk_logger_t *l)
+{
+    unsigned int i;
+    int has_lb = 0;
+    jk_worker_t *w = NULL;
+
+    JK_TRACE_ENTER(l);
+    for (i = 0; i < sw->we->num_of_workers; i++) {
+        w = wc_get_worker_for_name(sw->we->worker_list[i], l);
+        if (!w) {
+            jk_log(l, JK_LOG_WARNING,
+                   "could not find worker '%s'",
+                   sw->we->worker_list[i]);
+            continue;
+        }
+        if (w->type == JK_LB_WORKER_TYPE) {
+            if (!has_lb)
+                jk_puts(s, "<jk:balancers>\n");
+            has_lb = 1;
+            display_worker_xml(s, w, 0, l);
+        }
+    }
+    if (has_lb)
+        jk_puts(s, "</jk:balancers>\n");
+
+    for (i = 0; i < sw->we->num_of_workers; i++) {
+        w = wc_get_worker_for_name(sw->we->worker_list[i], l);
+        if (!w) {
+            jk_log(l, JK_LOG_WARNING,
+                   "could not find worker '%s'",
+                   sw->we->worker_list[i]);
+            continue;
+        }
+        if (w->type != JK_LB_WORKER_TYPE) {
+            display_worker_xml(s, w, 0, l);
+        }
+    }
+    JK_TRACE_EXIT(l);
+    return JK_TRUE;
+}
+
+static int list_workers_txt(jk_ws_service_t *s, status_worker_t *sw,
+                            jk_logger_t *l)
+{
+    unsigned int i;
+    int has_lb = 0;
+    jk_worker_t *w = NULL;
 
-    if (!dworker)
+    JK_TRACE_ENTER(l);
+    for (i = 0; i < sw->we->num_of_workers; i++) {
+        w = wc_get_worker_for_name(sw->we->worker_list[i], l);
+        if (!w) {
+            jk_log(l, JK_LOG_WARNING,
+                   "could not find worker '%s'",
+                   sw->we->worker_list[i]);
+            continue;
+        }
+        if (w->type == JK_LB_WORKER_TYPE) {
+            if (!has_lb)
+                jk_puts(s, "Load Balancers:\n");
+            has_lb = 1;
+            display_worker_txt(s, w, 0, l);
+        }
+    }
+
+    for (i = 0; i < sw->we->num_of_workers; i++) {
+        w = wc_get_worker_for_name(sw->we->worker_list[i], l);
+        if (!w) {
+            jk_log(l, JK_LOG_WARNING,
+                   "could not find worker '%s'",
+                   sw->we->worker_list[i]);
+            continue;
+        }
+        if (w->type != JK_LB_WORKER_TYPE) {
+            display_worker_txt(s, w, 0, l);
+        }
+    }
+    JK_TRACE_EXIT(l);
+    return JK_TRUE;
+}
+
+static int show_worker(jk_ws_service_t *s, status_worker_t *sw,
+                       const char *worker, const char *sub_worker,
+                       int refresh, jk_logger_t *l)
+{
+    unsigned int i;
+    jk_worker_t *w = NULL;
+
+    JK_TRACE_ENTER(l);
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG,
+               "showing worker '%s'",
+               worker ? worker : "(null)");
+    if (!worker || !worker[0]) {
+        jk_log(l, JK_LOG_WARNING,
+               "NULL or EMPTY worker param");
+        JK_TRACE_EXIT(l);
         return JK_FALSE;
-    if (!lb_name) {
-        w = wc_get_worker_for_name(dworker, l);
-        if (!w)
-            return JK_FALSE;
-        display_worker(s, w, refresh, 1, l);
     }
-    else  {
-        lb_worker_t *lb = NULL;
-        worker_record_t *wr = NULL;
-        w = wc_get_worker_for_name(lb_name, l);
-        if (!w || w->type != JK_LB_WORKER_TYPE)
-            return JK_FALSE;
-        lb = (lb_worker_t *)w->worker_private;
-        for (i = 0; i < (int)lb->num_of_workers; i++) {
-            wr = &(lb->lb_workers[i]);
-            if (strcmp(dworker, wr->s->name) == 0)
-                break;
-        }
-        if (!wr || i == (int)lb->num_of_workers)
-            return JK_FALSE;
-        display_worker(s, w, refresh, 1, l);
+    w = wc_get_worker_for_name(worker, l);
+    if (!w) {
+        jk_log(l, JK_LOG_WARNING,
+               "could not find worker '%s'",
+               worker);
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
     }
+    /* XXX : Until now we use the lb view even if we only want to show a member */
+    display_worker(s, w, refresh, 1, l);
     display_legend(s, l);
+    JK_TRACE_EXIT(l);
     return JK_TRUE;
 }
 
-
-static int edit_worker(jk_ws_service_t *s, status_worker_t *sw,
-                       const char *dworker, const char *lb_name,
-                       const char *from, int refresh, jk_logger_t *l)
+static int show_worker_xml(jk_ws_service_t *s, status_worker_t *sw,
+                           const char *worker, const char *sub_worker,
+                           jk_logger_t *l)
 {
     unsigned int i;
     jk_worker_t *w = NULL;
 
-    if (!dworker)
+    JK_TRACE_ENTER(l);
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG,
+               "showing worker '%s'",
+               worker ? worker : "(null)");
+    if (!worker || !worker[0]) {
+        jk_log(l, JK_LOG_WARNING,
+               "NULL or EMPTY worker param");
+        JK_TRACE_EXIT(l);
         return JK_FALSE;
-    if (!lb_name) {
-        w = wc_get_worker_for_name(dworker, l);
-        if (!w)
-            return JK_FALSE;
-        form_worker(s, w, from, refresh, l);
     }
-    else  {
-        lb_worker_t *lb = NULL;
-        worker_record_t *wr = NULL;
-        w = wc_get_worker_for_name(lb_name, l);
-        if (!w || w->type != JK_LB_WORKER_TYPE)
-            return JK_FALSE;
-        lb = (lb_worker_t *)w->worker_private;
-        for (i = 0; i < (int)lb->num_of_workers; i++) {
-            wr = &(lb->lb_workers[i]);
-            if (strcmp(dworker, wr->s->name) == 0)
-                break;
-        }
-        if (!wr || i == (int)lb->num_of_workers)
-            return JK_FALSE;
-        form_member(s, wr, lb_name, from, refresh, l);
+    w = wc_get_worker_for_name(worker, l);
+    if (!w) {
+        jk_log(l, JK_LOG_WARNING,
+               "could not find worker '%s'",
+               worker);
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
     }
-    display_legend(s, l);
+    display_worker_xml(s, w, 1, l);
+    JK_TRACE_EXIT(l);
     return JK_TRUE;
 }
 
+static int show_worker_txt(jk_ws_service_t *s, status_worker_t *sw,
+                           const char *worker, const char *sub_worker,
+                           jk_logger_t *l)
+{
+    unsigned int i;
+    jk_worker_t *w = NULL;
 
-static void dump_config(jk_ws_service_t *s, status_worker_t *sw,
-                        jk_logger_t *l)
+    JK_TRACE_ENTER(l);
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG,
+               "showing worker '%s'",
+               worker ? worker : "(null)");
+    if (!worker || !worker[0]) {
+        jk_log(l, JK_LOG_WARNING,
+               "NULL or EMPTY worker param");
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+    w = wc_get_worker_for_name(worker, l);
+    if (!w) {
+        jk_log(l, JK_LOG_WARNING,
+               "could not find worker '%s'",
+               worker);
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+    display_worker_txt(s, w, 1, l);
+    JK_TRACE_EXIT(l);
+    return JK_TRUE;
+}
+
+static int edit_worker(jk_ws_service_t *s, status_worker_t *sw,
+                       const char *worker, const char *sub_worker,
+                       int from, int refresh,
+                       jk_logger_t *l)
 {
     unsigned int i;
-    char buf[32];
-    int has_lb = 0;
+    jk_worker_t *w = NULL;
 
-    for (i = 0; i < sw->we->num_of_workers; i++) {
-        jk_worker_t *w = wc_get_worker_for_name(sw->we->worker_list[i], l);
-        if (w == NULL)
-            continue;
-        if (w->type == JK_LB_WORKER_TYPE) {
-            has_lb = 1;
-            break;
-        }
+    JK_TRACE_ENTER(l);
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG,
+               "editing worker '%s' sub worker '%s'",
+               worker ? worker : "(null)", sub_worker ? sub_worker : "(null)");
+    if (!worker || !worker[0]) {
+        jk_log(l, JK_LOG_WARNING,
+               "NULL or EMPTY worker param");
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+    w = wc_get_worker_for_name(worker, l);
+    if (!w) {
+        jk_log(l, JK_LOG_WARNING,
+               "could not find worker '%s'",
+               worker);
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
     }
+    if (!sub_worker || !sub_worker[0]) {
+        char buf[128];
 
-    jk_printf(s, "  <jk:server name=\"%s\" port=\"%d\" software=\"%s\" version=\"%s\" />\n",
-              s->server_name, s->server_port, s->server_software,  JK_VERSTRING);
-    if (has_lb)
-        jk_puts(s, "  <jk:balancers>\n");
-    for (i = 0; i < sw->we->num_of_workers; i++) {
-        jk_worker_t *w = wc_get_worker_for_name(sw->we->worker_list[i], l);
+        if (status_get_arg_raw(JK_STATUS_ARG_LB_MEMBER_ATT, s->query_string, buf, sizeof(buf)))
+            form_all_members(s, w, buf, from, refresh, l);
+        else
+            form_worker(s, w, from, refresh, l);
+    }
+    else  {
         lb_worker_t *lb = NULL;
-        unsigned int j;
-
-        if (w == NULL)
-            continue;
-        if (w->type == JK_LB_WORKER_TYPE) {
-            lb = (lb_worker_t *)w->worker_private;
+        worker_record_t *wr = NULL;
+        if (w->type != JK_LB_WORKER_TYPE) {
+            jk_log(l, JK_LOG_WARNING,
+                   "worker type not implemented");
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
         }
-        else {
-            /* Skip non lb workers */
-            continue;
+        lb = (lb_worker_t *)w->worker_private;
+        if (!lb) {
+            jk_log(l, JK_LOG_WARNING,
+                   "lb structure is (NULL)");
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
         }
-
-        jk_shm_lock();
-        if (lb->sequence != lb->s->sequence)
-            jk_lb_pull(lb, l);
-        jk_shm_unlock();
-
-        jk_printf(s, "  <jk:balancer id=\"%d\" name=\"%s\" type=\"%s\" sticky=\"%s\" stickyforce=\"%s\" retries=\"%d\" recover=\"%d\" method=\"%s\" lock=\"%s\" busy=\"%d\" max_busy=\"%d\">\n",
-             i,
-             lb->s->name,
-             status_worker_type(w->type),
-             status_val_bool(lb->sticky_session),
-             status_val_bool(lb->sticky_session_force),
-             lb->retries,
-             lb->recover_wait_time,
-             jk_lb_get_lock(lb, l),
-             jk_lb_get_method(lb, l),
-             lb->s->busy,
-             lb->s->max_busy);
-        for (j = 0; j < lb->num_of_workers; j++) {
-            worker_record_t *wr = &(lb->lb_workers[j]);
-            ajp_worker_t *a = (ajp_worker_t *)wr->w->worker_private;
-            /* TODO: descriptive status */
-            jk_printf(s, "      <jk:member id=\"%d\" name=\"%s\" type=\"%s\" host=\"%s\" port=\"%d\" address=\"%s\" activation=\"%s\" state=\"%s\"",
-                j,
-                wr->s->name,
-                status_worker_type(wr->w->type),
-                a->host,
-                a->port,
-                jk_dump_hinfo(&a->worker_inet_addr, buf),
-                jk_lb_get_activation(wr, l),
-                jk_lb_get_state(wr, l));
-
-            jk_printf(s, " distance=\"%d\"", wr->s->distance);
-            jk_printf(s, " lbfactor=\"%d\"", wr->s->lb_factor);
-            jk_printf(s, " lbmult=\"%" JK_UINT64_T_FMT "\"", wr->s->lb_mult);
-            jk_printf(s, " lbvalue=\"%" JK_UINT64_T_FMT "\"", wr->s->lb_value);
-            jk_printf(s, " elected=\"%" JK_UINT64_T_FMT "\"", wr->s->elected);
-            jk_printf(s, " errors=\"%" JK_UINT32_T_FMT "\"", wr->s->errors);
-            jk_printf(s, " clienterrors=\"%" JK_UINT32_T_FMT "\"", wr->s->client_errors);
-            jk_printf(s, " transferred=\"%" JK_UINT64_T_FMT "\"", wr->s->transferred);
-            jk_printf(s, " readed=\"%" JK_UINT64_T_FMT "\"", wr->s->readed);
-            jk_printf(s, " busy=\"%u\"", wr->s->busy);
-            jk_printf(s, " maxbusy=\"%u\"", wr->s->max_busy);
-            if (wr->s->jvm_route && *wr->s->jvm_route)
-                jk_printf(s, " jvm_route=\"%s\"", wr->s->jvm_route);
-            if (wr->s->redirect && *wr->s->redirect)
-                jk_printf(s, " redirect=\"%s\"", wr->s->redirect);
-            if (wr->s->domain && *wr->s->domain)
-                jk_printf(s, " domain=\"%s\"", wr->s->domain);
-            jk_puts(s, " />\n");
+        for (i = 0; i < (int)lb->num_of_workers; i++) {
+            wr = &(lb->lb_workers[i]);
+            if (strcmp(sub_worker, wr->s->name) == 0)
+                break;
         }
-        dump_maps(s, s->uw_map, lb->s->name, l);
-        jk_puts(s, "  </jk:balancer>\n");
-
+        if (!wr || i == (int)lb->num_of_workers) {
+            jk_log(l, JK_LOG_WARNING,
+                   "could not find worker '%s'",
+                   sub_worker);
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+        }
+        form_member(s, wr, worker, from, refresh, l);
     }
-    if (has_lb)
-        jk_puts(s, "  </jk:balancers>\n");
-
+    display_legend(s, l);
+    JK_TRACE_EXIT(l);
+    return JK_TRUE;
 }
 
 static int update_worker(jk_ws_service_t *s, status_worker_t *sw,
-                         const char *dworker, const char *lb_name,
+                         const char *worker, const char *sub_worker,
                          jk_logger_t *l)
 {
-    int i;
-    char buf[1024];
-    const char *b;
-    lb_worker_t *lb;
+    unsigned int i;
     jk_worker_t *w = NULL;
 
-    if (!lb_name) {
-        w = wc_get_worker_for_name(dworker, l);
-        if (!w || w->type != JK_LB_WORKER_TYPE)
-            return JK_FALSE;
-        lb = (lb_worker_t *)w->worker_private;
-
-        if (lb->sequence != lb->s->sequence)
-            jk_lb_pull(lb, l);
+    JK_TRACE_ENTER(l);
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG,
+               "updating worker '%s' sub worker '%s'",
+               worker ? worker : "(null)", sub_worker ? sub_worker : "(null)");
+    if (!worker || !worker[0]) {
+        jk_log(l, JK_LOG_WARNING,
+               "NULL or EMPTY worker param");
+        JK_TRACE_EXIT(l);

[... 775 lines stripped ...]


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


Mime
View raw message