package org.apache.aries.subsystem.scope.impl; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.aries.subsystem.scope.InstallInfo; import org.apache.aries.subsystem.scope.Scope; import org.apache.aries.subsystem.scope.ScopeUpdate; import org.apache.aries.subsystem.scope.SharePolicy; import org.apache.aries.subsystem.scope.internal.Activator; import org.osgi.framework.Bundle; import org.osgi.framework.BundleException; public class ScopeUpdateImpl implements ScopeUpdate { public static ScopeUpdateImpl newInstance(ScopeImpl scope) { ScopeUpdateImpl scopeUpdate = new ScopeUpdateImpl(scope, null); for (Scope child : scopeUpdate.scope.getChildren()) { scopeUpdate.children.add(new ScopeUpdateImpl((ScopeImpl)child, scopeUpdate)); } return scopeUpdate; } private static final IdGenerator idGenerator = new IdGenerator(1); private final Set<Bundle> bundles = Collections.synchronizedSet(new HashSet<Bundle>()); private final List<InstallInfo> bundlesToInstall = Collections.synchronizedList(new ArrayList<InstallInfo>()); private final Set<ScopeUpdate> children = Collections.synchronizedSet(new HashSet<ScopeUpdate>()); private final long id = idGenerator.nextId(); private final ScopeUpdateImpl parent; private final ScopeImpl scope; private final SharePolicies sharePolicies = new SharePolicies(); private ScopeUpdateImpl(String name, String location, ScopeUpdateImpl parent) { this.parent = parent; this.scope = new ScopeImpl( ((ScopeImpl)parent.getScope()).getScopes().nextScopeId(), name, location, parent.getScope().getId(), null, new SharePolicies(), ((ScopeImpl)parent.getScope()).getScopes()); } private ScopeUpdateImpl(ScopeImpl scope, ScopeUpdateImpl parent) { this.scope = scope; this.parent = parent; bundles.addAll(scope.getBundles()); sharePolicies.replaceAll(scope.getSharePolicies()); } public boolean commit() throws BundleException { if (parent != null) throw new IllegalStateException("Only the root ScopeUpdate may be committed"); return commit0(); } public Collection<Bundle> getBundles() { return bundles; } public List<InstallInfo> getBundlesToInstall() { return bundlesToInstall; } public Collection<ScopeUpdate> getChildren() { return children; } public String getName() { return scope.getName(); } public Scope getScope() { return scope; } public Map<String, List<SharePolicy>> getSharePolicies(String type) { return sharePolicies.getSharePolicies(type); } public ScopeUpdate newChild(String name) { return newChild(name, null); } public ScopeUpdate newChild(String name, String location) { return new ScopeUpdateImpl(name, location, this); } private void addBundles() { for (Bundle b : getBundles()) { if (!getScope().getBundles().contains(b)) { if (contains(b, this)) { throw new IllegalStateException("Bundle " + b.getSymbolicName() + " being added to scope " + getName() + " but already exists in another scope"); } scope.getScopes().addBundle(b, scope); } } } private synchronized boolean commit0() throws BundleException { synchronized (scope) { if (scope.getLastUpdate() > id) return false; scope.setUpdating(true); synchronized (bundles) { removeBundles(); } synchronized (children) { for (ScopeUpdate child : children) { if (!((ScopeUpdateImpl)child).commit0()) return false; } uninstallChildren(); } synchronized (bundles) { addBundles(); } synchronized (bundlesToInstall) { installBundles(); } updateSharePolicies(); scope.setLastUpdate(id); scope.getScopes().addScope(scope); scope.setUpdating(false); return true; } } private boolean contains(Bundle bundle, ScopeUpdateImpl scopeUpdate) { // Recurse to the top of the tree and then perform a depth-first search. return parent == null ? contains0(bundle, scopeUpdate) : parent.contains(bundle, scopeUpdate); } private boolean contains0(Bundle bundle, ScopeUpdateImpl scopeUpdate) { if (!equals(scopeUpdate) && bundles.contains(bundle)) return true; // Depth-first search. for (ScopeUpdate child : children) { if (((ScopeUpdateImpl)child).contains0(bundle, scopeUpdate)) return true; } return false; } private void installBundles() throws BundleException { for (InstallInfo installInfo : getBundlesToInstall()) { ScopeManager.installingBundleToScope.put(installInfo.getLocation(), scope); Activator.getBundleContext().installBundle(installInfo.getLocation(), installInfo.getContent()); } } private void removeBundles() throws BundleException { Collection<Bundle> bundles = new HashSet<Bundle>(scope.getBundles()); for (Bundle b : bundles) { if (!getBundles().contains(b)) { if (!contains(b, null)) { b.uninstall(); } else { scope.getScopes().removeBundle(b); } } } } private void uninstall(ScopeImpl scope) throws BundleException { for (Scope child : scope.getChildren()) { uninstall((ScopeImpl)child); } Collection<Bundle> bundles = new HashSet<Bundle>(scope.getBundles()); for (Bundle bundle : bundles) { if (!contains(bundle, null)) { bundle.uninstall(); } } scope.getScopes().removeScope(scope); } private void uninstallChildren() throws BundleException { Collection<Scope> children = new HashSet<Scope>(scope.getChildren()); for (Scope child : children) { // for (Iterator<Scope> i = scope.children.iterator(); i.hasNext();) { // Scope child = i.next(); boolean found = false; for (ScopeUpdate su : getChildren()) { if (child.equals(su.getScope())) { found = true; break; } } if (!found) { uninstall((ScopeImpl)child); } } } private void updateSharePolicies() { scope.getSharePolicies().replaceAll(sharePolicies); } }