You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2020/04/17 15:21:19 UTC

[sling-whiteboard] branch master updated (0c9db2d -> 07320c1)

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

rombert pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/sling-whiteboard.git.


    from 0c9db2d  README about DataFetcher definitions
     new a2187c2  Commit changes since initial contribution.
     new 44d595d  Updating README
     new 07320c1  Changing repository URL.

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 slingpackager/README.md                         |  23 ++
 slingpackager/cmds/{install.js => build.js}     |   6 +-
 slingpackager/cmds/delete.js                    |   2 +-
 slingpackager/cmds/install.js                   |   2 +-
 slingpackager/cmds/list.js                      |   2 +-
 slingpackager/cmds/test.js                      |  18 ++
 slingpackager/cmds/uninstall.js                 |   2 +-
 slingpackager/cmds/upload.js                    |   2 +-
 slingpackager/package-lock.json                 |   2 +-
 slingpackager/package.json                      |   4 +-
 slingpackager/test/slingpackager.tests.js       |  17 ++
 slingpackager/test/testContent-1.0-SNAPSHOT.zip | Bin 2680 -> 0 bytes
 slingpackager/utils/aempackager.js              | 197 ++++++++++-------
 slingpackager/utils/composumpackager.js         | 278 ++++++++++++++++--------
 14 files changed, 373 insertions(+), 182 deletions(-)
 copy slingpackager/cmds/{install.js => build.js} (87%)
 delete mode 100644 slingpackager/test/testContent-1.0-SNAPSHOT.zip


[sling-whiteboard] 03/03: Changing repository URL.

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-whiteboard.git

commit 07320c13790c09cfff57fdc78fd12b308516a41a
Author: Henry Saginor <hs...@gmail.com>
AuthorDate: Fri Apr 17 08:18:54 2020 -0700

    Changing repository URL.
---
 slingpackager/package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/slingpackager/package.json b/slingpackager/package.json
index 4962262..888790b 100644
--- a/slingpackager/package.json
+++ b/slingpackager/package.json
@@ -5,7 +5,7 @@
   "main": "index.js",
   "repository": {
     "type": "git",
-    "url": "git+https://github.com/hsaginor/sling-whiteboard.git"
+    "url": "git+https://github.com/apache/sling-whiteboard"
   },
   "scripts": {
     "test": "mocha"


[sling-whiteboard] 02/03: Updating README

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-whiteboard.git

commit 44d595d98806325b9a8af3dd3789ff0aebf44a9c
Author: Henry Saginor <hs...@gmail.com>
AuthorDate: Thu Apr 16 15:04:25 2020 -0700

    Updating README
---
 slingpackager/README.md | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/slingpackager/README.md b/slingpackager/README.md
index f27c054..f77013c 100644
--- a/slingpackager/README.md
+++ b/slingpackager/README.md
@@ -45,10 +45,12 @@ to run slingpackager in your project.
 slingpackager <command>
 
 Commands:
+  slingpackager build <package>      build package on server
   slingpackager delete <package>     delete package on server
   slingpackager install <package>    install package on server
   slingpackager list                 list installed packages
   slingpackager package <folder>     create a package
+  slingpackager test                 test package manager service connection
   slingpackager uninstall <package>  uninstall package on server
   slingpackager upload <package>     upload package to server
 
@@ -182,6 +184,22 @@ Options:
   --verbose, -v  turn on verbose output
 ```
 
+### Build
+
+```
+slingpackager build <package>
+
+build package on server
+
+Options:
+  --version      Show version number                                   [boolean]
+  --help         Show help                                             [boolean]
+  --server, -s   server url                   [default: "http://localhost:8080"]
+  --user, -u     server credentials in the form username:password
+                                                        [default: "admin:admin"]
+  --verbose, -v  turn on verbose output
+```
+
 ### Delete
 
 ```
@@ -262,6 +280,11 @@ slingpackager install /themeclean-flex/ui.apps-1.0-SNAPSHOT.zip
 slingpackager uninstall /themeclean-flex/ui.apps-1.0-SNAPSHOT.zip
 ```
 
+#### Build package.
+```
+slingpackager build /themeclean-flex/ui.apps-1.0-SNAPSHOT.zip
+```
+
 #### Delete package.
 ```
 slingpackager delete /themeclean-flex/ui.apps-1.0-SNAPSHOT.zip


[sling-whiteboard] 01/03: Commit changes since initial contribution.

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-whiteboard.git

commit a2187c2491127a6011c9b5fe8d439ccf4aa1fc93
Author: Henry Saginor <hs...@gmail.com>
AuthorDate: Thu Apr 16 14:50:46 2020 -0700

    Commit changes since initial contribution.
---
 slingpackager/cmds/{delete.js => build.js}      |   6 +-
 slingpackager/cmds/delete.js                    |   2 +-
 slingpackager/cmds/install.js                   |   2 +-
 slingpackager/cmds/list.js                      |   2 +-
 slingpackager/cmds/test.js                      |  18 ++
 slingpackager/cmds/uninstall.js                 |   2 +-
 slingpackager/cmds/upload.js                    |   2 +-
 slingpackager/package-lock.json                 |   2 +-
 slingpackager/package.json                      |   4 +-
 slingpackager/test/slingpackager.tests.js       |  17 ++
 slingpackager/test/testContent-1.0-SNAPSHOT.zip | Bin 2680 -> 0 bytes
 slingpackager/utils/aempackager.js              | 197 ++++++++++-------
 slingpackager/utils/composumpackager.js         | 278 ++++++++++++++++--------
 13 files changed, 350 insertions(+), 182 deletions(-)

diff --git a/slingpackager/cmds/delete.js b/slingpackager/cmds/build.js
similarity index 87%
copy from slingpackager/cmds/delete.js
copy to slingpackager/cmds/build.js
index be297e4..d2b18be 100644
--- a/slingpackager/cmds/delete.js
+++ b/slingpackager/cmds/build.js
@@ -19,8 +19,8 @@
 const packager = require('../utils/packager')
 const logger = require('../utils/consoleLogger')
 
-exports.command = 'delete <package>'
-exports.desc = 'delete package on server'
+exports.command = 'build <package>'
+exports.desc = 'build package on server'
 exports.handler = (argv) => {
   let user = argv.user.split(':');
   let userName = user[0];
@@ -32,7 +32,7 @@ exports.handler = (argv) => {
   logger.init(argv);
   packager.test(argv, (success, packageManager) => {
     if(success) {
-        packageManager.deletePackage(argv.server, userName, pass, argv.package);
+        packageManager.buildPackage(argv.server, userName, pass, argv.package, argv.retry);
     }
   });
 
diff --git a/slingpackager/cmds/delete.js b/slingpackager/cmds/delete.js
index be297e4..7f9f56f 100644
--- a/slingpackager/cmds/delete.js
+++ b/slingpackager/cmds/delete.js
@@ -32,7 +32,7 @@ exports.handler = (argv) => {
   logger.init(argv);
   packager.test(argv, (success, packageManager) => {
     if(success) {
-        packageManager.deletePackage(argv.server, userName, pass, argv.package);
+        packageManager.deletePackage(argv.server, userName, pass, argv.package, argv.retry);
     }
   });
 
diff --git a/slingpackager/cmds/install.js b/slingpackager/cmds/install.js
index c01831e..1dc0817 100644
--- a/slingpackager/cmds/install.js
+++ b/slingpackager/cmds/install.js
@@ -32,7 +32,7 @@ exports.handler = (argv) => {
   logger.init(argv);
   packager.test(argv, (success, packageManager) => {
     if(success) {
-        packageManager.installPackage(argv.server, userName, pass, argv.package);
+        packageManager.installPackage(argv.server, userName, pass, argv.package, argv.retry);
     }
   });
 
diff --git a/slingpackager/cmds/list.js b/slingpackager/cmds/list.js
index ffad50f..6f40dab 100644
--- a/slingpackager/cmds/list.js
+++ b/slingpackager/cmds/list.js
@@ -33,7 +33,7 @@ exports.handler = (argv) => {
 
   packager.test(argv, (success, packageManager) => {
     if(success) {
-        packageManager.list(argv.server, userName, pass);
+        packageManager.list(argv.server, userName, pass,  argv.retry);
     }
   });
 
diff --git a/slingpackager/cmds/test.js b/slingpackager/cmds/test.js
index ab2c208..dc53901 100644
--- a/slingpackager/cmds/test.js
+++ b/slingpackager/cmds/test.js
@@ -1,3 +1,21 @@
+/*
+ * 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.
+ */
 const packager = require('../utils/packager')
 const logger = require('../utils/consoleLogger')
 
diff --git a/slingpackager/cmds/uninstall.js b/slingpackager/cmds/uninstall.js
index 23599b4..940d1bb 100644
--- a/slingpackager/cmds/uninstall.js
+++ b/slingpackager/cmds/uninstall.js
@@ -32,7 +32,7 @@ exports.handler = (argv) => {
   logger.init(argv);
   packager.test(argv, (success, packageManager) => {
     if(success) {
-        packageManager.uninstallPackage(argv.server, userName, pass, argv.package);
+        packageManager.uninstallPackage(argv.server, userName, pass, argv.package,  argv.retry);
     }
   });
 
diff --git a/slingpackager/cmds/upload.js b/slingpackager/cmds/upload.js
index 5bafcb5..4a22552 100644
--- a/slingpackager/cmds/upload.js
+++ b/slingpackager/cmds/upload.js
@@ -53,7 +53,7 @@ exports.handler = (argv) => {
 
   packager.test(argv, (success, packageManager) => {
     if(success) {
-        packageManager.uploadPackage(argv.server, userName, pass, packagePath, argv.install);
+        packageManager.uploadPackage(argv.server, userName, pass, packagePath, argv.install, argv.retry);
     }
   });
 
diff --git a/slingpackager/package-lock.json b/slingpackager/package-lock.json
index 278adc6..fed1d9a 100644
--- a/slingpackager/package-lock.json
+++ b/slingpackager/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "@apachesling/slingpackager",
-  "version": "0.0.3",
+  "version": "0.0.4",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
diff --git a/slingpackager/package.json b/slingpackager/package.json
index c5a4a34..4962262 100644
--- a/slingpackager/package.json
+++ b/slingpackager/package.json
@@ -1,11 +1,11 @@
 {
   "name": "@apachesling/slingpackager",
-  "version": "0.0.3",
+  "version": "0.0.4",
   "description": "prepare a sling package file for sling and install it",
   "main": "index.js",
   "repository": {
     "type": "git",
-    "url": "git+https://github.com/peregrine-cms/slingpackager.git"
+    "url": "git+https://github.com/hsaginor/sling-whiteboard.git"
   },
   "scripts": {
     "test": "mocha"
diff --git a/slingpackager/test/slingpackager.tests.js b/slingpackager/test/slingpackager.tests.js
index 94962b7..dc70cf6 100644
--- a/slingpackager/test/slingpackager.tests.js
+++ b/slingpackager/test/slingpackager.tests.js
@@ -41,6 +41,8 @@ const password = serverInfo.password;
 
 describe('slingpackager', function() {
 
+    this.timeout(30000);
+    
     // package test
     describe('package', function() {
         it('should create package', function() {
@@ -69,6 +71,13 @@ describe('slingpackager', function() {
         });
     });
 
+    // build test
+    describe('build', function() {
+        it('should build package', function(done) {
+            testBuild(done);
+        });
+    });
+
     // uninstall test
     describe('uninstall', function() {
         it('should uninstall package', function(done) {
@@ -118,6 +127,14 @@ function testInstall(done) {
     assert200(server + testInstallPath, done);
 };
 
+// build command test
+function testBuild(done) {
+    var cmd = 'node bin/slingpackager build ' + packServerName + ' -s ' + server;
+    var output = exec(cmd);
+    logger.debug(output);
+    assert200(server + testInstallPath, done);
+};
+
 // uninstall command test
 function testUninstall(done) {
     var cmd = 'node bin/slingpackager uninstall ' + packServerName + ' -s ' + server;
diff --git a/slingpackager/test/testContent-1.0-SNAPSHOT.zip b/slingpackager/test/testContent-1.0-SNAPSHOT.zip
deleted file mode 100644
index d65de1c..0000000
Binary files a/slingpackager/test/testContent-1.0-SNAPSHOT.zip and /dev/null differ
diff --git a/slingpackager/utils/aempackager.js b/slingpackager/utils/aempackager.js
index 8f72f48..c30f015 100644
--- a/slingpackager/utils/aempackager.js
+++ b/slingpackager/utils/aempackager.js
@@ -41,36 +41,24 @@ const checkService = (url, username, password, callback) => {
     logger.debug('request =', JSON.stringify(req.toJSON()));
 }
 
-const list = (url, username, password) => {
+const list = (url, username, password, maxRetry) => {
   logger.log('Listing packages on', url)
 
   let serviceURL = url + endpoint + '?cmd=ls';
-  logger.debug('Service call: ', serviceURL);
-  let post = request.post({url: serviceURL}, (error, response, body) => {
+  var post = callService({serviceURL, username, password, maxRetry}, (error, result) => {
     if(error) {
       logger.error(error);
+      process.exit(1);
     }
 
-    if(response && response.statusCode===200) {
-      // logger.log(body);
-      xml2js.parseString(body, (error, result) => {
-        if(result) {
-          var data = getData(result);
-          displayPackages(data[0].packages[0].package);
-        } else if(error) {
-          logger.error(error);
-        }
-      });
-    } else {
-      logger.error('Unable to connect to server. statusCode:', response && response.statusCode);
+    if(result) {
+      var data = getData(result);
+      displayPackages(data[0].packages[0].package);
     }
-
-  }).auth(username, password);
-
-  logger.debug(JSON.stringify(post.toJSON()));
+  });
 }
 
-const uploadPackage = (url, username, password, packagePath, install) => {
+const uploadPackage = (url, username, password, packagePath, install, maxRetry) => {
   logger.log('Uploading AEM package',packagePath,'on', url);
 
   let serviceURL = url + endpoint;
@@ -78,86 +66,124 @@ const uploadPackage = (url, username, password, packagePath, install) => {
     serviceURL = serviceURL + '?install=true'
   }
 
-  logger.debug('Service call: ', serviceURL);
-  var post = request.post({url: serviceURL}, (error, response, body) => {
-    if(error) {
-      logger.error(error);
-    }
-
-    if(response && response.statusCode===200) {
-      xml2js.parseString(body, (error, result) => {
-        if(result) {
-          if(getStatusCode(result) === '200') {
-            logger.log("Done!");
-          } else {
-            logger.error("Something went wrong! Check server logs.");
-          }
-        } else if(error) {
-          logger.error(error);
-        }
-        logger.debug(body);
-      });
-    } else {
-      logger.error('Unable to connect to server. statusCode:', response && response.statusCode);
-    }
-
-  }).auth(username, password);
+  var post = executeCommand(serviceURL, username, password, maxRetry);
   post.form().append('file', fs.createReadStream(packagePath));
-  logger.debug(JSON.stringify(post.toJSON()));
 }
 
-const deletePackage = (url, username, password, package) => {
+const deletePackage = (url, username, password, package, maxRetry) => {
   logger.log('Deleting AEM package',package,'on', url);
-  let post = executePackageCommand(url, username, password, package, 'rm');
-  logger.debug(JSON.stringify(post.toJSON()));
+  executePackageCommand(url, username, password, package, 'rm', maxRetry);
 }
 
-const installPackage = (url, username, password, package) => {
+const installPackage = (url, username, password, package, maxRetry) => {
   logger.log('Installing AEM package',package,'on', url);
-  let post = executePackageCommand(url, username, password, package, 'inst');
-  logger.debug(JSON.stringify(post.toJSON()));
+  executePackageCommand(url, username, password, package, 'inst', maxRetry);
 }
 
-const uninstallPackage = (url, username, password, package) => {
+const uninstallPackage = (url, username, password, package, maxRetry) => {
   logger.log('Uninstalling AEM package',package,'on', url);
-  let post = executePackageCommand(url, username, password, package, 'uninst');
-  logger.debug(JSON.stringify(post.toJSON()));
+  executePackageCommand(url, username, password, package, 'uninst', maxRetry);
+}
+
+const buildPackage = (url, username, password, package, maxRetry) => {
+  logger.log('Building AEM package', package, 'on', url);
+  executePackageCommand(url, username, password, package, 'build', maxRetry);
 }
 
 const getName = () => {
   return 'AEM Package Manager';
 }
 
-function executePackageCommand(url, username, password, packageName, cmd) {
+function executePackageCommand(url, username, password, packageName, cmd, maxRetry) {
   let serviceURL = url + endpoint + '?cmd=' + cmd;
-  logger.debug('Service call: ', serviceURL);
-  var post = request.post({url: serviceURL}, (error, response, body) => {
+  var post = executeCommand(serviceURL, username, password, maxRetry);
+  post.form().append('name', packageName);
+  return post;
+}
+
+function executeCommand(serviceURL, username, password, maxRetry) {
+  var post = callService({serviceURL, username, password, maxRetry}, (error, result) => {
     if(error) {
       logger.error(error);
+      process.exit(1);
     }
 
-    if(response && response.statusCode===200) {
-      xml2js.parseString(body, (error, result) => {
-        if(result) {
-          if(getStatusCode(result) === '200') {
-            logger.log("Done!");
-          } else {
-            logger.log(getStatusText(result));
-          }
-        } else if(error) {
-          logger.error(error);
-        }
-      });
-    } else {
-      logger.error('Unable to connect to server. statusCode:', response && response.statusCode);
+    if(result) {
+      var respLog = getResponseLog(result);
+      if(respLog) {
+        logger.log(respLog);
+      } else {
+        logger.log(getStatusText(result));
+      }
     }
-
-  }).auth(username, password);
-  post.form().append('name', packageName);
+  });
 
   return post;
 }
 
+function callService(data, callback) {
+  if(data.retryCount === undefined) {
+      data.retryCount = 0;
+      if(data.maxRetry === undefined) {
+          data.maxRetry = 10;
+      }
+  }
+
+  logger.debug(data.retryCount + '. Service call: ', data.serviceURL);
+
+  let req = request.post({ url: data.serviceURL }, (error, response, body) => {
+      var statusCodeLine = (response === undefined) ? "" : "Response: " + response.statusCode + " : " + response.statusMessage;
+      logger.debug(statusCodeLine);
+
+      if (error) {
+          if(data.retryCount < data.maxRetry) {
+              data.retryCount++;
+              callService(data, callback);
+          } else  { 
+              logger.error(error);
+              callback(error + " " + statusCodeLine, undefined); 
+          }
+
+          return;
+      } else if(response && response.statusCode===200) {
+        xml2js.parseString(body, (error, result) => {
+          if(result) {
+            logger.debug('Response body: ',body);
+            if(getStatusCode(result) === '200') {
+              callback(undefined, result);
+            } else {
+              logger.debug(body);
+              logger.warn("Response status:",getStatusCode(result),":",getStatusText(result));
+              retryCallService(data, 'Unable to parse service response for', data.serviceURL, callback);
+            }
+          } else if(error) {
+            logger.debug(body);
+            logger.warn('Unable to parse service response for', data.serviceURL);
+            retryCallService(data, 'Unable to parse service response for', data.serviceURL, callback);
+          }
+        });
+
+        return;
+      } 
+
+      retryCallService(data, "Error calling service " + data.serviceURL, callback)
+
+      return;
+  }).auth(data.username, data.password);
+
+  logger.debug(JSON.stringify(req.toJSON()));
+  return req;
+}
+
+function retryCallService(data, error, callback) {
+  if(data.retryCount < data.maxRetry) {
+    data.retryCount++;
+    callService(data, callback);
+   } else  { 
+    callback(error, undefined);
+   }
+}
+
 function displayPackages(packages) {
   for(var i=0; i<packages.length; i++) {
     logger.log('name='+packages[i].name[0]+
@@ -168,11 +194,29 @@ function displayPackages(packages) {
 }
 
 function getStatusCode(result) {
-  return result.crx.response[0].status[0].$.code;
+  try {
+    return result.crx.response[0].status[0].$.code;
+  } catch(e) {
+    return undefined;
+  }
 }
 
 function getStatusText(result) {
-  return result.crx.response[0].status[0]._;
+  try {
+    return result.crx.response[0].status[0]._;
+  } catch(e) {
+    return undefined;
+  }
+}
+
+function getResponseLog(result) {
+  if(result.crx.response[0].log) {
+    return result.crx.response[0].log;
+  }
+  if(result.crx.response[0].data && result.crx.response[0].data[0].log) {
+    return result.crx.response[0].data[0].log;
+  } 
+  return undefined;
 }
 
 function getData(result) {
@@ -186,6 +230,7 @@ module.exports = {
     deletePackage,
     installPackage,
     uninstallPackage,
+    buildPackage,
     getName
 }
 
diff --git a/slingpackager/utils/composumpackager.js b/slingpackager/utils/composumpackager.js
index 4b4817b..da3c817 100644
--- a/slingpackager/utils/composumpackager.js
+++ b/slingpackager/utils/composumpackager.js
@@ -41,153 +41,117 @@ const checkService = (url, username, password, callback) => {
     }).auth(username, password);
 }
 
-const list = (url, username, password) => {
+const list = (url, username, password, maxRetry) => {
     logger.log('Listing packages on', url);
-    listPackages(url, username, password, '');
+    listPackages(url, username, password, '', maxRetry);
 }
 
-const uploadPackage = (url, username, password, packagePath, install) => {
+const uploadPackage = (url, username, password, packagePath, install, maxRetry) => {
     logger.log('Uploading package', packagePath, 'on', url);
 
     let serviceURL = url + uploadEndpoint;
-    logger.debug('Service call: ', serviceURL);
-    let post = request.post({ url: serviceURL }, (error, response, body) => {
-        if (error) {
+    let post = callPostService({serviceURL, username, password, maxRetry}, (error, json) => {
+        if(error) {
+            logger.error('Unable to upload package', packagePath);
             logger.error(error);
-        }
-
-        if (response && response.statusCode === 200) {
-            var json = JSON.parse(body);
+            process.exit(1);
+        } else {
             logger.log(json.status)
             logger.debug(JSON.stringify(json));
 
             if(install) {
-                logger.debug('installing', json.path);
-                installPackage(url, username, password, json.path);
+                logger.info('installing', json.path);
+                installPackage(url, username, password, json.path, maxRetry);
             }
-
-        } else {
-            logger.error('Unable to upload package. statusCode:', response && response.statusCode);
-            logger.debug(body);
         }
-    }).auth(username, password);
+    });
 
     post.form().append('file', fs.createReadStream(packagePath));
 
     logger.debug(JSON.stringify(post.toJSON()));
 }
 
-const deletePackage = (url, username, password, package) => {
+const deletePackage = (url, username, password, package, maxRetry) => {
     logger.log('Deleting package', package, 'on', url);
 
     let serviceURL = url + deleteEndpoint + package;
-    logger.debug('Service call: ', serviceURL);
-    let req = request({ url: serviceURL, method: 'DELETE' }, (error, response, body) => {
-        if (error) {
+    let req = callService({serviceURL, method: 'DELETE', username, password, maxRetry}, (error, json) => {
+        if(error) {
+            logger.error('Unable to delete package', package);
             logger.error(error);
-        }
-
-        if (response && response.statusCode === 200) {
-            if (body) {
-                var json = JSON.parse(body);
-                logger.log(json.status)
-            } else {
-                logger.error('Unable to delete package. Check package name (try by path).');
-            }
+            process.exit(1);
         } else {
-            logger.error('Unable to delete package. statusCode:', response && response.statusCode);
-            logger.debug(body);
+            logger.log(json.status);
+            logger.debug(JSON.stringify(json));
         }
-    }).auth(username, password);
+    });
 
     logger.debug(JSON.stringify(req.toJSON()));
 }
 
-const installPackage = (url, username, password, package) => {
+const installPackage = (url, username, password, package, maxRetry) => {
     logger.log('Installing package', package, 'on', url);
 
-    let serviceURL = url + installEndpoint + package;
-    logger.debug('Service call: ', serviceURL);
-    let post = request.post({ url: serviceURL }, (error, response, body) => {
-        if (error) {
+    let post = postJob({url, username, password, package, maxRetry}, 'install', (error, result) => {
+        if(error) {
+            logger.error('Unable to uninstall package', package);
             logger.error(error);
-        }
-
-        if (response && response.statusCode === 200) {
-            if (body) {
-                var json = JSON.parse(body);
-                logger.log(json.status)
-            }
-        } else {
-            logger.error('Unable to install package. statusCode:', response && response.statusCode);
-            logger.debug(body);
-        }
-    }).auth(username, password);
-
-    logger.debug(JSON.stringify(post.toJSON()));
+            process.exit(1);
+        } 
+    });
 }
 
-const uninstallPackage = (url, username, password, package) => {
+const uninstallPackage = (url, username, password, package, maxRetry) => {
     logger.log('Uninstalling package', package, 'on', url);
 
-    let serviceURL = url + '/bin/cpm/core/jobcontrol.job.json';
-    logger.debug('Service call: ', serviceURL);
-    // Commented out code bellow does not work wirh Composum 1.7/Sling9
-    // var post = request.post({ url: url + uninstallEndpoint + package }, (error, response, body) => {
-    var post = request.post({ url: serviceURL }, (error, response, body) => {
-        if (error) {
+    let post = postJob({url, username, password, package, maxRetry}, 'uninstall', (error, result) => {
+        if(error) {
+            logger.error('Unable to uninstall package', package);
             logger.error(error);
-        }
-
-        if (response && response.statusCode === 200) {
-            if (body) {
-                var json = JSON.parse(body);
-                logger.log('done');
-                // Commented out for Composum 1.7/Sling9
-                // logger.log(json.status)
-            }
+            process.exit(1);
         } else {
-            logger.error('Unable to uninstall package. statusCode:', response && response.statusCode);
-            logger.debug(body);
+            if(result && (typeof(result) === 'string') && result.startsWith('Unable')) {
+                logger.error(result);
+                process.exit(1);
+            }
         }
-    }).auth(username, password);
+    });
+}
 
-    // These parameters are not needed when using uninstallEndpoint with Sling11.
-    // This is a workaround for Composum 1.7 and Sling9 which does not have this endpoint.
-    var form = post.form();
-    form.append('event.job.topic', 'com/composum/sling/core/pckgmgr/PackageJobExecutor');
-    form.append('reference', package);
-    form.append('_charset_', 'UTF-8');
-    form.append('operation', 'uninstall');
+const buildPackage = (url, username, password, package, maxRetry) => {
+    logger.log('Building package', package, 'on', url);
 
-    logger.debug(JSON.stringify(post.toJSON()));
+    let post = postJob({url, username, password, package, maxRetry}, 'assemble', (error, result) => {
+        if(error) {
+            logger.error('Unable to build package', package);
+            logger.error(error);
+            process.exit(1);
+        } else {
+            if(result && (typeof(result) === 'string') && result.startsWith('Unable')) {
+                logger.error(result);
+                process.exit(1);
+            }
+        }
+    });
 }
 
 const getName = () => {
     return 'Composum Package Manager';
 }
 
-function listPackages(url, username, password, path) {
+function listPackages(url, username, password, path, maxRetry) {
     var serviceURL = url + listEndpoint + path;
-    logger.debug('Service call: ', serviceURL);
-
-    let req = request.get({ url: serviceURL}, (error, response, body) => {
-        if (error) {
+    
+    let req = callGetService({serviceURL, username, password, maxRetry}, (error, json) => {
+        if(error) {
+            logger.error("Unable to list packages.");
             logger.error(error);
-        }
-
-        if (response && response.statusCode === 200) {
-            var json = JSON.parse(body);
-            
-            // Check for structure diff between Composume in Sling9 and Sling11
+            process.exit(1);
+        } else {
             var packages = json.children ? json.children : json;
             displayPackages(url, username, password, packages);
-        } else {
-            logger.error('Unable to connect to server. statusCode:', response && response.statusCode);
-            logger.debug(body);
         }
-
-    }).auth(username, password);
+    }); 
 
     logger.debug(JSON.stringify(req.toJSON()));
 }
@@ -206,6 +170,129 @@ function displayPackages(url, username, password, packages) {
     }
 }
 
+function postJob(data, operation, callback) {
+    data.serviceURL = data.url + '/bin/cpm/core/jobcontrol.job.json';
+    let post = callPostService(data, (error, json) => {
+        if(json && json['slingevent:eventId']) {
+            setTimeout(() => {
+                getJobOutput(data.url, data.username, data.password, json['slingevent:eventId'], callback)
+            },100);
+        } else {
+            callback(error, json); 
+        }
+    });
+
+    var form = post.form();
+    form.append('event.job.topic', 'com/composum/sling/core/pckgmgr/PackageJobExecutor');
+    form.append('_charset_', 'UTF-8');
+    form.append('operation', operation);
+    form.append('reference', data.package);
+
+    return post;
+}
+
+function callGetService(data, callback) {
+    data.method = "GET";
+    return callService(data, callback);
+}
+
+function callPostService(data, callback) {
+    data.method = "POST";
+    var post = callService(data, callback);
+    logger.debug('POST:', JSON.stringify(post.toJSON(), undefined, '   '));
+    return post;
+}
+
+function callService(data, callback) {
+    if(data.retryCount === undefined) {
+        data.retryCount = 0;
+        if(data.maxRetry === undefined) {
+            data.maxRetry = 10;
+        }
+    }
+
+    logger.debug(data.retryCount + '. Service call: ', data.serviceURL);
+
+    let req = request({ url: data.serviceURL, method: data.method }, (error, response, body) => {
+        var statusCodeLine = (response === undefined) ? "" : "Response: " + response.statusCode + " : " + response.statusMessage;
+        logger.debug(statusCodeLine);
+
+        if (error) {
+            if(data.retryCount < data.maxRetry) {
+                data.retryCount++;
+                callService(data, callback);
+            } else  { 
+                logger.error(error);
+                callback(error + " " + statusCodeLine, undefined); 
+            }
+
+            return;
+        }
+
+        if (response && response.statusCode === 200) {
+            if (body) {
+                var json = JSON.parse(body);
+                logger.debug('Response:', JSON.stringify(json, undefined, '   '));
+                callback(undefined, json);
+                return;
+            } else {
+                logger.debug("Respons has no body.");
+            }
+        }
+
+        if(data.retryCount < data.maxRetry) {
+            data.retryCount++;
+            callService(data, callback);
+        } else  { 
+            callback("Error calling service " + data.serviceURL, undefined);
+        }
+
+        return;
+    }).auth(data.username, data.password);
+    return req;
+}
+
+function getJobOutput(url, username, password, eventId, callback, jobState) {
+    var requestData = {url: url + "/bin/cpm/core/jobcontrol.outfile.txt/" + eventId};
+    if(jobState === undefined || jobState === "ACTIVE" || jobState === "QUEUED") {
+        requestData.url = url + "/bin/cpm/core/jobcontrol.job.json/" + eventId;
+    } 
+
+    request.get(requestData, (error, response, body) => {
+        var statusCodeLine = (response === undefined) ? "" : "Response: " + response.statusCode + " : " + response.statusMessage;
+        logger.debug(statusCodeLine);
+
+        if(error) {
+            logger.error(error);
+        } else if(body) {
+            if(body.trim().startsWith("{")) {
+                var json = JSON.parse(body);
+                logger.debug('Response:', JSON.stringify(json, undefined, '   '));
+                if(json["jobState"]) {
+                    setTimeout(()=>{
+                        getJobOutput(url, username, password, eventId, callback, json["jobState"]);
+                    }, 100);
+                    return;
+                }
+            } 
+            
+            logger.log(body.trim());
+        } else if (response && response.statusCode != 200) {
+            if(callback) {
+                callback('Package manager job service failed. '+statusCodeLine, undefined);
+            } else {
+                logger.error('Package manager job service failed.', statusCodeLine);
+                process.exit(1);
+            }
+        }
+
+        if(callback) {
+            callback(error, body); 
+        } 
+
+    }).auth(username, password);
+}
+
 module.exports = {
     checkService,
     list,
@@ -213,6 +300,7 @@ module.exports = {
     deletePackage,
     installPackage,
     uninstallPackage,
+    buildPackage,
     getName
 }