[FELIX-2066] File Install fails to move fragment bundle into RESOLVED state
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1301567 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/fileinstall/pom.xml b/fileinstall/pom.xml
index 39593dc..b21f022 100644
--- a/fileinstall/pom.xml
+++ b/fileinstall/pom.xml
@@ -71,7 +71,9 @@
</Export-Package>
<Private-Package>
org.apache.felix.fileinstall.internal,
+ org.apache.felix.utils.manifest,
org.apache.felix.utils.properties,
+ org.apache.felix.utils.version,
</Private-Package>
<Import-Package>
org.osgi.service.log;resolution:=optional,
diff --git a/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/DirectoryWatcher.java b/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/DirectoryWatcher.java
index eb0d1cd..d77b128 100644
--- a/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/DirectoryWatcher.java
+++ b/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/DirectoryWatcher.java
@@ -30,11 +30,13 @@
import java.net.URL;
import java.security.SecureRandom;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -46,6 +48,10 @@
import org.apache.felix.fileinstall.ArtifactTransformer;
import org.apache.felix.fileinstall.ArtifactUrlTransformer;
import org.apache.felix.fileinstall.internal.Util.Logger;
+import org.apache.felix.utils.manifest.Clause;
+import org.apache.felix.utils.manifest.Parser;
+import org.apache.felix.utils.version.VersionRange;
+import org.apache.felix.utils.version.VersionTable;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
@@ -460,10 +466,17 @@
Collection uninstalledBundles = uninstall(deleted);
Collection updatedBundles = update(modified);
Collection installedBundles = install(created);
- if (uninstalledBundles.size() > 0 || updatedBundles.size() > 0)
+
+ Set toRefresh = new HashSet();
+ toRefresh.addAll( uninstalledBundles );
+ toRefresh.addAll(updatedBundles);
+ toRefresh.addAll( installedBundles );
+ findBundlesWithFragmentsToRefresh( toRefresh );
+ findBundlesWithOptionalPackagesToRefresh( toRefresh );
+ if (toRefresh.size() > 0)
{
// Refresh if any bundle got uninstalled or updated.
- refresh();
+ refresh((Bundle[]) toRefresh.toArray(new Bundle[toRefresh.size()]));
}
if (startBundles)
@@ -644,12 +657,12 @@
/**
* Convenience to refresh the packages
*/
- void refresh()
+ void refresh(Bundle[] bundles)
{
PackageAdmin padmin = FileInstall.getPackageAdmin();
if (padmin != null)
{
- padmin.refreshPackages(null);
+ padmin.refreshPackages(bundles);
}
}
@@ -1227,4 +1240,117 @@
}
return false;
}
+
+ protected void findBundlesWithFragmentsToRefresh(Set toRefresh) {
+ for (Iterator iterator = toRefresh.iterator(); iterator.hasNext();) {
+ Bundle b = (Bundle) iterator.next();
+ if (b.getState() != Bundle.UNINSTALLED) {
+ String hostHeader = (String) b.getHeaders().get(Constants.FRAGMENT_HOST);
+ if (hostHeader != null) {
+ Clause[] clauses = Parser.parseHeader(hostHeader);
+ if (clauses != null && clauses.length > 0) {
+ Clause path = clauses[0];
+ Bundle[] bundles = context.getBundles();
+ for (int i = 0; i < bundles.length; i++) {
+ Bundle hostBundle = bundles[i];
+ if (hostBundle.getSymbolicName().equals(path.getName())) {
+ String ver = path.getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE);
+ if (ver != null) {
+ VersionRange v = VersionRange.parseVersionRange(ver);
+ if (v.contains(VersionTable.getVersion((String) hostBundle.getHeaders().get(Constants.BUNDLE_VERSION)))) {
+ toRefresh.add(hostBundle);
+ }
+ } else {
+ toRefresh.add(hostBundle);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ protected void findBundlesWithOptionalPackagesToRefresh(Set toRefresh) {
+ // First pass: include all bundles contained in these features
+ Set bundles = new HashSet(Arrays.asList(context.getBundles()));
+ bundles.removeAll(toRefresh);
+ if (bundles.isEmpty()) {
+ return;
+ }
+ // Second pass: for each bundle, check if there is any unresolved optional package that could be resolved
+ Map imports = new HashMap();
+ for (Iterator it = bundles.iterator(); it.hasNext(); ) {
+ Bundle b = (Bundle) it.next();
+ String importsStr = (String) b.getHeaders().get(Constants.IMPORT_PACKAGE);
+ List importsList = getOptionalImports(importsStr);
+ if (importsList.isEmpty()) {
+ it.remove();
+ } else {
+ imports.put(b, importsList);
+ }
+ }
+ if (bundles.isEmpty()) {
+ return;
+ }
+ // Third pass: compute a list of packages that are exported by our bundles and see if
+ // some exported packages can be wired to the optional imports
+ List exports = new ArrayList();
+ for (Iterator iterator = toRefresh.iterator(); iterator.hasNext();) {
+ Bundle b = (Bundle) iterator.next();
+ if (b.getState() != Bundle.UNINSTALLED) {
+ String exportsStr = (String) b.getHeaders().get(Constants.EXPORT_PACKAGE);
+ if (exportsStr != null) {
+ Clause[] exportsList = Parser.parseHeader(exportsStr);
+ exports.addAll(Arrays.asList(exportsList));
+ }
+ }
+ }
+ for (Iterator it = bundles.iterator(); it.hasNext(); ) {
+ Bundle b = (Bundle) it.next();
+ List importsList = (List) imports.get(b);
+ for (Iterator itpi = importsList.iterator(); itpi.hasNext(); ) {
+ Clause pi = (Clause) itpi.next();
+ boolean matching = false;
+ for (Iterator itcl = exports.iterator(); itcl.hasNext(); ) {
+ Clause pe = (Clause) itcl.next();
+ if (pi.getName().equals(pe.getName())) {
+ String evStr = pe.getAttribute(Constants.VERSION_ATTRIBUTE);
+ String ivStr = pi.getAttribute(Constants.VERSION_ATTRIBUTE);
+ Version exported = evStr != null ? Version.parseVersion(evStr) : Version.emptyVersion;
+ VersionRange imported = ivStr != null ? VersionRange.parseVersionRange(ivStr) : VersionRange.ANY_VERSION;
+ if (imported.contains(exported)) {
+ matching = true;
+ break;
+ }
+ }
+ }
+ if (!matching) {
+ itpi.remove();
+ }
+ }
+ if (importsList.isEmpty()) {
+ it.remove();
+// } else {
+// LOGGER.debug("Refreshing bundle {} ({}) to solve the following optional imports", b.getSymbolicName(), b.getBundleId());
+// for (Clause p : importsList) {
+// LOGGER.debug(" {}", p);
+// }
+//
+ }
+ }
+ toRefresh.addAll(bundles);
+ }
+
+ protected List getOptionalImports(String importsStr) {
+ Clause[] imports = Parser.parseHeader(importsStr);
+ List result = new LinkedList();
+ for (int i = 0; i < imports.length; i++) {
+ String resolution = imports[i].getDirective(Constants.RESOLUTION_DIRECTIVE);
+ if (Constants.RESOLUTION_OPTIONAL.equals(resolution)) {
+ result.add(imports[i]);
+ }
+ }
+ return result;
+ }
}