You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@celix.apache.org by pn...@apache.org on 2019/07/24 19:11:35 UTC

[celix] branch develop updated: Enables the websocket admin in http admin by default. Updates the web based shell ui example and promotes it to shell_wui and adds a new http example

This is an automated email from the ASF dual-hosted git repository.

pnoltes pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/celix.git


The following commit(s) were added to refs/heads/develop by this push:
     new d63baaf  Enables the websocket admin in http admin by default. Updates the web based shell ui example and promotes it to shell_wui and adds a new http example
d63baaf is described below

commit d63baaf76f0ca11251ef71e27b691afd35697582
Author: Pepijn Noltes <pe...@gmail.com>
AuthorDate: Wed Jul 24 21:10:05 2019 +0200

    Enables the websocket admin in http admin by default. Updates the web based shell ui example and promotes it to shell_wui and adds a new http example
---
 LICENSE                                            |  27 ++
 bundles/CMakeLists.txt                             |   2 +-
 bundles/http_admin/http_admin/CMakeLists.txt       |  14 +-
 bundles/http_admin/http_admin/src/activator.c      |   2 +-
 bundles/shell/CMakeLists.txt                       |   1 +
 bundles/shell/shell_wui/CMakeLists.txt             |  41 ++
 .../shell/shell_wui}/README.md                     |  18 +-
 bundles/shell/shell_wui/resources/ansi_up.js       | 417 +++++++++++++++++++++
 .../shell/shell_wui}/resources/index.html          |   5 +-
 .../shell/shell_wui}/resources/script.js           |   7 +-
 .../shell_wui/src/shell_wui_bundle_activator.c     |  17 +-
 examples/celix-examples/CMakeLists.txt             |   5 +-
 examples/celix-examples/README.md                  |   4 +-
 examples/celix-examples/civetweb/CMakeLists.txt    |  38 --
 .../celix-examples/http_example/CMakeLists.txt     |  43 +++
 .../celix-examples/{ => http_example}/README.md    |  14 +-
 .../resources/index.html                           |   8 +-
 .../resources/script.js}                           |  30 +-
 .../src/http_example_bundle_activator.c            |  63 ++++
 libs/framework/src/bundle_cache.c                  |   5 +-
 20 files changed, 667 insertions(+), 94 deletions(-)

diff --git a/LICENSE b/LICENSE
index 0e2feaa..42b357f 100644
--- a/LICENSE
+++ b/LICENSE
@@ -354,3 +354,30 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
+
+---------------------------------------------------------------------------------
+
+This product bundles ansi_up (bundles/shell/shell_wui/resources/ansi_up.js),
+which is available under the MIT license. For more details see
+https://github.com/drudru/ansi_up
+
+The MIT License (MIT)
+Copyright (c) 2011-2012 MIT
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/bundles/CMakeLists.txt b/bundles/CMakeLists.txt
index 6db120b..351a4ff 100644
--- a/bundles/CMakeLists.txt
+++ b/bundles/CMakeLists.txt
@@ -19,7 +19,7 @@ add_subdirectory(config_admin)
 add_subdirectory(device_access)
 add_subdirectory(deployment_admin)
 add_subdirectory(remote_services)
-add_subdirectory(shell)
 add_subdirectory(http_admin)
+add_subdirectory(shell)
 add_subdirectory(logging)
 add_subdirectory(pubsub)
diff --git a/bundles/http_admin/http_admin/CMakeLists.txt b/bundles/http_admin/http_admin/CMakeLists.txt
index 3dca412..6a24137 100644
--- a/bundles/http_admin/http_admin/CMakeLists.txt
+++ b/bundles/http_admin/http_admin/CMakeLists.txt
@@ -15,7 +15,7 @@
 # specific language governing permissions and limitations
 # under the License.
 
-add_celix_bundle(http_admin_service
+add_celix_bundle(http_admin
     SOURCES
         src/http_admin
         src/websocket_admin
@@ -26,14 +26,14 @@ add_celix_bundle(http_admin_service
     GROUP "Celix/HTTP_admin"
     NAME "Apache Celix HTTP Admin"
 )
-target_include_directories(http_admin_service PRIVATE src)
+target_include_directories(http_admin PRIVATE src)
 
-target_link_libraries(http_admin_service PUBLIC Celix::http_admin_api)
-celix_bundle_private_libs(http_admin_service civetweb_shared)
+target_link_libraries(http_admin PUBLIC Celix::http_admin_api)
+celix_bundle_private_libs(http_admin civetweb_shared)
 file(MAKE_DIRECTORY resources)
-celix_bundle_add_dir(http_admin_service resources/ DESTINATION root/)
+celix_bundle_add_dir(http_admin resources/ DESTINATION root/)
 
 
-install_celix_bundle(http_admin_service EXPORT celix COMPONENT http_admin_service)
+install_celix_bundle(http_admin EXPORT celix COMPONENT http_admin)
 #Setup target aliases to match external usage
-add_library(Celix::http_admin_service ALIAS http_admin_service)
+add_library(Celix::http_admin ALIAS http_admin)
diff --git a/bundles/http_admin/http_admin/src/activator.c b/bundles/http_admin/http_admin/src/activator.c
index 435f0d4..f3d1594 100644
--- a/bundles/http_admin/http_admin/src/activator.c
+++ b/bundles/http_admin/http_admin/src/activator.c
@@ -213,7 +213,7 @@ static int http_admin_start(http_admin_activator_t *act, celix_bundle_context_t
     }
 
     const char *prop_doc_root = act->root;
-    bool prop_use_websockets = celix_bundleContext_getPropertyAsBool(ctx, "USE_WEBSOCKETS", false);
+    bool prop_use_websockets = celix_bundleContext_getPropertyAsBool(ctx, "USE_WEBSOCKETS", true);
     const char *prop_port = celix_bundleContext_getProperty(ctx, "LISTENING_PORTS", "8080");
     const char *prop_timeout = celix_bundleContext_getProperty(ctx, "WEBSOCKET_TIMEOUT_MS", "3600000");
     long prop_port_min = celix_bundleContext_getPropertyAsLong(ctx, "PORT_RANGE_MIN", 8000);
diff --git a/bundles/shell/CMakeLists.txt b/bundles/shell/CMakeLists.txt
index ae2d39a..839d5ce 100644
--- a/bundles/shell/CMakeLists.txt
+++ b/bundles/shell/CMakeLists.txt
@@ -19,3 +19,4 @@ add_subdirectory(shell)
 add_subdirectory(remote_shell)
 add_subdirectory(shell_bonjour)
 add_subdirectory(shell_tui)
+add_subdirectory(shell_wui)
diff --git a/bundles/shell/shell_wui/CMakeLists.txt b/bundles/shell/shell_wui/CMakeLists.txt
new file mode 100644
index 0000000..50bdf0d
--- /dev/null
+++ b/bundles/shell/shell_wui/CMakeLists.txt
@@ -0,0 +1,41 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+celix_subproject(SHELL_WUI "Option to enable building the Shell Web User Interface bundles" ON DEPS SHELL HTTP_ADMIN)
+if (SHELL_WUI)
+
+
+    add_celix_bundle(shell_wui
+        SYMBOLIC_NAME "apache_celix_shell_wui"
+        VERSION "1.0.0"
+        NAME "Apache Celix Shell WUI"
+        GROUP "Celix/Shell"
+        SOURCES
+            src/shell_wui_bundle_activator.c
+    )
+
+    target_link_libraries(shell_wui PRIVATE Celix::shell_api Celix::http_admin_api)
+    celix_bundle_private_libs(shell_wui civetweb_shared)
+    celix_bundle_add_dir(shell_wui resources)
+    celix_bundle_headers(shell_wui "X-Web-Resource: /shell$<SEMICOLON>/resources")
+
+
+    install_celix_bundle(shell_wui EXPORT celix)
+    #Alias setup to match external usage
+    add_library(Celix::shell_wui ALIAS shell_wui)
+
+
+endif ()
\ No newline at end of file
diff --git a/examples/celix-examples/README.md b/bundles/shell/shell_wui/README.md
similarity index 70%
copy from examples/celix-examples/README.md
copy to bundles/shell/shell_wui/README.md
index c44524b..37c714b 100644
--- a/examples/celix-examples/README.md
+++ b/bundles/shell/shell_wui/README.md
@@ -5,7 +5,7 @@ this work for additional information regarding copyright ownership.
 The ASF licenses this file to You under the Apache License, Version 2.0
 (the "License"); you may not use this file except in compliance with
 the License.  You may obtain a copy of the License at
-
+   
     http://www.apache.org/licenses/LICENSE-2.0
 
 Unless required by applicable law or agreed to in writing, software
@@ -15,6 +15,18 @@ See the License for the specific language governing permissions and
 limitations under the License.
 -->
 
-# Celix Example
+# Shell WUI
+
+The Celix Shell Web User Interface implements a web based user interface for the Celix Shell.
+
+## CMake option
+    BUILD_SHELL_WUI=ON
+
+## Config options
+
+N/A
+
+## Using info
 
-TODO document the examples
+If the Celix Shell WUI is installed, 'find_package(Celix)' will set:
+ - The `Celix::shell_wui` bundle target if the shell_wui is installed
diff --git a/bundles/shell/shell_wui/resources/ansi_up.js b/bundles/shell/shell_wui/resources/ansi_up.js
new file mode 100644
index 0000000..ca32b83
--- /dev/null
+++ b/bundles/shell/shell_wui/resources/ansi_up.js
@@ -0,0 +1,417 @@
+/*  ansi_up.js
+ *  author : Dru Nelson
+ *  license : MIT
+ *  http://github.com/drudru/ansi_up
+ */
+(function (root, factory) {
+    if (typeof define === 'function' && define.amd) {
+        // AMD. Register as an anonymous module.
+        define(['exports'], factory);
+    } else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {
+        // CommonJS
+        factory(exports);
+    } else {
+        // Browser globals
+        var exp = {};
+        factory(exp);
+        root.AnsiUp = exp.default;
+    }
+}(this, function (exports) {
+    "use strict";
+    var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) {
+        if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; }
+        return cooked;
+    };
+    var PacketKind;
+    (function (PacketKind) {
+        PacketKind[PacketKind["EOS"] = 0] = "EOS";
+        PacketKind[PacketKind["Text"] = 1] = "Text";
+        PacketKind[PacketKind["Incomplete"] = 2] = "Incomplete";
+        PacketKind[PacketKind["ESC"] = 3] = "ESC";
+        PacketKind[PacketKind["Unknown"] = 4] = "Unknown";
+        PacketKind[PacketKind["SGR"] = 5] = "SGR";
+        PacketKind[PacketKind["OSCURL"] = 6] = "OSCURL";
+    })(PacketKind || (PacketKind = {}));
+    var AnsiUp = (function () {
+        function AnsiUp() {
+            this.VERSION = "4.0.1";
+            this.setup_palettes();
+            this._use_classes = false;
+            this._escape_for_html = true;
+            this.bold = false;
+            this.fg = this.bg = null;
+            this._buffer = '';
+            this._url_whitelist = { 'http': 1, 'https': 1 };
+        }
+        Object.defineProperty(AnsiUp.prototype, "use_classes", {
+            get: function () {
+                return this._use_classes;
+            },
+            set: function (arg) {
+                this._use_classes = arg;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(AnsiUp.prototype, "escape_for_html", {
+            get: function () {
+                return this._escape_for_html;
+            },
+            set: function (arg) {
+                this._escape_for_html = arg;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(AnsiUp.prototype, "url_whitelist", {
+            get: function () {
+                return this._url_whitelist;
+            },
+            set: function (arg) {
+                this._url_whitelist = arg;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        AnsiUp.prototype.setup_palettes = function () {
+            var _this = this;
+            this.ansi_colors =
+                [
+                    [
+                        { rgb: [0, 0, 0], class_name: "ansi-black" },
+                        { rgb: [187, 0, 0], class_name: "ansi-red" },
+                        { rgb: [0, 187, 0], class_name: "ansi-green" },
+                        { rgb: [187, 187, 0], class_name: "ansi-yellow" },
+                        { rgb: [0, 0, 187], class_name: "ansi-blue" },
+                        { rgb: [187, 0, 187], class_name: "ansi-magenta" },
+                        { rgb: [0, 187, 187], class_name: "ansi-cyan" },
+                        { rgb: [255, 255, 255], class_name: "ansi-white" }
+                    ],
+                    [
+                        { rgb: [85, 85, 85], class_name: "ansi-bright-black" },
+                        { rgb: [255, 85, 85], class_name: "ansi-bright-red" },
+                        { rgb: [0, 255, 0], class_name: "ansi-bright-green" },
+                        { rgb: [255, 255, 85], class_name: "ansi-bright-yellow" },
+                        { rgb: [85, 85, 255], class_name: "ansi-bright-blue" },
+                        { rgb: [255, 85, 255], class_name: "ansi-bright-magenta" },
+                        { rgb: [85, 255, 255], class_name: "ansi-bright-cyan" },
+                        { rgb: [255, 255, 255], class_name: "ansi-bright-white" }
+                    ]
+                ];
+            this.palette_256 = [];
+            this.ansi_colors.forEach(function (palette) {
+                palette.forEach(function (rec) {
+                    _this.palette_256.push(rec);
+                });
+            });
+            var levels = [0, 95, 135, 175, 215, 255];
+            for (var r = 0; r < 6; ++r) {
+                for (var g = 0; g < 6; ++g) {
+                    for (var b = 0; b < 6; ++b) {
+                        var col = { rgb: [levels[r], levels[g], levels[b]], class_name: 'truecolor' };
+                        this.palette_256.push(col);
+                    }
+                }
+            }
+            var grey_level = 8;
+            for (var i = 0; i < 24; ++i, grey_level += 10) {
+                var gry = { rgb: [grey_level, grey_level, grey_level], class_name: 'truecolor' };
+                this.palette_256.push(gry);
+            }
+        };
+        AnsiUp.prototype.escape_txt_for_html = function (txt) {
+            return txt.replace(/[&<>]/gm, function (str) {
+                if (str === "&")
+                    return "&amp;";
+                if (str === "<")
+                    return "&lt;";
+                if (str === ">")
+                    return "&gt;";
+            });
+        };
+        AnsiUp.prototype.append_buffer = function (txt) {
+            var str = this._buffer + txt;
+            this._buffer = str;
+        };
+        AnsiUp.prototype.get_next_packet = function () {
+            var pkt = {
+                kind: PacketKind.EOS,
+                text: '',
+                url: ''
+            };
+            var len = this._buffer.length;
+            if (len == 0)
+                return pkt;
+            var pos = this._buffer.indexOf("\x1B");
+            if (pos == -1) {
+                pkt.kind = PacketKind.Text;
+                pkt.text = this._buffer;
+                this._buffer = '';
+                return pkt;
+            }
+            if (pos > 0) {
+                pkt.kind = PacketKind.Text;
+                pkt.text = this._buffer.slice(0, pos);
+                this._buffer = this._buffer.slice(pos);
+                return pkt;
+            }
+            if (pos == 0) {
+                if (len == 1) {
+                    pkt.kind = PacketKind.Incomplete;
+                    return pkt;
+                }
+                var next_char = this._buffer.charAt(1);
+                if ((next_char != '[') && (next_char != ']')) {
+                    pkt.kind = PacketKind.ESC;
+                    pkt.text = this._buffer.slice(0, 1);
+                    this._buffer = this._buffer.slice(1);
+                    return pkt;
+                }
+                if (next_char == '[') {
+                    if (!this._csi_regex) {
+                        this._csi_regex = rgx(__makeTemplateObject(["\n                        ^                           # beginning of line\n                                                    #\n                                                    # First attempt\n                        (?:                         # legal sequence\n                          \u001B[                      # CSI\n                          ([<-?]?)              # private-mode char\n                        [...]
+                    }
+                    var match = this._buffer.match(this._csi_regex);
+                    if (match === null) {
+                        pkt.kind = PacketKind.Incomplete;
+                        return pkt;
+                    }
+                    if (match[4]) {
+                        pkt.kind = PacketKind.ESC;
+                        pkt.text = this._buffer.slice(0, 1);
+                        this._buffer = this._buffer.slice(1);
+                        return pkt;
+                    }
+                    if ((match[1] != '') || (match[3] != 'm'))
+                        pkt.kind = PacketKind.Unknown;
+                    else
+                        pkt.kind = PacketKind.SGR;
+                    pkt.text = match[2];
+                    var rpos = match[0].length;
+                    this._buffer = this._buffer.slice(rpos);
+                    return pkt;
+                }
+                if (next_char == ']') {
+                    if ((this._buffer.charAt(2) != '8')
+                        || (this._buffer.charAt(3) != ';')) {
+                        pkt.kind = PacketKind.ESC;
+                        pkt.text = this._buffer.slice(0, 1);
+                        this._buffer = this._buffer.slice(1);
+                        return pkt;
+                    }
+                    if (!this._osc_st) {
+                        this._osc_st = rgxG(__makeTemplateObject(["\n                        (?:                         # legal sequence\n                          (\u001B\\)                    # ESC                           |                           # alternate\n                          (\u0007)                      # BEL (what xterm did)\n                        )\n                        |                           # alternate (second attempt)\n                        (           [...]
+                    }
+                    this._osc_st.lastIndex = 0;
+                    {
+                        var match_1 = this._osc_st.exec(this._buffer);
+                        if (match_1 === null) {
+                            pkt.kind = PacketKind.Incomplete;
+                            return pkt;
+                        }
+                        if (match_1[3]) {
+                            pkt.kind = PacketKind.ESC;
+                            pkt.text = this._buffer.slice(0, 1);
+                            this._buffer = this._buffer.slice(1);
+                            return pkt;
+                        }
+                    }
+                    {
+                        var match_2 = this._osc_st.exec(this._buffer);
+                        if (match_2 === null) {
+                            pkt.kind = PacketKind.Incomplete;
+                            return pkt;
+                        }
+                        if (match_2[3]) {
+                            pkt.kind = PacketKind.ESC;
+                            pkt.text = this._buffer.slice(0, 1);
+                            this._buffer = this._buffer.slice(1);
+                            return pkt;
+                        }
+                    }
+                    if (!this._osc_regex) {
+                        this._osc_regex = rgx(__makeTemplateObject(["\n                        ^                           # beginning of line\n                                                    #\n                        \u001B]8;                    # OSC Hyperlink\n                        [ -:<-~]*       # params (excluding ;)\n                        ;                           # end of params\n                        ([!-~]{0,512})        # URL capture\n                        (?:   [...]
+                    }
+                    var match = this._buffer.match(this._osc_regex);
+                    if (match === null) {
+                        pkt.kind = PacketKind.ESC;
+                        pkt.text = this._buffer.slice(0, 1);
+                        this._buffer = this._buffer.slice(1);
+                        return pkt;
+                    }
+                    pkt.kind = PacketKind.OSCURL;
+                    pkt.url = match[1];
+                    pkt.text = match[2];
+                    var rpos = match[0].length;
+                    this._buffer = this._buffer.slice(rpos);
+                    return pkt;
+                }
+            }
+        };
+        AnsiUp.prototype.ansi_to_html = function (txt) {
+            this.append_buffer(txt);
+            var blocks = [];
+            while (true) {
+                var packet = this.get_next_packet();
+                if ((packet.kind == PacketKind.EOS)
+                    || (packet.kind == PacketKind.Incomplete))
+                    break;
+                if ((packet.kind == PacketKind.ESC)
+                    || (packet.kind == PacketKind.Unknown))
+                    continue;
+                if (packet.kind == PacketKind.Text)
+                    blocks.push(this.transform_to_html(this.with_state(packet)));
+                else if (packet.kind == PacketKind.SGR)
+                    this.process_ansi(packet);
+                else if (packet.kind == PacketKind.OSCURL)
+                    blocks.push(this.process_hyperlink(packet));
+            }
+            return blocks.join("");
+        };
+        AnsiUp.prototype.with_state = function (pkt) {
+            return { bold: this.bold, fg: this.fg, bg: this.bg, text: pkt.text };
+        };
+        AnsiUp.prototype.process_ansi = function (pkt) {
+            var sgr_cmds = pkt.text.split(';');
+            while (sgr_cmds.length > 0) {
+                var sgr_cmd_str = sgr_cmds.shift();
+                var num = parseInt(sgr_cmd_str, 10);
+                if (isNaN(num) || num === 0) {
+                    this.fg = this.bg = null;
+                    this.bold = false;
+                }
+                else if (num === 1) {
+                    this.bold = true;
+                }
+                else if (num === 22) {
+                    this.bold = false;
+                }
+                else if (num === 39) {
+                    this.fg = null;
+                }
+                else if (num === 49) {
+                    this.bg = null;
+                }
+                else if ((num >= 30) && (num < 38)) {
+                    this.fg = this.ansi_colors[0][(num - 30)];
+                }
+                else if ((num >= 40) && (num < 48)) {
+                    this.bg = this.ansi_colors[0][(num - 40)];
+                }
+                else if ((num >= 90) && (num < 98)) {
+                    this.fg = this.ansi_colors[1][(num - 90)];
+                }
+                else if ((num >= 100) && (num < 108)) {
+                    this.bg = this.ansi_colors[1][(num - 100)];
+                }
+                else if (num === 38 || num === 48) {
+                    if (sgr_cmds.length > 0) {
+                        var is_foreground = (num === 38);
+                        var mode_cmd = sgr_cmds.shift();
+                        if (mode_cmd === '5' && sgr_cmds.length > 0) {
+                            var palette_index = parseInt(sgr_cmds.shift(), 10);
+                            if (palette_index >= 0 && palette_index <= 255) {
+                                if (is_foreground)
+                                    this.fg = this.palette_256[palette_index];
+                                else
+                                    this.bg = this.palette_256[palette_index];
+                            }
+                        }
+                        if (mode_cmd === '2' && sgr_cmds.length > 2) {
+                            var r = parseInt(sgr_cmds.shift(), 10);
+                            var g = parseInt(sgr_cmds.shift(), 10);
+                            var b = parseInt(sgr_cmds.shift(), 10);
+                            if ((r >= 0 && r <= 255) && (g >= 0 && g <= 255) && (b >= 0 && b <= 255)) {
+                                var c = { rgb: [r, g, b], class_name: 'truecolor' };
+                                if (is_foreground)
+                                    this.fg = c;
+                                else
+                                    this.bg = c;
+                            }
+                        }
+                    }
+                }
+            }
+        };
+        AnsiUp.prototype.transform_to_html = function (fragment) {
+            var txt = fragment.text;
+            if (txt.length === 0)
+                return txt;
+            if (this._escape_for_html)
+                txt = this.escape_txt_for_html(txt);
+            if (!fragment.bold && fragment.fg === null && fragment.bg === null)
+                return txt;
+            var styles = [];
+            var classes = [];
+            var fg = fragment.fg;
+            var bg = fragment.bg;
+            if (fragment.bold)
+                styles.push('font-weight:bold');
+            if (!this._use_classes) {
+                if (fg)
+                    styles.push("color:rgb(" + fg.rgb.join(',') + ")");
+                if (bg)
+                    styles.push("background-color:rgb(" + bg.rgb + ")");
+            }
+            else {
+                if (fg) {
+                    if (fg.class_name !== 'truecolor') {
+                        classes.push(fg.class_name + "-fg");
+                    }
+                    else {
+                        styles.push("color:rgb(" + fg.rgb.join(',') + ")");
+                    }
+                }
+                if (bg) {
+                    if (bg.class_name !== 'truecolor') {
+                        classes.push(bg.class_name + "-bg");
+                    }
+                    else {
+                        styles.push("background-color:rgb(" + bg.rgb.join(',') + ")");
+                    }
+                }
+            }
+            var class_string = '';
+            var style_string = '';
+            if (classes.length)
+                class_string = " class=\"" + classes.join(' ') + "\"";
+            if (styles.length)
+                style_string = " style=\"" + styles.join(';') + "\"";
+            return "<span" + style_string + class_string + ">" + txt + "</span>";
+        };
+        ;
+        AnsiUp.prototype.process_hyperlink = function (pkt) {
+            var parts = pkt.url.split(':');
+            if (parts.length < 1)
+                return '';
+            if (!this._url_whitelist[parts[0]])
+                return '';
+            var result = "<a href=\"" + this.escape_txt_for_html(pkt.url) + "\">" + this.escape_txt_for_html(pkt.text) + "</a>";
+            return result;
+        };
+        return AnsiUp;
+    }());
+    function rgx(tmplObj) {
+        var subst = [];
+        for (var _i = 1; _i < arguments.length; _i++) {
+            subst[_i - 1] = arguments[_i];
+        }
+        var regexText = tmplObj.raw[0];
+        var wsrgx = /^\s+|\s+\n|\s*#[\s\S]*?\n|\n/gm;
+        var txt2 = regexText.replace(wsrgx, '');
+        return new RegExp(txt2);
+    }
+    function rgxG(tmplObj) {
+        var subst = [];
+        for (var _i = 1; _i < arguments.length; _i++) {
+            subst[_i - 1] = arguments[_i];
+        }
+        var regexText = tmplObj.raw[0];
+        var wsrgx = /^\s+|\s+\n|\s*#[\s\S]*?\n|\n/gm;
+        var txt2 = regexText.replace(wsrgx, '');
+        return new RegExp(txt2, 'g');
+    }
+//# sourceMappingURL=ansi_up.js.map
+    Object.defineProperty(exports, "__esModule", { value: true });
+    exports.default = AnsiUp;
+}));
diff --git a/examples/celix-examples/civetweb/resources/index.html b/bundles/shell/shell_wui/resources/index.html
similarity index 83%
copy from examples/celix-examples/civetweb/resources/index.html
copy to bundles/shell/shell_wui/resources/index.html
index dac5c4c..c3846ae 100644
--- a/examples/celix-examples/civetweb/resources/index.html
+++ b/bundles/shell/shell_wui/resources/index.html
@@ -20,7 +20,8 @@
 <html lang="en">
 <head>
     <meta charset="utf-8"/>
-    <title>Apache Celix Embedded Civetweb example</title>
+    <title>Apache Celix Shell Web User Interface</title>
+    <script src="ansi_up.js" type="text/javascript"></script>
     <script src="script.js"></script>
 </head>
 <body>
@@ -28,7 +29,7 @@
         <input type="text" id="command_input"/>
         <input type="button" id="command_button" value="send"/>
     </div>
-    <textarea rows="50" cols="80" id="console_output"></textarea>
+    <div style="white-space: pre;background:grey;margin:10px;padding:2px;" id="console_output"></div>
     <script>docReady();</script>
 </body>
 </html>
diff --git a/examples/celix-examples/civetweb/resources/script.js b/bundles/shell/shell_wui/resources/script.js
similarity index 87%
rename from examples/celix-examples/civetweb/resources/script.js
rename to bundles/shell/shell_wui/resources/script.js
index 5cabf42..db49dcf 100644
--- a/examples/celix-examples/civetweb/resources/script.js
+++ b/bundles/shell/shell_wui/resources/script.js
@@ -18,11 +18,14 @@
  */
 
 function docReady() {
+    var ansi_up = new AnsiUp;
+
     var host = window.location.host;
-    var shellSocket = new WebSocket("ws://" + host + "/shellsocket");
+    var shellSocket = new WebSocket("ws://" + host + "/shell/socket");
 
     shellSocket.onmessage = function (event) {
-        document.getElementById("console_output").value = event.data;
+        var html = ansi_up.ansi_to_html(event.data);
+        document.getElementById("console_output").innerHTML = html;
     };
     shellSocket.onopen = function (event) {
         shellSocket.send("lb");
diff --git a/examples/celix-examples/civetweb/src/bundle_activator.c b/bundles/shell/shell_wui/src/shell_wui_bundle_activator.c
similarity index 78%
rename from examples/celix-examples/civetweb/src/bundle_activator.c
rename to bundles/shell/shell_wui/src/shell_wui_bundle_activator.c
index 86ec8c2..19d2dd1 100644
--- a/examples/celix-examples/civetweb/src/bundle_activator.c
+++ b/bundles/shell/shell_wui/src/shell_wui_bundle_activator.c
@@ -26,12 +26,12 @@
 
 #include "http_admin/api.h"
 
-typedef struct activator_data {
+typedef struct shell_wui_activator_data {
     celix_bundle_context_t *ctx;
 
     celix_websocket_service_t sockSvc;
     long sockSvcId;
-} activator_data_t;
+} shell_wui_activator_data_t;
 
 struct use_shell_arg {
     char *command;
@@ -50,7 +50,7 @@ static void useShell(void *handle, void *svc) {
 };
 
 static int websocket_data_handler(struct mg_connection *conn, int bits, char *data, size_t data_len, void *handle) {
-    activator_data_t *act = handle;
+    shell_wui_activator_data_t *act = handle;
     struct use_shell_arg arg;
     arg.conn = conn;
 
@@ -62,7 +62,8 @@ static int websocket_data_handler(struct mg_connection *conn, int bits, char *da
 
     bool called = celix_bundleContext_useService(act->ctx, OSGI_SHELL_SERVICE_NAME, &arg, useShell);
     if (!called) {
-        fprintf(stderr, "No shell available!\n");
+        const char *msg = "No shell available!";
+        mg_websocket_write(conn, MG_WEBSOCKET_OPCODE_TEXT, msg , strlen(msg));
     }
 
     free(arg.command);
@@ -70,11 +71,11 @@ static int websocket_data_handler(struct mg_connection *conn, int bits, char *da
 }
 
 
-static celix_status_t activator_start(activator_data_t *data, celix_bundle_context_t *ctx) {
+static celix_status_t shellWui_activator_start(shell_wui_activator_data_t *data, celix_bundle_context_t *ctx) {
     data->ctx = ctx;
 
     celix_properties_t *props = celix_properties_create();
-    celix_properties_set(props, WEBSOCKET_ADMIN_URI, "/shellsocket");
+    celix_properties_set(props, WEBSOCKET_ADMIN_URI, "/shell/socket");
     data->sockSvc.handle = data;
     data->sockSvc.data = websocket_data_handler;
     data->sockSvcId = celix_bundleContext_registerService(ctx, &data->sockSvc, WEBSOCKET_ADMIN_SERVICE_NAME, props);
@@ -82,11 +83,11 @@ static celix_status_t activator_start(activator_data_t *data, celix_bundle_conte
     return CELIX_SUCCESS;
 }
 
-static celix_status_t activator_stop(activator_data_t *data, celix_bundle_context_t *ctx) {
+static celix_status_t shellWui_activator_stop(shell_wui_activator_data_t *data, celix_bundle_context_t *ctx) {
 
     celix_bundleContext_unregisterService(ctx, data->sockSvcId);
 
     return CELIX_SUCCESS;
 }
 
-CELIX_GEN_BUNDLE_ACTIVATOR(activator_data_t, activator_start, activator_stop)
\ No newline at end of file
+CELIX_GEN_BUNDLE_ACTIVATOR(shell_wui_activator_data_t, shellWui_activator_start, shellWui_activator_stop)
\ No newline at end of file
diff --git a/examples/celix-examples/CMakeLists.txt b/examples/celix-examples/CMakeLists.txt
index ea137e3..221fb63 100644
--- a/examples/celix-examples/CMakeLists.txt
+++ b/examples/celix-examples/CMakeLists.txt
@@ -34,9 +34,8 @@ if (EXAMPLES)
 
     #TODO refactor export_import
     #add_subdirectory(export_import)
-    if (NOT ANDROID)
-        add_subdirectory(civetweb)
-    endif()
+
+    add_subdirectory(http_example)
     add_subdirectory(embedding)
     add_subdirectory(track_tracker_example)
     add_subdirectory(log_service_example)
diff --git a/examples/celix-examples/README.md b/examples/celix-examples/README.md
index c44524b..5bc365c 100644
--- a/examples/celix-examples/README.md
+++ b/examples/celix-examples/README.md
@@ -15,6 +15,4 @@ See the License for the specific language governing permissions and
 limitations under the License.
 -->
 
-# Celix Example
-
-TODO document the examples
+# Celix Examples
diff --git a/examples/celix-examples/civetweb/CMakeLists.txt b/examples/celix-examples/civetweb/CMakeLists.txt
deleted file mode 100644
index a77cbdf..0000000
--- a/examples/celix-examples/civetweb/CMakeLists.txt
+++ /dev/null
@@ -1,38 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-add_celix_bundle(embedded_civetweb
-    VERSION 1.0.0
-    SOURCES src/bundle_activator.c
-)
-target_link_libraries(embedded_civetweb PRIVATE Celix::shell_api Celix::http_admin_api)
-celix_bundle_private_libs(embedded_civetweb civetweb_shared)
-celix_bundle_add_dir(embedded_civetweb resources)
-celix_bundle_headers(embedded_civetweb
-        "X-Web-Resource: /shellsocket$<SEMICOLON>/resources"
-)
-
-add_celix_container(civetweb_example
-    BUNDLES
-        Celix::shell
-        Celix::shell_tui
-        Celix::http_admin_service
-        embedded_civetweb
-    PROPERTIES
-        USE_WEBSOCKETS=true
-        LISTENING_PORTS=8081
-)
diff --git a/examples/celix-examples/http_example/CMakeLists.txt b/examples/celix-examples/http_example/CMakeLists.txt
new file mode 100644
index 0000000..ac55add
--- /dev/null
+++ b/examples/celix-examples/http_example/CMakeLists.txt
@@ -0,0 +1,43 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+if (TARGET Celix::http_admin AND TARGET Celix::shell AND TARGET Celix::shell_wui)
+
+
+    add_celix_bundle(http_example
+        SYMBOLIC_NAME "apache_celix_http_example"
+        VERSION "1.0.0"
+        NAME "Apache Celix HTTP Example"
+        SOURCES
+            src/http_example_bundle_activator.c
+    )
+
+    target_link_libraries(http_example PRIVATE Celix::http_admin_api)
+    celix_bundle_private_libs(http_example civetweb_shared)
+    celix_bundle_add_dir(http_example resources)
+    celix_bundle_headers(http_example "X-Web-Resource: /hello$<SEMICOLON>/resources")
+
+    add_celix_container(http_example_cnt
+            BUNDLES
+                Celix::http_admin
+                Celix::shell
+                Celix::shell_wui
+                http_example
+            PROPERTIES
+            #SHELL_USE_ANSI_COLORS=false
+    )
+
+endif ()
\ No newline at end of file
diff --git a/examples/celix-examples/README.md b/examples/celix-examples/http_example/README.md
similarity index 58%
copy from examples/celix-examples/README.md
copy to examples/celix-examples/http_example/README.md
index c44524b..4ff61d2 100644
--- a/examples/celix-examples/README.md
+++ b/examples/celix-examples/http_example/README.md
@@ -15,6 +15,16 @@ See the License for the specific language governing permissions and
 limitations under the License.
 -->
 
-# Celix Example
+# HTTP Example
+
+This examples shows how the Celix HTTP Admin can be used. 
+
+This examples contains a `http_example` example bundle and uses the `Celix::http_admin` `Celix::shell` and `Celix::shell_wui` 
+bundles to create a `http_example_cnt` Celix container.
+
+The `http_example_cnt` Celix container shows how you can create a use existing http/websocket services.
+After running the container browse to localhost:8080. 
+Under the /shell uri you find the `Celix::shell_wui` functionality and under /hello uri you find the `http_example` functionality
+
+
 
-TODO document the examples
diff --git a/examples/celix-examples/civetweb/resources/index.html b/examples/celix-examples/http_example/resources/index.html
similarity index 80%
copy from examples/celix-examples/civetweb/resources/index.html
copy to examples/celix-examples/http_example/resources/index.html
index dac5c4c..304d241 100644
--- a/examples/celix-examples/civetweb/resources/index.html
+++ b/examples/celix-examples/http_example/resources/index.html
@@ -20,15 +20,15 @@
 <html lang="en">
 <head>
     <meta charset="utf-8"/>
-    <title>Apache Celix Embedded Civetweb example</title>
+    <title>Apache Celix HTTP Example</title>
     <script src="script.js"></script>
 </head>
 <body>
     <div>
-        <input type="text" id="command_input"/>
-        <input type="button" id="command_button" value="send"/>
+        Hello World 1
+    </div>
+    <div id="reply_result">
     </div>
-    <textarea rows="50" cols="80" id="console_output"></textarea>
     <script>docReady();</script>
 </body>
 </html>
diff --git a/examples/celix-examples/civetweb/resources/index.html b/examples/celix-examples/http_example/resources/script.js
similarity index 64%
rename from examples/celix-examples/civetweb/resources/index.html
rename to examples/celix-examples/http_example/resources/script.js
index dac5c4c..59a552d 100644
--- a/examples/celix-examples/civetweb/resources/index.html
+++ b/examples/celix-examples/http_example/resources/script.js
@@ -1,4 +1,4 @@
-<!--
+/**
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -15,20 +15,14 @@
  *  KIND, either express or implied.  See the License for the
  * specific language governing permissions and limitations
  * under the License.
--->
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="utf-8"/>
-    <title>Apache Celix Embedded Civetweb example</title>
-    <script src="script.js"></script>
-</head>
-<body>
-    <div>
-        <input type="text" id="command_input"/>
-        <input type="button" id="command_button" value="send"/>
-    </div>
-    <textarea rows="50" cols="80" id="console_output"></textarea>
-    <script>docReady();</script>
-</body>
-</html>
+ */
+
+function docReady() {
+    var host = window.location.host;
+    var shellSocket = new WebSocket("ws://" + host + "/hello/socket");
+
+    shellSocket.onmessage = function (event) {
+        console.log(event);
+        document.getElementById("reply_result").innerHTML = event.data;
+    };
+}
diff --git a/examples/celix-examples/http_example/src/http_example_bundle_activator.c b/examples/celix-examples/http_example/src/http_example_bundle_activator.c
new file mode 100644
index 0000000..731ff99
--- /dev/null
+++ b/examples/celix-examples/http_example/src/http_example_bundle_activator.c
@@ -0,0 +1,63 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <string.h>
+#include <stdio.h>
+
+#include <celix_api.h>
+#include <civetweb.h>
+
+#include "http_admin/api.h"
+
+typedef struct http_example_activator_data {
+    celix_bundle_context_t *ctx;
+
+    celix_websocket_service_t sockSvc;
+    long sockSvcId;
+} http_example_activator_data_t;
+
+
+static void websocket_ready_handler(struct mg_connection *conn, void *handle) {
+    //http_example_activator_data_t *act = handle;
+
+    const char *msg = "Hello World from Websocket";
+    mg_websocket_write(conn, MG_WEBSOCKET_OPCODE_TEXT, msg, strlen(msg));
+}
+
+
+static celix_status_t httpExample_activator_start(http_example_activator_data_t *data, celix_bundle_context_t *ctx) {
+    data->ctx = ctx;
+
+    celix_properties_t *props = celix_properties_create();
+    celix_properties_set(props, WEBSOCKET_ADMIN_URI, "/hello/socket");
+    data->sockSvc.handle = data;
+    data->sockSvc.ready = websocket_ready_handler;
+    data->sockSvcId = celix_bundleContext_registerService(ctx, &data->sockSvc, WEBSOCKET_ADMIN_SERVICE_NAME, props);
+
+    return CELIX_SUCCESS;
+}
+
+static celix_status_t httpExample_activator_stop(http_example_activator_data_t *data, celix_bundle_context_t *ctx) {
+
+    celix_bundleContext_unregisterService(ctx, data->sockSvcId);
+
+    return CELIX_SUCCESS;
+}
+
+CELIX_GEN_BUNDLE_ACTIVATOR(http_example_activator_data_t, httpExample_activator_start, httpExample_activator_stop)
\ No newline at end of file
diff --git a/libs/framework/src/bundle_cache.c b/libs/framework/src/bundle_cache.c
index 9e32d3b..6b419f8 100644
--- a/libs/framework/src/bundle_cache.c
+++ b/libs/framework/src/bundle_cache.c
@@ -212,7 +212,7 @@ static celix_status_t bundleCache_deleteTree(bundle_cache_pt cache, char * direc
 				char subdir[512];
 				snprintf(subdir, sizeof(subdir), "%s/%s", directory, dent->d_name);
 
-				if (stat(subdir, &st) == 0) {
+				if (lstat(subdir, &st) == 0) {
 					if (S_ISDIR (st.st_mode)) {
 						status = bundleCache_deleteTree(cache, subdir, false);
 					} else {
@@ -242,7 +242,8 @@ static celix_status_t bundleCache_deleteTree(bundle_cache_pt cache, char * direc
 
     if (!root) {
         //note root dir can be non existing
-        framework_logIfError(logger, status, NULL, "Failed to delete tree at dir '%s'", directory);
+        char *err = errno == 0 ? "" : strerror(errno);
+        framework_logIfError(logger, status, NULL, "Failed to delete tree at dir '%s'. %s", directory, err);
     }
 
 	return status;