You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by kw...@apache.org on 2019/02/25 12:43:11 UTC

[sling-org-apache-sling-installer-provider-installhook] branch feature/SLING-8291_expose-error updated (b0b2841 -> 2fe294d)

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

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


 discard b0b2841  SLING-8291 expose error message as well for existing bundle which is not yet in INSTALLED state
     new 2fe294d  SLING-8291 expose error message as well for existing bundle which is not yet in INSTALLED state

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (b0b2841)
            \
             N -- N -- N   refs/heads/feature/SLING-8291_expose-error (2fe294d)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 24 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:
 .../apache/sling/installer/provider/installhook/OsgiInstallerHook.java  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)


[sling-org-apache-sling-installer-provider-installhook] 01/24: SLING-7790 Initial implementation

Posted by kw...@apache.org.
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 <ge...@netcentric.biz>
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();
+	}
+
+}


[sling-org-apache-sling-installer-provider-installhook] 21/24: [maven-release-plugin] prepare for next development iteration

Posted by kw...@apache.org.
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 5dd3ab3b2726d12fd952fd5fb8c03299daff143b
Author: georg.henzler <ge...@netcentric.biz>
AuthorDate: Fri Nov 9 13:27:17 2018 +0100

    [maven-release-plugin] prepare for next development iteration
---
 pom.xml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/pom.xml b/pom.xml
index 6ccac11..4b62962 100644
--- a/pom.xml
+++ b/pom.xml
@@ -20,7 +20,7 @@
     </parent>
 
     <artifactId>org.apache.sling.installer.provider.installhook</artifactId>
-    <version>1.0.4</version>
+    <version>1.0.5-SNAPSHOT</version>
     <packaging>bundle</packaging>
 
     <name>Sling Installer Vault Package Install Hook</name>
@@ -41,7 +41,7 @@
         <connection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-installer-provider-installhook.git</connection>
         <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-installer-provider-installhook.git</developerConnection>
         <url>https://gitbox.apache.org/repos/asf?p=sling-org-apache-sling-installer-provider-installhook.git</url>
-        <tag>org.apache.sling.installer.provider.installhook-1.0.4</tag>
+        <tag>HEAD</tag>
     </scm>
 
     <build>


[sling-org-apache-sling-installer-provider-installhook] 08/24: trivial: added license header to *.md files

Posted by kw...@apache.org.
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 deb3cc1e2798c242bcec7ad6b560bc1f8560a7ff
Author: Radu Cotescu <co...@adobe.com>
AuthorDate: Thu Sep 20 11:09:04 2018 +0200

    trivial: added license header to *.md files
---
 CODE_OF_CONDUCT.md | 18 ++++++++++++++++++
 CONTRIBUTING.md    | 18 ++++++++++++++++++
 2 files changed, 36 insertions(+)

diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 52f21cb..0fa18e5 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/-->
 Apache Software Foundation Code of Conduct
 ====
 
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index ca36072..ac82a1a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/-->
 Contributing
 ====
 


[sling-org-apache-sling-installer-provider-installhook] 04/24: SLING-7790 JUnit Tests

Posted by kw...@apache.org.
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 a373a922b7fa773bf3c2c188eb9c514b3319d173
Author: Georg Henzler <ge...@netcentric.biz>
AuthorDate: Wed Aug 22 20:25:14 2018 +0200

    SLING-7790 JUnit Tests
---
 pom.xml                                            |  19 ++-
 .../provider/installhook/OsgiInstallerHook.java    |  61 +++++-----
 .../installhook/OsgiInstallerListener.java         |   6 +-
 .../installhook/OsgiInstallerHookTest.java         | 129 +++++++++++++++++++++
 .../installhook/OsgiInstallerListenerTest.java     |  72 ++++++++++++
 5 files changed, 249 insertions(+), 38 deletions(-)

diff --git a/pom.xml b/pom.xml
index 533af9b..7dc69c3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -102,7 +102,22 @@
             <artifactId>osgi.cmpn</artifactId>
             <scope>provided</scope>
         </dependency>		
-		
-	</dependencies>
+
+        <!-- Basic dependencies for Unit Tests -->
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <version>2.13.0</version>
+            <scope>test</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
index 362ae6e..a8d910b 100644
--- a/src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerHook.java
+++ b/src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerHook.java
@@ -34,7 +34,6 @@ 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;
 
@@ -68,26 +67,27 @@ 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 PACKAGE_PROP_INSTALL_PATH_REGEX = "installPathRegex";
 
-	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 int DEFAULT_PRIORITY_INSTALL_HOOK = 2000;
+	private static final int DEFAULT_MAX_WAIT_IN_SEC = 60;
 
 	public static final String URL_SCHEME = "jcrinstall";
 	public static final String CONFIG_SUFFIX = ".config";
 	public static final String JAR_SUFFIX = ".jar";
 
-	public static final String PACKAGE_PROP_INSTALL_PATH_REGEX = "installPathRegex";
+	private static final String MANIFEST_BUNDLE_SYMBOLIC_NAME = "Bundle-SymbolicName";
+	private static final String MANIFEST_BUNDLE_VERSION = "Bundle-Version";
+	private static final String FOLDER_META_INF = "META-INF";
+
+	static final String JCR_CONTENT = "jcr:content";
+	static final String JCR_CONTENT_DATA = JCR_CONTENT + "/jcr:data";
+	static final String JCR_LAST_MODIFIED = "jcr:lastModified";
+	static final String JCR_CONTENT_LAST_MODIFIED = JCR_CONTENT + "/" + JCR_LAST_MODIFIED;
 
 	public static final String DOT = ".";
-	public static final int PRIORITY_INSTALL_HOOK = 2000;
 
-	private InstallHookLogger logger = new InstallHookLogger();
+	InstallHookLogger logger = new InstallHookLogger();
 
 	@Override
 	public void execute(InstallContext context) throws PackageException {
@@ -210,7 +210,7 @@ public class OsgiInstallerHook implements InstallHook {
 
 	private Set<String> getConfigPidsToInstall(List<String> configResourcePaths, Session session,
 			List<InstallableResource> installableResources, ConfigurationAdmin confAdmin)
-			throws IOException, InvalidSyntaxException, PathNotFoundException, RepositoryException {
+			throws IOException, InvalidSyntaxException, RepositoryException {
 		Set<String> configIdsToInstall = new HashSet<>();
 		for (String configResourcePath : configResourcePaths) {
 			boolean needsInstallation = false;
@@ -247,8 +247,7 @@ public class OsgiInstallerHook implements InstallHook {
 
 	private Set<String> getBundlesToInstall(List<BundleInPackage> bundleResources,
 			Map<String, String> bundleVersionsBySymbolicId, Session session,
-			List<InstallableResource> installableResources)
-			throws PathNotFoundException, RepositoryException, IOException {
+			List<InstallableResource> installableResources) throws RepositoryException, IOException {
 		Set<String> bundleSymbolicNamesToInstall = new HashSet<>();
 		Iterator<BundleInPackage> bundlesIt = bundleResources.iterator();
 		while (bundlesIt.hasNext()) {
@@ -278,37 +277,35 @@ public class OsgiInstallerHook implements InstallHook {
 		return bundleSymbolicNamesToInstall;
 	}
 
-	private void collectResources(Archive archive, Entry entry, String dirPath, List<BundleInPackage> bundleResources,
+	void collectResources(Archive archive, Entry entry, String dirPath, List<BundleInPackage> bundleResources,
 			List<String> configResources, String installPathRegex, Set<String> actualRunmodes) {
 		String entryName = entry.getName();
-		if(entryName.equals("META-INF")) {
+		if (entryName.equals(FOLDER_META_INF)) {
 			return;
-		} 
+		}
 
 		String dirPathWithoutJcrRoot = StringUtils.substringAfter(dirPath, "/jcr_root");
 		String entryPath = dirPathWithoutJcrRoot + entryName;
 		String dirPathWithoutSlash = StringUtils.chomp(dirPathWithoutJcrRoot, "/");
 
-		String dirPathWithoutRunmodes;
 		boolean runmodesMatch;
 		if (dirPathWithoutSlash.contains(DOT)) {
-			String[] bits = dirPathWithoutSlash.split("\\"+DOT, 2);
-			dirPathWithoutRunmodes = bits[0];
-			List<String> runmodesOfResource = Arrays.asList(bits[1].split("\\"+DOT));
+			String[] bits = dirPathWithoutSlash.split("\\" + DOT, 2);
+			List<String> runmodesOfResource = Arrays.asList(bits[1].split("\\" + DOT));
 			Set<String> matchingRunmodes = new HashSet<String>(runmodesOfResource);
 			matchingRunmodes.retainAll(actualRunmodes);
-			LOG.debug("Entry with runmode(s): entryPath={} runmodesOfResource={} actualRunmodes={} matchingRunmodes={}", entryPath, runmodesOfResource, actualRunmodes, matchingRunmodes);
+			LOG.debug("Entry with runmode(s): entryPath={} runmodesOfResource={} actualRunmodes={} matchingRunmodes={}",
+					entryPath, runmodesOfResource, actualRunmodes, matchingRunmodes);
 			runmodesMatch = matchingRunmodes.size() == runmodesOfResource.size();
 			if (!runmodesMatch) {
 				logger.log("Skipping installation of  " + entryPath
 						+ " because the path is not matching all actual runmodes " + actualRunmodes);
 			}
 		} else {
-			dirPathWithoutRunmodes = dirPathWithoutSlash;
 			runmodesMatch = true;
 		}
 
-		if (dirPathWithoutRunmodes.matches(installPathRegex) && runmodesMatch) {
+		if (entryPath.matches(installPathRegex) && runmodesMatch) {
 
 			if (entryName.endsWith(CONFIG_SUFFIX)) {
 				configResources.add(entryPath);
@@ -334,18 +331,18 @@ public class OsgiInstallerHook implements InstallHook {
 		}
 	}
 
-	private InstallableResource convert(final Node node, final String path) throws IOException, RepositoryException {
+	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, PRIORITY_INSTALL_HOOK);
+		return new InstallableResource(path, is, dict, digest, null, DEFAULT_PRIORITY_INSTALL_HOOK);
 	}
 
 	// always get fresh bundle context to avoid "Dynamic class loader has already
 	// been deactivated" exceptions
-	public BundleContext getBundleContext() {
+	private BundleContext getBundleContext() {
 		// use the vault bundle to hook into the OSGi world
 		Bundle currentBundle = FrameworkUtil.getBundle(InstallHook.class);
 		if (currentBundle == null) {
@@ -378,16 +375,15 @@ public class OsgiInstallerHook implements InstallHook {
 
 	}
 
-	class InstallHookLogger {
+	static class InstallHookLogger {
 
-		private ImportOptions options;
+		private ProgressTrackerListener listener;
 
 		public void setOptions(ImportOptions options) {
-			this.options = options;
+			this.listener = options.getListener();
 		}
 
 		public void logError(Logger logger, String message, Throwable throwable) {
-			ProgressTrackerListener listener = options.getListener();
 			if (listener != null) {
 				listener.onMessage(ProgressTrackerListener.Mode.TEXT, "ERROR: " + message, "");
 			}
@@ -399,7 +395,6 @@ public class OsgiInstallerHook implements InstallHook {
 		}
 
 		public void log(Logger logger, String message) {
-			ProgressTrackerListener listener = options.getListener();
 			if (listener != null) {
 				listener.onMessage(ProgressTrackerListener.Mode.TEXT, message, "");
 				logger.debug(message);
diff --git a/src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerListener.java b/src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerListener.java
index ae15a24..c0b1ace 100644
--- a/src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerListener.java
+++ b/src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerListener.java
@@ -33,8 +33,8 @@ public class OsgiInstallerListener implements InstallationListener {
 
 	private static final Logger LOG = LoggerFactory.getLogger(OsgiInstallerListener.class);
 
-	private static final String ENTITY_ID_PREFIX_BUNDLE = "bundle:";
-	private static final String ENTITY_ID_PREFIX_CONFIG = "config:";
+	static final String ENTITY_ID_PREFIX_BUNDLE = "bundle:";
+	static final String ENTITY_ID_PREFIX_CONFIG = "config:";
 
 	private final Set<String> requiredBundleSymbolicNames;
 	private final Set<String> requiredConfigPids;
@@ -78,7 +78,7 @@ public class OsgiInstallerListener implements InstallationListener {
 		LOG.trace("requiredConfigPids: {}", requiredConfigPids);
 		LOG.trace("installedConfigPids: {}", installedConfigPids);
 		HashSet<String> configsLeftToInstall = new HashSet<String>(requiredConfigPids);
-		requiredConfigPids.removeAll(installedConfigPids);
+		configsLeftToInstall.removeAll(installedConfigPids);
 		LOG.debug("configsLeftToInstall: {}", configsLeftToInstall);
 
 		return bundlesLeftToInstall.isEmpty() && configsLeftToInstall.isEmpty();
diff --git a/src/test/java/org/apache/sling/installer/provider/installhook/OsgiInstallerHookTest.java b/src/test/java/org/apache/sling/installer/provider/installhook/OsgiInstallerHookTest.java
new file mode 100644
index 0000000..7d347d1
--- /dev/null
+++ b/src/test/java/org/apache/sling/installer/provider/installhook/OsgiInstallerHookTest.java
@@ -0,0 +1,129 @@
+/*
+    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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.HashSet;
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.vault.fs.io.Archive;
+import org.apache.jackrabbit.vault.fs.io.Archive.Entry;
+import org.apache.sling.installer.api.InstallableResource;
+import org.apache.sling.installer.provider.installhook.OsgiInstallerHook.BundleInPackage;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class OsgiInstallerHookTest {
+
+	OsgiInstallerHook osgiInstallerHook;
+	
+	@Mock
+	Node node;
+
+	@Mock
+	Node parentNode;
+	
+	@Mock
+	Property lastModifiedProperty;
+	
+	@Mock
+	Property contentDataProperty;	
+
+	@Mock
+	Archive archive;	
+	
+	@Mock
+	Entry entry;	
+	
+	Calendar now;
+
+	@Before
+	public void setup() throws RepositoryException  {
+		osgiInstallerHook = new OsgiInstallerHook();
+		
+		now = Calendar.getInstance();
+		when(node.getProperty(OsgiInstallerHook.JCR_CONTENT_LAST_MODIFIED)).thenReturn(lastModifiedProperty);
+		when(lastModifiedProperty.getDate()).thenReturn(now);
+		when(node.getProperty(OsgiInstallerHook.JCR_CONTENT_DATA)).thenReturn(contentDataProperty);
+		when(node.getParent()).thenReturn(parentNode);
+		
+	}
+	
+	@Test
+	public void testConvert() throws IOException, RepositoryException {
+		
+		File pathToTest = new File("/apps/myproj/install/mybundle.jar");
+		when(parentNode.getName()).thenReturn(pathToTest.getParentFile().getName());
+		
+		InstallableResource installableResource = osgiInstallerHook.convert(node, pathToTest.getAbsolutePath());
+
+		assertEquals(String.valueOf(now.getTimeInMillis()), installableResource.getDigest());
+		assertEquals(pathToTest.getParentFile().getName(), installableResource.getDictionary().get(InstallableResource.INSTALLATION_HINT));
+		
+	}
+	
+	public void testCollectResources() throws IOException, RepositoryException {
+		
+		File pathToTest = new File("/apps/myproj/install.author/myconfig.config");
+		String dirPath = "/jcr_root" + pathToTest.getParentFile().getPath() + "/";
+
+		when(entry.getName()).thenReturn(pathToTest.getName());
+		
+		List<BundleInPackage> bundleResources = new ArrayList<BundleInPackage>();
+		List<String> configResources = new ArrayList<String>();
+
+		osgiInstallerHook.collectResources(archive, entry, dirPath, bundleResources, configResources, 
+				"/apps/other.*", new HashSet<String>(Arrays.asList("author", "dev")));
+				
+		assertTrue(bundleResources.isEmpty());
+		assertTrue(configResources.isEmpty());
+
+		osgiInstallerHook.collectResources(archive, entry, dirPath, bundleResources, configResources, 
+				"/apps/myproj.*", new HashSet<String>(Arrays.asList("publish", "dev")));
+				
+		assertTrue(bundleResources.isEmpty());
+		assertTrue(configResources.isEmpty());
+		
+		
+		osgiInstallerHook.collectResources(archive, entry, dirPath, bundleResources, configResources, 
+				"/apps/myproj.*", new HashSet<String>(Arrays.asList("author", "dev")));
+		
+		assertTrue(bundleResources.isEmpty());
+		assertEquals(1, configResources.size());
+		assertEquals(pathToTest.getAbsolutePath(), configResources.get(0));
+		
+	}
+
+}
diff --git a/src/test/java/org/apache/sling/installer/provider/installhook/OsgiInstallerListenerTest.java b/src/test/java/org/apache/sling/installer/provider/installhook/OsgiInstallerListenerTest.java
new file mode 100644
index 0000000..6cd02ea
--- /dev/null
+++ b/src/test/java/org/apache/sling/installer/provider/installhook/OsgiInstallerListenerTest.java
@@ -0,0 +1,72 @@
+/*
+    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 static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.sling.installer.api.event.InstallationEvent;
+import org.apache.sling.installer.api.event.InstallationEvent.TYPE;
+import org.apache.sling.installer.api.tasks.TaskResource;
+import org.junit.Test;
+
+public class OsgiInstallerListenerTest {
+
+	private OsgiInstallerListener osgiInstallerListener;
+	
+	@Test
+	public void testOsgiInstallerListener() {
+		
+		String bundleSymbolicId1 = "org.prj.bundle1";
+		String bundleSymbolicId2 = "org.prj.bundle2";
+		Set<String> requiredBundleSymbolicNames = new HashSet<String>(Arrays.asList(bundleSymbolicId1, bundleSymbolicId2));
+		String configPid1 = "org.prj.config1";
+		String configPid2 = "org.prj.config2";
+		Set<String> requiredConfigPids = new HashSet<String>(Arrays.asList(configPid1, configPid2));
+
+		osgiInstallerListener = new OsgiInstallerListener(requiredBundleSymbolicNames, requiredConfigPids);
+
+		assertFalse(osgiInstallerListener.isDone());
+		osgiInstallerListener.onEvent(getInstallationEventMock(OsgiInstallerListener.ENTITY_ID_PREFIX_BUNDLE + bundleSymbolicId1));
+		assertFalse(osgiInstallerListener.isDone());
+		osgiInstallerListener.onEvent(getInstallationEventMock(OsgiInstallerListener.ENTITY_ID_PREFIX_BUNDLE + bundleSymbolicId2));
+		assertFalse(osgiInstallerListener.isDone());
+		osgiInstallerListener.onEvent(getInstallationEventMock(OsgiInstallerListener.ENTITY_ID_PREFIX_CONFIG + configPid1));
+		assertFalse(osgiInstallerListener.isDone());
+		osgiInstallerListener.onEvent(getInstallationEventMock(OsgiInstallerListener.ENTITY_ID_PREFIX_CONFIG + configPid2));
+		assertTrue(osgiInstallerListener.isDone());
+		
+	}
+	
+	public InstallationEvent getInstallationEventMock(String entityId) {
+		InstallationEvent event = mock(InstallationEvent.class);
+		when(event.getType()).thenReturn(TYPE.PROCESSED);
+		TaskResource taskResource = mock(TaskResource.class);
+		when(event.getSource()).thenReturn(taskResource);
+		when(taskResource.getEntityId()).thenReturn(entityId);
+		return event;
+	}
+
+}


[sling-org-apache-sling-installer-provider-installhook] 20/24: [maven-release-plugin] prepare release org.apache.sling.installer.provider.installhook-1.0.4

Posted by kw...@apache.org.
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 6802d06476ccd14786bef9eae866a778991de60b
Author: georg.henzler <ge...@netcentric.biz>
AuthorDate: Fri Nov 9 13:26:58 2018 +0100

    [maven-release-plugin] prepare release org.apache.sling.installer.provider.installhook-1.0.4
---
 pom.xml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/pom.xml b/pom.xml
index deea689..6ccac11 100644
--- a/pom.xml
+++ b/pom.xml
@@ -20,7 +20,7 @@
     </parent>
 
     <artifactId>org.apache.sling.installer.provider.installhook</artifactId>
-    <version>1.0.3-SNAPSHOT</version>
+    <version>1.0.4</version>
     <packaging>bundle</packaging>
 
     <name>Sling Installer Vault Package Install Hook</name>
@@ -41,7 +41,7 @@
         <connection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-installer-provider-installhook.git</connection>
         <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-installer-provider-installhook.git</developerConnection>
         <url>https://gitbox.apache.org/repos/asf?p=sling-org-apache-sling-installer-provider-installhook.git</url>
-        <tag>HEAD</tag>
+        <tag>org.apache.sling.installer.provider.installhook-1.0.4</tag>
     </scm>
 
     <build>


[sling-org-apache-sling-installer-provider-installhook] 24/24: SLING-8291 expose error message as well for existing bundle which is not yet in INSTALLED state

Posted by kw...@apache.org.
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 2fe294dc2bd80e3778d716c785c324da4872301d
Author: Konrad Windszus <kw...@apache.org>
AuthorDate: Mon Feb 25 13:38:09 2019 +0100

    SLING-8291 expose error message as well for existing bundle which is not
    yet in INSTALLED state
---
 pom.xml                                                           | 2 +-
 .../sling/installer/provider/installhook/OsgiInstallerHook.java   | 8 +++++++-
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/pom.xml b/pom.xml
index 4b62962..ac7d8fe 100644
--- a/pom.xml
+++ b/pom.xml
@@ -90,7 +90,7 @@
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.installer.core</artifactId>
-            <version>3.6.8</version>
+            <version>3.8.0</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
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
index 6ca0eec..92d0a7b 100644
--- a/src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerHook.java
+++ b/src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerHook.java
@@ -20,6 +20,7 @@ package org.apache.sling.installer.provider.installhook;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Dictionary;
@@ -267,7 +268,12 @@ public class OsgiInstallerHook implements InstallHook {
                                 + currentlyActiveBundleVersion + " that matches " + bundle.version + " as provided in package");
                     }
                 } else {
-                    logger.log("Bundle " + bundle.symbolicName + " is not in state INSTALLED but in " + resource.getState());
+                    String msg = MessageFormat.format("Bundle {0} is not in state INSTALLED but in {1}", bundle.symbolicName, resource.getState());
+                    if (StringUtils.isNotEmpty(resource.getError())) {
+                        // related error if there is some
+                        msg += " due to error '" + resource.getError() + "'";
+                    }
+                    logger.log(msg);
                     needsInstallation = true;
                 }
 


[sling-org-apache-sling-installer-provider-installhook] 07/24: SLING-7215 - [nice-to-have] Add a CONTRIBUTING file to every module

Posted by kw...@apache.org.
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 4ff223342be49b850be663eb427a58f285eb92d3
Author: Radu Cotescu <co...@adobe.com>
AuthorDate: Wed Sep 19 18:29:18 2018 +0200

    SLING-7215 - [nice-to-have] Add a CONTRIBUTING file to every module
---
 CONTRIBUTING.md | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..ca36072
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,6 @@
+Contributing
+====
+
+Thanks for choosing to contribute!
+
+You will find all the necessary details about how you can do this at https://sling.apache.org/contributing.html.


[sling-org-apache-sling-installer-provider-installhook] 16/24: Merge pull request #1 from apache/bugfix/SLING-8083-avoid-exceptions-in-log-and-use-installation-state

Posted by kw...@apache.org.
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 887d30fcba1099e82a41d02e0dd9485e2dd1539c
Merge: 48b76a4 b6e00c2
Author: Georg Henzler <gh...@users.noreply.github.com>
AuthorDate: Fri Nov 9 10:38:33 2018 +0100

    Merge pull request #1 from apache/bugfix/SLING-8083-avoid-exceptions-in-log-and-use-installation-state
    
    SLING-8083 avoid ClassNotFoundExceptions, take infoProvider.getInstallationState().getInstalledResources() into account

 .../provider/installhook/OsgiInstallerHook.java    | 18 +++++++++---
 .../installhook/OsgiInstallerListener.java         | 34 +++++++++++++++++-----
 2 files changed, 41 insertions(+), 11 deletions(-)


[sling-org-apache-sling-installer-provider-installhook] 05/24: SLING-7790 Allow to reference install hook via package property (in case the install hook is installed as bundle). Improved description and cleaned up formatting

Posted by kw...@apache.org.
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 833558f84c788163e5f1a69754cc1ccd3ddd75c9
Author: Georg Henzler <ge...@netcentric.biz>
AuthorDate: Thu Aug 30 01:16:13 2018 +0200

    SLING-7790 Allow to reference install hook via package property
     (in case the install hook is installed as bundle). Improved
     description and cleaned up formatting
---
 pom.xml                                          | 173 ++++++++++++-----------
 src/main/appended-resources/META-INF/MANIFEST.MF |   1 +
 2 files changed, 88 insertions(+), 86 deletions(-)

diff --git a/pom.xml b/pom.xml
index 7dc69c3..f7c7411 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,107 +1,108 @@
 <?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>
+    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>
+    <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>
+    <artifactId>org.apache.sling.installer.provider.installhook</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+    <packaging>bundle</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 )
+    <name>Sling Installer Vault Package Install Hook</name>
+    <description>
+        Can be used in packages to ensure installation of bundles/configs is triggered during package 
+        installation and is finished once the package manager returns (circumventing the asynchronicity 
+        of org.apache.sling.installer.provider.jcr)
     </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>
+    <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>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <_include>src/main/appended-resources/META-INF/MANIFEST.MF</_include>
+                    </instructions>
+                </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>
+    <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>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.apache.sling</groupId>
             <artifactId>org.apache.sling.settings</artifactId>
             <version>1.0.0</version>
             <scope>provided</scope>
-        </dependency>		
-		<dependency>
-			<groupId>org.osgi</groupId>
-			<artifactId>osgi.core</artifactId>
-			<scope>provided</scope>
-		</dependency>
+        </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>		
+        </dependency>
 
         <!-- Basic dependencies for Unit Tests -->
         <dependency>
@@ -118,6 +119,6 @@
             <version>2.13.0</version>
             <scope>test</scope>
         </dependency>
-    </dependencies>		
+    </dependencies>
 
 </project>
diff --git a/src/main/appended-resources/META-INF/MANIFEST.MF b/src/main/appended-resources/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..4889165
--- /dev/null
+++ b/src/main/appended-resources/META-INF/MANIFEST.MF
@@ -0,0 +1 @@
+Main-Class: org.apache.sling.installer.provider.installhook.OsgiInstallerHook


[sling-org-apache-sling-installer-provider-installhook] 09/24: SLING-7790 use of InfoProvider, additional package properties for configuration

Posted by kw...@apache.org.
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 9a95ab7d4f7325e64f792c5cea4bf6445d4f0c43
Author: georg.henzler <ge...@netcentric.biz>
AuthorDate: Thu Oct 4 23:59:10 2018 +0200

    SLING-7790 use of InfoProvider, additional package properties for
    configuration
---
 .../provider/installhook/OsgiInstallerHook.java    | 784 ++++++++++++---------
 .../installhook/OsgiInstallerListener.java         |  91 +--
 .../installhook/OsgiInstallerHookTest.java         | 159 +++--
 .../installhook/OsgiInstallerListenerTest.java     |  87 ++-
 4 files changed, 625 insertions(+), 496 deletions(-)

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
index a8d910b..fd27716 100644
--- a/src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerHook.java
+++ b/src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerHook.java
@@ -50,357 +50,467 @@ 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.apache.sling.installer.api.info.InfoProvider;
+import org.apache.sling.installer.api.info.InstallationState;
+import org.apache.sling.installer.api.info.Resource;
+import org.apache.sling.installer.api.info.ResourceGroup;
+import org.apache.sling.installer.api.tasks.ResourceState;
 import org.apache.sling.settings.SlingSettingsService;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleListener;
 import org.osgi.framework.FrameworkUtil;
 import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
 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 Logger LOG = LoggerFactory.getLogger(OsgiInstallerHook.class);
 
-	private static final String PACKAGE_PROPERTY_MAX_WAIT_IN_SEC = "maxWaitForOsgiInstallerInSec";
-	private static final String PACKAGE_PROP_INSTALL_PATH_REGEX = "installPathRegex";
+    private static final String PACKAGE_PROP_INSTALL_PATH_REGEX = "installPathRegex";
+    private static final String PACKAGE_PROPERTY_MAX_WAIT_IN_SEC = "maxWaitForOsgiInstallerInSec";
+    private static final String PACKAGE_PROPERTY_INSTALL_PRIORITY = "osgiInstallerPriority";
+    private static final String PACKAGE_PROPERTY_WAIT_FOR_OSGI_EVENTS_QUIET_IN_SEC = "waitForOsgiEventsQuietInSec";
 
-	public static final int DEFAULT_PRIORITY_INSTALL_HOOK = 2000;
-	private static final int DEFAULT_MAX_WAIT_IN_SEC = 60;
+    public static final int DEFAULT_PRIORITY_INSTALL_HOOK = 2000;
+    private static final int DEFAULT_MAX_WAIT_IN_SEC = 60;
+    private static final int DEFAULT_WAIT_FOR_OSGI_EVENTS_QUIET_IN_SEC = 1;
 
-	public static final String URL_SCHEME = "jcrinstall";
-	public static final String CONFIG_SUFFIX = ".config";
-	public static final String JAR_SUFFIX = ".jar";
+    public static final String URL_SCHEME = "jcrinstall";
+    public static final String CONFIG_SUFFIX = ".config";
+    public static final String JAR_SUFFIX = ".jar";
+
+    private static final String ENTITY_ID_PREFIX_BUNDLE = "bundle:";
+
+    private static final String MANIFEST_BUNDLE_SYMBOLIC_NAME = "Bundle-SymbolicName";
+    private static final String MANIFEST_BUNDLE_VERSION = "Bundle-Version";
+    private static final String FOLDER_META_INF = "META-INF";
+
+    static final String JCR_CONTENT = "jcr:content";
+    static final String JCR_CONTENT_DATA = JCR_CONTENT + "/jcr:data";
+    static final String JCR_LAST_MODIFIED = "jcr:lastModified";
+    static final String JCR_CONTENT_LAST_MODIFIED = JCR_CONTENT + "/" + JCR_LAST_MODIFIED;
+
+    public static final String DOT = ".";
+
+    InstallHookLogger logger = new InstallHookLogger();
+
+    @Override
+    public void execute(InstallContext context) throws PackageException {
+
+        VaultPackage vaultPackage = context.getPackage();
+        PackageProperties packageProperties = vaultPackage.getProperties();
+        String installPathRegex = packageProperties.getProperty(PACKAGE_PROP_INSTALL_PATH_REGEX);
+
+        ServiceReference<OsgiInstaller> osgiInstallerServiceRef = null;
+        ServiceReference<SlingSettingsService> slingSettingsServiceRef = null;
+        ServiceRegistration<InstallationListener> hookInstallationListenerServiceRegistration = null;
+
+        ServiceReference<InfoProvider> infoProviderServiceRef = null;
+
+        try {
+            switch (context.getPhase()) {
+            case PREPARE:
+                if (StringUtils.isBlank(installPathRegex)) {
+                    throw new IllegalArgumentException(
+                            "When using OSGi installer install hook for synchronous installation, the package property "
+                                    + PACKAGE_PROP_INSTALL_PATH_REGEX + " has to be provided.");
+                }
+                break;
+            case INSTALLED:
+                ImportOptions options = context.getOptions();
+                logger.setOptions(options);
+
+                logger.log(getClass().getSimpleName() + " is active in " + vaultPackage.getId());
+
+                List<BundleInPackage> bundleResources = new ArrayList<>();
+                List<String> configResourcePaths = new ArrayList<>();
+                Archive archive = vaultPackage.getArchive();
+
+                infoProviderServiceRef = getBundleContext().getServiceReference(InfoProvider.class);
+                InfoProvider infoProvider = (InfoProvider) getBundleContext().getService(infoProviderServiceRef);
+                InstallationState installationState = infoProvider.getInstallationState();
+
+                slingSettingsServiceRef = getBundleContext().getServiceReference(SlingSettingsService.class);
+                SlingSettingsService slingSettingsService = (SlingSettingsService) getBundleContext().getService(slingSettingsServiceRef);
+                Set<String> runModes = slingSettingsService.getRunModes();
+
+                collectResources(archive, archive.getRoot(), "", bundleResources, configResourcePaths, installPathRegex,
+                        runModes);
+
+                logger.log("Bundles in package " + bundleResources);
+
+                Session session = context.getSession();
+
+                Map<String, InstallableResource> bundlesToInstallByUrl = getBundlesToInstall(bundleResources, session, installationState,
+                        packageProperties);
+                Map<String, InstallableResource> configsToInstallByUrl = getConfigsToInstall(configResourcePaths, session,
+                        installationState, packageProperties);
+
+                if (bundlesToInstallByUrl.isEmpty() && configsToInstallByUrl.isEmpty()) {
+                    logger.log("No installable resources that are not installed yet found.");
+                    return;
+                }
+
+                logger.log("Installing " + bundlesToInstallByUrl.size() + " bundles and "
+                        + configsToInstallByUrl.size() + " configs");
+                osgiInstallerServiceRef = getBundleContext().getServiceReference(OsgiInstaller.class);
+                OsgiInstaller osgiInstaller = getBundleContext().getService(osgiInstallerServiceRef);
+
+                OsgiInstallerListener hookInstallationListener = new OsgiInstallerListener(bundlesToInstallByUrl.keySet(),
+                        configsToInstallByUrl.keySet());
+                hookInstallationListenerServiceRegistration = getBundleContext()
+                        .registerService(InstallationListener.class, hookInstallationListener, null);
+
+                List<InstallableResource> resourcesToUpdate = new ArrayList<>();
+                resourcesToUpdate.addAll(bundlesToInstallByUrl.values());
+                resourcesToUpdate.addAll(configsToInstallByUrl.values());
+                logger.log("Updating resources " + resourcesToUpdate);
+                osgiInstaller.updateResources(URL_SCHEME, resourcesToUpdate.toArray(new InstallableResource[resourcesToUpdate.size()]),
+                        null);
+
+                int maxWaitForOsgiInstallerInSec = getNumericPackageProperty(packageProperties, PACKAGE_PROPERTY_MAX_WAIT_IN_SEC,
+                        DEFAULT_MAX_WAIT_IN_SEC);
+
+                long startTime = System.currentTimeMillis();
+                int bundlesLeftToInstall = 0;
+                int configsLeftToInstall = 0;
+                while ((bundlesLeftToInstall = hookInstallationListener.bundlesLeftToInstall()) > 0
+                        || (configsLeftToInstall = hookInstallationListener.configsLeftToInstall()) > 0) {
+                    if ((System.currentTimeMillis() - startTime) > maxWaitForOsgiInstallerInSec * 1000) {
+                        logger.log("Installable resources " + resourcesToUpdate
+                                + " could not be installed even after waiting " + maxWaitForOsgiInstallerInSec + "sec");
+                        break;
+                    }
+                    logger.log("Waiting for " + bundlesLeftToInstall + " bundles / " + configsLeftToInstall + " configs to be installed");
+                    Thread.sleep(1000);
+                }
+                if (bundlesLeftToInstall == 0 && configsLeftToInstall == 0) {
+                    logger.log("All " + bundlesToInstallByUrl.size() + " bundles / " + configsToInstallByUrl.size()
+                            + " configs have been successfully installed");
+                }
+
+                int waitForOsgiEventsQuietInSec = getNumericPackageProperty(packageProperties,
+                        PACKAGE_PROPERTY_WAIT_FOR_OSGI_EVENTS_QUIET_IN_SEC, DEFAULT_WAIT_FOR_OSGI_EVENTS_QUIET_IN_SEC);
+                waitForServiceChanges(waitForOsgiEventsQuietInSec);
+
+                break;
+            default:
+                break;
+            }
+        } catch (Exception e) {
+            throw new PackageException("Could not execute install hook to for synchronous installation: " + e, e);
+        } finally {
+            if (osgiInstallerServiceRef != null) {
+                getBundleContext().ungetService(osgiInstallerServiceRef);
+            }
+            if (slingSettingsServiceRef != null) {
+                getBundleContext().ungetService(slingSettingsServiceRef);
+            }
+            if (infoProviderServiceRef != null) {
+                getBundleContext().ungetService(infoProviderServiceRef);
+            }
+
+            if (hookInstallationListenerServiceRegistration != null) {
+                hookInstallationListenerServiceRegistration.unregister();
+            }
+        }
+    }
+
+    private int getNumericPackageProperty(PackageProperties packageProperties, String propertyName, int defaultVal) {
+        String strVal = packageProperties.getProperty(propertyName);
+        int intVal = strVal != null ? Integer.parseInt(strVal) : defaultVal;
+        return intVal;
+    }
+
+    private Map<String, InstallableResource> getBundlesToInstall(List<BundleInPackage> bundlesInPackage, Session session,
+            InstallationState installationState, PackageProperties packageProperties) throws RepositoryException, IOException {
+        Map<String, InstallableResource> installableResources = new HashMap<>();
+        Iterator<BundleInPackage> bundlesIt = bundlesInPackage.iterator();
+        while (bundlesIt.hasNext()) {
+            BundleInPackage bundle = bundlesIt.next();
+
+            List<Resource> currentInstallerBundleResources = getBundleResources(installationState, bundle.symbolicName);
+
+            boolean needsInstallation = false;
+            if (currentInstallerBundleResources.isEmpty()) {
+                needsInstallation = true;
+            } else if (currentInstallerBundleResources.size() == 1) {
+                Resource resource = currentInstallerBundleResources.get(0);
+
+                if (resource.getState() == ResourceState.INSTALLED) {
+                    String currentlyActiveBundleVersion = resource.getVersion().toString();
+                    if (!StringUtils.equals(currentlyActiveBundleVersion, 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 + " that matches " + bundle.version + " as provided in package");
+                    }
+                } else {
+                    logger.log("Bundle " + bundle.symbolicName + " is not in state INSTALLED but in " + resource.getState());
+                    needsInstallation = true;
+                }
+
+            } else {
+                logger.log("Bundle " + bundle.symbolicName + " exists with multiple installer resources");
+                boolean installedBundleResourceFound = false;
+                for (Resource resource : currentInstallerBundleResources) {
+                    logger.log("Resource " + resource);
+                    if (resource.getState() == ResourceState.INSTALLED
+                            && StringUtils.equals(resource.getVersion().toString(), bundle.version)) {
+                        installedBundleResourceFound = true;
+                    }
+                }
+                if (!installedBundleResourceFound) {
+                    needsInstallation = true;
+                }
+
+            }
+
+            if (needsInstallation) {
+                logger.log("Bundle " + bundle.symbolicName + " requires installation");
+                Node node = session.getNode(bundle.path);
+                InstallableResource installableResource = convert(node, bundle.path, packageProperties);
+                String bundleUrl = URL_SCHEME + ":" + bundle.path;
+                installableResources.put(bundleUrl, installableResource);
+            }
+        }
+        return installableResources;
+    }
+
+    private List<Resource> getBundleResources(InstallationState installationState, String symbolicId) {
+
+        List<Resource> bundleResources = new ArrayList<Resource>();
+
+        List<ResourceGroup> allGroups = new ArrayList<ResourceGroup>();
+        allGroups.addAll(installationState.getInstalledResources());
+        allGroups.addAll(installationState.getActiveResources());
+        for (ResourceGroup resourceGroup : allGroups) {
+            List<Resource> resources = resourceGroup.getResources();
+            for (Resource resource : resources) {
+                if (StringUtils.equals(resource.getEntityId(), ENTITY_ID_PREFIX_BUNDLE + symbolicId)) {
+                    bundleResources.add(resource);
+                }
+            }
+        }
+        return bundleResources;
+    }
+
+    private Map<String, InstallableResource> getConfigsToInstall(List<String> configResourcePaths, Session session,
+            InstallationState installationState, PackageProperties packageProperties)
+            throws IOException, InvalidSyntaxException, RepositoryException {
+        Map<String, InstallableResource> configsToInstallByUrl = new HashMap<>();
+        for (String configResourcePath : configResourcePaths) {
+            boolean needsInstallation = false;
+
+            String configUrl = URL_SCHEME + ":" + configResourcePath;
+            boolean configFound = false;
+            List<ResourceGroup> installedResources = installationState.getInstalledResources();
+            for (ResourceGroup resourceGroup : installedResources) {
+                for (Resource resource : resourceGroup.getResources()) {
+                    if (StringUtils.equals(configUrl, resource.getURL())) {
+                        configFound = true;
+                        logger.log("Config " + configResourcePath + " is already installed");
+                    }
+                }
+            }
+            if (!configFound) {
+                logger.log("Config " + configResourcePath + " has not been installed");
+                needsInstallation = true;
+            }
+
+            if (needsInstallation) {
+
+                Node node = session.getNode(configResourcePath);
+                InstallableResource installableResource = convert(node, configResourcePath, packageProperties);
+
+                configsToInstallByUrl.put(configUrl, installableResource);
+            }
+        }
+        return configsToInstallByUrl;
+    }
+
+    void collectResources(Archive archive, Entry entry, String dirPath, List<BundleInPackage> bundleResources,
+            List<String> configResources, String installPathRegex, Set<String> actualRunmodes) {
+        String entryName = entry.getName();
+        if (entryName.equals(FOLDER_META_INF)) {
+            return;
+        }
+
+        String dirPathWithoutJcrRoot = StringUtils.substringAfter(dirPath, "/jcr_root");
+        String entryPath = dirPathWithoutJcrRoot + entryName;
+        String dirPathWithoutSlash = StringUtils.chomp(dirPathWithoutJcrRoot, "/");
+
+        boolean runmodesMatch;
+        if (dirPathWithoutSlash.contains(DOT)) {
+            String[] bits = dirPathWithoutSlash.split("\\" + DOT, 2);
+            List<String> runmodesOfResource = Arrays.asList(bits[1].split("\\" + DOT));
+            Set<String> matchingRunmodes = new HashSet<String>(runmodesOfResource);
+            matchingRunmodes.retainAll(actualRunmodes);
+            LOG.debug("Entry with runmode(s): entryPath={} runmodesOfResource={} actualRunmodes={} matchingRunmodes={}",
+                    entryPath, runmodesOfResource, actualRunmodes, matchingRunmodes);
+            runmodesMatch = matchingRunmodes.size() == runmodesOfResource.size();
+            if (!runmodesMatch) {
+                logger.log("Skipping installation of  " + entryPath
+                        + " because the path is not matching all actual runmodes " + actualRunmodes);
+            }
+        } else {
+            runmodesMatch = true;
+        }
+
+        if (entryPath.matches(installPathRegex) && runmodesMatch) {
+
+            if (entryName.endsWith(CONFIG_SUFFIX)) {
+                configResources.add(entryPath);
+            } else if (entryName.endsWith(JAR_SUFFIX)) {
+                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);
+
+                    bundleResources.add(new BundleInPackage(entryPath, symbolicName, version));
+                } catch (Exception e) {
+                    throw new IllegalStateException(
+                            "Could not read symbolic name and version from manifest of bundle " + entryName);
+                }
+            }
+
+        }
+
+        for (Entry child : entry.getChildren()) {
+            collectResources(archive, child, dirPath + entryName + "/", bundleResources, configResources,
+                    installPathRegex, actualRunmodes);
+        }
+    }
+
+    InstallableResource convert(final Node node, final String path, PackageProperties packageProperties)
+            throws IOException, RepositoryException {
+        LOG.trace("Converting {} at path {}", node, 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());
+        int priority = getNumericPackageProperty(packageProperties, PACKAGE_PROPERTY_INSTALL_PRIORITY, DEFAULT_PRIORITY_INSTALL_HOOK);
+        return new InstallableResource(path, is, dict, digest, null, priority);
+    }
+
+    private void waitForServiceChanges(int waitForOsgiEventsQuietInSec) {
+        if (waitForOsgiEventsQuietInSec <= 0) {
+            return;
+        }
+        InstallerHookOsgiEventListener osgiListener = new InstallerHookOsgiEventListener();
+        BundleContext bundleContext = getBundleContext();
+        bundleContext.addServiceListener(osgiListener);
+        bundleContext.addBundleListener(osgiListener);
+
+        long waitStart = System.currentTimeMillis();
+        osgiListener.waitUntilQuiet(waitForOsgiEventsQuietInSec);
+        logger.log("Waited " + (System.currentTimeMillis() - waitStart) + "ms in total for OSGi events to become quiet (for at least "
+                + waitForOsgiEventsQuietInSec + "sec)");
+
+        bundleContext.removeServiceListener(osgiListener);
+        bundleContext.removeBundleListener(osgiListener);
+
+    }
+
+    // always get fresh bundle context to avoid "Dynamic class loader has already
+    // been deactivated" exceptions
+    private 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 + "]";
+        }
+
+    }
+
+    static class InstallHookLogger {
+
+        private ProgressTrackerListener listener;
+
+        public void setOptions(ImportOptions options) {
+            this.listener = options.getListener();
+        }
+
+        public void logError(Logger logger, String message, Throwable throwable) {
+            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) {
+            if (listener != null) {
+                listener.onMessage(ProgressTrackerListener.Mode.TEXT, message, "");
+                logger.debug(message);
+            } else {
+                logger.info(message);
+            }
+        }
+    }
+
+    static class InstallerHookOsgiEventListener implements ServiceListener, BundleListener {
+
+        private long lastEventTimestamp = System.currentTimeMillis();
+
+        @Override
+        public void serviceChanged(ServiceEvent event) {
+            lastEventTimestamp = System.currentTimeMillis();
+            LOG.trace("Service changed event {}", event);
+        }
+
+        @Override
+        public void bundleChanged(BundleEvent event) {
+            lastEventTimestamp = System.currentTimeMillis();
+            LOG.trace("Bundle changed event {}", event);
+        }
+
+        public void waitUntilQuiet(long waitInSec) {
+            try {
+                while (System.currentTimeMillis() - lastEventTimestamp < waitInSec * 1000) {
+                    Thread.sleep(100);
+                }
+            } catch (InterruptedException e) {
+                LOG.warn("Wait for OSGi events was interrupted");
+            }
+        }
+    }
 
-	private static final String MANIFEST_BUNDLE_SYMBOLIC_NAME = "Bundle-SymbolicName";
-	private static final String MANIFEST_BUNDLE_VERSION = "Bundle-Version";
-	private static final String FOLDER_META_INF = "META-INF";
-
-	static final String JCR_CONTENT = "jcr:content";
-	static final String JCR_CONTENT_DATA = JCR_CONTENT + "/jcr:data";
-	static final String JCR_LAST_MODIFIED = "jcr:lastModified";
-	static final String JCR_CONTENT_LAST_MODIFIED = JCR_CONTENT + "/" + JCR_LAST_MODIFIED;
-
-	public static final String DOT = ".";
-
-	InstallHookLogger logger = new InstallHookLogger();
-
-	@Override
-	public void execute(InstallContext context) throws PackageException {
-
-		VaultPackage vaultPackage = context.getPackage();
-		PackageProperties packageProperties = vaultPackage.getProperties();
-		String installPathRegex = packageProperties.getProperty(PACKAGE_PROP_INSTALL_PATH_REGEX);
-
-		ServiceReference<OsgiInstaller> osgiInstallerServiceRef = null;
-		ServiceReference<ConfigurationAdmin> configAdminServiceRef = null;
-		ServiceReference<SlingSettingsService> slingSettingsServiceRef = null;
-		ServiceRegistration<InstallationListener> hookInstallationListenerServiceRegistration = null;
-
-		try {
-			switch (context.getPhase()) {
-			case PREPARE:
-				if (StringUtils.isBlank(installPathRegex)) {
-					throw new IllegalArgumentException(
-							"When using OSGi installer install hook for synchronous installation, the package property "
-									+ PACKAGE_PROP_INSTALL_PATH_REGEX + " has to be provided.");
-				}
-				break;
-			case INSTALLED:
-				ImportOptions options = context.getOptions();
-				logger.setOptions(options);
-
-				logger.log(getClass().getSimpleName() + " is active in " + vaultPackage.getId());
-
-				List<BundleInPackage> bundleResources = new ArrayList<>();
-				List<String> configResourcePaths = new ArrayList<>();
-				Archive archive = vaultPackage.getArchive();
-
-				configAdminServiceRef = getBundleContext().getServiceReference(ConfigurationAdmin.class);
-				ConfigurationAdmin confAdmin = (ConfigurationAdmin) getBundleContext()
-						.getService(configAdminServiceRef);
-
-				slingSettingsServiceRef = getBundleContext().getServiceReference(SlingSettingsService.class);
-				SlingSettingsService slingSettingsService = (SlingSettingsService) getBundleContext()
-						.getService(slingSettingsServiceRef);
-				Set<String> runModes = slingSettingsService.getRunModes();
-
-				collectResources(archive, archive.getRoot(), "", bundleResources, configResourcePaths, installPathRegex,
-						runModes);
-
-				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);
-
-				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);
-
-				OsgiInstallerListener hookInstallationListener = new OsgiInstallerListener(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 = packageProperties
-						.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 for synchronous installation: " + e, e);
-		} finally {
-			if (osgiInstallerServiceRef != null) {
-				getBundleContext().ungetService(osgiInstallerServiceRef);
-			}
-			if (configAdminServiceRef != null) {
-				getBundleContext().ungetService(configAdminServiceRef);
-			}
-			if (slingSettingsServiceRef != null) {
-				getBundleContext().ungetService(slingSettingsServiceRef);
-			}
-
-			if (hookInstallationListenerServiceRegistration != null) {
-				hookInstallationListenerServiceRegistration.unregister();
-			}
-		}
-	}
-
-	private Set<String> getConfigPidsToInstall(List<String> configResourcePaths, Session session,
-			List<InstallableResource> installableResources, ConfigurationAdmin confAdmin)
-			throws IOException, InvalidSyntaxException, 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 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;
-	}
-
-	void collectResources(Archive archive, Entry entry, String dirPath, List<BundleInPackage> bundleResources,
-			List<String> configResources, String installPathRegex, Set<String> actualRunmodes) {
-		String entryName = entry.getName();
-		if (entryName.equals(FOLDER_META_INF)) {
-			return;
-		}
-
-		String dirPathWithoutJcrRoot = StringUtils.substringAfter(dirPath, "/jcr_root");
-		String entryPath = dirPathWithoutJcrRoot + entryName;
-		String dirPathWithoutSlash = StringUtils.chomp(dirPathWithoutJcrRoot, "/");
-
-		boolean runmodesMatch;
-		if (dirPathWithoutSlash.contains(DOT)) {
-			String[] bits = dirPathWithoutSlash.split("\\" + DOT, 2);
-			List<String> runmodesOfResource = Arrays.asList(bits[1].split("\\" + DOT));
-			Set<String> matchingRunmodes = new HashSet<String>(runmodesOfResource);
-			matchingRunmodes.retainAll(actualRunmodes);
-			LOG.debug("Entry with runmode(s): entryPath={} runmodesOfResource={} actualRunmodes={} matchingRunmodes={}",
-					entryPath, runmodesOfResource, actualRunmodes, matchingRunmodes);
-			runmodesMatch = matchingRunmodes.size() == runmodesOfResource.size();
-			if (!runmodesMatch) {
-				logger.log("Skipping installation of  " + entryPath
-						+ " because the path is not matching all actual runmodes " + actualRunmodes);
-			}
-		} else {
-			runmodesMatch = true;
-		}
-
-		if (entryPath.matches(installPathRegex) && runmodesMatch) {
-
-			if (entryName.endsWith(CONFIG_SUFFIX)) {
-				configResources.add(entryPath);
-			} else if (entryName.endsWith(JAR_SUFFIX)) {
-				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);
-
-					bundleResources.add(new BundleInPackage(entryPath, symbolicName, version));
-				} catch (Exception e) {
-					throw new IllegalStateException(
-							"Could not read symbolic name and version from manifest of bundle " + entryName);
-				}
-			}
-
-		}
-
-		for (Entry child : entry.getChildren()) {
-			collectResources(archive, child, dirPath + entryName + "/", bundleResources, configResources,
-					installPathRegex, actualRunmodes);
-		}
-	}
-
-	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, DEFAULT_PRIORITY_INSTALL_HOOK);
-	}
-
-	// always get fresh bundle context to avoid "Dynamic class loader has already
-	// been deactivated" exceptions
-	private 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 + "]";
-		}
-
-	}
-
-	static class InstallHookLogger {
-
-		private ProgressTrackerListener listener;
-
-		public void setOptions(ImportOptions options) {
-			this.listener = options.getListener();
-		}
-
-		public void logError(Logger logger, String message, Throwable throwable) {
-			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) {
-			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/OsgiInstallerListener.java b/src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerListener.java
index c0b1ace..4dd6b5a 100644
--- a/src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerListener.java
+++ b/src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerListener.java
@@ -18,10 +18,10 @@
 */
 package org.apache.sling.installer.provider.installhook;
 
+import java.util.Collections;
 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;
@@ -31,57 +31,60 @@ import org.slf4j.LoggerFactory;
 
 public class OsgiInstallerListener implements InstallationListener {
 
-	private static final Logger LOG = LoggerFactory.getLogger(OsgiInstallerListener.class);
+    private static final Logger LOG = LoggerFactory.getLogger(OsgiInstallerListener.class);
 
-	static final String ENTITY_ID_PREFIX_BUNDLE = "bundle:";
-	static final String ENTITY_ID_PREFIX_CONFIG = "config:";
+    private final Set<String> initialBundleUrlsToInstall;
+    private final Set<String> initialConfigUrlsToInstall;
 
-	private final Set<String> requiredBundleSymbolicNames;
-	private final Set<String> requiredConfigPids;
-	private final Set<String> installedBundleSymbolicNames = new HashSet<>();
-	private final Set<String> installedConfigPids = new HashSet<>();
+    private final Set<String> bundleUrlsToInstall;
+    private final Set<String> configUrlsToInstall;
 
-	public OsgiInstallerListener(Set<String> requiredBundleSymbolicNames, Set<String> requiredConfigPids) {
-		this.requiredBundleSymbolicNames = requiredBundleSymbolicNames;
-		this.requiredConfigPids = requiredConfigPids;
-	}
+    public OsgiInstallerListener(Set<String> bundleUrlsToInstall, Set<String> configUrlsToInstall) {
+        this.initialBundleUrlsToInstall = bundleUrlsToInstall;
+        this.initialConfigUrlsToInstall = configUrlsToInstall;
 
-	@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();
+        this.bundleUrlsToInstall = Collections.synchronizedSet(new HashSet<>(initialBundleUrlsToInstall));
+        this.configUrlsToInstall = Collections.synchronizedSet(new HashSet<>(initialConfigUrlsToInstall));
+    }
 
-			LOG.debug("Received event about processed entityId {}", entityId);
+    @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();
+            String url = source.getURL();
 
-			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);
-			}
-		}
-	}
+            LOG.trace("Received event about processed entityId={} url={}", entityId, url);
 
-	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);
+            if (bundleUrlsToInstall.contains(url)) {
+                LOG.debug("Received event for bundle installed with url={}", url);
+                bundleUrlsToInstall.remove(url);
+            }
+            if (configUrlsToInstall.contains(url)) {
+                LOG.debug("Received event for config installed with url={}", url);
+                configUrlsToInstall.remove(url);
+            }
+        }
+    }
 
-		LOG.trace("requiredConfigPids: {}", requiredConfigPids);
-		LOG.trace("installedConfigPids: {}", installedConfigPids);
-		HashSet<String> configsLeftToInstall = new HashSet<String>(requiredConfigPids);
-		configsLeftToInstall.removeAll(installedConfigPids);
-		LOG.debug("configsLeftToInstall: {}", configsLeftToInstall);
+    public int bundlesLeftToInstall() {
+        if (LOG.isTraceEnabled()) {
+            LOG.trace("initialBundleUrlsToInstall: {}", initialBundleUrlsToInstall);
+            LOG.trace("bundleUrlsToInstall: {}", bundleUrlsToInstall);
+        }
+        return bundleUrlsToInstall.size();
+    }
 
-		return bundlesLeftToInstall.isEmpty() && configsLeftToInstall.isEmpty();
-	}
+    public int configsLeftToInstall() {
+        if (LOG.isTraceEnabled()) {
+            LOG.trace("initialConfigUrlsToInstall: {}", initialConfigUrlsToInstall);
+            LOG.trace("configUrlsToInstall: {}", configUrlsToInstall);
+        }
+        return configUrlsToInstall.size();
+    }
 
 }
diff --git a/src/test/java/org/apache/sling/installer/provider/installhook/OsgiInstallerHookTest.java b/src/test/java/org/apache/sling/installer/provider/installhook/OsgiInstallerHookTest.java
index 7d347d1..156fbfe 100644
--- a/src/test/java/org/apache/sling/installer/provider/installhook/OsgiInstallerHookTest.java
+++ b/src/test/java/org/apache/sling/installer/provider/installhook/OsgiInstallerHookTest.java
@@ -36,6 +36,7 @@ import javax.jcr.RepositoryException;
 
 import org.apache.jackrabbit.vault.fs.io.Archive;
 import org.apache.jackrabbit.vault.fs.io.Archive.Entry;
+import org.apache.jackrabbit.vault.packaging.PackageProperties;
 import org.apache.sling.installer.api.InstallableResource;
 import org.apache.sling.installer.provider.installhook.OsgiInstallerHook.BundleInPackage;
 import org.junit.Before;
@@ -47,83 +48,85 @@ import org.mockito.junit.MockitoJUnitRunner;
 @RunWith(MockitoJUnitRunner.class)
 public class OsgiInstallerHookTest {
 
-	OsgiInstallerHook osgiInstallerHook;
-	
-	@Mock
-	Node node;
-
-	@Mock
-	Node parentNode;
-	
-	@Mock
-	Property lastModifiedProperty;
-	
-	@Mock
-	Property contentDataProperty;	
-
-	@Mock
-	Archive archive;	
-	
-	@Mock
-	Entry entry;	
-	
-	Calendar now;
-
-	@Before
-	public void setup() throws RepositoryException  {
-		osgiInstallerHook = new OsgiInstallerHook();
-		
-		now = Calendar.getInstance();
-		when(node.getProperty(OsgiInstallerHook.JCR_CONTENT_LAST_MODIFIED)).thenReturn(lastModifiedProperty);
-		when(lastModifiedProperty.getDate()).thenReturn(now);
-		when(node.getProperty(OsgiInstallerHook.JCR_CONTENT_DATA)).thenReturn(contentDataProperty);
-		when(node.getParent()).thenReturn(parentNode);
-		
-	}
-	
-	@Test
-	public void testConvert() throws IOException, RepositoryException {
-		
-		File pathToTest = new File("/apps/myproj/install/mybundle.jar");
-		when(parentNode.getName()).thenReturn(pathToTest.getParentFile().getName());
-		
-		InstallableResource installableResource = osgiInstallerHook.convert(node, pathToTest.getAbsolutePath());
-
-		assertEquals(String.valueOf(now.getTimeInMillis()), installableResource.getDigest());
-		assertEquals(pathToTest.getParentFile().getName(), installableResource.getDictionary().get(InstallableResource.INSTALLATION_HINT));
-		
-	}
-	
-	public void testCollectResources() throws IOException, RepositoryException {
-		
-		File pathToTest = new File("/apps/myproj/install.author/myconfig.config");
-		String dirPath = "/jcr_root" + pathToTest.getParentFile().getPath() + "/";
-
-		when(entry.getName()).thenReturn(pathToTest.getName());
-		
-		List<BundleInPackage> bundleResources = new ArrayList<BundleInPackage>();
-		List<String> configResources = new ArrayList<String>();
-
-		osgiInstallerHook.collectResources(archive, entry, dirPath, bundleResources, configResources, 
-				"/apps/other.*", new HashSet<String>(Arrays.asList("author", "dev")));
-				
-		assertTrue(bundleResources.isEmpty());
-		assertTrue(configResources.isEmpty());
-
-		osgiInstallerHook.collectResources(archive, entry, dirPath, bundleResources, configResources, 
-				"/apps/myproj.*", new HashSet<String>(Arrays.asList("publish", "dev")));
-				
-		assertTrue(bundleResources.isEmpty());
-		assertTrue(configResources.isEmpty());
-		
-		
-		osgiInstallerHook.collectResources(archive, entry, dirPath, bundleResources, configResources, 
-				"/apps/myproj.*", new HashSet<String>(Arrays.asList("author", "dev")));
-		
-		assertTrue(bundleResources.isEmpty());
-		assertEquals(1, configResources.size());
-		assertEquals(pathToTest.getAbsolutePath(), configResources.get(0));
-		
-	}
+    OsgiInstallerHook osgiInstallerHook;
+
+    @Mock
+    Node node;
+
+    @Mock
+    Node parentNode;
+
+    @Mock
+    Property lastModifiedProperty;
+
+    @Mock
+    Property contentDataProperty;
+
+    @Mock
+    Archive archive;
+
+    @Mock
+    PackageProperties packageProperties;
+
+    @Mock
+    Entry entry;
+
+    Calendar now;
+
+    @Before
+    public void setup() throws RepositoryException {
+        osgiInstallerHook = new OsgiInstallerHook();
+
+        now = Calendar.getInstance();
+        when(node.getProperty(OsgiInstallerHook.JCR_CONTENT_LAST_MODIFIED)).thenReturn(lastModifiedProperty);
+        when(lastModifiedProperty.getDate()).thenReturn(now);
+        when(node.getProperty(OsgiInstallerHook.JCR_CONTENT_DATA)).thenReturn(contentDataProperty);
+        when(node.getParent()).thenReturn(parentNode);
+
+    }
+
+    @Test
+    public void testConvert() throws IOException, RepositoryException {
+
+        File pathToTest = new File("/apps/myproj/install/mybundle.jar");
+        when(parentNode.getName()).thenReturn(pathToTest.getParentFile().getName());
+
+        InstallableResource installableResource = osgiInstallerHook.convert(node, pathToTest.getAbsolutePath(), packageProperties);
+
+        assertEquals(String.valueOf(now.getTimeInMillis()), installableResource.getDigest());
+        assertEquals(pathToTest.getParentFile().getName(), installableResource.getDictionary().get(InstallableResource.INSTALLATION_HINT));
+
+    }
+
+    public void testCollectResources() throws IOException, RepositoryException {
+
+        File pathToTest = new File("/apps/myproj/install.author/myconfig.config");
+        String dirPath = "/jcr_root" + pathToTest.getParentFile().getPath() + "/";
+
+        when(entry.getName()).thenReturn(pathToTest.getName());
+
+        List<BundleInPackage> bundleResources = new ArrayList<BundleInPackage>();
+        List<String> configResources = new ArrayList<String>();
+
+        osgiInstallerHook.collectResources(archive, entry, dirPath, bundleResources, configResources,
+                "/apps/other.*", new HashSet<String>(Arrays.asList("author", "dev")));
+
+        assertTrue(bundleResources.isEmpty());
+        assertTrue(configResources.isEmpty());
+
+        osgiInstallerHook.collectResources(archive, entry, dirPath, bundleResources, configResources,
+                "/apps/myproj.*", new HashSet<String>(Arrays.asList("publish", "dev")));
+
+        assertTrue(bundleResources.isEmpty());
+        assertTrue(configResources.isEmpty());
+
+        osgiInstallerHook.collectResources(archive, entry, dirPath, bundleResources, configResources,
+                "/apps/myproj.*", new HashSet<String>(Arrays.asList("author", "dev")));
+
+        assertTrue(bundleResources.isEmpty());
+        assertEquals(1, configResources.size());
+        assertEquals(pathToTest.getAbsolutePath(), configResources.get(0));
+
+    }
 
 }
diff --git a/src/test/java/org/apache/sling/installer/provider/installhook/OsgiInstallerListenerTest.java b/src/test/java/org/apache/sling/installer/provider/installhook/OsgiInstallerListenerTest.java
index 6cd02ea..90c755b 100644
--- a/src/test/java/org/apache/sling/installer/provider/installhook/OsgiInstallerListenerTest.java
+++ b/src/test/java/org/apache/sling/installer/provider/installhook/OsgiInstallerListenerTest.java
@@ -18,12 +18,10 @@
 */
 package org.apache.sling.installer.provider.installhook;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
-import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -34,39 +32,54 @@ import org.junit.Test;
 
 public class OsgiInstallerListenerTest {
 
-	private OsgiInstallerListener osgiInstallerListener;
-	
-	@Test
-	public void testOsgiInstallerListener() {
-		
-		String bundleSymbolicId1 = "org.prj.bundle1";
-		String bundleSymbolicId2 = "org.prj.bundle2";
-		Set<String> requiredBundleSymbolicNames = new HashSet<String>(Arrays.asList(bundleSymbolicId1, bundleSymbolicId2));
-		String configPid1 = "org.prj.config1";
-		String configPid2 = "org.prj.config2";
-		Set<String> requiredConfigPids = new HashSet<String>(Arrays.asList(configPid1, configPid2));
-
-		osgiInstallerListener = new OsgiInstallerListener(requiredBundleSymbolicNames, requiredConfigPids);
-
-		assertFalse(osgiInstallerListener.isDone());
-		osgiInstallerListener.onEvent(getInstallationEventMock(OsgiInstallerListener.ENTITY_ID_PREFIX_BUNDLE + bundleSymbolicId1));
-		assertFalse(osgiInstallerListener.isDone());
-		osgiInstallerListener.onEvent(getInstallationEventMock(OsgiInstallerListener.ENTITY_ID_PREFIX_BUNDLE + bundleSymbolicId2));
-		assertFalse(osgiInstallerListener.isDone());
-		osgiInstallerListener.onEvent(getInstallationEventMock(OsgiInstallerListener.ENTITY_ID_PREFIX_CONFIG + configPid1));
-		assertFalse(osgiInstallerListener.isDone());
-		osgiInstallerListener.onEvent(getInstallationEventMock(OsgiInstallerListener.ENTITY_ID_PREFIX_CONFIG + configPid2));
-		assertTrue(osgiInstallerListener.isDone());
-		
-	}
-	
-	public InstallationEvent getInstallationEventMock(String entityId) {
-		InstallationEvent event = mock(InstallationEvent.class);
-		when(event.getType()).thenReturn(TYPE.PROCESSED);
-		TaskResource taskResource = mock(TaskResource.class);
-		when(event.getSource()).thenReturn(taskResource);
-		when(taskResource.getEntityId()).thenReturn(entityId);
-		return event;
-	}
+    private OsgiInstallerListener osgiInstallerListener;
+
+    @Test
+    public void testOsgiInstallerListener() {
+
+        Set<String> bundleUrlsToInstall = new HashSet<String>();
+        String bundleUrl1 = "jcrinstall:/apps/myproj/install/mybundle1.jar";
+        bundleUrlsToInstall.add(bundleUrl1);
+        String bundleUrl2 = "jcrinstall:/apps/myproj/install/mybundle2.jar";
+        bundleUrlsToInstall.add(bundleUrl2);
+
+        Set<String> configUrlsToInstall = new HashSet<String>();
+        String configUrl1 = "jcrinstall:/apps/myproj/config/conf1.config";
+        configUrlsToInstall.add(configUrl1);
+        String configUrl2 = "jcrinstall:/apps/myproj/config/conf2.config";
+        configUrlsToInstall.add(configUrl2);
+
+        osgiInstallerListener = new OsgiInstallerListener(bundleUrlsToInstall, configUrlsToInstall);
+
+        assertEquals(2, osgiInstallerListener.bundlesLeftToInstall());
+        assertEquals(2, osgiInstallerListener.configsLeftToInstall());
+
+        osgiInstallerListener.onEvent(getInstallationEventMock(bundleUrl1));
+        assertEquals(1, osgiInstallerListener.bundlesLeftToInstall());
+        assertEquals(2, osgiInstallerListener.configsLeftToInstall());
+
+        osgiInstallerListener.onEvent(getInstallationEventMock(bundleUrl2));
+        assertEquals(0, osgiInstallerListener.bundlesLeftToInstall());
+        assertEquals(2, osgiInstallerListener.configsLeftToInstall());
+
+        osgiInstallerListener.onEvent(getInstallationEventMock(configUrl1));
+        assertEquals(0, osgiInstallerListener.bundlesLeftToInstall());
+        assertEquals(1, osgiInstallerListener.configsLeftToInstall());
+
+        osgiInstallerListener.onEvent(getInstallationEventMock(configUrl2));
+        assertEquals(0, osgiInstallerListener.bundlesLeftToInstall());
+        assertEquals(0, osgiInstallerListener.configsLeftToInstall());
+
+    }
+
+    public InstallationEvent getInstallationEventMock(String url) {
+        InstallationEvent event = mock(InstallationEvent.class);
+        when(event.getType()).thenReturn(TYPE.PROCESSED);
+        TaskResource taskResource = mock(TaskResource.class);
+        when(event.getSource()).thenReturn(taskResource);
+        when(taskResource.getURL()).thenReturn(url);
+        when(taskResource.getEntityId()).thenReturn("dummyEntityId");
+        return event;
+    }
 
 }


[sling-org-apache-sling-installer-provider-installhook] 12/24: SLING-7790 bumped version

Posted by kw...@apache.org.
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 d573c1b5d23bd75a40b7ae23a7125a448c22e601
Author: georg.henzler <ge...@netcentric.biz>
AuthorDate: Fri Oct 12 01:35:25 2018 +0200

    SLING-7790 bumped version
---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index 762c3b2..48f35d1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -20,7 +20,7 @@
     </parent>
 
     <artifactId>org.apache.sling.installer.provider.installhook</artifactId>
-    <version>1.0.0-SNAPSHOT</version>
+    <version>1.0.2-SNAPSHOT</version>
     <packaging>bundle</packaging>
 
     <name>Sling Installer Vault Package Install Hook</name>


[sling-org-apache-sling-installer-provider-installhook] 17/24: Updated documentation link

Posted by kw...@apache.org.
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 cf19d1524365b18453b913fa04403c394a02ce03
Author: georg.henzler <ge...@netcentric.biz>
AuthorDate: Fri Nov 9 10:40:20 2018 +0100

    Updated documentation link
---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index fdde553..ad4b2e1 100644
--- a/README.md
+++ b/README.md
@@ -6,4 +6,4 @@
 
 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. 
+Allows to synchronously install bundles and configurations as contained in a vault package. See [https://sling.apache.org/documentation/bundles/installer-provider-installhook.html) for details.
\ No newline at end of file


[sling-org-apache-sling-installer-provider-installhook] 02/24: SLING-7790 Fixed typo

Posted by kw...@apache.org.
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 4dce3e98bfce71cc49bb5a53dd9102ef29dc7807
Author: Georg Henzler <ge...@netcentric.biz>
AuthorDate: Tue Jul 31 00:02:52 2018 +0200

    SLING-7790 Fixed typo
---
 .../sling/installer/provider/installhook/OsgiInstallerHook.java     | 2 +-
 .../{OsigInstallerListener.java => OsgiInstallerListener.java}      | 6 +++---
 2 files changed, 4 insertions(+), 4 deletions(-)

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
index de615ce..1245725 100644
--- a/src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerHook.java
+++ b/src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerHook.java
@@ -131,7 +131,7 @@ public class OsgiInstallerHook implements InstallHook {
 				osgiInstallerServiceRef = getBundleContext().getServiceReference(OsgiInstaller.class);
 				OsgiInstaller osgiInstaller = getBundleContext().getService(osgiInstallerServiceRef);
 
-				OsigInstallerListener hookInstallationListener = new OsigInstallerListener(bundleSymbolicNamesToInstall,
+				OsgiInstallerListener hookInstallationListener = new OsgiInstallerListener(bundleSymbolicNamesToInstall,
 						configPidsToInstall);
 				hookInstallationListenerServiceRegistration = getBundleContext()
 						.registerService(InstallationListener.class, hookInstallationListener, null);
diff --git a/src/main/java/org/apache/sling/installer/provider/installhook/OsigInstallerListener.java b/src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerListener.java
similarity index 94%
rename from src/main/java/org/apache/sling/installer/provider/installhook/OsigInstallerListener.java
rename to src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerListener.java
index 8cb83e7..ae15a24 100644
--- a/src/main/java/org/apache/sling/installer/provider/installhook/OsigInstallerListener.java
+++ b/src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerListener.java
@@ -29,9 +29,9 @@ import org.apache.sling.installer.api.tasks.TaskResource;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class OsigInstallerListener implements InstallationListener {
+public class OsgiInstallerListener implements InstallationListener {
 
-	private static final Logger LOG = LoggerFactory.getLogger(OsigInstallerListener.class);
+	private static final Logger LOG = LoggerFactory.getLogger(OsgiInstallerListener.class);
 
 	private static final String ENTITY_ID_PREFIX_BUNDLE = "bundle:";
 	private static final String ENTITY_ID_PREFIX_CONFIG = "config:";
@@ -41,7 +41,7 @@ public class OsigInstallerListener implements InstallationListener {
 	private final Set<String> installedBundleSymbolicNames = new HashSet<>();
 	private final Set<String> installedConfigPids = new HashSet<>();
 
-	public OsigInstallerListener(Set<String> requiredBundleSymbolicNames, Set<String> requiredConfigPids) {
+	public OsgiInstallerListener(Set<String> requiredBundleSymbolicNames, Set<String> requiredConfigPids) {
 		this.requiredBundleSymbolicNames = requiredBundleSymbolicNames;
 		this.requiredConfigPids = requiredConfigPids;
 	}


[sling-org-apache-sling-installer-provider-installhook] 15/24: SLING-8083 avoid ClassNotFoundExceptions, take infoProvider.getInstallationState().getInstalledResources() into account

Posted by kw...@apache.org.
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 b6e00c2b4fec43501263427d5c06bdcae8788aef
Author: georg.henzler <ge...@netcentric.biz>
AuthorDate: Thu Nov 8 10:56:28 2018 +0100

    SLING-8083 avoid ClassNotFoundExceptions, take
    infoProvider.getInstallationState().getInstalledResources() into account
---
 .../provider/installhook/OsgiInstallerHook.java    | 18 +++++++++---
 .../installhook/OsgiInstallerListener.java         | 34 +++++++++++++++++-----
 2 files changed, 41 insertions(+), 11 deletions(-)

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
index 2683309..6ca0eec 100644
--- a/src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerHook.java
+++ b/src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerHook.java
@@ -49,12 +49,14 @@ import org.apache.jackrabbit.vault.packaging.PackageProperties;
 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.InstallationEvent;
 import org.apache.sling.installer.api.event.InstallationListener;
 import org.apache.sling.installer.api.info.InfoProvider;
 import org.apache.sling.installer.api.info.InstallationState;
 import org.apache.sling.installer.api.info.Resource;
 import org.apache.sling.installer.api.info.ResourceGroup;
 import org.apache.sling.installer.api.tasks.ResourceState;
+import org.apache.sling.installer.api.tasks.TaskResource;
 import org.apache.sling.settings.SlingSettingsService;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
@@ -101,6 +103,13 @@ public class OsgiInstallerHook implements InstallHook {
 
     InstallHookLogger logger = new InstallHookLogger();
 
+    public OsgiInstallerHook() {
+        LOG.debug("Preloading classes to ensure to not run into a NoClassDefFoundError"
+                + " due to a reloading dynamic classloader: {}, {}, {}, {}",
+                new Object[] { TaskResource.class, InstallationEvent.TYPE.class, ResourceState.class,
+                        InstallerHookOsgiEventListener.class });
+    }
+
     @Override
     public void execute(InstallContext context) throws PackageException {
 
@@ -190,10 +199,13 @@ public class OsgiInstallerHook implements InstallHook {
                     }
                     logger.log("Waiting for " + bundlesLeftToInstall + " bundles / " + configsLeftToInstall + " configs to be installed");
                     Thread.sleep(1000);
+
+                    // the events are not always reliably received, also update listener explicitly with current installation state
+                    hookInstallationListener.updateWith(infoProvider.getInstallationState().getInstalledResources());
                 }
                 if (bundlesLeftToInstall == 0 && configsLeftToInstall == 0) {
                     logger.log("All " + bundlesToInstallByUrl.size() + " bundles / " + configsToInstallByUrl.size()
-                            + " configs have been successfully installed");
+                            + " configs have been successfully installed in " + (System.currentTimeMillis() - startTime) + "ms");
                 }
 
                 int waitForOsgiEventsQuietInSec = getNumericPackageProperty(packageProperties,
@@ -479,10 +491,8 @@ public class OsgiInstallerHook implements InstallHook {
         public void log(Logger logger, String message) {
             if (listener != null) {
                 listener.onMessage(ProgressTrackerListener.Mode.TEXT, message, "");
-                logger.debug(message);
-            } else {
-                logger.info(message);
             }
+            logger.info(message);
         }
     }
 
diff --git a/src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerListener.java b/src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerListener.java
index 63c64c3..497c90d 100644
--- a/src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerListener.java
+++ b/src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerListener.java
@@ -20,11 +20,15 @@ package org.apache.sling.installer.provider.installhook;
 
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 
 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.info.Resource;
+import org.apache.sling.installer.api.info.ResourceGroup;
+import org.apache.sling.installer.api.tasks.ResourceState;
 import org.apache.sling.installer.api.tasks.TaskResource;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -58,15 +62,31 @@ class OsgiInstallerListener implements InstallationListener {
             String entityId = source.getEntityId();
             String url = source.getURL();
 
-            LOG.trace("Received event about processed entityId={} url={}", entityId, url);
+            LOG.debug("Received event about processed entityId={} url={}", entityId, url);
 
-            if (bundleUrlsToInstall.contains(url)) {
-                LOG.debug("Received event for bundle installed with url={}", url);
-                bundleUrlsToInstall.remove(url);
+            if (bundleUrlsToInstall.remove(url)) {
+                LOG.info("Received bundle installed event url={}", url);
             }
-            if (configUrlsToInstall.contains(url)) {
-                LOG.debug("Received event for config installed with url={}", url);
-                configUrlsToInstall.remove(url);
+            if (configUrlsToInstall.remove(url)) {
+                LOG.info("Received config installed event url={}", url);
+            }
+        }
+    }
+
+    public void updateWith(List<ResourceGroup> installedGroups) {
+        for (ResourceGroup resourceGroup : installedGroups) {
+            List<Resource> resources = resourceGroup.getResources();
+            for (Resource resource : resources) {
+                if (resource.getState() == ResourceState.INSTALLED) {
+                    String url = resource.getURL();
+
+                    if (bundleUrlsToInstall.remove(url)) {
+                        LOG.info("Found bundle in already installed resources url={}", url);
+                    }
+                    if (configUrlsToInstall.remove(url)) {
+                        LOG.info("Found config in already installed resources url={}", url);
+                    }
+                }
             }
         }
     }


[sling-org-apache-sling-installer-provider-installhook] 03/24: SLING-7790 Introduced package property "installPathRegex" to control what bundles/configs are taken into consideration and runmode support

Posted by kw...@apache.org.
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 19eba2c26f323ca7851c41530ec1257264d2a2f1
Author: Georg Henzler <ge...@netcentric.biz>
AuthorDate: Tue Aug 21 17:15:34 2018 +0200

    SLING-7790 Introduced package property "installPathRegex" to control
    what bundles/configs are taken into consideration and runmode support
---
 pom.xml                                            |   6 ++
 .../provider/installhook/OsgiInstallerHook.java    | 113 ++++++++++++++++-----
 2 files changed, 91 insertions(+), 28 deletions(-)

diff --git a/pom.xml b/pom.xml
index 820c8bb..533af9b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -86,6 +86,12 @@
 			<version>3.6.8</version>
 			<scope>provided</scope>
 		</dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.settings</artifactId>
+            <version>1.0.0</version>
+            <scope>provided</scope>
+        </dependency>		
 		<dependency>
 			<groupId>org.osgi</groupId>
 			<artifactId>osgi.core</artifactId>
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
index 1245725..362ae6e 100644
--- a/src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerHook.java
+++ b/src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerHook.java
@@ -21,6 +21,7 @@ package org.apache.sling.installer.provider.installhook;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Dictionary;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -45,10 +46,12 @@ 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.PackageProperties;
 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.apache.sling.settings.SlingSettingsService;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.FrameworkUtil;
@@ -77,28 +80,57 @@ public class OsgiInstallerHook implements InstallHook {
 
 	public static final String URL_SCHEME = "jcrinstall";
 	public static final String CONFIG_SUFFIX = ".config";
+	public static final String JAR_SUFFIX = ".jar";
+
+	public static final String PACKAGE_PROP_INSTALL_PATH_REGEX = "installPathRegex";
+
+	public static final String DOT = ".";
+	public static final int PRIORITY_INSTALL_HOOK = 2000;
 
 	private InstallHookLogger logger = new InstallHookLogger();
 
 	@Override
 	public void execute(InstallContext context) throws PackageException {
 
+		VaultPackage vaultPackage = context.getPackage();
+		PackageProperties packageProperties = vaultPackage.getProperties();
+		String installPathRegex = packageProperties.getProperty(PACKAGE_PROP_INSTALL_PATH_REGEX);
+
 		ServiceReference<OsgiInstaller> osgiInstallerServiceRef = null;
 		ServiceReference<ConfigurationAdmin> configAdminServiceRef = null;
+		ServiceReference<SlingSettingsService> slingSettingsServiceRef = null;
 		ServiceRegistration<InstallationListener> hookInstallationListenerServiceRegistration = null;
+
 		try {
 			switch (context.getPhase()) {
+			case PREPARE:
+				if (StringUtils.isBlank(installPathRegex)) {
+					throw new IllegalArgumentException(
+							"When using OSGi installer install hook for synchronous installation, the package property "
+									+ PACKAGE_PROP_INSTALL_PATH_REGEX + " has to be provided.");
+				}
+				break;
 			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);
+
+				configAdminServiceRef = getBundleContext().getServiceReference(ConfigurationAdmin.class);
+				ConfigurationAdmin confAdmin = (ConfigurationAdmin) getBundleContext()
+						.getService(configAdminServiceRef);
+
+				slingSettingsServiceRef = getBundleContext().getServiceReference(SlingSettingsService.class);
+				SlingSettingsService slingSettingsService = (SlingSettingsService) getBundleContext()
+						.getService(slingSettingsServiceRef);
+				Set<String> runModes = slingSettingsService.getRunModes();
+
+				collectResources(archive, archive.getRoot(), "", bundleResources, configResourcePaths, installPathRegex,
+						runModes);
 
 				logger.log("Bundles in package " + bundleResources);
 
@@ -114,10 +146,6 @@ public class OsgiInstallerHook implements InstallHook {
 				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);
 
@@ -140,7 +168,7 @@ public class OsgiInstallerHook implements InstallHook {
 				osgiInstaller.updateResources(URL_SCHEME,
 						installableResources.toArray(new InstallableResource[installableResources.size()]), null);
 
-				String maxWaitForOsgiInstallerInSecStr = vaultPackage.getProperties()
+				String maxWaitForOsgiInstallerInSecStr = packageProperties
 						.getProperty(PACKAGE_PROPERTY_MAX_WAIT_IN_SEC);
 				int maxWaitForOsgiInstallerInSec = maxWaitForOsgiInstallerInSecStr != null
 						? Integer.parseInt(maxWaitForOsgiInstallerInSecStr)
@@ -162,7 +190,7 @@ public class OsgiInstallerHook implements InstallHook {
 				break;
 			}
 		} catch (Exception e) {
-			throw new PackageException("Could not execute install hook to apply env vars: " + e, e);
+			throw new PackageException("Could not execute install hook to for synchronous installation: " + e, e);
 		} finally {
 			if (osgiInstallerServiceRef != null) {
 				getBundleContext().ungetService(osgiInstallerServiceRef);
@@ -170,6 +198,9 @@ public class OsgiInstallerHook implements InstallHook {
 			if (configAdminServiceRef != null) {
 				getBundleContext().ungetService(configAdminServiceRef);
 			}
+			if (slingSettingsServiceRef != null) {
+				getBundleContext().ungetService(slingSettingsServiceRef);
+			}
 
 			if (hookInstallationListenerServiceRegistration != null) {
 				hookInstallationListenerServiceRegistration.unregister();
@@ -248,31 +279,58 @@ public class OsgiInstallerHook implements InstallHook {
 	}
 
 	private void collectResources(Archive archive, Entry entry, String dirPath, List<BundleInPackage> bundleResources,
-			List<String> configResources) {
+			List<String> configResources, String installPathRegex, Set<String> actualRunmodes) {
 		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.equals("META-INF")) {
+			return;
+		} 
+
+		String dirPathWithoutJcrRoot = StringUtils.substringAfter(dirPath, "/jcr_root");
+		String entryPath = dirPathWithoutJcrRoot + entryName;
+		String dirPathWithoutSlash = StringUtils.chomp(dirPathWithoutJcrRoot, "/");
+
+		String dirPathWithoutRunmodes;
+		boolean runmodesMatch;
+		if (dirPathWithoutSlash.contains(DOT)) {
+			String[] bits = dirPathWithoutSlash.split("\\"+DOT, 2);
+			dirPathWithoutRunmodes = bits[0];
+			List<String> runmodesOfResource = Arrays.asList(bits[1].split("\\"+DOT));
+			Set<String> matchingRunmodes = new HashSet<String>(runmodesOfResource);
+			matchingRunmodes.retainAll(actualRunmodes);
+			LOG.debug("Entry with runmode(s): entryPath={} runmodesOfResource={} actualRunmodes={} matchingRunmodes={}", entryPath, runmodesOfResource, actualRunmodes, matchingRunmodes);
+			runmodesMatch = matchingRunmodes.size() == runmodesOfResource.size();
+			if (!runmodesMatch) {
+				logger.log("Skipping installation of  " + entryPath
+						+ " because the path is not matching all actual runmodes " + actualRunmodes);
 			}
+		} else {
+			dirPathWithoutRunmodes = dirPathWithoutSlash;
+			runmodesMatch = true;
 		}
 
-		if (entryName.endsWith(CONFIG_SUFFIX) && dirPath.contains("/config")) {
-			String configPath = StringUtils.substringAfter(dirPath + entryName, "/jcr_root");
-			configResources.add(configPath);
+		if (dirPathWithoutRunmodes.matches(installPathRegex) && runmodesMatch) {
+
+			if (entryName.endsWith(CONFIG_SUFFIX)) {
+				configResources.add(entryPath);
+			} else if (entryName.endsWith(JAR_SUFFIX)) {
+				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);
+
+					bundleResources.add(new BundleInPackage(entryPath, symbolicName, version));
+				} catch (Exception e) {
+					throw new IllegalStateException(
+							"Could not read symbolic name and version from manifest of bundle " + entryName);
+				}
+			}
+
 		}
 
 		for (Entry child : entry.getChildren()) {
-			collectResources(archive, child, dirPath + entryName + "/", bundleResources, configResources);
+			collectResources(archive, child, dirPath + entryName + "/", bundleResources, configResources,
+					installPathRegex, actualRunmodes);
 		}
 	}
 
@@ -282,7 +340,7 @@ public class OsgiInstallerHook implements InstallHook {
 		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);
+		return new InstallableResource(path, is, dict, digest, null, PRIORITY_INSTALL_HOOK);
 	}
 
 	// always get fresh bundle context to avoid "Dynamic class loader has already
@@ -294,7 +352,6 @@ public class OsgiInstallerHook implements InstallHook {
 			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);


[sling-org-apache-sling-installer-provider-installhook] 14/24: [maven-release-plugin] prepare for next development iteration

Posted by kw...@apache.org.
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 48b76a4951b4275e7bd82e7427a825b235cfdc10
Author: georg.henzler <ge...@netcentric.biz>
AuthorDate: Fri Oct 12 01:40:58 2018 +0200

    [maven-release-plugin] prepare for next development iteration
---
 pom.xml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/pom.xml b/pom.xml
index 60ccfa4..deea689 100644
--- a/pom.xml
+++ b/pom.xml
@@ -20,7 +20,7 @@
     </parent>
 
     <artifactId>org.apache.sling.installer.provider.installhook</artifactId>
-    <version>1.0.2</version>
+    <version>1.0.3-SNAPSHOT</version>
     <packaging>bundle</packaging>
 
     <name>Sling Installer Vault Package Install Hook</name>
@@ -41,7 +41,7 @@
         <connection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-installer-provider-installhook.git</connection>
         <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-installer-provider-installhook.git</developerConnection>
         <url>https://gitbox.apache.org/repos/asf?p=sling-org-apache-sling-installer-provider-installhook.git</url>
-        <tag>org.apache.sling.installer.provider.installhook-1.0.2</tag>
+        <tag>HEAD</tag>
     </scm>
 
     <build>


[sling-org-apache-sling-installer-provider-installhook] 06/24: SLING-7216 - [nice-to-have] Add a CODE_OF_CONDUCT file to every module

Posted by kw...@apache.org.
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 d14c4a3544539f587d4ff92047c8f0ca2fc14942
Author: Radu Cotescu <co...@adobe.com>
AuthorDate: Wed Sep 19 18:04:01 2018 +0200

    SLING-7216 - [nice-to-have] Add a CODE_OF_CONDUCT file to every module
---
 CODE_OF_CONDUCT.md | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..52f21cb
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,4 @@
+Apache Software Foundation Code of Conduct
+====
+
+Being an Apache project, Apache Sling adheres to the Apache Software Foundation's [Code of Conduct](https://www.apache.org/foundation/policies/conduct.html).


[sling-org-apache-sling-installer-provider-installhook] 10/24: SLING-7790 adding info to pom

Posted by kw...@apache.org.
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 d7712abfc17369b4abcb592e0dcc625d641527d6
Author: georg.henzler <ge...@netcentric.biz>
AuthorDate: Wed Oct 10 21:08:35 2018 +0200

    SLING-7790 adding <scm> info to pom
---
 pom.xml | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/pom.xml b/pom.xml
index f7c7411..1a9f03e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -38,6 +38,13 @@
         <jackrabbit.version>2.10.1</jackrabbit.version>
     </properties>
 
+    <scm>
+        <connection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-installer-provider-installhook.git</connection>
+        <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-installer-provider-installhook.git</developerConnection>
+        <url>https://gitbox.apache.org/repos/asf?p=sling-org-apache-sling-installer-provider-installhook.git</url>
+        <tag>HEAD</tag>
+    </scm>
+
     <build>
         <plugins>
             <plugin>


[sling-org-apache-sling-installer-provider-installhook] 19/24: README fix

Posted by kw...@apache.org.
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 073bb998afc0b9ff0500717c45f4957178de3d79
Author: georg.henzler <ge...@netcentric.biz>
AuthorDate: Fri Nov 9 10:42:46 2018 +0100

    README fix
---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index ad4b2e1..e29d605 100644
--- a/README.md
+++ b/README.md
@@ -6,4 +6,4 @@
 
 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. See [https://sling.apache.org/documentation/bundles/installer-provider-installhook.html) for details.
\ No newline at end of file
+Allows to synchronously install bundles and configurations as contained in a vault package. See [Sling documentation](https://sling.apache.org/documentation/bundles/installer-provider-installhook.html) for details.


[sling-org-apache-sling-installer-provider-installhook] 11/24: SLING-7790 feedback from relase vote (removed deprecated api, OsgiInstallerListener non-public)

Posted by kw...@apache.org.
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 391d900ef520739a9dd00f0223e33b3333f9e444
Author: georg.henzler <ge...@netcentric.biz>
AuthorDate: Fri Oct 12 00:59:30 2018 +0200

    SLING-7790 feedback from relase vote (removed deprecated api,
    OsgiInstallerListener non-public)
---
 pom.xml                                                              | 3 +--
 .../sling/installer/provider/installhook/OsgiInstallerHook.java      | 2 +-
 .../sling/installer/provider/installhook/OsgiInstallerListener.java  | 2 +-
 .../sling/installer/provider/installhook/OsgiInstallerHookTest.java  | 5 +++++
 4 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/pom.xml b/pom.xml
index 1a9f03e..762c3b2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,8 +9,7 @@
     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">
+<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>
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
index fd27716..2683309 100644
--- a/src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerHook.java
+++ b/src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerHook.java
@@ -396,7 +396,7 @@ public class OsgiInstallerHook implements InstallHook {
             throws IOException, RepositoryException {
         LOG.trace("Converting {} at path {}", node, path);
         final String digest = String.valueOf(node.getProperty(JCR_CONTENT_LAST_MODIFIED).getDate().getTimeInMillis());
-        final InputStream is = node.getProperty(JCR_CONTENT_DATA).getStream();
+        final InputStream is = node.getProperty(JCR_CONTENT_DATA).getBinary().getStream();
         final Dictionary<String, Object> dict = new Hashtable<String, Object>();
         dict.put(InstallableResource.INSTALLATION_HINT, node.getParent().getName());
         int priority = getNumericPackageProperty(packageProperties, PACKAGE_PROPERTY_INSTALL_PRIORITY, DEFAULT_PRIORITY_INSTALL_HOOK);
diff --git a/src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerListener.java b/src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerListener.java
index 4dd6b5a..63c64c3 100644
--- a/src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerListener.java
+++ b/src/main/java/org/apache/sling/installer/provider/installhook/OsgiInstallerListener.java
@@ -29,7 +29,7 @@ import org.apache.sling.installer.api.tasks.TaskResource;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class OsgiInstallerListener implements InstallationListener {
+class OsgiInstallerListener implements InstallationListener {
 
     private static final Logger LOG = LoggerFactory.getLogger(OsgiInstallerListener.class);
 
diff --git a/src/test/java/org/apache/sling/installer/provider/installhook/OsgiInstallerHookTest.java b/src/test/java/org/apache/sling/installer/provider/installhook/OsgiInstallerHookTest.java
index 156fbfe..5aeea2e 100644
--- a/src/test/java/org/apache/sling/installer/provider/installhook/OsgiInstallerHookTest.java
+++ b/src/test/java/org/apache/sling/installer/provider/installhook/OsgiInstallerHookTest.java
@@ -30,6 +30,7 @@ import java.util.Calendar;
 import java.util.HashSet;
 import java.util.List;
 
+import javax.jcr.Binary;
 import javax.jcr.Node;
 import javax.jcr.Property;
 import javax.jcr.RepositoryException;
@@ -54,6 +55,9 @@ public class OsgiInstallerHookTest {
     Node node;
 
     @Mock
+    Binary binary;
+
+    @Mock
     Node parentNode;
 
     @Mock
@@ -81,6 +85,7 @@ public class OsgiInstallerHookTest {
         when(node.getProperty(OsgiInstallerHook.JCR_CONTENT_LAST_MODIFIED)).thenReturn(lastModifiedProperty);
         when(lastModifiedProperty.getDate()).thenReturn(now);
         when(node.getProperty(OsgiInstallerHook.JCR_CONTENT_DATA)).thenReturn(contentDataProperty);
+        when(contentDataProperty.getBinary()).thenReturn(binary);
         when(node.getParent()).thenReturn(parentNode);
 
     }


[sling-org-apache-sling-installer-provider-installhook] 13/24: [maven-release-plugin] prepare release org.apache.sling.installer.provider.installhook-1.0.2

Posted by kw...@apache.org.
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 3bd16faf3f96fdb5280ee86a65d8abc56227c2b3
Author: georg.henzler <ge...@netcentric.biz>
AuthorDate: Fri Oct 12 01:40:38 2018 +0200

    [maven-release-plugin] prepare release org.apache.sling.installer.provider.installhook-1.0.2
---
 pom.xml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/pom.xml b/pom.xml
index 48f35d1..60ccfa4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -20,7 +20,7 @@
     </parent>
 
     <artifactId>org.apache.sling.installer.provider.installhook</artifactId>
-    <version>1.0.2-SNAPSHOT</version>
+    <version>1.0.2</version>
     <packaging>bundle</packaging>
 
     <name>Sling Installer Vault Package Install Hook</name>
@@ -41,7 +41,7 @@
         <connection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-installer-provider-installhook.git</connection>
         <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-installer-provider-installhook.git</developerConnection>
         <url>https://gitbox.apache.org/repos/asf?p=sling-org-apache-sling-installer-provider-installhook.git</url>
-        <tag>HEAD</tag>
+        <tag>org.apache.sling.installer.provider.installhook-1.0.2</tag>
     </scm>
 
     <build>


[sling-org-apache-sling-installer-provider-installhook] 18/24: Merge branch 'master' of git@github.com:apache/sling-org-apache-sling-installer-provider-installhook.git

Posted by kw...@apache.org.
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 e2494ce0bfd18592ccd8b14d0db58ef3884c874d
Merge: cf19d15 887d30f
Author: georg.henzler <ge...@netcentric.biz>
AuthorDate: Fri Nov 9 10:41:42 2018 +0100

    Merge branch 'master' of git@github.com:apache/sling-org-apache-sling-installer-provider-installhook.git



[sling-org-apache-sling-installer-provider-installhook] 23/24: Updating badges for org-apache-sling-installer-provider-installhook

Posted by kw...@apache.org.
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 4a5f5812003262b7ac7687bcaadc96bc094dec2d
Author: Radu Cotescu <ra...@apache.org>
AuthorDate: Thu Jan 31 13:15:15 2019 +0100

    Updating badges for org-apache-sling-installer-provider-installhook
---
 README.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index e29d605..5de4a0f 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-[<img src="http://sling.apache.org/res/logos/sling.png"/>](http://sling.apache.org)
+[<img src="https://sling.apache.org/res/logos/sling.png"/>](https://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_ [...]
+ [![Build Status](https://builds.apache.org/buildStatus/icon?job=Sling/sling-org-apache-sling-installer-provider-installhook/master)](https://builds.apache.org/job/Sling/job/sling-org-apache-sling-installer-provider-installhook/job/master) [![Test Status](https://img.shields.io/jenkins/t/https/builds.apache.org/job/Sling/job/sling-org-apache-sling-installer-provider-installhook/job/master.svg)](https://builds.apache.org/job/Sling/job/sling-org-apache-sling-installer-provider-installhook/ [...]
 
 # Apache Sling JCR Installer Install Hook
 


[sling-org-apache-sling-installer-provider-installhook] 22/24: SLING-7245 - Validate pull requests using Jenkins

Posted by kw...@apache.org.
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 95c2f9d9b94a805e4b006c20dcf476d25d499fa0
Author: Robert Munteanu <ro...@apache.org>
AuthorDate: Tue Jan 29 23:11:57 2019 +0100

    SLING-7245 - Validate pull requests using Jenkins
---
 Jenkinsfile | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/Jenkinsfile b/Jenkinsfile
new file mode 100644
index 0000000..f582519
--- /dev/null
+++ b/Jenkinsfile
@@ -0,0 +1,20 @@
+/**
+ * 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.
+ */
+
+slingOsgiBundleBuild()