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/01/07 20:11:53 UTC

[celix] 05/22: CELIX-438: Adds an initial setup for handling resources in bundles

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

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

commit 34dac36bbc4462ec3c4bb3adcf656a05c905352a
Author: Pepijn Noltes <pe...@gmail.com>
AuthorDate: Thu Jan 3 22:40:58 2019 +0100

    CELIX-438: Adds an initial setup for handling resources in bundles
---
 .travis.yml                                        |   3 +-
 CMakeLists.txt                                     |   1 +
 bundles/shell/cxx_shell/CMakeLists.txt             |  21 +-
 .../shell/cxx_shell/resources/version.properties   |   3 +
 bundles/shell/cxx_shell/src/ShellActivator.cc      |  10 +-
 bundles/shell/cxx_shell/src/VersionCommand.cc      |  51 +++++
 bundles/shell/cxx_shell/src/commands.h             |   1 +
 bundles/shell/cxx_shell_tui/CMakeLists.txt         |   2 -
 .../shell/cxx_shell_tui/src/ShellTuiActivator.cc   |   1 +
 bundles/shell/cxx_shell_tui/src/shell_test.cc      |   1 -
 cmake/celix_project/AddGLog.cmake                  |   8 +-
 cmake/celix_project/AddGTest.cmake                 |   1 +
 .../{AddGLog.cmake => AddLibzip.cmake}             |  20 +-
 libs/framework_cxx/CMakeLists.txt                  |  10 +-
 libs/framework_cxx/include/celix/Framework.h       |  25 ++-
 libs/framework_cxx/src/Bundle.cc                   |  24 ++-
 libs/framework_cxx/src/Bundle.h                    |  18 +-
 libs/framework_cxx/src/BundleController.h          | 196 ++++++++++++++++++-
 libs/framework_cxx/src/Framework.cc                | 103 ++++++++--
 libs/registry/CMakeLists.txt                       |   3 +-
 libs/registry/include/celix/IResourceBundle.h      |  11 +-
 libs/registry/include/celix/Properties.h           |   8 +-
 libs/registry/src/Properties.cc                    | 215 +++++++++++++++++++++
 libs/registry/src/ServiceRegistry.cc               |  11 +-
 24 files changed, 665 insertions(+), 82 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index e3536d2..a95432b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -35,9 +35,8 @@ matrix:
 #services: docker
 
 before_install:
-  - if [ "$TRAVIS_OS_NAME" = "linux" ] sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
   - if [ "$TRAVIS_OS_NAME" = "linux" ] sudo apt-get update -qq
-  - if [ "$TRAVIS_OS_NAME" = "linux" ] sudo apt-get install -qq uuid-dev libxml2-dev lcov libffi-dev libgoogle-glog-dev libczmq-dev libcpputest-dev libjansson-dev
+  - if [ "$TRAVIS_OS_NAME" = "linux" ] sudo apt-get install -qq uuid-dev libxml2-dev lcov libffi-dev libczmq-dev libcpputest-dev libjansson-dev
   - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew update && brew install lcov libffi zeromq czmq glog cpputest jansson && brew link --force libffi; fi
 
 before_script:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c1c4a43..7025630 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -58,6 +58,7 @@ if (ENABLE_TESTING)
     include(cmake/celix_project/AddGTest.cmake)
 endif ()
 include(cmake/celix_project/AddGLog.cmake)
+include(cmake/celix_project/AddLibzip.cmake)
 
 # Default bundle version
 set(DEFAULT_VERSION 1.0.0)
diff --git a/bundles/shell/cxx_shell/CMakeLists.txt b/bundles/shell/cxx_shell/CMakeLists.txt
index a4ad2df..d2b2113 100644
--- a/bundles/shell/cxx_shell/CMakeLists.txt
+++ b/bundles/shell/cxx_shell/CMakeLists.txt
@@ -15,8 +15,6 @@
 # specific language governing permissions and limitations
 # under the License.
 
-find_package(glog REQUIRED)
-
 #TODO rename to celix::shell && celix::shell_api
 
 add_library(celix_cxx_shell_api INTERFACE)
@@ -25,6 +23,17 @@ target_include_directories(celix_cxx_shell_api INTERFACE
     $<INSTALL_INTERFACE:include/celix/shell>
 )
 
+#TODO should eventually be done by a CMake command (i.e. bundle_add_resources)
+add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/celix_cxx_shell_resources.zip
+        COMMAND zip -rq ${CMAKE_CURRENT_BINARY_DIR}/celix_cxx_shell_resources.zip *
+        COMMENT "Packaging Resources"
+        WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/resources
+)
+
+add_custom_target(create_resources ALL
+    DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/celix_cxx_shell_resources.zip
+)
+
 add_library(celix_cxx_shell SHARED
         src/ShellActivator.cc
         src/LbCommand.cc
@@ -32,10 +41,18 @@ add_library(celix_cxx_shell SHARED
         src/StopAndStartCommand.cc
         src/InspectCommand.cc
         src/QueryCommand.cc
+        src/VersionCommand.cc
 )
 target_include_directories(celix_cxx_shell PRIVATE src)
 target_link_libraries(celix_cxx_shell PRIVATE celix_cxx_shell_api glog::glog)
 target_link_libraries(celix_cxx_shell PUBLIC celix_framework_cxx)
+add_dependencies(celix_cxx_shell create_resources)
+if (UNIX AND NOT APPLE)
+    target_link_libraries(celix_cxx_shell PRIVATE -Wl,--format=binary -Wl,celix_cxx_shell_resources.zip -Wl,--format=default)
+else ()
+    #TODO apple
+endif ()
+
 
 #if (ENABLE_TESTING)
 #    add_subdirectory(gtest)
diff --git a/bundles/shell/cxx_shell/resources/version.properties b/bundles/shell/cxx_shell/resources/version.properties
new file mode 100644
index 0000000..2840a93
--- /dev/null
+++ b/bundles/shell/cxx_shell/resources/version.properties
@@ -0,0 +1,3 @@
+CELIX_VERSION=3.0.0
+COMMIT_ID=TODO
+COMMIT_DATA=TODO
\ No newline at end of file
diff --git a/bundles/shell/cxx_shell/src/ShellActivator.cc b/bundles/shell/cxx_shell/src/ShellActivator.cc
index bfbbb8f..ea54219 100644
--- a/bundles/shell/cxx_shell/src/ShellActivator.cc
+++ b/bundles/shell/cxx_shell/src/ShellActivator.cc
@@ -17,12 +17,17 @@
  *under the License.
  */
 
+#include <glog/logging.h>
+
 #include "celix/api.h"
 #include "celix/IShellCommand.h"
 #include "celix/IShell.h"
 
 #include "commands.h"
 
+extern const uint8_t resources[]       asm("_binary_celix_cxx_shell_resources_zip_start");
+extern const uint8_t resources_end[]   asm("_binary_celix_cxx_shell_resources_zip_end");
+
 namespace {
 
     class Shell : public celix::IShell {
@@ -86,6 +91,7 @@ namespace {
             registrations.push_back(impl::registerStart(ctx));
             registrations.push_back(impl::registerInspect(ctx));
             registrations.push_back(impl::registerQuery(ctx));
+            registrations.push_back(impl::registerVersion(ctx));
 
             registrations.push_back(ctx->registerService(std::shared_ptr<celix::IShell>{new Shell{ctx}}));
         }
@@ -93,12 +99,14 @@ namespace {
         std::vector<celix::ServiceRegistration> registrations{};
     };
 
+    //NOTE that eventually the (ctor) bundle register will be generated by a CMake command (i.e. add_bundle)
+    //This also applies for the resources, resources_end asm entries.
     __attribute__((constructor))
     static void registerShellBundle() {
         celix::Properties manifest{};
         manifest[celix::MANIFEST_BUNDLE_NAME] = "Shell";
         manifest[celix::MANIFEST_BUNDLE_GROUP] = "Celix";
         manifest[celix::MANIFEST_BUNDLE_VERSION] = "1.0.0";
-        celix::registerStaticBundle<ShellBundleActivator>("celix::Shell", manifest);
+        celix::registerStaticBundle<ShellBundleActivator>("celix::Shell", manifest, resources, resources_end - resources);
     }
 }
diff --git a/bundles/shell/cxx_shell/src/VersionCommand.cc b/bundles/shell/cxx_shell/src/VersionCommand.cc
new file mode 100644
index 0000000..d95b7a4
--- /dev/null
+++ b/bundles/shell/cxx_shell/src/VersionCommand.cc
@@ -0,0 +1,51 @@
+/**
+ *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 "commands.h"
+
+#include <functional>
+
+#include "celix/api.h"
+#include "celix/IShellCommand.h"
+
+namespace {
+
+    void version(std::shared_ptr<celix::BundleContext> ctx, const std::string &, const std::vector<std::string> &, std::ostream &out, std::ostream &err) {
+        if (ctx->bundle()->hasCacheEntry("version.properties")) {
+            auto path = ctx->bundle()->absPathForCacheEntry("version.properties");
+            celix::Properties versionInfo = celix::loadProperties(path);
+            std::string version = celix::getProperty(versionInfo, "CELIX_VERSION", "");
+            out << "Celix Version: " << version << std::endl;
+        } else {
+            err << "Cannot find version.properties entry in the " << ctx->bundle()->group() << " " << ctx->bundle()->name() << " bundle" << std::endl;
+        }
+    }
+}
+
+
+celix::ServiceRegistration impl::registerVersion(std::shared_ptr<celix::BundleContext> ctx) {
+    using namespace std::placeholders;
+    celix::ShellCommandFunction cmd = std::bind(&version, ctx, _1, _2, _3, _4);
+
+    celix::Properties props{};
+    props[celix::SHELL_COMMAND_FUNCTION_COMMAND_NAME] = "version";
+    props[celix::SHELL_COMMAND_FUNCTION_COMMAND_USAGE] = "version";
+    props[celix::SHELL_COMMAND_FUNCTION_COMMAND_DESCRIPTION] = "Show version information about the framework (TODO and installed bundles)";
+    return ctx->registerFunctionService(celix::SHELL_COMMAND_FUNCTION_SERVICE_FQN, std::move(cmd), std::move(props));
+}
\ No newline at end of file
diff --git a/bundles/shell/cxx_shell/src/commands.h b/bundles/shell/cxx_shell/src/commands.h
index b9d9a8f..9f1f18b 100644
--- a/bundles/shell/cxx_shell/src/commands.h
+++ b/bundles/shell/cxx_shell/src/commands.h
@@ -29,6 +29,7 @@ namespace impl {
     celix::ServiceRegistration registerStart(std::shared_ptr<celix::BundleContext> ctx);
     celix::ServiceRegistration registerInspect(std::shared_ptr<celix::BundleContext> ctx);
     celix::ServiceRegistration registerQuery(std::shared_ptr<celix::BundleContext> ctx);
+    celix::ServiceRegistration registerVersion(std::shared_ptr<celix::BundleContext> ctx);
 }
 
 
diff --git a/bundles/shell/cxx_shell_tui/CMakeLists.txt b/bundles/shell/cxx_shell_tui/CMakeLists.txt
index cfbf6cf..e9fa13b 100644
--- a/bundles/shell/cxx_shell_tui/CMakeLists.txt
+++ b/bundles/shell/cxx_shell_tui/CMakeLists.txt
@@ -15,8 +15,6 @@
 # specific language governing permissions and limitations
 # under the License.
 
-find_package(glog REQUIRED)
-
 #TODO rename to celix::shell_tui
 
 #OR static lib, but then with all symbols to force constructor attribute
diff --git a/bundles/shell/cxx_shell_tui/src/ShellTuiActivator.cc b/bundles/shell/cxx_shell_tui/src/ShellTuiActivator.cc
index a66bea5..ec48f6b 100644
--- a/bundles/shell/cxx_shell_tui/src/ShellTuiActivator.cc
+++ b/bundles/shell/cxx_shell_tui/src/ShellTuiActivator.cc
@@ -140,6 +140,7 @@ namespace {
         celix::ServiceTracker trk{};
     };
 
+    //NOTE that eventually the (ctor) bundle register will be generated by a CMake command (i.e. add_bundle)
     __attribute__((constructor))
     static void registerShellBundle() {
         celix::Properties manifest{};
diff --git a/bundles/shell/cxx_shell_tui/src/shell_test.cc b/bundles/shell/cxx_shell_tui/src/shell_test.cc
index cd3af3c..8e92107 100644
--- a/bundles/shell/cxx_shell_tui/src/shell_test.cc
+++ b/bundles/shell/cxx_shell_tui/src/shell_test.cc
@@ -29,7 +29,6 @@ int main(int /*argc*/, char **argv) {
     google::LogToStderr();
 
     auto fw = celix::Framework{};
-    std::cout << "Waiting for Framework shutdown\n";
     fw.waitForShutdown();
     return 0;
 }
\ No newline at end of file
diff --git a/cmake/celix_project/AddGLog.cmake b/cmake/celix_project/AddGLog.cmake
index 8cc3414..4897f35 100644
--- a/cmake/celix_project/AddGLog.cmake
+++ b/cmake/celix_project/AddGLog.cmake
@@ -21,9 +21,15 @@ ExternalProject_Add(
         googlelog_project
         GIT_REPOSITORY https://github.com/google/glog.git
         GIT_TAG v0.3.5
+        UPDATE_DISCONNECTED TRUE
         PREFIX ${CMAKE_CURRENT_BINARY_DIR}/glog
         CMAKE_ARGS -DWITH_GFLAGS=OFF -DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/glog -DCMAKE_CXX_FLAGS=-w
 )
 
-set(CMAKE_PREFIX_PATH "${CMAKE_CURRENT_BINARY_DIR}/glog ${CMAKE_PREFIX_PATH}")
+add_library(glog::glog IMPORTED STATIC GLOBAL)
+add_dependencies(glog::glog googlelog_project)
+set_target_properties(glog::glog PROPERTIES
+    IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/glog/lib/libglog.a"
+    INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/glog/include"
+)
 
diff --git a/cmake/celix_project/AddGTest.cmake b/cmake/celix_project/AddGTest.cmake
index 93227c1..2da1a47 100644
--- a/cmake/celix_project/AddGTest.cmake
+++ b/cmake/celix_project/AddGTest.cmake
@@ -20,6 +20,7 @@ ExternalProject_Add(
         googletest_project
         GIT_REPOSITORY https://github.com/google/googletest.git
         GIT_TAG release-1.8.1
+        UPDATE_DISCONNECTED TRUE
         PREFIX ${CMAKE_CURRENT_BINARY_DIR}/gtest
         INSTALL_COMMAND ""
 )
diff --git a/cmake/celix_project/AddGLog.cmake b/cmake/celix_project/AddLibzip.cmake
similarity index 54%
copy from cmake/celix_project/AddGLog.cmake
copy to cmake/celix_project/AddLibzip.cmake
index 8cc3414..7d14cb6 100644
--- a/cmake/celix_project/AddGLog.cmake
+++ b/cmake/celix_project/AddLibzip.cmake
@@ -15,15 +15,19 @@
 # specific language governing permissions and limitations
 # under the License.
 
-
 include(ExternalProject)
 ExternalProject_Add(
-        googlelog_project
-        GIT_REPOSITORY https://github.com/google/glog.git
-        GIT_TAG v0.3.5
-        PREFIX ${CMAKE_CURRENT_BINARY_DIR}/glog
-        CMAKE_ARGS -DWITH_GFLAGS=OFF -DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/glog -DCMAKE_CXX_FLAGS=-w
+        libzip_project
+        GIT_REPOSITORY https://github.com/nih-at/libzip.git
+        GIT_TAG rel-1-5-1
+        UPDATE_DISCONNECTED TRUE
+        PREFIX ${CMAKE_CURRENT_BINARY_DIR}/libzip
+        CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/libzip -DCMAKE_C_FLAGS=-fPIC -DBUILD_SHARED_LIBS=OFF -DENABLE_COMMONCRYPTO=OFF -DENABLE_GNUTLS=OFF -DENABLE_OPENSSL=OFF -Wno-dev
 )
 
-set(CMAKE_PREFIX_PATH "${CMAKE_CURRENT_BINARY_DIR}/glog ${CMAKE_PREFIX_PATH}")
-
+add_library(libzip::libzip IMPORTED STATIC GLOBAL)
+add_dependencies(libzip::libzip libzip_project)
+set_target_properties(libzip::libzip PROPERTIES
+        IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/libzip/lib64/libzip.a"
+        INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/libzip/include"
+)
\ No newline at end of file
diff --git a/libs/framework_cxx/CMakeLists.txt b/libs/framework_cxx/CMakeLists.txt
index c47ae80..f3c116e 100644
--- a/libs/framework_cxx/CMakeLists.txt
+++ b/libs/framework_cxx/CMakeLists.txt
@@ -15,17 +15,19 @@
 # specific language governing permissions and limitations
 # under the License.
 
-find_package(glog REQUIRED)
+find_package(UUID REQUIRED)
 
 #TODO rename to celix::framework
 add_library(celix_framework_cxx SHARED
         src/Framework.cc
         src/BundleContext.cc
-        src/Bundle.cc)
+        src/Bundle.cc
+)
 target_include_directories(celix_framework_cxx PRIVATE src)
 target_include_directories(celix_framework_cxx PUBLIC include)
-target_link_libraries(celix_framework_cxx PRIVATE glog::glog)
-target_link_libraries(celix_framework_cxx PUBLIC celix::registry)
+target_link_libraries(celix_framework_cxx PRIVATE glog::glog libzip::libzip)
+#NOTE because of libzil libbz2 and libz is also needed. maybe more to other form of resources (i.e. tar)
+target_link_libraries(celix_framework_cxx PUBLIC celix::registry bz2 z ${UUID_LIBRARY})
 
 if (ENABLE_TESTING)
     add_subdirectory(gtest)
diff --git a/libs/framework_cxx/include/celix/Framework.h b/libs/framework_cxx/include/celix/Framework.h
index 27718f8..89e9a3b 100644
--- a/libs/framework_cxx/include/celix/Framework.h
+++ b/libs/framework_cxx/include/celix/Framework.h
@@ -35,16 +35,20 @@ namespace celix {
     void registerStaticBundle(
             std::string symbolicName,
             std::function<celix::IBundleActivator*(std::shared_ptr<celix::BundleContext>)> bundleActivatorFactory = {},
-            celix::Properties manifest = {});
+            celix::Properties manifest = {},
+            const uint8_t *resourcesZip = nullptr,
+            size_t resourcesZipLen = 0);
 
     template<typename T>
     void registerStaticBundle(
             std::string symbolicName,
-            celix::Properties manifest = {}) {
+            celix::Properties manifest = {},
+            const uint8_t *resourcesZip = nullptr,
+            size_t resourcesZipLen = 0) {
         auto actFactory = [](std::shared_ptr<celix::BundleContext> ctx) {
             return new T{std::move(ctx)};
         };
-        celix::registerStaticBundle(std::move(symbolicName), actFactory, std::move(manifest));
+        celix::registerStaticBundle(std::move(symbolicName), std::move(actFactory), std::move(manifest), resourcesZip, resourcesZipLen);
     }
 
     class Framework {
@@ -58,14 +62,20 @@ namespace celix {
         Framework& operator=(const Framework &rhs) = delete;
 
         template<typename T>
-        long installBundle(std::string name, celix::Properties manifest = {}, bool autoStart = true) {
+        long installBundle(std::string name, celix::Properties manifest = {}, bool autoStart = true, const uint8_t *resourcesZip = nullptr, size_t resourcesZipLen = 0) {
             auto actFactory = [](std::shared_ptr<celix::BundleContext> ctx) {
                 return new T{std::move(ctx)};
             };
-            return installBundle(name, std::move(actFactory), manifest, autoStart);
+            return installBundle(name, std::move(actFactory), std::move(manifest), autoStart, resourcesZip, resourcesZipLen);
         }
 
-        long installBundle(std::string name, std::function<celix::IBundleActivator*(std::shared_ptr<celix::BundleContext>)> actFactory, celix::Properties manifest = {}, bool autoStart = true);
+        long installBundle(
+                std::string name,
+                std::function<celix::IBundleActivator*(std::shared_ptr<celix::BundleContext>)> actFactory,
+                celix::Properties manifest = {},
+                bool autoStart = true,
+                const uint8_t *resourcesZip = nullptr,
+                const size_t resourcesZipLen = 0);
 
 
         //long installBundle(const std::string &path);
@@ -76,6 +86,9 @@ namespace celix {
         bool useBundle(long bndId, std::function<void(const celix::IBundle &bnd)> use) const;
         int useBundles(std::function<void(const celix::IBundle &bnd)> use, bool includeFrameworkBundle = false) const;
 
+        std::string cacheDir() const;
+        std::string uuid() const;
+
         //TODO trackBundles
 
         //long bundleIdForName(const std::string &bndName) const;
diff --git a/libs/framework_cxx/src/Bundle.cc b/libs/framework_cxx/src/Bundle.cc
index 6f167f7..aba14a2 100644
--- a/libs/framework_cxx/src/Bundle.cc
+++ b/libs/framework_cxx/src/Bundle.cc
@@ -18,20 +18,30 @@
  */
 
 #include <string>
+#include <sys/stat.h>
+#include <unistd.h>
 
 #include "Bundle.h"
 
-bool celix::Bundle::has(const std::string &) const noexcept { return false; } //TODO
+bool celix::Bundle::hasCacheEntry(const std::string &path) const noexcept {
+    auto abs = absPathForCacheEntry(path);
+    struct stat st;
+    bool exists = stat(abs.c_str(), &st) == 0;
+    return exists;
+}
 
-bool celix::Bundle::isDir(const std::string &) const noexcept { return false; } //TODO
+bool celix::Bundle::isCacheEntryDir(const std::string &) const noexcept { return false; } //TODO
 
-bool celix::Bundle::isFile(const std::string &) const noexcept { return false; } //TODO
+bool celix::Bundle::isCacheEntryFile(const std::string &) const noexcept { return false; } //TODO
 
-std::vector <std::string> celix::Bundle::readDir(const std::string &) const noexcept { //TODO
+std::vector <std::string> celix::Bundle::readCacheDir(const std::string &) const noexcept { //TODO
     return std::vector < std::string > {};
 }
 
-const std::string& celix::Bundle::root() const noexcept { //TODO
-    static std::string empty{};
-    return empty;
+const std::string& celix::Bundle::cacheRoot() const noexcept {
+    return bundleCache;
+}
+
+std::string celix::Bundle::absPathForCacheEntry(const std::string &entryPath) const noexcept {
+    return bundleCache + "/" + entryPath;
 }
\ No newline at end of file
diff --git a/libs/framework_cxx/src/Bundle.h b/libs/framework_cxx/src/Bundle.h
index 5541013..ce5e456 100644
--- a/libs/framework_cxx/src/Bundle.h
+++ b/libs/framework_cxx/src/Bundle.h
@@ -30,7 +30,9 @@ namespace celix {
     class Bundle : public celix::IBundle {
     public:
         Bundle(long _bndId, celix::Framework *_fw, celix::Properties _manifest) :
-                bndId{_bndId}, fw{_fw}, bndManifest{std::move(_manifest)}, bndState{BundleState::INSTALLED} {
+                bndId{_bndId}, fw{_fw}, bndManifest{std::move(_manifest)},
+                bundleCache{framework().cacheDir() + "/bundle" + std::to_string(_bndId)},
+                bndState{BundleState::INSTALLED} {
             bndState.store(BundleState::INSTALLED, std::memory_order_release);
         }
 
@@ -38,11 +40,12 @@ namespace celix {
         Bundle& operator=(const Bundle&) = delete;
 
         //resource part
-        bool has(const std::string &) const noexcept override;
-        bool isDir(const std::string &) const noexcept override;
-        bool isFile(const std::string &) const noexcept override;
-        std::vector <std::string> readDir(const std::string &) const noexcept override;
-        const std::string &root() const noexcept override;
+        bool hasCacheEntry(const std::string &) const noexcept override;
+        bool isCacheEntryDir(const std::string &) const noexcept override;
+        bool isCacheEntryFile(const std::string &) const noexcept override;
+        std::string absPathForCacheEntry(const std::string &) const noexcept override;
+        std::vector <std::string> readCacheDir(const std::string &) const noexcept override;
+        const std::string &cacheRoot() const noexcept override;
 
         //bundle part
         bool isFrameworkBundle() const noexcept override { return false; }
@@ -58,7 +61,7 @@ namespace celix {
         }
 
         const std::string &group() const noexcept override {
-            return bndManifest.at(celix::MANIFEST_BUNDLE_SYMBOLIC_NAME);
+            return bndManifest.at(celix::MANIFEST_BUNDLE_GROUP);
         }
 
         const std::string &version() const noexcept override { return bndManifest.at(celix::MANIFEST_BUNDLE_VERSION); }
@@ -81,6 +84,7 @@ namespace celix {
         const long bndId;
         celix::Framework *const fw;
         const celix::Properties bndManifest;
+        const std::string bundleCache;
 
         std::atomic<BundleState> bndState;
     };
diff --git a/libs/framework_cxx/src/BundleController.h b/libs/framework_cxx/src/BundleController.h
index 3f4e1b7..1228066 100644
--- a/libs/framework_cxx/src/BundleController.h
+++ b/libs/framework_cxx/src/BundleController.h
@@ -20,7 +20,14 @@
 #ifndef CXX_CELIX_BUNDLECONTROLLER_H
 #define CXX_CELIX_BUNDLECONTROLLER_H
 
+#include <fstream>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
 #include <glog/logging.h>
+#include <zip.h>
+#include <dirent.h>
 
 #include "celix/IBundle.h"
 #include "celix/BundleContext.h"
@@ -32,8 +39,13 @@ namespace celix {
         BundleController(
                 std::function<celix::IBundleActivator*(std::shared_ptr<celix::BundleContext>)> _actFactory,
                 std::shared_ptr<celix::Bundle> _bnd,
-                std::shared_ptr<celix::BundleContext> _ctx) :
-                actFactory{std::move(_actFactory)}, bnd{std::move(_bnd)}, ctx{std::move(_ctx)} {}
+                std::shared_ptr<celix::BundleContext> _ctx,
+                const uint8_t *rZip,
+                size_t rZipLen) :
+                actFactory{std::move(_actFactory)}, bnd{std::move(_bnd)}, ctx{std::move(_ctx)}, resourcesZip{rZip}, resourcesZipLen{rZipLen} {}
+
+        BundleController(const BundleController&) = delete;
+        BundleController& operator=(const BundleController&) = delete;
 
         //specific part
         bool transitionTo(BundleState desired) {
@@ -44,13 +56,15 @@ namespace celix {
                 //nop
                 success = true;
             } else if (state == BundleState::INSTALLED && desired == BundleState::ACTIVE) {
-                act = std::unique_ptr<celix::IBundleActivator>{actFactory(ctx)};
-                bnd->setState(BundleState::ACTIVE);
-                success = true;
+                success = createBundleCache();
+                if (success) {
+                    act = std::unique_ptr<celix::IBundleActivator>{actFactory(ctx)};
+                    bnd->setState(BundleState::ACTIVE);
+                }
             } else if (state == BundleState::ACTIVE && desired == BundleState::INSTALLED ) {
                 act = nullptr;
-                bnd->setState(BundleState::INSTALLED);
-                success = true;
+                success = deleteBundleCache();
+                bnd->setState(BundleState::INSTALLED); //note still going to installed
             } else {
                 //LOG(ERROR) << "Unexpected desired state " << desired << " from state " << bndState << std::endl;
                 LOG(ERROR) << "Unexpected desired/form state combination " << std::endl;
@@ -61,9 +75,177 @@ namespace celix {
         std::shared_ptr<celix::Bundle> bundle() const { return bnd; }
         std::shared_ptr<celix::BundleContext> context() const { return ctx; }
     private:
+        bool createBundleCache() {
+            auto bundleCache =  bundle()->cacheRoot();
+            bool success = createDir(bundleCache);
+            if (success) {
+                //auto manifestPath = bundle()->absPathForCacheEntry("META-INF/manifest.mf");
+                //TODO success = celix::storeProperties(bundle()->manifest(), manifestPath);
+            }
+            if (success) {
+                success = extractResources(bundleCache);
+            }
+            return success;
+        }
+
+        bool deleteBundleCache() {
+            return deleteDir(bundle()->cacheRoot());
+        }
+
+        bool deleteDir(const std::string &path) {
+            bool success = false;
+            DIR *dir;
+            dir = opendir(path.c_str());
+            if (dir == NULL) {
+                LOG(WARNING) << "Cannot delete dir " << path << ". " << strerror(errno) << std::endl;
+            } else {
+                struct dirent* dent = NULL;
+                errno = 0;
+                dent = readdir(dir);
+                while (errno == 0 && dent != NULL) {
+                    if ((strcmp((dent->d_name), ".") != 0) && (strcmp((dent->d_name), "..") != 0)) {
+                        char subdir[512];
+                        snprintf(subdir, 512, "%s/%s", path.c_str(), dent->d_name);
+
+                        struct stat st;
+                        if (stat(subdir, &st) == 0) {
+                            if (S_ISDIR (st.st_mode)) {
+                                std::string sd{subdir};
+                                success = deleteDir(sd);
+                            } else {
+                                if (remove(subdir) != 0) {
+                                    LOG(WARNING) << "Cannot delete dir " << path << ". " << strerror(errno) << std::endl;
+                                }
+                            }
+                        }
+                    }
+                    errno = 0;
+                    dent = readdir(dir);
+                }
+
+                if (errno != 0) {
+                    LOG(WARNING) << "Cannot delete dir " << path << ". " << strerror(errno) << std::endl;
+                } else if (closedir(dir) != 0) {
+                    LOG(WARNING) << "Cannot close dir " << path << ". " << strerror(errno) << std::endl;
+                } else {
+                    if (rmdir(path.c_str()) != 0) {
+                        LOG(WARNING) << "Cannot delete dir " << path << ". " << strerror(errno) << std::endl;
+                    } else {
+                        success = true;
+                    }
+                }
+            }
+
+            return success;
+        }
+
+        bool createDir(const std::string &path) {
+            bool success = false;
+
+            struct stat st;
+            bool exists = stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode);
+
+            if (!exists) {
+                const char *slashAt = strrchr(path.c_str(), '/');
+                if (slashAt == nullptr) {
+                    //no sub dir
+                    if (mkdir(path.c_str(), S_IRWXU) == 0) {
+                        success = true;
+                    } else {
+                        LOG(WARNING) << "Cannot create dir " << path << ". " << strerror(errno) << std::endl;
+                    }
+                } else {
+                    std::string subdir = path.substr(0, slashAt - path.c_str());
+                    bool subdirCreated = createDir(subdir);
+                    if (subdirCreated) {
+                        if (mkdir(path.c_str(), S_IRWXU) == 0) {
+                            success = true;
+                        } else {
+                            LOG(WARNING) << "Cannot create dir " << path << ". " << strerror(errno) << std::endl;
+                        }
+                    } else {
+                        //nop, error should be reported in the recursion
+                    }
+                }
+            } else {
+                //exists, so true
+                success = true;
+            }
+
+
+            return success;
+        }
+
+        bool extractResources(const std::string &bundleCache) {
+            bool success = false;
+            if (resourcesZip != nullptr) {
+                zip_error_t error;
+                zip_source_t *source = zip_source_buffer_create(resourcesZip, resourcesZipLen, 1, &error);
+                if (source != nullptr) {
+                    zip_t *zip = zip_open_from_source(source, ZIP_RDONLY, &error);
+                    if (zip != nullptr) {
+                        extractZipSource(zip, bundleCache);
+                        //zip_close(zip); ? not needed on zip_source ?
+                        zip_source_close(source);
+                        success = true;
+                    } else {
+                        LOG(WARNING) << "Cannot create zip from source: " << zip_error_strerror(&error) << std::endl;
+                    }
+                } else {
+                    LOG(WARNING) << "Cannot create zip source: " << zip_error_strerror(&error) << std::endl;
+                }
+            } else {
+                //no resources
+                success = true;
+            }
+            return success;
+        }
+
+        bool extractZipSource(zip_t *zip, const std::string &bundleCache) {
+            bool succes = true;
+            char buf[128];
+            zip_int64_t nrOfEntries = zip_get_num_entries(zip, 0);
+            for (int i = 0; i < nrOfEntries; ++i) {
+                zip_stat_t st;
+                zip_stat_index(zip, i, 0, &st);
+                if (st.name[strlen(st.name) - 1] == '/') {
+                    //dir
+                    //TODO
+                    LOG(ERROR) << "TODO extract dirs" << std::endl;
+                    succes = false;
+                } else {
+                    //file
+                    zip_file_t *f = zip_fopen_index(zip, i, 0);
+                    if (f != nullptr) {
+                        std::ofstream outf;
+                        std::string p = bundleCache + "/" + st.name;
+                        outf.open(p);
+                        if (!outf.fail()) {
+                            zip_int64_t read = zip_fread(f, buf, 128);
+                            while (read != 0) {
+                                outf.write(buf, read);
+                                read = zip_fread(f, buf, 128);
+                            }
+                            outf.close();
+                        } else {
+                            LOG(WARNING) << "Cannot open file '" << p << "': " << std::endl;
+                        }
+                        zip_fclose(f);
+                    } else {
+                        LOG(WARNING) << "Cannot read file from zip: " << zip_strerror(zip) << std::endl;
+                        succes = false;
+                        break;
+                    }
+                }
+            }
+            return succes;
+        }
+
         const std::function<celix::IBundleActivator*(std::shared_ptr<celix::BundleContext>)> actFactory;
         const std::shared_ptr<celix::Bundle> bnd;
         const std::shared_ptr<celix::BundleContext> ctx;
+        const uint8_t *resourcesZip;
+        const size_t resourcesZipLen;
 
         mutable std::mutex mutex{};
         std::unique_ptr<celix::IBundleActivator> act{nullptr};
diff --git a/libs/framework_cxx/src/Framework.cc b/libs/framework_cxx/src/Framework.cc
index a96ce86..0b1d856 100644
--- a/libs/framework_cxx/src/Framework.cc
+++ b/libs/framework_cxx/src/Framework.cc
@@ -21,6 +21,8 @@
  *under the License.
  */
 
+#include "celix/Framework.h"
+
 #include <unordered_map>
 #include <mutex>
 #include <iostream>
@@ -32,15 +34,20 @@
 #include <linux/limits.h>
 
 #include <glog/logging.h>
-
-#include "celix/Framework.h"
+#include <uuid/uuid.h>
 
 #include "BundleController.h"
 
+
+extern bool extractBundle(const char *bundleZip, const char *targetPath); //FROM miniunz.c
+
+
 struct StaticBundleEntry {
     const std::string symbolicName;
     const celix::Properties manifest;
     const std::function<celix::IBundleActivator*(std::shared_ptr<celix::BundleContext>)> activatorFactory;
+    const uint8_t *resourcesZip;
+    const size_t resourcesZipLen;
 };
 
 static struct {
@@ -55,7 +62,9 @@ static void unregisterFramework(celix::Framework *fw);
 
 class celix::Framework::Impl : public IBundle {
 public:
-    Impl(celix::Framework *_fw, celix::Properties _config) : fw{_fw}, config{std::move(_config)}, bndManifest{createManifest()}, cwd{createCwd()} {}
+    Impl(celix::Framework *_fw, celix::Properties _config) : fw{_fw}, config{std::move(_config)}, bndManifest{createManifest()}, cwd{cwdString()}, fwUUID{uuidString()}{
+        startFramework();
+    }
 
     Impl(const Impl&) = delete;
     Impl& operator=(const Impl&) = delete;
@@ -78,8 +87,13 @@ public:
         return result;
     }
 
-    long installBundle(std::string symbolicName, std::function<celix::IBundleActivator*(std::shared_ptr<celix::BundleContext>)> actFactory, celix::Properties manifest, bool autoStart) {
-        //TODO if activator is nullptr -> use empty activator
+    long installBundle(
+            std::string symbolicName,
+            std::function<celix::IBundleActivator*(std::shared_ptr<celix::BundleContext>)> actFactory,
+            celix::Properties manifest,
+            bool autoStart,
+            const uint8_t *resourcesZip,
+            size_t resourcesZipLen) {
         //TODO on separate thread ?? specific bundle resolve thread ??
         long bndId = -1L;
         if (symbolicName.empty()) {
@@ -104,7 +118,7 @@ public:
             bndId = bundles.nextBundleId++;
             auto bnd = std::shared_ptr<celix::Bundle>{new celix::Bundle{bndId, this->fw, std::move(manifest)}};
             auto ctx = std::shared_ptr<celix::BundleContext>{new celix::BundleContext{bnd}};
-            bndController = std::shared_ptr<celix::BundleController>{new celix::BundleController{std::move(actFactory), bnd, ctx}};
+            bndController = std::shared_ptr<celix::BundleController>{new celix::BundleController{std::move(actFactory), bnd, ctx, resourcesZip, resourcesZipLen}};
             bundles.entries.emplace(std::piecewise_construct,
                                      std::forward_as_tuple(bndId),
                                      std::forward_as_tuple(bndController));
@@ -243,11 +257,15 @@ public:
 
     //resource bundle part
     long id() const noexcept override { return 1L /*note registry empty bundle is id 0, framework is id 1*/; }
-    bool has(const std::string&) const noexcept override { return false; }
-    bool isDir(const std::string&) const noexcept override { return false; }
-    bool isFile(const std::string&) const noexcept override { return false; }
-    std::vector<std::string> readDir(const std::string&) const noexcept override { return std::vector<std::string>{};}
-    const std::string& root() const noexcept override { //TODO
+    bool hasCacheEntry(const std::string &) const noexcept override { return false; }
+    bool isCacheEntryDir(const std::string &) const noexcept override { return false; }
+    bool isCacheEntryFile(const std::string &) const noexcept override { return false; }
+
+    //virtual bool storeResource(const std::string &path, std::ostream content) noexcept = 0;
+    //virtual std::istream open(const std::string &path) const noexcept = 0;
+    //virtual std::fstream open(const std::string &path) noexcept = 0;
+    std::string absPathForCacheEntry(const std::string &) const noexcept override { return {}; }
+    const std::string& cacheRoot() const noexcept override { //TODO
         return cwd;
     }
 
@@ -263,7 +281,25 @@ public:
     bool isValid() const noexcept override { return true; }
     celix::Framework& framework() const noexcept override { return *fw; }
 
+    std::string cacheDir() const {
+        return cwd + "/.cache"; //TODO make configurable
+    }
+
+    std::vector<std::string> readCacheDir(const std::string &) const noexcept override { return {}; } //TODO
+
+    std::string uuid() const {
+        return fwUUID;
+    }
+
+    bool startFramework() {
+        //TODO create cache dir using a process id (and  lock file?).
+        //Maybe also move to /var/cache or /tmp and when framework stop delete all framework caches of not running processes
+        std::cout << "Celix Framework Started";
+        return true;
+    }
+
     bool stopFramework() {
+        //TODO create cache dir
         std::lock_guard<std::mutex> lck{shutdown.mutex};
         if (!shutdown.shutdownStarted) {
             shutdown.future = std::async(std::launch::async, [this]{
@@ -279,6 +315,7 @@ public:
             shutdown.shutdownStarted = true;
             shutdown.cv.notify_all();
         }
+        std::cout << "Celix Framework Stopped";
         return true;
     }
 
@@ -299,7 +336,15 @@ private:
         return m;
     }
 
-    std::string createCwd() {
+    std::string uuidString() {
+        char uuidStr[37];
+        uuid_t uuid;
+        uuid_generate_time_safe(uuid);
+        uuid_unparse(uuid, uuidStr);
+        return std::string{uuidStr};
+    }
+
+    std::string cwdString() {
         char workdir[PATH_MAX];
         if (getcwd(workdir, sizeof(workdir)) != NULL) {
             return std::string{workdir};
@@ -313,6 +358,7 @@ private:
     const celix::Properties config;
     const celix::Properties bndManifest;
     const std::string cwd;
+    const std::string fwUUID;
 
 
     struct {
@@ -352,8 +398,14 @@ celix::Framework::Framework(Framework &&rhs) = default;
 celix::Framework& celix::Framework::operator=(Framework&& rhs) = default;
 
 
-long celix::Framework::installBundle(std::string name, std::function<celix::IBundleActivator*(std::shared_ptr<celix::BundleContext>)> actFactory, celix::Properties manifest, bool autoStart) {
-    return pimpl->installBundle(std::move(name), actFactory, std::move(manifest), autoStart);
+long celix::Framework::installBundle(
+        std::string name,
+        std::function<celix::IBundleActivator*(std::shared_ptr<celix::BundleContext>)> actFactory,
+        celix::Properties manifest,
+        bool autoStart,
+        const uint8_t *resourcesZip,
+        size_t resourcesZipLen) {
+    return pimpl->installBundle(std::move(name), std::move(actFactory), std::move(manifest), autoStart, resourcesZip, resourcesZipLen);
 }
 
 
@@ -373,6 +425,14 @@ bool celix::Framework::uninstallBundle(long bndId) { return pimpl->uninstallBund
 celix::ServiceRegistry& celix::Framework::registry(const std::string &lang) { return pimpl->registry(lang); }
 
 bool celix::Framework::waitForShutdown() const { return pimpl->waitForShutdown(); }
+std::string celix::Framework::cacheDir() const {
+    return pimpl->cacheDir();
+}
+
+std::string celix::Framework::uuid() const {
+    return pimpl->uuid();
+}
+
 
 /***********************************************************************************************************************
  * Celix 'global' functions
@@ -381,19 +441,26 @@ bool celix::Framework::waitForShutdown() const { return pimpl->waitForShutdown()
 void celix::registerStaticBundle(
         std::string symbolicName,
         std::function<celix::IBundleActivator*(std::shared_ptr<celix::BundleContext>)> bundleActivatorFactory,
-        celix::Properties manifest) {
+        celix::Properties manifest,
+        const uint8_t *resourcesZip,
+        size_t resourcesZipLen) {
     std::lock_guard<std::mutex> lck{staticRegistry.mutex};
     for (auto fw : staticRegistry.frameworks) {
-        fw->installBundle(symbolicName, bundleActivatorFactory, manifest);
+        fw->installBundle(symbolicName, bundleActivatorFactory, manifest, true, resourcesZip, resourcesZipLen);
     }
-    staticRegistry.bundles.emplace_back(StaticBundleEntry{.symbolicName = std::move(symbolicName), .manifest = std::move(manifest), .activatorFactory = std::move(bundleActivatorFactory)});
+    staticRegistry.bundles.emplace_back(StaticBundleEntry{
+        .symbolicName = std::move(symbolicName),
+        .manifest = std::move(manifest),
+        .activatorFactory = std::move(bundleActivatorFactory),
+        .resourcesZip = resourcesZip,
+        .resourcesZipLen = resourcesZipLen});
 }
 
 static void registerFramework(celix::Framework *fw) {
     std::lock_guard<std::mutex> lck{staticRegistry.mutex};
     staticRegistry.frameworks.insert(fw);
     for (auto &entry : staticRegistry.bundles) {
-        fw->installBundle(entry.symbolicName, entry.activatorFactory, entry.manifest);
+        fw->installBundle(entry.symbolicName, entry.activatorFactory, entry.manifest, true, entry.resourcesZip, entry.resourcesZipLen);
     }
 }
 
diff --git a/libs/registry/CMakeLists.txt b/libs/registry/CMakeLists.txt
index 2a279de..5880f52 100644
--- a/libs/registry/CMakeLists.txt
+++ b/libs/registry/CMakeLists.txt
@@ -15,11 +15,10 @@
 # specific language governing permissions and limitations
 # under the License.
 
-find_package(glog REQUIRED)
-
 add_library(celix_registry STATIC
         src/ServiceRegistry.cc
         src/Filter.cc
+        src/Properties.cc
 )
 target_include_directories(celix_registry PRIVATE src)
 target_include_directories(celix_registry PUBLIC include)
diff --git a/libs/registry/include/celix/IResourceBundle.h b/libs/registry/include/celix/IResourceBundle.h
index 38b76b3..2b97c65 100644
--- a/libs/registry/include/celix/IResourceBundle.h
+++ b/libs/registry/include/celix/IResourceBundle.h
@@ -31,16 +31,17 @@ namespace celix {
 
         virtual long id() const noexcept  = 0;
 
-        virtual const std::string& root() const noexcept = 0;
+        virtual const std::string& cacheRoot() const noexcept = 0;
 
-        virtual bool has(const std::string &path) const noexcept = 0;
-        virtual bool isDir(const std::string &path) const noexcept = 0;
-        virtual bool isFile(const std::string &path) const noexcept = 0;
+        virtual bool hasCacheEntry(const std::string &entryPath) const noexcept = 0;
+        virtual bool isCacheEntryDir(const std::string &path) const noexcept = 0;
+        virtual bool isCacheEntryFile(const std::string &path) const noexcept = 0;
 
         //virtual bool storeResource(const std::string &path, std::ostream content) noexcept = 0;
         //virtual std::istream open(const std::string &path) const noexcept = 0;
         //virtual std::fstream open(const std::string &path) noexcept = 0;
-        virtual std::vector<std::string> readDir(const std::string &path) const noexcept = 0;
+        virtual std::string absPathForCacheEntry(const std::string &entry) const noexcept = 0;
+        virtual std::vector<std::string> readCacheDir(const std::string &path) const noexcept = 0;
     };
 
 
diff --git a/libs/registry/include/celix/Properties.h b/libs/registry/include/celix/Properties.h
index 1002bfc..649cf4c 100644
--- a/libs/registry/include/celix/Properties.h
+++ b/libs/registry/include/celix/Properties.h
@@ -56,11 +56,11 @@ namespace celix {
         return std::stoul(val, nullptr, 10);
     }
 
-    /*TODO
+
     celix::Properties loadProperties(const std::string &path);
-    celix::Properties loadProperties(std::istream stream);
-    bool storeProperties(const celix::Properties &props, const std::string &path);
-     */
+    celix::Properties loadProperties(std::istream &stream);
+    //bool storeProperties(const celix::Properties &props, const std::string &path); TODO
+    //bool storeProperties(const celix::Properties &props, std::ofstream &stream); TODO
 }
 
 #endif //CXX_CELIX_PROPERTIES_H
diff --git a/libs/registry/src/Properties.cc b/libs/registry/src/Properties.cc
new file mode 100644
index 0000000..403dee7
--- /dev/null
+++ b/libs/registry/src/Properties.cc
@@ -0,0 +1,215 @@
+/**
+ *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 <fstream>
+#include <string>
+#include <cstring>
+
+#include <glog/logging.h>
+
+#include "celix/Properties.h"
+
+#define MALLOC_BLOCK_SIZE		5
+
+
+static char * utils_stringTrim(char * string) {
+    char* copy = string;
+
+    char *end;
+    // Trim leading space
+    while (isspace(*copy)) {
+        copy++;
+    }
+
+    // Trim trailing space
+    end = copy + strlen(copy) - 1;
+    while(end > copy && isspace(*end)) {
+        *(end) = '\0';
+        end--;
+    }
+
+    if (copy != string) {
+        //beginning whitespaces -> move char in copy to to begin string
+        //This to ensure free still works on the same pointer.
+        char* nstring = string;
+        while(*copy != '\0') {
+            *(nstring++) = *(copy++);
+        }
+        (*nstring) = '\0';
+    }
+
+    return string;
+}
+
+static void updateBuffers(char **key, char ** value, char **output, int outputPos, int *key_len, int *value_len) {
+    if (*output == *key) {
+        if (outputPos == (*key_len) - 1) {
+            (*key_len) += MALLOC_BLOCK_SIZE;
+            *key = (char*)realloc(*key, *key_len);
+            *output = *key;
+        }
+    }
+    else {
+        if (outputPos == (*value_len) - 1) {
+            (*value_len) += MALLOC_BLOCK_SIZE;
+            *value = (char*)realloc(*value, *value_len);
+            *output = *value;
+        }
+    }
+}
+
+static void parseLine(const char* line, celix::Properties &props) {
+    int linePos = 0;
+    bool precedingCharIsBackslash = false;
+    bool isComment = false;
+    int outputPos = 0;
+    char *output = NULL;
+    int key_len = MALLOC_BLOCK_SIZE;
+    int value_len = MALLOC_BLOCK_SIZE;
+    linePos = 0;
+    precedingCharIsBackslash = false;
+    isComment = false;
+    output = NULL;
+    outputPos = 0;
+
+    //Ignore empty lines
+    if (line[0] == '\n' && line[1] == '\0') {
+        return;
+    }
+
+    char *key = (char*)calloc(1, key_len);
+    char *value = (char*)calloc(1, value_len);
+    key[0] = '\0';
+    value[0] = '\0';
+
+    while (line[linePos] != '\0') {
+        if (line[linePos] == ' ' || line[linePos] == '\t') {
+            if (output == NULL) {
+                //ignore
+                linePos += 1;
+                continue;
+            }
+        }
+        else {
+            if (output == NULL) {
+                output = key;
+            }
+        }
+        if (line[linePos] == '=' || line[linePos] == ':' || line[linePos] == '#' || line[linePos] == '!') {
+            if (precedingCharIsBackslash) {
+                //escaped special character
+                output[outputPos++] = line[linePos];
+                updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len);
+                precedingCharIsBackslash = false;
+            }
+            else {
+                if (line[linePos] == '#' || line[linePos] == '!') {
+                    if (outputPos == 0) {
+                        isComment = true;
+                        break;
+                    }
+                    else {
+                        output[outputPos++] = line[linePos];
+                        updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len);
+                    }
+                }
+                else { // = or :
+                    if (output == value) { //already have a seperator
+                        output[outputPos++] = line[linePos];
+                        updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len);
+                    }
+                    else {
+                        output[outputPos++] = '\0';
+                        updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len);
+                        output = value;
+                        outputPos = 0;
+                    }
+                }
+            }
+        }
+        else if (line[linePos] == '\\') {
+            if (precedingCharIsBackslash) { //double backslash -> backslash
+                output[outputPos++] = '\\';
+                updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len);
+            }
+            precedingCharIsBackslash = true;
+        }
+        else { //normal character
+            precedingCharIsBackslash = false;
+            output[outputPos++] = line[linePos];
+            updateBuffers(&key, &value, &output, outputPos, &key_len, &value_len);
+        }
+        linePos += 1;
+    }
+    if (output != NULL) {
+        output[outputPos] = '\0';
+    }
+
+    if (!isComment) {
+        //printf("putting 'key'/'value' '%s'/'%s' in properties\n", utils_stringTrim(key), utils_stringTrim(value));
+        props[std::string{utils_stringTrim(key)}] = std::string{utils_stringTrim(value)};
+    }
+    if(key) {
+        free(key);
+    }
+    if(value) {
+        free(value);
+    }
+
+}
+
+celix::Properties celix::loadProperties(const std::string &path) {
+    std::ifstream file;
+    file.open(path);
+    if (file.fail()) {
+        LOG(WARNING) << "Cannot open file " << path << ". " << file.failbit << std::endl;
+        return celix::Properties{};
+    } else {
+        return celix::loadProperties(file);
+    }
+}
+
+celix::Properties celix::loadProperties(std::istream &stream) {
+    celix::Properties props{};
+
+    if (!stream.fail()) {
+        stream.seekg(0, stream.end);
+        long size = stream.tellg();
+        stream.seekg(0, stream.beg);
+
+        if (size > 0){
+            while (!stream.eof()) {
+                std::string line;
+                std::getline(stream, line);
+                parseLine(line.c_str(), props);
+            }
+        }
+    }
+
+    //TODO howto singal an error with parsing ...
+    return props;
+}
+
+//bool celix::storeProperties(const celix::Properties &props, const std::string &path) {
+//    return false; //TODO
+//}
+//
+//bool celix::storeProperties(const celix::Properties &props, std::ofstream &stream) {
+//    return false; //TODO
+//}
\ No newline at end of file
diff --git a/libs/registry/src/ServiceRegistry.cc b/libs/registry/src/ServiceRegistry.cc
index a7465e2..001b08e 100644
--- a/libs/registry/src/ServiceRegistry.cc
+++ b/libs/registry/src/ServiceRegistry.cc
@@ -42,18 +42,19 @@ namespace {
 
         long id() const noexcept override { return 0; }
 
-        const std::string& root() const noexcept override {
+        const std::string& cacheRoot() const noexcept override {
             static std::string empty{};
             return empty;
         }
 
-        bool has(const std::string&) const noexcept override { return false; }
-        bool isDir(const std::string&) const noexcept override { return false; }
-        bool isFile(const std::string&) const noexcept override { return false; }
+        bool hasCacheEntry(const std::string&) const noexcept override { return false; }
+        bool isCacheEntryDir(const std::string&) const noexcept override { return false; }
+        bool isCacheEntryFile(const std::string&) const noexcept override { return false; }
 
         //std::istream& open(const std::string &path) override {}
         //std::fstream& open(const std::string &path) override {}
-        std::vector<std::string> readDir(const std::string&) const noexcept override { return std::vector<std::string>{};}
+        std::string absPathForCacheEntry(const std::string &) const noexcept override { return {}; }
+        std::vector<std::string> readCacheDir(const std::string&) const noexcept override { return std::vector<std::string>{};}
     };
 
     struct SvcEntry {