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

kwin pushed a commit to branch feature/SLING-8291_expose-error
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-installer-provider-installhook.git

commit f813390c44954337b5acf5e9a80fc61d883cb22f
Author: Georg Henzler <[email protected]>
AuthorDate: Fri Jul 27 17:56:18 2018 +0200

    SLING-7790 Initial implementation
---
 .gitignore                                         |   5 +
 README.md                                          |   9 +
 pom.xml                                            | 102 ++++++
 .../provider/installhook/OsgiInstallerHook.java    | 354 +++++++++++++++++++++
 .../installhook/OsigInstallerListener.java         |  87 +++++
 5 files changed, 557 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8b0fe0c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+/target/
+.project
+.classpath
+.settings/
+.DS_Store
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..fdde553
--- /dev/null
+++ b/README.md
@@ -0,0 +1,9 @@
+[<img 
src="http://sling.apache.org/res/logos/sling.png"/>](http://sling.apache.org)
+
+ [![Build 
Status](https://builds.apache.org/buildStatus/icon?job=org.apache.sling.installer.provider.installhook-1.8)](https://builds.apache.org/view/S-Z/view/Sling/job/org.apache.sling.installer.provider.installhook-1.8)
 [![Test 
Status](https://img.shields.io/jenkins/t/https/builds.apache.org/view/S-Z/view/Sling/job/org.apache.sling.installer.provider.installhook-1.8.svg)](https://builds.apache.org/view/S-Z/view/Sling/job/org.apache.sling.installer.provider.installhook-1.8/test_results_
 [...]
+
+# Apache Sling JCR Installer Install Hook
+
+This module is part of the [Apache Sling](https://sling.apache.org) project.
+
+Allows to synchronously install bundles and configurations as contained in a 
vault package. 
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..820c8bb
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0";
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+       <modelVersion>4.0.0</modelVersion>
+
+       <parent>
+               <groupId>org.apache.sling</groupId>
+               <artifactId>sling</artifactId>
+               <version>30</version>
+               <relativePath />
+       </parent>
+
+       <artifactId>org.apache.sling.installer.provider.installhook</artifactId>
+       <version>1.0.0-SNAPSHOT</version>
+       <packaging>jar</packaging>
+
+       <name>Sling Installer Vault Package Install Hook</name>
+       <description>
+        Can be used in packages to ensure installation of bundles/configs 
during package installation (circumventing )
+    </description>
+
+       <properties>
+               <jackrabbit.version>2.10.0</jackrabbit.version>
+               <sling.java.version>7</sling.java.version>
+               <filevault.version>3.1.18</filevault.version>
+               <jackrabbit.version>2.10.1</jackrabbit.version>
+       </properties>
+
+       <build>
+               <plugins>
+                       <plugin>
+                               <artifactId>maven-jar-plugin</artifactId>
+                               <configuration>
+                                       <archive>
+                                               <manifest>
+                                                       
<mainClass>org.apache.sling.installer.provider.installhook.OsgiInstallerHook</mainClass>
+                                               </manifest>
+                                       </archive>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+
+       <dependencies>
+               <dependency>
+                       <groupId>org.slf4j</groupId>
+                       <artifactId>slf4j-api</artifactId>
+               </dependency>
+       
+               <dependency>
+                       <groupId>org.apache.jackrabbit.vault</groupId>
+                       <artifactId>org.apache.jackrabbit.vault</artifactId>
+                       <version>${filevault.version}</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.apache.jackrabbit</groupId>
+                       <artifactId>jackrabbit-jcr-commons</artifactId>
+                       <version>${jackrabbit.version}</version>
+               </dependency>
+
+               <dependency>
+                       <groupId>javax.jcr</groupId>
+                       <artifactId>jcr</artifactId>
+                       <scope>provided</scope>
+               </dependency>
+               <dependency>
+                       <groupId>commons-lang</groupId>
+                       <artifactId>commons-lang</artifactId>
+                       <version>2.5</version>
+                       <scope>provided</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.apache.sling</groupId>
+                       <artifactId>org.apache.sling.installer.core</artifactId>
+                       <version>3.6.8</version>
+                       <scope>provided</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.osgi</groupId>
+                       <artifactId>osgi.core</artifactId>
+                       <scope>provided</scope>
+               </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>osgi.cmpn</artifactId>
+            <scope>provided</scope>
+        </dependency>          
+               
+       </dependencies>
+
+</project>
diff --git 
a/src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerHook.java
 
b/src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerHook.java
new file mode 100644
index 0000000..de615ce
--- /dev/null
+++ 
b/src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerHook.java
@@ -0,0 +1,354 @@
+/*
+    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.
+*/
+package org.apache.sling.installer.provider.installhook;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.jar.JarInputStream;
+import java.util.jar.Manifest;
+
+import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.jackrabbit.vault.fs.api.ProgressTrackerListener;
+import org.apache.jackrabbit.vault.fs.io.Archive;
+import org.apache.jackrabbit.vault.fs.io.Archive.Entry;
+import org.apache.jackrabbit.vault.fs.io.ImportOptions;
+import org.apache.jackrabbit.vault.packaging.InstallContext;
+import org.apache.jackrabbit.vault.packaging.InstallHook;
+import org.apache.jackrabbit.vault.packaging.PackageException;
+import org.apache.jackrabbit.vault.packaging.VaultPackage;
+import org.apache.sling.installer.api.InstallableResource;
+import org.apache.sling.installer.api.OsgiInstaller;
+import org.apache.sling.installer.api.event.InstallationListener;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class OsgiInstallerHook implements InstallHook {
+
+       private static final Logger LOG = 
LoggerFactory.getLogger(OsgiInstallerHook.class);
+
+       private static final String PACKAGE_PROPERTY_MAX_WAIT_IN_SEC = 
"maxWaitForOsgiInstallerInSec";
+       private static final int DEFAULT_MAX_WAIT_IN_SEC = 120;
+
+       private static final String MANIFEST_BUNDLE_SYMBOLIC_NAME = 
"Bundle-SymbolicName";
+       private static final String MANIFEST_BUNDLE_VERSION = "Bundle-Version";
+
+       private static final String JCR_CONTENT = "jcr:content";
+       private static final String JCR_CONTENT_DATA = JCR_CONTENT + 
"/jcr:data";
+       private static final String JCR_LAST_MODIFIED = "jcr:lastModified";
+       private static final String JCR_CONTENT_LAST_MODIFIED = JCR_CONTENT + 
"/" + JCR_LAST_MODIFIED;
+
+       public static final String URL_SCHEME = "jcrinstall";
+       public static final String CONFIG_SUFFIX = ".config";
+
+       private InstallHookLogger logger = new InstallHookLogger();
+
+       @Override
+       public void execute(InstallContext context) throws PackageException {
+
+               ServiceReference<OsgiInstaller> osgiInstallerServiceRef = null;
+               ServiceReference<ConfigurationAdmin> configAdminServiceRef = 
null;
+               ServiceRegistration<InstallationListener> 
hookInstallationListenerServiceRegistration = null;
+               try {
+                       switch (context.getPhase()) {
+                       case INSTALLED:
+                               ImportOptions options = context.getOptions();
+                               logger.setOptions(options);
+                               VaultPackage vaultPackage = 
context.getPackage();
+
+                               logger.log(getClass().getSimpleName() + " is 
active in " + vaultPackage.getId());
+
+                               List<BundleInPackage> bundleResources = new 
ArrayList<>();
+                               List<String> configResourcePaths = new 
ArrayList<>();
+                               Archive archive = vaultPackage.getArchive();
+                               collectResources(archive, archive.getRoot(), 
"", bundleResources, configResourcePaths);
+
+                               logger.log("Bundles in package " + 
bundleResources);
+
+                               Map<String, String> bundleVersionsBySymbolicId 
= new HashMap<>();
+                               for (Bundle bundle : 
getBundleContext().getBundles()) {
+                                       
bundleVersionsBySymbolicId.put(bundle.getSymbolicName(), 
bundle.getVersion().toString());
+                               }
+
+                               Session session = context.getSession();
+
+                               List<InstallableResource> installableResources 
= new ArrayList<>();
+
+                               Set<String> bundleSymbolicNamesToInstall = 
getBundlesToInstall(bundleResources,
+                                               bundleVersionsBySymbolicId, 
session, installableResources);
+
+                               configAdminServiceRef = 
getBundleContext().getServiceReference(ConfigurationAdmin.class);
+                               ConfigurationAdmin confAdmin = 
(ConfigurationAdmin) getBundleContext()
+                                               
.getService(configAdminServiceRef);
+
+                               Set<String> configPidsToInstall = 
getConfigPidsToInstall(configResourcePaths, session,
+                                               installableResources, 
confAdmin);
+
+                               if (installableResources.isEmpty()) {
+                                       logger.log("No installable resources 
that are not installed yet found.");
+                                       return;
+                               }
+
+                               logger.log("Installing " + 
bundleSymbolicNamesToInstall.size() + " bundles and "
+                                               + configPidsToInstall.size() + 
" configs");
+                               osgiInstallerServiceRef = 
getBundleContext().getServiceReference(OsgiInstaller.class);
+                               OsgiInstaller osgiInstaller = 
getBundleContext().getService(osgiInstallerServiceRef);
+
+                               OsigInstallerListener hookInstallationListener 
= new OsigInstallerListener(bundleSymbolicNamesToInstall,
+                                               configPidsToInstall);
+                               hookInstallationListenerServiceRegistration = 
getBundleContext()
+                                               
.registerService(InstallationListener.class, hookInstallationListener, null);
+
+                               logger.log("Update resources " + 
installableResources);
+                               osgiInstaller.updateResources(URL_SCHEME,
+                                               
installableResources.toArray(new 
InstallableResource[installableResources.size()]), null);
+
+                               String maxWaitForOsgiInstallerInSecStr = 
vaultPackage.getProperties()
+                                               
.getProperty(PACKAGE_PROPERTY_MAX_WAIT_IN_SEC);
+                               int maxWaitForOsgiInstallerInSec = 
maxWaitForOsgiInstallerInSecStr != null
+                                               ? 
Integer.parseInt(maxWaitForOsgiInstallerInSecStr)
+                                               : DEFAULT_MAX_WAIT_IN_SEC;
+
+                               long startTime = System.currentTimeMillis();
+                               while (!hookInstallationListener.isDone()) {
+                                       if ((System.currentTimeMillis() - 
startTime) > maxWaitForOsgiInstallerInSec * 1000) {
+                                               logger.log("Installable 
resources " + installableResources
+                                                               + " could not 
be installed even after waiting " + maxWaitForOsgiInstallerInSec + "sec");
+                                               break;
+                                       }
+                                       logger.log("Waiting for " + 
installableResources.size() + " to be installed");
+                                       Thread.sleep(1000);
+                               }
+
+                               break;
+                       default:
+                               break;
+                       }
+               } catch (Exception e) {
+                       throw new PackageException("Could not execute install 
hook to apply env vars: " + e, e);
+               } finally {
+                       if (osgiInstallerServiceRef != null) {
+                               
getBundleContext().ungetService(osgiInstallerServiceRef);
+                       }
+                       if (configAdminServiceRef != null) {
+                               
getBundleContext().ungetService(configAdminServiceRef);
+                       }
+
+                       if (hookInstallationListenerServiceRegistration != 
null) {
+                               
hookInstallationListenerServiceRegistration.unregister();
+                       }
+               }
+       }
+
+       private Set<String> getConfigPidsToInstall(List<String> 
configResourcePaths, Session session,
+                       List<InstallableResource> installableResources, 
ConfigurationAdmin confAdmin)
+                       throws IOException, InvalidSyntaxException, 
PathNotFoundException, RepositoryException {
+               Set<String> configIdsToInstall = new HashSet<>();
+               for (String configResourcePath : configResourcePaths) {
+                       boolean needsInstallation = false;
+                       String configIdToInstall = StringUtils
+                                       
.substringBefore(StringUtils.substringAfterLast(configResourcePath, "/"), 
CONFIG_SUFFIX);
+                       if (!configIdToInstall.contains("-")) {
+                               // non-factory configs
+                               Configuration[] activeConfigs = 
confAdmin.listConfigurations("(service.pid=" + configIdToInstall + ")");
+                               if (activeConfigs == null) {
+                                       logger.log("Config PID " + 
configIdToInstall + " requires installation");
+
+                                       needsInstallation = true;
+                               }
+                       } else {
+                               // non-factory configs
+                               String factoryPid = 
StringUtils.substringBefore(configIdToInstall, "-");
+                               Configuration[] activeConfigs = 
confAdmin.listConfigurations("(service.factoryPid=" + factoryPid + ")");
+                               if (activeConfigs == null) {
+                                       logger.log("There is not a single 
config for factory PID " + factoryPid + " in system, "
+                                                       + configIdToInstall + " 
requires installation");
+                                       needsInstallation = true;
+                               }
+                       }
+
+                       if (needsInstallation) {
+                               Node node = session.getNode(configResourcePath);
+                               InstallableResource installableResource = 
convert(node, configResourcePath);
+                               installableResources.add(installableResource);
+                               configIdsToInstall.add(configIdToInstall);
+                       }
+               }
+               return configIdsToInstall;
+       }
+
+       private Set<String> getBundlesToInstall(List<BundleInPackage> 
bundleResources,
+                       Map<String, String> bundleVersionsBySymbolicId, Session 
session,
+                       List<InstallableResource> installableResources)
+                       throws PathNotFoundException, RepositoryException, 
IOException {
+               Set<String> bundleSymbolicNamesToInstall = new HashSet<>();
+               Iterator<BundleInPackage> bundlesIt = 
bundleResources.iterator();
+               while (bundlesIt.hasNext()) {
+                       BundleInPackage bundle = bundlesIt.next();
+
+                       String currentlyActiveBundleVersion = 
bundleVersionsBySymbolicId.get(bundle.symbolicName);
+                       boolean needsInstallation = false;
+                       if (currentlyActiveBundleVersion == null) {
+                               logger.log("Bundle " + bundle.symbolicName + " 
is not installed");
+                               needsInstallation = true;
+                       } else if 
(!currentlyActiveBundleVersion.equals(bundle.version)) {
+                               logger.log("Bundle " + bundle.symbolicName + " 
is installed with version "
+                                               + currentlyActiveBundleVersion 
+ " but package contains version " + bundle.version);
+                               needsInstallation = true;
+                       } else {
+                               logger.log("Bundle " + bundle.symbolicName + " 
is already installed with version "
+                                               + currentlyActiveBundleVersion);
+                       }
+                       if (needsInstallation) {
+                               logger.log("Bundle " + bundle.symbolicName + " 
requires installation");
+                               Node node = session.getNode(bundle.path);
+                               InstallableResource installableResource = 
convert(node, bundle.path);
+                               installableResources.add(installableResource);
+                               
bundleSymbolicNamesToInstall.add(bundle.symbolicName);
+                       }
+               }
+               return bundleSymbolicNamesToInstall;
+       }
+
+       private void collectResources(Archive archive, Entry entry, String 
dirPath, List<BundleInPackage> bundleResources,
+                       List<String> configResources) {
+               String entryName = entry.getName();
+
+               if (entryName.endsWith(".jar") && dirPath.contains("/install")) 
{
+
+                       try (InputStream entryInputStream = 
archive.getInputSource(entry).getByteStream();
+                                       JarInputStream jarInputStream = new 
JarInputStream(entryInputStream)) {
+                               Manifest manifest = 
jarInputStream.getManifest();
+                               String symbolicName = 
manifest.getMainAttributes().getValue(MANIFEST_BUNDLE_SYMBOLIC_NAME);
+                               String version = 
manifest.getMainAttributes().getValue(MANIFEST_BUNDLE_VERSION);
+                               String bundlePath = 
StringUtils.substringAfter(dirPath + entryName, "/jcr_root");
+                               bundleResources.add(new 
BundleInPackage(bundlePath, symbolicName, version));
+                       } catch (Exception e) {
+                               throw new IllegalStateException(
+                                               "Could not read symbolic name 
and version from manifest of bundle " + entryName);
+                       }
+               }
+
+               if (entryName.endsWith(CONFIG_SUFFIX) && 
dirPath.contains("/config")) {
+                       String configPath = StringUtils.substringAfter(dirPath 
+ entryName, "/jcr_root");
+                       configResources.add(configPath);
+               }
+
+               for (Entry child : entry.getChildren()) {
+                       collectResources(archive, child, dirPath + entryName + 
"/", bundleResources, configResources);
+               }
+       }
+
+       private InstallableResource convert(final Node node, final String path) 
throws IOException, RepositoryException {
+               logger.log("Converting " + node + " at path " + path);
+               final String digest = 
String.valueOf(node.getProperty(JCR_CONTENT_LAST_MODIFIED).getDate().getTimeInMillis());
+               final InputStream is = 
node.getProperty(JCR_CONTENT_DATA).getStream();
+               final Dictionary<String, Object> dict = new Hashtable<String, 
Object>();
+               dict.put(InstallableResource.INSTALLATION_HINT, 
node.getParent().getName());
+               return new InstallableResource(path, is, dict, digest, null, 
null);
+       }
+
+       // always get fresh bundle context to avoid "Dynamic class loader has 
already
+       // been deactivated" exceptions
+       public BundleContext getBundleContext() {
+               // use the vault bundle to hook into the OSGi world
+               Bundle currentBundle = 
FrameworkUtil.getBundle(InstallHook.class);
+               if (currentBundle == null) {
+                       throw new IllegalStateException(
+                                       "The class " + InstallHook.class + " 
was not loaded through a bundle classloader");
+               }
+
+               BundleContext bundleContext = currentBundle.getBundleContext();
+               if (bundleContext == null) {
+                       throw new IllegalStateException("Could not get bundle 
context for bundle " + currentBundle);
+               }
+               return bundleContext;
+       }
+
+       class BundleInPackage {
+               final String path;
+               final String symbolicName;
+               final String version;
+
+               public BundleInPackage(String path, String symbolicName, String 
version) {
+                       super();
+                       this.path = path;
+                       this.symbolicName = symbolicName;
+                       this.version = version;
+               }
+
+               @Override
+               public String toString() {
+                       return "BundleInPackage [path=" + path + ", 
symbolicId=" + symbolicName + ", version=" + version + "]";
+               }
+
+       }
+
+       class InstallHookLogger {
+
+               private ImportOptions options;
+
+               public void setOptions(ImportOptions options) {
+                       this.options = options;
+               }
+
+               public void logError(Logger logger, String message, Throwable 
throwable) {
+                       ProgressTrackerListener listener = 
options.getListener();
+                       if (listener != null) {
+                               
listener.onMessage(ProgressTrackerListener.Mode.TEXT, "ERROR: " + message, "");
+                       }
+                       logger.error(message, throwable);
+               }
+
+               public void log(String message) {
+                       log(LOG, message);
+               }
+
+               public void log(Logger logger, String message) {
+                       ProgressTrackerListener listener = 
options.getListener();
+                       if (listener != null) {
+                               
listener.onMessage(ProgressTrackerListener.Mode.TEXT, message, "");
+                               logger.debug(message);
+                       } else {
+                               logger.info(message);
+                       }
+               }
+       }
+}
diff --git 
a/src/main/java/org/apache/sling/installer/provider/installhook/OsigInstallerListener.java
 
b/src/main/java/org/apache/sling/installer/provider/installhook/OsigInstallerListener.java
new file mode 100644
index 0000000..8cb83e7
--- /dev/null
+++ 
b/src/main/java/org/apache/sling/installer/provider/installhook/OsigInstallerListener.java
@@ -0,0 +1,87 @@
+/*
+    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.
+*/
+package org.apache.sling.installer.provider.installhook;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.sling.installer.api.event.InstallationEvent;
+import org.apache.sling.installer.api.event.InstallationEvent.TYPE;
+import org.apache.sling.installer.api.event.InstallationListener;
+import org.apache.sling.installer.api.tasks.TaskResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class OsigInstallerListener implements InstallationListener {
+
+       private static final Logger LOG = 
LoggerFactory.getLogger(OsigInstallerListener.class);
+
+       private static final String ENTITY_ID_PREFIX_BUNDLE = "bundle:";
+       private static final String ENTITY_ID_PREFIX_CONFIG = "config:";
+
+       private final Set<String> requiredBundleSymbolicNames;
+       private final Set<String> requiredConfigPids;
+       private final Set<String> installedBundleSymbolicNames = new 
HashSet<>();
+       private final Set<String> installedConfigPids = new HashSet<>();
+
+       public OsigInstallerListener(Set<String> requiredBundleSymbolicNames, 
Set<String> requiredConfigPids) {
+               this.requiredBundleSymbolicNames = requiredBundleSymbolicNames;
+               this.requiredConfigPids = requiredConfigPids;
+       }
+
+       @Override
+       public void onEvent(InstallationEvent installationEvent) {
+               if (installationEvent.getType() == TYPE.PROCESSED) {
+                       Object sourceRaw = installationEvent.getSource();
+                       if (!(sourceRaw instanceof TaskResource)) {
+                               throw new IllegalStateException("Expected 
source of type " + TaskResource.class.getName());
+                       }
+                       TaskResource source = (TaskResource) sourceRaw;
+                       String entityId = source.getEntityId();
+
+                       LOG.debug("Received event about processed entityId {}", 
entityId);
+
+                       if (entityId.startsWith(ENTITY_ID_PREFIX_BUNDLE)) {
+                               String installedBundleSymbolicName = 
StringUtils.substringAfter(entityId, ENTITY_ID_PREFIX_BUNDLE);
+                               
installedBundleSymbolicNames.add(installedBundleSymbolicName);
+                       } else if 
(entityId.startsWith(ENTITY_ID_PREFIX_CONFIG)) {
+                               String installedConfigPid = 
StringUtils.substringAfter(entityId, ENTITY_ID_PREFIX_CONFIG);
+                               installedConfigPids.add(installedConfigPid);
+                       }
+               }
+       }
+
+       public boolean isDone() {
+               LOG.trace("requiredBundleSymbolicNames: {}", 
requiredBundleSymbolicNames);
+               LOG.trace("installedBundleSymbolicNames: {}", 
installedBundleSymbolicNames);
+               HashSet<String> bundlesLeftToInstall = new 
HashSet<String>(requiredBundleSymbolicNames);
+               bundlesLeftToInstall.removeAll(installedBundleSymbolicNames);
+               LOG.debug("bundlesLeftToInstall: {}", bundlesLeftToInstall);
+
+               LOG.trace("requiredConfigPids: {}", requiredConfigPids);
+               LOG.trace("installedConfigPids: {}", installedConfigPids);
+               HashSet<String> configsLeftToInstall = new 
HashSet<String>(requiredConfigPids);
+               requiredConfigPids.removeAll(installedConfigPids);
+               LOG.debug("configsLeftToInstall: {}", configsLeftToInstall);
+
+               return bundlesLeftToInstall.isEmpty() && 
configsLeftToInstall.isEmpty();
+       }
+
+}

Reply via email to