Author: jwross
Date: Thu Jan 28 19:27:49 2016
New Revision: 1727424

URL: http://svn.apache.org/viewvc?rev=1727424&view=rev
Log:
[ARIES-1383] Provide option to disable the provisioning of dependencies at 
install time.

As part of the implemented locking strategy, three locks are used. The Global 
Read/Write Lock (GRWL) is used to ensure thread safety among all operations: 
install, install
dependencies, resolve, start, stop, and uninstall. The write lock is acquired 
for install, install dependencies, and uninstall. The read lock is acquired for 
resolve, start, and stop.
The Global Mutual Exlusion Lock (GMEL) is used to prevent cycle deadlocks when 
acquiring the state change locks of individual subsystems. Every subsystem has 
a Local State Change Lock
(LSCL). These locks are used to prevent more than one state change operation at 
a time to occur for the same subsystem.

A condition exists for the GMEL. The condition is used in order to notify 
waiting threads that the LSCL of at least one subsystem was released and that 
at least one thread may now be
able to proceed. Threads that fail to acquire the LSCL of a subsystem while 
holding the GMEL will release all currently held locks and wait for the 
condition to apply. This is
necessary because, when starting, the target subsystem must be locked before 
any other affected subsystems since the latter may only be computed after 
dependencies, which may include
other subsystems, are installed.

The consequences of this strategy is that installs and uninstalls are 
synchronous while resolutions, starts, and stops are asynchronous as long as 
the same subsystem is not affected.
It may be possible to create an even more granular locking mechanism in the 
future if dictated by performance requirements.

Added:
    
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/LockingStrategy.java
Modified:
    
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/BasicSubsystem.java
    
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/InstallAction.java
    
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/ResolveContext.java
    
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/StartAction.java
    
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/StopAction.java
    
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemResource.java
    
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemResourceInstaller.java
    
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/UninstallAction.java
    
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/WovenClassListener.java
    
aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/defect/Aries1383Test.java
    
aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/util/SubsystemArchiveBuilder.java

Modified: 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/BasicSubsystem.java
URL: 
http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/BasicSubsystem.java?rev=1727424&r1=1727423&r2=1727424&view=diff
==============================================================================
--- 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/BasicSubsystem.java
 (original)
+++ 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/BasicSubsystem.java
 Thu Jan 28 19:27:49 2016
@@ -32,6 +32,7 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
+import java.util.concurrent.locks.ReentrantLock;
 
 import org.apache.aries.subsystem.AriesSubsystem;
 import 
org.apache.aries.subsystem.core.archive.AriesProvisionDependenciesDirective;
@@ -621,6 +622,11 @@ public class BasicSubsystem implements R
                }
        }
        
+       private final ReentrantLock stateChangeLock = new ReentrantLock();
+       ReentrantLock stateChangeLock() {
+               return stateChangeLock;
+       }
+       
        private String getDeploymentManifestHeaderValue(String name) {
                DeploymentManifest manifest = getDeploymentManifest();
                if (manifest == null)
@@ -754,8 +760,8 @@ public class BasicSubsystem implements R
                }
        }
        
-       void computeDependenciesPostInstallation() throws IOException {
-               resource.computeDependencies(null);
+       void computeDependenciesPostInstallation(Coordination coordination) 
throws IOException {
+               resource.computeDependencies(null, coordination);
                ProvisionResourceHeader header = 
resource.computeProvisionResourceHeader();
                setDeploymentManifest(
                                new DeploymentManifest.Builder()

Modified: 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/InstallAction.java
URL: 
http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/InstallAction.java?rev=1727424&r1=1727423&r2=1727424&view=diff
==============================================================================
--- 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/InstallAction.java
 (original)
+++ 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/InstallAction.java
 Thu Jan 28 19:27:49 2016
@@ -41,57 +41,68 @@ public class InstallAction implements Pr
        
        @Override
        public BasicSubsystem run() {
-               State state = parent.getState();
-           if (State.INSTALLING.equals(state)) {
-               throw new SubsystemException("A child subsystem may not be 
installed while the parent is in the INSTALLING state");
-           }
-               // Initialization of a null coordination must be privileged and,
-               // therefore, occur in the run() method rather than in the 
constructor.
-               Coordination coordination = Utils.createCoordination(parent);
+               // Doesn't appear to be any need of protecting against re-entry 
in the
+               // case of installation.
                BasicSubsystem result = null;
+               // Acquire the global write lock to prevent all other 
operations until
+               // the installation is complete. There is no need to hold any 
other locks.
+               LockingStrategy.writeLock();
                try {
-                       TargetRegion region = new TargetRegion(parent);
-                       SubsystemResource ssr = new SubsystemResource(location, 
content, parent);
-                       result = 
Activator.getInstance().getSubsystems().getSubsystemByLocation(location);
-                       if (result != null) {
-                               checkLifecyclePermission(result);
-                               if (!region.contains(result))
-                                       throw new SubsystemException("Location 
already exists but existing subsystem is not part of target region: " + 
location);
-                               if 
(!(result.getSymbolicName().equals(ssr.getSubsystemManifest().getSubsystemSymbolicNameHeader().getSymbolicName())
-                                               && 
result.getVersion().equals(ssr.getSubsystemManifest().getSubsystemVersionHeader().getVersion())
-                                               && 
result.getType().equals(ssr.getSubsystemManifest().getSubsystemTypeHeader().getType())))
-                                       throw new SubsystemException("Location 
already exists but symbolic name, version, and type are not the same: " + 
location);
-                               return 
(BasicSubsystem)ResourceInstaller.newInstance(coordination, result, 
parent).install();
+                       State state = parent.getState();
+                       if (State.INSTALLING.equals(state)) {
+                               throw new SubsystemException("A child subsystem 
may not be installed while the parent is in the INSTALLING state");
                        }
-                       result = (BasicSubsystem)region.find(
-                                       
ssr.getSubsystemManifest().getSubsystemSymbolicNameHeader().getSymbolicName(), 
-                                       
ssr.getSubsystemManifest().getSubsystemVersionHeader().getVersion());
-                       if (result != null) {
+                       // Initialization of a null coordination must be 
privileged and,
+                       // therefore, occur in the run() method rather than in 
the constructor.
+                       Coordination coordination = 
Utils.createCoordination(parent);
+                       try {
+                               TargetRegion region = new TargetRegion(parent);
+                               SubsystemResource ssr = new 
SubsystemResource(location, content, parent, coordination);
+                               result = 
Activator.getInstance().getSubsystems().getSubsystemByLocation(location);
+                               if (result != null) {
+                                       if (!region.contains(result))
+                                               throw new 
SubsystemException("Location already exists but existing subsystem is not part 
of target region: " + location);
+                                       if 
(!(result.getSymbolicName().equals(ssr.getSubsystemManifest().getSubsystemSymbolicNameHeader().getSymbolicName())
+                                                       && 
result.getVersion().equals(ssr.getSubsystemManifest().getSubsystemVersionHeader().getVersion())
+                                                       && 
result.getType().equals(ssr.getSubsystemManifest().getSubsystemTypeHeader().getType())))
+                                               throw new 
SubsystemException("Location already exists but symbolic name, version, and 
type are not the same: " + location);
+                               }
+                               else {
+                                       result = (BasicSubsystem)region.find(
+                                                       
ssr.getSubsystemManifest().getSubsystemSymbolicNameHeader().getSymbolicName(), 
+                                                       
ssr.getSubsystemManifest().getSubsystemVersionHeader().getVersion());
+                                       if (result != null) {
+                                               if 
(!result.getType().equals(ssr.getSubsystemManifest().getSubsystemTypeHeader().getType()))
+                                                       throw new 
SubsystemException("Subsystem already exists in target region but has a 
different type: " + location);
+                                       }
+                                       else {
+                                               result = new 
BasicSubsystem(ssr, deploymentManifest);
+                                       }
+                               }
                                checkLifecyclePermission(result);
-                               if 
(!result.getType().equals(ssr.getSubsystemManifest().getSubsystemTypeHeader().getType()))
-                                       throw new SubsystemException("Subsystem 
already exists in target region but has a different type: " + location);
                                return 
(BasicSubsystem)ResourceInstaller.newInstance(coordination, result, 
parent).install();
                        }
-                       result = new BasicSubsystem(ssr, deploymentManifest);
-                       checkLifecyclePermission(result);
-                       return 
(BasicSubsystem)ResourceInstaller.newInstance(coordination, result, 
parent).install();
-               }
-               catch (Throwable t) {
-                       coordination.fail(t);
-               }
-               finally {
-                       try {
-                               coordination.end();
+                       catch (Throwable t) {
+                               coordination.fail(t);
                        }
-                       catch (CoordinationException e) {
-                               Throwable t = e.getCause();
-                               if (t instanceof SubsystemException)
-                                       throw (SubsystemException)t;
-                               if (t instanceof SecurityException)
-                                       throw (SecurityException)t;
-                               throw new SubsystemException(t);
+                       finally {
+                               try {
+                                       coordination.end();
+                               }
+                               catch (CoordinationException e) {
+                                       Throwable t = e.getCause();
+                                       if (t instanceof SubsystemException)
+                                               throw (SubsystemException)t;
+                                       if (t instanceof SecurityException)
+                                               throw (SecurityException)t;
+                                       throw new SubsystemException(t);
+                               }
                        }
                }
+               finally {
+                       // Release the global write lock.
+                       LockingStrategy.writeUnlock();
+               }
                return result;
        }
        

Added: 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/LockingStrategy.java
URL: 
http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/LockingStrategy.java?rev=1727424&view=auto
==============================================================================
--- 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/LockingStrategy.java
 (added)
+++ 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/LockingStrategy.java
 Thu Jan 28 19:27:49 2016
@@ -0,0 +1,164 @@
+package org.apache.aries.subsystem.core.internal;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import org.osgi.service.subsystem.Subsystem;
+import org.osgi.service.subsystem.SubsystemException;
+
+public class LockingStrategy {
+       private static final int TRY_LOCK_TIME = 30000;
+       private static final TimeUnit TRY_LOCK_TIME_UNIT = 
TimeUnit.MILLISECONDS;
+       
+       /*
+        * A mutual exclusion lock used when acquiring the state change locks of
+        * a collection of subsystems in order to prevent cycle deadlocks.
+        */
+       private static final ReentrantLock lock = new ReentrantLock();
+       /*
+        * Used when the state change lock of a subsystem cannot be acquired. 
All
+        * other state change locks are released while waiting. The condition 
is met
+        * whenever the state change lock of one or more subsystems is released.
+        */
+       private static final Condition condition = lock.newCondition();
+       
+       /*
+        * Allow only one of the following operations to be executing at the 
same 
+        * time.
+        * 
+        * (1) Install
+        * (2) Install Dependencies
+        * (3) Uninstall
+        * 
+        * Allow any number of the following operations to be executing at the 
same
+        * time.
+        * 
+        * (1) Resolve
+        * (2) Start
+        * (3) Stop
+        */
+       private static final ReentrantReadWriteLock rwlock = new 
ReentrantReadWriteLock();
+       
+       private static final ThreadLocal<Map<Subsystem.State, 
Set<BasicSubsystem>>> local = new ThreadLocal<Map<Subsystem.State, 
Set<BasicSubsystem>>>() {
+               @Override
+               protected Map<Subsystem.State, Set<BasicSubsystem>> 
initialValue() {
+                       return new HashMap<Subsystem.State, 
Set<BasicSubsystem>>();
+               }
+       };
+       
+       public static void lock() {
+               try {
+                       if (!lock.tryLock(TRY_LOCK_TIME, TRY_LOCK_TIME_UNIT)) {
+                               throw new SubsystemException("Unable to acquire 
the global mutual exclusion lock in time.");
+                       }
+               }
+               catch (InterruptedException e) {
+                       throw new SubsystemException(e);
+               }
+       }
+       
+       public static void unlock() {
+               lock.unlock();
+       }
+       
+       public static void lock(Collection<BasicSubsystem> subsystems) {
+               Collection<BasicSubsystem> locked = new 
ArrayList<BasicSubsystem>(subsystems.size());
+               try {
+                       while (locked.size() < subsystems.size()) {
+                               for (BasicSubsystem subsystem : subsystems) {
+                                       if 
(!subsystem.stateChangeLock().tryLock()) {
+                                               unlock(locked);
+                                               locked.clear();
+                                               if 
(!LockingStrategy.condition.await(TRY_LOCK_TIME, TimeUnit.SECONDS)) {
+                                                       throw new 
SubsystemException("Unable to acquire the state change lock in time: " + 
subsystem);
+                                               }
+                                               break;
+                                       }
+                                       locked.add(subsystem);
+                               }
+                       }
+               }
+               catch (InterruptedException e) {
+                       unlock(locked);
+                       throw new SubsystemException(e);
+               }
+       }
+       
+       public static void unlock(Collection<BasicSubsystem> subsystems) {
+               for (BasicSubsystem subsystem : subsystems) {
+                       subsystem.stateChangeLock().unlock();
+               }
+               signalAll();
+       }
+       
+       private static void signalAll() {
+               lock();
+               try {
+                       condition.signalAll();
+               }
+               finally {
+                       unlock();
+               }
+       }
+       
+       public static boolean set(Subsystem.State state, BasicSubsystem 
subsystem) {
+               Map<Subsystem.State, Set<BasicSubsystem>> map = local.get();
+               Set<BasicSubsystem> subsystems = map.get(state);
+               if (subsystems == null) {
+                       subsystems = new HashSet<BasicSubsystem>();
+                       map.put(state, subsystems);
+                       local.set(map);
+               }
+               if (subsystems.contains(subsystem)) {
+                       return false;
+               }
+               subsystems.add(subsystem);
+               return true;
+       }
+       
+       public static void unset(Subsystem.State state, BasicSubsystem 
subsystem) {
+               Map<Subsystem.State, Set<BasicSubsystem>> map = local.get();
+               Set<BasicSubsystem> subsystems = map.get(state);
+               if (subsystems != null) {
+                       subsystems.remove(subsystem);
+               }
+       }
+       
+       public static void readLock() {
+               try {
+                       if (!rwlock.readLock().tryLock(TRY_LOCK_TIME, 
TRY_LOCK_TIME_UNIT)) {
+                               throw new SubsystemException("Unable to acquire 
the global read lock in time.");
+                       }
+               }
+               catch (InterruptedException e) {
+                       throw new SubsystemException(e);
+               }
+       }
+       
+       public static void readUnlock() {
+               rwlock.readLock().unlock();
+       }
+       
+       public static void writeLock() {
+               try {
+                       if (!rwlock.writeLock().tryLock(TRY_LOCK_TIME, 
TRY_LOCK_TIME_UNIT)) {
+                               throw new SubsystemException("Unable to acquire 
the global write lock in time.");
+                       }
+               }
+               catch (InterruptedException e) {
+                       throw new SubsystemException(e);
+               }
+       }
+       
+       public static void writeUnlock() {
+               rwlock.writeLock().unlock();
+       }
+}

Modified: 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/ResolveContext.java
URL: 
http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/ResolveContext.java?rev=1727424&r1=1727423&r2=1727424&view=diff
==============================================================================
--- 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/ResolveContext.java
 (original)
+++ 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/ResolveContext.java
 Thu Jan 28 19:27:49 2016
@@ -15,6 +15,7 @@ package org.apache.aries.subsystem.core.
 
 import java.io.IOException;
 import java.net.URISyntaxException;
+import java.security.AccessController;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -27,6 +28,7 @@ import org.apache.aries.subsystem.core.a
 import org.apache.aries.subsystem.core.archive.SubsystemTypeHeader;
 import 
org.apache.aries.subsystem.core.internal.BundleResourceInstaller.BundleConstituent;
 import 
org.apache.aries.subsystem.core.internal.DependencyCalculator.MissingCapability;
+import org.apache.aries.subsystem.core.internal.StartAction.Restriction;
 import org.apache.aries.subsystem.core.repository.Repository;
 import org.eclipse.equinox.region.Region;
 import org.osgi.framework.BundleException;
@@ -42,6 +44,7 @@ import org.osgi.resource.Requirement;
 import org.osgi.resource.Resource;
 import org.osgi.resource.Wiring;
 import org.osgi.service.resolver.HostedCapability;
+import org.osgi.service.subsystem.Subsystem.State;
 import org.osgi.service.subsystem.SubsystemException;
 
 public class ResolveContext extends org.osgi.service.resolver.ResolveContext {
@@ -61,9 +64,40 @@ public class ResolveContext extends org.
                repositoryServiceRepository = new RepositoryServiceRepository();
                systemRepository = 
Activator.getInstance().getSystemRepository();
        }
+       
+       private void installDependenciesOfRequirerIfNecessary(Requirement 
requirement) {
+               if (requirement == null) {
+                       return;
+               }
+               Resource requirer = requirement.getResource();
+               if (resource.equals(requirer)) {
+                       return;
+               }
+               Collection<BasicSubsystem> subsystems;
+               if (requirer instanceof BasicSubsystem) {
+                       BasicSubsystem subsystem = (BasicSubsystem)requirer;
+                       subsystems = Collections.singletonList(subsystem);
+               }
+               else if (requirer instanceof BundleRevision) {
+                       BundleRevision revision = (BundleRevision)requirer;
+                       BundleConstituent constituent = new 
BundleConstituent(null, revision);
+                       subsystems = 
Activator.getInstance().getSubsystems().getSubsystemsByConstituent(constituent);
+               }
+               else {
+                       return;
+               }
+               for (BasicSubsystem subsystem : subsystems) {
+                       if (Utils.isProvisionDependenciesInstall(subsystem) 
+                                       || 
!State.INSTALLING.equals(subsystem.getState())) {
+                               continue;
+                       }
+                       AccessController.doPrivileged(new 
StartAction(subsystem, subsystem, subsystem, Restriction.INSTALL_ONLY));
+               }
+       }
 
        @Override
        public List<Capability> findProviders(Requirement requirement) {
+               installDependenciesOfRequirerIfNecessary(requirement);
                ArrayList<Capability> result = new ArrayList<Capability>();
                try {
                        // Only check the system repository for osgi.ee and 
osgi.native
@@ -160,7 +194,8 @@ public class ResolveContext extends org.
        }
 
        private boolean addDependenciesFromSystemRepository(Requirement 
requirement, List<Capability> capabilities) throws BundleException, 
IOException, InvalidSyntaxException, URISyntaxException {
-               return addDependencies(systemRepository, requirement, 
capabilities, true);
+               boolean result = addDependencies(systemRepository, requirement, 
capabilities, true);
+               return result;
        }
 
        private void addValidCapabilities(Collection<Capability> from, 
Collection<Capability> to, Requirement requirement, boolean validate) throws 
BundleException, IOException, InvalidSyntaxException, URISyntaxException {

Modified: 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/StartAction.java
URL: 
http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/StartAction.java?rev=1727424&r1=1727423&r2=1727424&view=diff
==============================================================================
--- 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/StartAction.java
 (original)
+++ 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/StartAction.java
 Thu Jan 28 19:27:49 2016
@@ -19,7 +19,7 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
-import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map.Entry;
 import java.util.Set;
@@ -57,155 +57,283 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 public class StartAction extends AbstractAction {
-       private static final Logger logger = 
LoggerFactory.getLogger(StartAction.class);
+       public static enum Restriction {
+               NONE,
+               INSTALL_ONLY,
+               RESOLVE_ONLY
+       }
        
-       private static final ThreadLocal<Set<Subsystem>> 
subsystemsStartingOnCurrentThread = new ThreadLocal<Set<Subsystem>>() {
-               @Override
-               protected Set<Subsystem> initialValue() {
-                       return new HashSet<Subsystem>();
-               }
-       };
+       private static final Logger logger = 
LoggerFactory.getLogger(StartAction.class);
 
        private final Coordination coordination;
        private final BasicSubsystem instigator;
-       private final boolean resolveOnly;
+       private final Restriction restriction;
 
        public StartAction(BasicSubsystem instigator, BasicSubsystem requestor, 
BasicSubsystem target) {
-               this(instigator, requestor, target, false);
+               this(instigator, requestor, target, Restriction.NONE);
        }
 
-       public StartAction(BasicSubsystem instigator, BasicSubsystem requestor, 
BasicSubsystem target, boolean resolveOnly) {
-               this(instigator, requestor, target, null, resolveOnly);
+       public StartAction(BasicSubsystem instigator, BasicSubsystem requestor, 
BasicSubsystem target, Restriction restriction) {
+               this(instigator, requestor, target, null, restriction);
        }
        
        public StartAction(BasicSubsystem instigator, BasicSubsystem requestor, 
BasicSubsystem target, Coordination coordination) {
-               this(instigator, requestor, target, coordination, false);
+               this(instigator, requestor, target, coordination, 
Restriction.NONE);
        }
        
-       public StartAction(BasicSubsystem instigator, BasicSubsystem requestor, 
BasicSubsystem target, Coordination coordination, boolean resolveOnly) {
+       public StartAction(BasicSubsystem instigator, BasicSubsystem requestor, 
BasicSubsystem target, Coordination coordination, Restriction restriction) {
                super(requestor, target, false);
                this.instigator = instigator;
                this.coordination = coordination;
-               this.resolveOnly = resolveOnly;
+               this.restriction = restriction;
        }
        
-       private Object doRun() {
-               // TODO We now support circular dependencies so a sane locking 
strategy
-               // is required now more than ever before. Needs to be much more 
granular
-               // (and complex) than this. Perhaps something along the lines 
of a state
-               // change lock per subsystem then use a global lock only while 
acquiring
-               // the necessary state change locks.
-               synchronized (StartAction.class) {
-                       State state = target.getState();
-                   // The following states are illegal.
-                   if (EnumSet.of(State.INSTALL_FAILED, State.UNINSTALLED, 
State.UNINSTALLING).contains(state))
-                       throw new SubsystemException("Cannot start from state " 
+ state);
-                   // The following states must wait with the exception of 
INSTALLING
-                   // combined with 
apache-aries-provision-dependencies:=resolve.
-                   if ((State.INSTALLING.equals(state) 
-                               && Utils.isProvisionDependenciesInstall(target))
-                                   || EnumSet.of(State.RESOLVING, 
State.STARTING, State.STOPPING).contains(state)) {
-                       waitForStateChange(state);
-                       return new StartAction(instigator, requestor, target, 
coordination).run();
-                   }
-                   // The following states mean the requested state has 
already been attained.
-                   if (State.ACTIVE.equals(state))
-                       return null;
-                   // Always start if target is content of requestor.
-                   if (!Utils.isContent(requestor, target)) {
-                       // Always start if target is a dependency of requestor.
-                       if (!Utils.isDependency(requestor, target)) {
-                           // Always start if instigator equals target 
(explicit start).
-                           if (!instigator.equals(target)) {
-                               // Don't start if instigator is root (restart) 
and target is not ready.
-                               if (instigator.isRoot() && 
!target.isReadyToStart()) {
-                                   return null;
-                               }
-                           }
-                       }
-                   }
-                   Coordination coordination = this.coordination;
-                   if (coordination == null) {
-                       coordination = Utils.createCoordination(target);
-                   }
-                   try {
-                       // If necessary, install the dependencies.
-                       if (State.INSTALLING.equals(target.getState()) && 
-                                       
!Utils.isProvisionDependenciesInstall(target)) {
-                               // The following line is necessary in order to 
ensure that
-                               // the export sharing policies of composites 
are in place
-                               // for capability validation.
-                               
setExportPolicyOfAllInstallingSubsystemsWithProvisionDependenciesResolve(coordination);
-                               Collection<Subsystem> subsystems = new 
ArrayList<Subsystem>();
-                               
subsystems.addAll(Activator.getInstance().getSubsystems().getChildren(target));
-                               subsystems.addAll(target.getParents());
-                               for (Subsystem subsystem : subsystems) {
-                                       if 
(State.INSTALLING.equals(subsystem.getState())) {
-                                               BasicSubsystem bs = 
(BasicSubsystem)subsystem;
-                                               
bs.computeDependenciesPostInstallation();
-                                   new InstallDependencies().install(bs, null, 
coordination);
-                                   bs.setState(State.INSTALLED);
-                                       }
-                               }
-                               target.computeDependenciesPostInstallation();
-                           new InstallDependencies().install(target, null, 
coordination);
-                           target.setState(State.INSTALLED);
-                       }
-                       // Resolve if necessary.
-                       if (State.INSTALLED.equals(target.getState()))
-                           resolve(target, coordination);
-                       if (resolveOnly)
-                           return null;
-                       target.setState(State.STARTING);
-                       // TODO Need to hold a lock here to guarantee that 
another start
-                       // operation can't occur when the state goes to 
RESOLVED.
-                       // Start the subsystem.
-                       List<Resource> resources = new 
ArrayList<Resource>(Activator.getInstance().getSubsystems().getResourcesReferencedBy(target));
-                       SubsystemContentHeader header = 
target.getSubsystemManifest().getSubsystemContentHeader();
-                       if (header != null)
-                           Collections.sort(resources, new 
StartResourceComparator(header));
-                       for (Resource resource : resources)
-                           startResource(resource, coordination);
-                       target.setState(State.ACTIVE);
-                   } catch (Throwable t) {
-                       coordination.fail(t);
-                       // TODO Need to reinstate complete isolation by 
disconnecting the
-                       // region and transition to INSTALLED.
-                   } finally {
-                       try {
-                           // Don't end the coordination if the subsystem 
being started
-                           // (i.e. the target) did not begin it.
-                           if 
(coordination.getName().equals(Utils.computeCoordinationName(target)))
-                               coordination.end();
-                       } catch (CoordinationException e) {
-                               // If the target's state is INSTALLING then 
installing the
-                               // dependencies failed, in which case we want 
to leave it as is.
-                               if 
(!State.INSTALLING.equals(target.getState())) {
-                                       target.setState(State.RESOLVED);
-                               }
-                           Throwable t = e.getCause();
-                           if (t instanceof SubsystemException)
-                               throw (SubsystemException)t;
-                           throw new SubsystemException(t);
-                       }
-                   }
-                   return null;
+       private static boolean isTargetStartable(BasicSubsystem instigator, 
BasicSubsystem requestor, BasicSubsystem target) {
+               State state = target.getState();
+           // The following states are illegal.
+           if (EnumSet.of(State.INSTALL_FAILED, 
State.UNINSTALLED).contains(state))
+               throw new SubsystemException("Cannot start from state " + 
state);
+           // The following states mean the requested state has already been 
attained.
+           if (State.ACTIVE.equals(state))
+               return false;
+               // Always start if target is content of requestor.
+           if (!Utils.isContent(requestor, target)) {
+               // Always start if target is a dependency of requestor.
+               if (!Utils.isDependency(requestor, target)) {
+                   // Always start if instigator equals target (explicit 
start).
+                   if (!instigator.equals(target)) {
+                       // Don't start if instigator is root (restart) and 
target is not ready.
+                       if (instigator.isRoot() && !target.isReadyToStart()) {
+                           return false;
+                       }
+                   }
+               }
+           }
+           return true;
+       }
+       
+       private void installDependencies(BasicSubsystem target, Coordination 
coordination) throws Exception {
+               for (Subsystem parent : target.getParents()) {
+                       AccessController.doPrivileged(new 
StartAction(instigator, target, (BasicSubsystem)parent, coordination, 
Restriction.INSTALL_ONLY));
+               }
+               
installDependencies(Collections.<Subsystem>singletonList(target), coordination);
+               for (Subsystem child : 
Activator.getInstance().getSubsystems().getChildren(target)) {
+                       AccessController.doPrivileged(new 
StartAction(instigator, target, (BasicSubsystem)child, coordination, 
Restriction.INSTALL_ONLY));
+               }
+       }
+       
+       private static void installDependencies(Collection<Subsystem> 
subsystems, Coordination coordination) throws Exception {
+               for (Subsystem subsystem : subsystems) {
+                       if (State.INSTALLING.equals(subsystem.getState())) {
+                               BasicSubsystem bs = (BasicSubsystem)subsystem;
+                               
bs.computeDependenciesPostInstallation(coordination);
+                               new InstallDependencies().install(bs, null, 
coordination);
+                               bs.setState(State.INSTALLED);
+                       }
+               }
+       }
+       
+       private Coordination createCoordination() {
+               Coordination coordination = this.coordination;
+           if (coordination == null) {
+               coordination = Utils.createCoordination(target);
+           }
+           return coordination;
+       }
+       
+       private static LinkedHashSet<BasicSubsystem> 
computeAffectedSubsystems(BasicSubsystem target) {
+               LinkedHashSet<BasicSubsystem> result = new 
LinkedHashSet<BasicSubsystem>();
+               Subsystems subsystems = Activator.getInstance().getSubsystems();
+               for (Resource dep : 
subsystems.getResourcesReferencedBy(target)) {
+                       if (dep instanceof BasicSubsystem 
+                                       && 
!subsystems.getChildren(target).contains(dep)) {
+                               result.add((BasicSubsystem)dep);
+                       }
+                       else if (dep instanceof BundleRevision) {
+                               BundleConstituent constituent = new 
BundleConstituent(null, (BundleRevision)dep);
+                               if 
(!target.getConstituents().contains(constituent)) {
+                                       for (BasicSubsystem constituentOf : 
subsystems.getSubsystemsByConstituent(
+                                                       new 
BundleConstituent(null, (BundleRevision)dep))) {
+                                               result.add(constituentOf);
+                                       }
+                               }
+                       }
                }
+               for (Subsystem child : subsystems.getChildren(target)) {
+                       result.add((BasicSubsystem)child);
+               }
+               for (Resource resource : 
target.getResource().getSharedContent()) {
+                       for (BasicSubsystem constituentOf : 
subsystems.getSubsystemsByConstituent(
+                                       resource instanceof BundleRevision ? 
new BundleConstituent(null, (BundleRevision)resource) : resource)) {
+                               result.add(constituentOf);
+                       }
+               }
+               result.add(target);
+               return result;
        }
 
        @Override
        public Object run() {
-               Set<Subsystem> subsystems = 
subsystemsStartingOnCurrentThread.get();
-               if (subsystems.contains(target)) {
+               // Protect against re-entry now that cycles are supported.
+               if (!LockingStrategy.set(State.STARTING, target)) {
                        return null;
                }
-               subsystems.add(target);
-           try {
-               return doRun();
-           }
-           finally {
-               subsystems.remove(target);
-           }
+               try {
+                       Collection<BasicSubsystem> subsystems;
+                       // We are now protected against re-entry.
+                       // If necessary, install the dependencies.
+               if (State.INSTALLING.equals(target.getState()) && 
!Utils.isProvisionDependenciesInstall(target)) {
+                       // Acquire the global write lock while installing 
dependencies.
+                               LockingStrategy.writeLock();
+                               try {
+                                       // We are now protected against 
installs, starts, stops, and uninstalls.
+                               // We need a separate coordination when 
installing 
+                                       // dependencies because cleaning up the 
temporary export 
+                                       // sharing policies must be done while 
holding the write lock.
+                               Coordination c = 
Utils.createCoordination(target);
+                               try {
+                                       installDependencies(target, c);
+                                       // Associated subsystems must be 
computed after all dependencies 
+                                               // are installed because some 
of the dependencies may be 
+                                               // subsystems. This is safe to 
do while only holding the read
+                                               // lock since we know that 
nothing can be added or removed.
+                                               subsystems = 
computeAffectedSubsystems(target);
+                                               for (BasicSubsystem subsystem : 
subsystems) {
+                                                       if 
(State.INSTALLING.equals(subsystem.getState())
+                                                                       && 
!Utils.isProvisionDependenciesInstall(subsystem)) {
+                                                               
installDependencies(subsystem, c);
+                                                       }
+                                               }
+                                               // Downgrade to the read lock 
in order to prevent 
+                                       // installs and uninstalls but allow 
starts and stops.
+                                               LockingStrategy.readLock();
+                               }
+                               catch (Throwable t) {
+                                       c.fail(t);
+                               }
+                               finally {
+                                       // This will clean up the temporary 
export sharing
+                                       // policies. Must be done while holding 
the write lock.
+                                       c.end();
+                               }
+                               }
+                               finally {
+                                       // Release the global write lock as 
soon as possible.
+                                       LockingStrategy.writeUnlock();
+                               }
+               }
+               else {
+                       // Acquire the read lock in order to prevent installs 
and
+                       // uninstalls but allow starts and stops.
+                       LockingStrategy.readLock();
+               }
+               try {
+                       // We now hold the read lock and are protected against 
installs
+                       // and uninstalls.
+                       if (Restriction.INSTALL_ONLY.equals(restriction)) {
+                                       return null;
+                               }
+                       // Compute associated subsystems here in case (1) they 
weren't
+                       // computed previously while holding the write lock or 
(2) they
+                       // were computed previously and more were subsequently 
added. 
+                               // This is safe to do while only holding the 
read lock since we
+                               // know that nothing can be added or removed.
+                               subsystems = computeAffectedSubsystems(target);
+                               // Acquire the global mutual exclusion lock 
while acquiring the
+                               // state change locks of affected subsystems.
+                               LockingStrategy.lock();
+                               try {
+                                       // We are now protected against cycles.
+                                       // Acquire the state change locks of 
affected subsystems.
+                                       LockingStrategy.lock(subsystems);
+                               }
+                               finally {
+                                       // Release the global mutual exclusion 
lock as soon as possible.
+                                       LockingStrategy.unlock();
+                               }
+                               Coordination coordination = this.coordination;
+                               try {
+                                       coordination = createCoordination();
+                                       // We are now protected against other 
starts and stops of the affected subsystems.
+                                       if (!isTargetStartable(instigator, 
requestor, target)) {
+                                               return null;
+                                       }
+                                       
+                                       // Resolve if necessary.
+                                       if 
(State.INSTALLED.equals(target.getState()))
+                                               resolve(instigator, target, 
target, coordination, subsystems);
+                                       if 
(Restriction.RESOLVE_ONLY.equals(restriction))
+                                               return null;
+                                       target.setState(State.STARTING);
+                                       // Be sure to set the state back to 
RESOLVED if starting fails.
+                                       coordination.addParticipant(new 
Participant() {
+                                               @Override
+                                               public void ended(Coordination 
coordination) throws Exception {
+                                                       // Nothing.
+                                               }
+
+                                               @Override
+                                               public void failed(Coordination 
coordination) throws Exception {
+                                                       
target.setState(State.RESOLVED);
+                                               }
+                                       });
+                                       for (BasicSubsystem subsystem : 
subsystems) {
+                                               if (!target.equals(subsystem)) {
+                                                       
startSubsystemResource(subsystem, coordination);
+                                               }
+                                       }
+                                       List<Resource> resources = new 
ArrayList<Resource>(Activator.getInstance().getSubsystems().getResourcesReferencedBy(target));
+                                       SubsystemContentHeader header = 
target.getSubsystemManifest().getSubsystemContentHeader();
+                                       if (header != null)
+                                               Collections.sort(resources, new 
StartResourceComparator(header));
+                                       for (Resource resource : resources)
+                                               startResource(resource, 
coordination);
+                                       target.setState(State.ACTIVE);
+                                       
+                               }
+                               catch (Throwable t) {
+                                       // We catch exceptions and fail the 
coordination here to
+                                       // ensure we are still holding the 
state change locks when
+                                       // the participant sets the state to 
RESOLVED.
+                                       coordination.fail(t);
+                               }
+                               finally {
+                                       try {
+                                               // Don't end a coordination 
that was not begun as part
+                                               // of this start action.
+                                               if 
(coordination.getName().equals(Utils.computeCoordinationName(target))) {
+                                                       coordination.end();
+                                               }
+                                       }
+                                       finally {
+                                               // Release the state change 
locks of affected subsystems.
+                                               
LockingStrategy.unlock(subsystems);
+                                       }
+                               }
+               }
+               finally {
+                               // Release the read lock.
+                               LockingStrategy.readUnlock();
+                       }
+               }
+               catch (CoordinationException e) {
+                       Throwable t = e.getCause();
+                       if (t == null) {
+                               throw new SubsystemException(e);
+                       }
+                       if (t instanceof SecurityException) {
+                               throw (SecurityException)t;
+                       }
+                       if (t instanceof SubsystemException) {
+                               throw (SubsystemException)t;
+                       }
+                       throw new SubsystemException(t);
+               }
+               finally {
+                       // Protection against re-entry no longer required.
+                       LockingStrategy.unset(State.STARTING, target);
+               }
+               return null;
        }
 
        private static Collection<Bundle> getBundles(BasicSubsystem subsystem) {
@@ -233,41 +361,21 @@ public class StartAction extends Abstrac
                        subsystem.setState(State.RESOLVED);
        }
        
-       private static void resolveSubsystems(BasicSubsystem subsystem, 
Coordination coordination) {
-               //resolve dependencies to ensure framework resolution succeeds
-               Subsystems subsystems = Activator.getInstance().getSubsystems();
-               for (Resource dep : 
subsystems.getResourcesReferencedBy(subsystem)) {
-                       if (dep instanceof BasicSubsystem 
-                                       && 
!subsystems.getChildren(subsystem).contains(dep)) {
-                               resolveSubsystem((BasicSubsystem)dep, 
coordination);
-                       }
-                       else if (dep instanceof BundleRevision
-                                       && 
!subsystem.getConstituents().contains(dep)) {
-                               for (BasicSubsystem constituentOf : 
subsystems.getSubsystemsByConstituent(
-                                               new BundleConstituent(null, 
(BundleRevision)dep))) {
-                                       resolveSubsystem(constituentOf, 
coordination);
-                               }
-                       }
-               }
-               for (Subsystem child : subsystems.getChildren(subsystem)) {
-                       resolveSubsystem((BasicSubsystem)child, coordination);
-               }
-               for (Resource resource : 
subsystem.getResource().getSharedContent()) {
-                       for (BasicSubsystem constituentOf : 
subsystems.getSubsystemsByConstituent(
-                                       resource instanceof BundleRevision ? 
new BundleConstituent(null, (BundleRevision)resource) : resource)) {
-                               resolveSubsystem(constituentOf, coordination);
-                       }
+       private static void resolveSubsystems(BasicSubsystem instigator, 
BasicSubsystem target, Coordination coordination, Collection<BasicSubsystem> 
subsystems) throws Exception {
+               for (BasicSubsystem subsystem : subsystems) {
+                       resolveSubsystem(instigator, target, subsystem, 
coordination);
                }
        }
        
-       private static void resolveSubsystem(BasicSubsystem subsystem, 
Coordination coordination) {
+       private static void resolveSubsystem(BasicSubsystem instigator, 
BasicSubsystem target, BasicSubsystem subsystem, Coordination coordination) 
throws Exception {
                State state = subsystem.getState();
                if (State.INSTALLED.equals(state)) {
-                       AccessController.doPrivileged(new 
StartAction(subsystem, subsystem, subsystem, coordination, true));
-               }
-               else if (State.INSTALLING.equals(state)
-                               && 
!Utils.isProvisionDependenciesInstall(subsystem)) {
-                       AccessController.doPrivileged(new 
StartAction(subsystem, subsystem, subsystem, coordination, true));
+                       if (target.equals(subsystem)) {
+                               resolve(instigator, target, subsystem, 
coordination, Collections.<BasicSubsystem>emptyList());
+                       }
+                       else {
+                               AccessController.doPrivileged(new 
StartAction(instigator, target, subsystem, coordination, 
Restriction.RESOLVE_ONLY));
+                       }
                }
        }
        
@@ -282,7 +390,7 @@ public class StartAction extends Abstrac
                }
        }
 
-       private static void resolve(BasicSubsystem subsystem, Coordination 
coordination) {
+       private static void resolve(BasicSubsystem instigator, BasicSubsystem 
target, BasicSubsystem subsystem, Coordination coordination, 
Collection<BasicSubsystem> subsystems) {
                emitResolvingEvent(subsystem);
                try {
                        // The root subsystem should follow the same event 
pattern for
@@ -291,7 +399,7 @@ public class StartAction extends Abstrac
                        // actually doing the resolution work.
                        if (!subsystem.isRoot()) {
                                setExportIsolationPolicy(subsystem, 
coordination);
-                               resolveSubsystems(subsystem, coordination);
+                               resolveSubsystems(instigator, target, 
coordination, subsystems);
                                resolveBundles(subsystem);
                        }
                        emitResolvedEvent(subsystem);
@@ -491,8 +599,11 @@ public class StartAction extends Abstrac
         return false;
     }
 
-       private void startSubsystemResource(Resource resource, Coordination 
coordination) throws IOException {
+       private void startSubsystemResource(Resource resource, final 
Coordination coordination) throws IOException {
                final BasicSubsystem subsystem = (BasicSubsystem)resource;
+               if (!isTargetStartable(instigator, target, subsystem)) {
+                        return;
+               }
                // Subsystems that are content resources of another subsystem 
must have
                // their autostart setting set to started.
                if (Utils.isContent(this.target, subsystem))
@@ -560,7 +671,7 @@ public class StartAction extends Abstrac
                logger.error(diagnostics.toString());
        }
        
-       private static void 
setExportPolicyOfAllInstallingSubsystemsWithProvisionDependenciesResolve(Coordination
 coordination) throws InvalidSyntaxException {
+       static void 
setExportPolicyOfAllInstallingSubsystemsWithProvisionDependenciesResolve(Coordination
 coordination) throws InvalidSyntaxException {
                for (BasicSubsystem subsystem : 
Activator.getInstance().getSubsystems().getSubsystems()) {
                        if (!State.INSTALLING.equals(subsystem.getState())
                                        || 
Utils.isProvisionDependenciesInstall(subsystem)) {

Modified: 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/StopAction.java
URL: 
http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/StopAction.java?rev=1727424&r1=1727423&r2=1727424&view=diff
==============================================================================
--- 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/StopAction.java
 (original)
+++ 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/StopAction.java
 Thu Jan 28 19:27:49 2016
@@ -17,6 +17,7 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.EnumSet;
+import java.util.LinkedHashSet;
 import java.util.List;
 
 import org.apache.aries.subsystem.ContentHandler;
@@ -42,54 +43,102 @@ public class StopAction extends Abstract
 
        @Override
        public Object run() {
-               checkRoot();
-               State state = target.getState();
-               if (EnumSet.of(State.INSTALLED, State.RESOLVED).contains(state))
+               // Protect against re-entry now that cycles are supported.
+               if (!LockingStrategy.set(State.STOPPING, target)) {
                        return null;
-               else if (EnumSet.of(State.INSTALL_FAILED, State.UNINSTALLING, 
State.UNINSTALLED).contains(state))
-                       throw new IllegalStateException("Cannot stop from state 
" + state);
-               else if (State.INSTALLING.equals(state) && 
!Utils.isProvisionDependenciesInstall(target)) {
-                       return null;
-               }
-               else if (EnumSet.of(State.INSTALLING, State.RESOLVING, 
State.STARTING, State.STOPPING).contains(state)) {
-                       waitForStateChange(state);
-                       return new StopAction(requestor, target, 
disableRootCheck).run();
                }
-               target.setState(State.STOPPING);
-               List<Resource> resources = new 
ArrayList<Resource>(Activator.getInstance().getSubsystems().getResourcesReferencedBy(target));
-               SubsystemContentHeader header = 
target.getSubsystemManifest().getSubsystemContentHeader();
-               if (header != null) {
-                       Collections.sort(resources, new 
StartResourceComparator(target.getSubsystemManifest().getSubsystemContentHeader()));
-                       Collections.reverse(resources);
-               }
-               for (Resource resource : resources) {
-                       // Don't stop the region context bundle.
-                       if (Utils.isRegionContextBundle(resource))
-                               continue;
+               try {
+                       // We are now protected against re-entry.
+                       // Acquire the global read lock to prevent installs and 
uninstalls
+                       // but allow starts and stops.
+                       LockingStrategy.readLock();
                        try {
-                               stopResource(resource);
+                               // We are now protected against installs and 
uninstalls.
+                               checkRoot();
+                               // Compute affected subsystems. This is safe to 
do while only 
+                               // holding the read lock since we know that 
nothing can be added 
+                               // or removed.
+                               LinkedHashSet<BasicSubsystem> subsystems = new 
LinkedHashSet<BasicSubsystem>();
+                               subsystems.add(target);
+                               List<Resource> resources = new 
ArrayList<Resource>(Activator.getInstance().getSubsystems().getResourcesReferencedBy(target));
+                               for (Resource resource : resources) {
+                                       if (resource instanceof BasicSubsystem) 
{
+                                               
subsystems.add((BasicSubsystem)resource);
+                                       }
+                               }
+                               // Acquire the global mutual exclusion lock 
while acquiring the
+                               // state change locks of affected subsystems.
+                               LockingStrategy.lock();
+                               try {
+                                       // We are now protected against cycles.
+                                       // Acquire the state change locks of 
affected subsystems.
+                                       LockingStrategy.lock(subsystems);
+                               }
+                               finally {
+                                       // Release the global mutual exclusion 
lock as soon as possible.
+                                       LockingStrategy.unlock();
+                               }
+                               try {
+                                       // We are now protected against other 
starts and stops of the affected subsystems.
+                                       State state = target.getState();
+                                       if (EnumSet.of(State.INSTALLED, 
State.INSTALLING, State.RESOLVED).contains(state)) {
+                                               // INSTALLING is included 
because a subsystem may
+                                               // persist in this state 
without being locked when
+                                               // 
apache-aries-provision-dependencies:=resolve.
+                                               return null;
+                                       }
+                                       else if 
(EnumSet.of(State.INSTALL_FAILED, State.UNINSTALLED).contains(state)) {
+                                               throw new 
IllegalStateException("Cannot stop from state " + state);
+                                       }
+                                       target.setState(State.STOPPING);
+                                       SubsystemContentHeader header = 
target.getSubsystemManifest().getSubsystemContentHeader();
+                                       if (header != null) {
+                                               Collections.sort(resources, new 
StartResourceComparator(target.getSubsystemManifest().getSubsystemContentHeader()));
+                                               Collections.reverse(resources);
+                                       }
+                                       for (Resource resource : resources) {
+                                               // Don't stop the region 
context bundle.
+                                               if 
(Utils.isRegionContextBundle(resource))
+                                                       continue;
+                                               try {
+                                                       stopResource(resource);
+                                               }
+                                               catch (Exception e) {
+                                                       logger.error("An error 
occurred while stopping resource " + resource + " of subsystem " + target, e);
+                                               }
+                                       }
+                                       // TODO Can we automatically assume it 
actually is resolved?
+                                       target.setState(State.RESOLVED);
+                                       try {
+                                               synchronized (target) {
+                                                       
target.setDeploymentManifest(new DeploymentManifest(
+                                                                       
target.getDeploymentManifest(),
+                                                                       null,
+                                                                       
target.isAutostart(),
+                                                                       
target.getSubsystemId(),
+                                                                       
SubsystemIdentifier.getLastId(),
+                                                                       
target.getLocation(),
+                                                                       false,
+                                                                       false));
+                                               }
+                                       }
+                                       catch (Exception e) {
+                                               throw new SubsystemException(e);
+                                       }
+                               }
+                               finally {
+                                       // Release the state change locks of 
affected subsystems.
+                                       LockingStrategy.unlock(subsystems);
+                               }
                        }
-                       catch (Exception e) {
-                               logger.error("An error occurred while stopping 
resource " + resource + " of subsystem " + target, e);
-                       }
-               }
-               // TODO Can we automatically assume it actually is resolved?
-               target.setState(State.RESOLVED);
-               try {
-                       synchronized (target) {
-                               target.setDeploymentManifest(new 
DeploymentManifest(
-                                               target.getDeploymentManifest(),
-                                               null,
-                                               target.isAutostart(),
-                                               target.getSubsystemId(),
-                                               SubsystemIdentifier.getLastId(),
-                                               target.getLocation(),
-                                               false,
-                                               false));
+                       finally {
+                               // Release the read lock.
+                               LockingStrategy.readUnlock();
                        }
                }
-               catch (Exception e) {
-                       throw new SubsystemException(e);
+               finally {
+                       // Protection against re-entry no longer required.
+                       LockingStrategy.unset(State.STOPPING, target);
                }
                return null;
        }

Modified: 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemResource.java
URL: 
http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemResource.java?rev=1727424&r1=1727423&r2=1727424&view=diff
==============================================================================
--- 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemResource.java
 (original)
+++ 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemResource.java
 Thu Jan 28 19:27:49 2016
@@ -87,18 +87,18 @@ public class SubsystemResource implement
        private final Collection<Resource> sharedContent = new 
HashSet<Resource>();
        private final Collection<Resource> sharedDependencies = new 
HashSet<Resource>();
 
-       public SubsystemResource(String location, IDirectory content, 
BasicSubsystem parent) throws URISyntaxException, IOException, 
ResolutionException, BundleException, InvalidSyntaxException {
-               this(new RawSubsystemResource(location, content, parent), 
parent);
+       public SubsystemResource(String location, IDirectory content, 
BasicSubsystem parent, Coordination coordination) throws URISyntaxException, 
IOException, ResolutionException, BundleException, InvalidSyntaxException {
+               this(new RawSubsystemResource(location, content, parent), 
parent, coordination);
        }
 
-       public SubsystemResource(RawSubsystemResource resource, BasicSubsystem 
parent) throws IOException, BundleException, InvalidSyntaxException, 
URISyntaxException {
+       public SubsystemResource(RawSubsystemResource resource, BasicSubsystem 
parent, Coordination coordination) throws IOException, BundleException, 
InvalidSyntaxException, URISyntaxException {
                this.parent = parent;
                this.resource = resource;
                computeContentResources(resource.getDeploymentManifest());
                capabilities = computeCapabilities();
                if 
(this.getSubsystemManifest().getSubsystemTypeHeader().getAriesProvisionDependenciesDirective().isInstall())
 {
                    /* compute dependencies now only if we intend to provision 
them during install */
-               computeDependencies(resource.getDeploymentManifest());          
    
+               computeDependencies(resource.getDeploymentManifest(), 
coordination);                
                }
                deploymentManifest = computeDeploymentManifest();
        }
@@ -122,7 +122,7 @@ public class SubsystemResource implement
                capabilities = computeCapabilities();
         if 
(getSubsystemManifest().getSubsystemTypeHeader().getAriesProvisionDependenciesDirective().isInstall())
 {
             /* compute dependencies if we intend to provision them during 
install */
-            computeDependencies(resource.getDeploymentManifest());          
+            computeDependencies(resource.getDeploymentManifest(), null);       
   
         }
        }
 
@@ -367,9 +367,9 @@ public class SubsystemResource implement
                }
        }
 
-       void computeDependencies(DeploymentManifest manifest) {
+       void computeDependencies(DeploymentManifest manifest, Coordination 
coordination) {
            if (manifest == null) {
-               computeDependencies(getSubsystemManifest());
+               computeDependencies(getSubsystemManifest(), coordination);
            }
            else {
                ProvisionResourceHeader header = 
manifest.getProvisionResourceHeader();
@@ -384,9 +384,13 @@ public class SubsystemResource implement
            }
        }
 
-       private void computeDependencies(SubsystemManifest manifest)  {
+       private void computeDependencies(SubsystemManifest manifest, 
Coordination coordination)  {
                SubsystemContentHeader contentHeader = 
manifest.getSubsystemContentHeader();
                try {
+                       // The following line is necessary in order to ensure 
that the
+                       // export sharing policies of composites are in place 
for capability
+                       // validation.
+                       
StartAction.setExportPolicyOfAllInstallingSubsystemsWithProvisionDependenciesResolve(coordination);
                        Map<Resource, List<Wire>> resolution = 
Activator.getInstance().getResolver().resolve(createResolveContext());
                        setImportIsolationPolicy(resolution);
                        for (Map.Entry<Resource, List<Wire>> entry : 
resolution.entrySet()) {

Modified: 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemResourceInstaller.java
URL: 
http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemResourceInstaller.java?rev=1727424&r1=1727423&r2=1727424&view=diff
==============================================================================
--- 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemResourceInstaller.java
 (original)
+++ 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/SubsystemResourceInstaller.java
 Thu Jan 28 19:27:49 2016
@@ -83,15 +83,15 @@ public class SubsystemResourceInstaller
                }
                Comparator<Resource> comparator = new 
InstallResourceComparator();
                // Install dependencies first if appropriate...
-               if (Utils.isProvisionDependenciesInstall(subsystem)) {
-                   new InstallDependencies().install(subsystem, 
this.subsystem, coordination);
+               if (!subsystem.isRoot() && 
Utils.isProvisionDependenciesInstall(subsystem)) {
+                       new InstallDependencies().install(subsystem, 
this.subsystem, coordination);
                }
                // ...followed by content.
                // Simulate installation of shared content so that necessary 
relationships are established.
-        for (Resource content : subsystem.getResource().getSharedContent()) {
-            ResourceInstaller.newInstance(coordination, content, 
subsystem).install();
-        }
-        // Now take care of the installable content.
+               for (Resource content : 
subsystem.getResource().getSharedContent()) {
+                       ResourceInstaller.newInstance(coordination, content, 
subsystem).install();
+               }
+               // Now take care of the installable content.
                if (State.INSTALLING.equals(subsystem.getState())) {
                        List<Resource> installableContent = new 
ArrayList<Resource>(subsystem.getResource().getInstallableContent());
                        Collections.sort(installableContent, comparator);
@@ -101,7 +101,7 @@ public class SubsystemResourceInstaller
                // Only brand new subsystems should have acquired the 
INSTALLING state,
                // in which case an INSTALLED event must be propagated.
                if (State.INSTALLING.equals(subsystem.getState()) && 
-                       Utils.isProvisionDependenciesInstall(subsystem)) {
+                               
Utils.isProvisionDependenciesInstall(subsystem)) {
                        subsystem.setState(State.INSTALLED);
                }
                else {
@@ -112,7 +112,7 @@ public class SubsystemResourceInstaller
        }
 
        private BasicSubsystem installRawSubsystemResource(RawSubsystemResource 
resource) throws Exception {
-               SubsystemResource subsystemResource = new 
SubsystemResource(resource, provisionTo);
+               SubsystemResource subsystemResource = new 
SubsystemResource(resource, provisionTo, coordination);
                return installSubsystemResource(subsystemResource);
        }
 

Modified: 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/UninstallAction.java
URL: 
http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/UninstallAction.java?rev=1727424&r1=1727423&r2=1727424&view=diff
==============================================================================
--- 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/UninstallAction.java
 (original)
+++ 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/UninstallAction.java
 Thu Jan 28 19:27:49 2016
@@ -24,22 +24,35 @@ public class UninstallAction extends Abs
        
        @Override
        public Object run() {
-               checkValid();
-               checkRoot();
-               State state = target.getState();
-               if (EnumSet.of(State.UNINSTALLED).contains(state))
+               // Protect against re-entry now that cycles are supported.
+               if (!LockingStrategy.set(State.STOPPING, target)) {
                        return null;
-               else if ((State.INSTALLING.equals(state) && 
Utils.isProvisionDependenciesInstall(target))
-                               || EnumSet.of(State.RESOLVING, State.STARTING, 
State.STOPPING, State.UNINSTALLING).contains(state)) {
-                       waitForStateChange(state);
-                       target.uninstall();
                }
-               else if (state.equals(State.ACTIVE)) {
-                       new StopAction(requestor, target, 
disableRootCheck).run();
-                       target.uninstall();
+               try {
+                       // Acquire the global write lock to prevent all other 
operations until
+                       // the installation is complete. There is no need to 
hold any other locks.
+                       LockingStrategy.writeLock();
+                       try {
+                               checkRoot();
+                               checkValid();
+                               State state = target.getState();
+                               if 
(EnumSet.of(State.UNINSTALLED).contains(state)) {
+                                       return null;
+                               }
+                               if (state.equals(State.ACTIVE)) {
+                                       new StopAction(requestor, target, 
disableRootCheck).run();
+                               }
+                               ResourceUninstaller.newInstance(requestor, 
target).uninstall();
+                       }
+                       finally {
+                               // Release the global write lock.
+                               LockingStrategy.writeUnlock();
+                       }
+               }
+               finally {
+                       // Protection against re-entry no longer required.
+                       LockingStrategy.unset(State.STOPPING, target);
                }
-               else
-                       ResourceUninstaller.newInstance(requestor, 
target).uninstall();
                return null;
        }
 }

Modified: 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/WovenClassListener.java
URL: 
http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/WovenClassListener.java?rev=1727424&r1=1727423&r2=1727424&view=diff
==============================================================================
--- 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/WovenClassListener.java
 (original)
+++ 
aries/trunk/subsystem/subsystem-core/src/main/java/org/apache/aries/subsystem/core/internal/WovenClassListener.java
 Thu Jan 28 19:27:49 2016
@@ -30,6 +30,7 @@ import java.util.Set;
 import org.apache.aries.subsystem.core.archive.DynamicImportPackageHeader;
 import org.apache.aries.subsystem.core.archive.DynamicImportPackageRequirement;
 import 
org.apache.aries.subsystem.core.internal.BundleResourceInstaller.BundleConstituent;
+import org.apache.aries.subsystem.core.internal.StartAction.Restriction;
 import org.eclipse.equinox.region.Region;
 import org.eclipse.equinox.region.RegionDigraph.FilteredRegion;
 import org.eclipse.equinox.region.RegionDigraphVisitor;
@@ -111,7 +112,7 @@ public class WovenClassListener implemen
                        // package imports to the sharing policy in order to 
minimize 
                        // unpredictable wirings. Resolving the scoped 
subsystem will also
                        // resolve all of the unscoped subsystems in the region.
-                       AccessController.doPrivileged(new 
StartAction(subsystem, subsystem, subsystem, true));
+                       AccessController.doPrivileged(new 
StartAction(subsystem, subsystem, subsystem, Restriction.RESOLVE_ONLY));
                }
                Bundle systemBundle = 
context.getBundle(org.osgi.framework.Constants.SYSTEM_BUNDLE_LOCATION);
                FrameworkWiring frameworkWiring = 
systemBundle.adapt(FrameworkWiring.class);

Modified: 
aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/defect/Aries1383Test.java
URL: 
http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/defect/Aries1383Test.java?rev=1727424&r1=1727423&r2=1727424&view=diff
==============================================================================
--- 
aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/defect/Aries1383Test.java
 (original)
+++ 
aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/defect/Aries1383Test.java
 Thu Jan 28 19:27:49 2016
@@ -1443,7 +1443,7 @@ public class Aries1383Test extends Subsy
                assertConstituent(f1, "b2");
                startSubsystem(f1, false);
                stoppableSubsystems.add(f1);
-               assertState(State.RESOLVED, f2);
+               assertState(EnumSet.of(State.RESOLVED, State.ACTIVE), f2);
                assertConstituent(s1, "b4");
                assertConstituent(s1, "b5");
                assertConstituent(s1, "b6");
@@ -1821,7 +1821,33 @@ public class Aries1383Test extends Subsy
        
        public static class TestServiceImpl implements TestService {}
        
-       public static class TestServiceActivator implements BundleActivator {
+       public static class TestServiceClientActivator implements 
BundleActivator {
+               @Override
+               public void start(BundleContext context) throws Exception {
+                       ServiceReference<TestService> ref = null;
+                       for (int i = 0; i < 80; i++) { // 20 seconds with 250ms 
sleep.
+                               ref = 
context.getServiceReference(TestService.class);
+                               if (ref == null) {
+                                       Thread.sleep(250);
+                                       continue;
+                               }
+                               break;
+                       }
+                       try {
+                               TestService service = context.getService(ref);
+                               service.getClass();
+                       }
+                       finally {
+                               context.ungetService(ref);
+                       }
+               }
+
+               @Override
+               public void stop(BundleContext context) throws Exception {
+               }
+       }
+       
+       public static class TestServiceImplActivator implements BundleActivator 
{
                private ServiceRegistration<TestService> reg;
                
                @Override
@@ -2309,7 +2335,7 @@ public class Aries1383Test extends Subsy
                                                                                
                                .importPackage("org.osgi.framework")
                                                                                
                                .requireBundle("api")
                                                                                
                                .clazz(TestServiceImpl.class)
-                                                                               
                                .activator(TestServiceActivator.class)
+                                                                               
                                .activator(TestServiceImplActivator.class)
                                                                                
                                .build())
                                                                                
.build(),
                                                                false);
@@ -2397,4 +2423,297 @@ public class Aries1383Test extends Subsy
                startFutures.get(1).get();
                startFutures.get(2).get();
        }
+       
+       @Test
+       public void testComApiComImplComClient() throws Exception {
+               Subsystem root = getRootSubsystem();
+               final Subsystem shared = installSubsystem(
+                               root,
+                               "shared", 
+                               new SubsystemArchiveBuilder()
+                                               .symbolicName("shared")
+                                               
.type(SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE 
+                                                               + ';' 
+                                                               + 
AriesProvisionDependenciesDirective.RESOLVE.toString()
+                                                               + ';'
+                                                               + 
SubsystemConstants.PROVISION_POLICY_DIRECTIVE
+                                                               + ":="
+                                                               + 
SubsystemConstants.PROVISION_POLICY_ACCEPT_DEPENDENCIES)
+                                               
.importPackage("org.osgi.framework")
+                                               .build(),
+                               false
+               );
+               uninstallableSubsystems.add(shared);
+               shared.start();
+               stoppableSubsystems.add(shared);
+               @SuppressWarnings("unchecked")
+               Callable<Subsystem>[] installCallables = new Callable[] {
+                               new Callable<Subsystem>() {
+                                       @Override
+                                       public Subsystem call() throws 
Exception {
+                                               Subsystem result = 
installSubsystem(
+                                                               shared,
+                                                               "client", 
+                                                               new 
SubsystemArchiveBuilder()
+                                                                               
.symbolicName("client")
+                                                                               
.type(SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE)
+                                                                               
.content("client;version=\"[0,0]\"")
+                                                                               
.importPackage("org.osgi.framework")
+                                                                               
.requireCapability("osgi.service;filter:=\"(objectClass="
+                                                                               
                + TestService.class.getName()
+                                                                               
                + ")\";effective:=active")
+                                                                               
.importService(TestService.class.getName())
+                                                                               
.requireBundle("api,impl")
+                                                                               
.bundle(
+                                                                               
                "client", 
+                                                                               
                new BundleArchiveBuilder()
+                                                                               
                                .symbolicName("client")
+                                                                               
                                .importPackage("org.osgi.framework")
+                                                                               
                                
.requireCapability("osgi.service;filter:=\"(objectClass="
+                                                                               
                                                + TestService.class.getName()
+                                                                               
                                                + ")\";effective:=active")
+                                                                               
                                .requireBundle("api,impl")
+                                                                               
                                .activator(TestServiceClientActivator.class)
+                                                                               
                                .build())
+                                                                               
.build(),
+                                                               false);
+                                               return result;
+                                       }
+                                       
+                               },
+                               new Callable<Subsystem>() {
+                                       @Override
+                                       public Subsystem call() throws 
Exception {
+                                               Subsystem result = 
installSubsystem(
+                                                               shared,
+                                                               "impl", 
+                                                               new 
SubsystemArchiveBuilder()
+                                                                               
.symbolicName("impl")
+                                                                               
.type(SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE)
+                                                                               
.content("impl;version=\"[0,0]\"")
+                                                                               
.provideCapability("osgi.service;objectClass:List<String>=\"" 
+                                                                               
                + TestService.class.getName() 
+                                                                               
                + "\"")
+                                                                               
.exportService(TestService.class.getName())
+                                                                               
.importPackage("org.osgi.framework")
+                                                                               
.requireBundle("api")
+                                                                               
.provideCapability("osgi.wiring.bundle;osgi.wiring.bundle=impl;bundle-version=0")
+                                                                               
.bundle(
+                                                                               
                "impl", 
+                                                                               
                new BundleArchiveBuilder()
+                                                                               
                                .symbolicName("impl")
+                                                                               
                                
.provideCapability("osgi.service;objectClass:List<String>=\"" 
+                                                                               
                                                + TestService.class.getName() 
+                                                                               
                                                + "\"")
+                                                                               
                                .importPackage("org.osgi.framework")
+                                                                               
                                .requireBundle("api")
+                                                                               
                                .clazz(TestServiceImpl.class)
+                                                                               
                                .activator(TestServiceImplActivator.class)
+                                                                               
                                .build())
+                                                                               
.build(),
+                                                               false);
+                                               return result;
+                                       }
+                                       
+                               },
+                               new Callable<Subsystem>() {
+                                       @Override
+                                       public Subsystem call() throws 
Exception {
+                                               Subsystem result = 
installSubsystem(
+                                                               shared,
+                                                               "api", 
+                                                               new 
SubsystemArchiveBuilder()
+                                                                               
.symbolicName("api")
+                                                                               
.type(SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE)
+                                                                               
.content("api;version=\"[0,0]\"")
+                                                                               
.exportPackage("org.apache.aries.subsystem.itests.defect")
+                                                                               
.provideCapability("osgi.wiring.bundle;osgi.wiring.bundle=api;bundle-version=0")
+                                                                               
.bundle(
+                                                                               
                "api", 
+                                                                               
                new BundleArchiveBuilder()
+                                                                               
                                .symbolicName("api")
+                                                                               
                                
.exportPackage("org.apache.aries.subsystem.itests.defect")
+                                                                               
                                .clazz(TestService.class)
+                                                                               
                                .build())
+                                                                               
.build(),
+                                                               false);
+                                               return result;
+                                       }
+                                       
+                               }
+               };
+               ExecutorService executor = Executors.newFixedThreadPool(3);
+               List<Future<Subsystem>> installFutures = 
executor.invokeAll(Arrays.asList(installCallables));
+               final Subsystem client = installFutures.get(0).get();
+               final Subsystem impl = installFutures.get(1).get();
+               final Subsystem api = installFutures.get(2).get();
+               @SuppressWarnings("unchecked")
+               Callable<Void>[] startCallables = new Callable[] {
+                       new Callable<Void>() {
+                               @Override
+                               public Void call() throws Exception {
+                                       client.start();
+                                       assertEvent(client, State.INSTALLED, 
subsystemEvents.poll(client.getSubsystemId(), 5000));
+                                       assertEvent(client, State.RESOLVING, 
subsystemEvents.poll(client.getSubsystemId(), 5000));
+                                       assertEvent(client, State.RESOLVED, 
subsystemEvents.poll(client.getSubsystemId(), 5000));
+                                       assertEvent(client, State.STARTING, 
subsystemEvents.poll(client.getSubsystemId(), 5000));
+                                       assertEvent(client, State.ACTIVE, 
subsystemEvents.poll(client.getSubsystemId(), 5000));
+                                       return null;
+                               }
+                       },
+                       new Callable<Void>() {
+                               @Override
+                               public Void call() throws Exception {
+                                       impl.start();
+                                       assertEvent(impl, State.INSTALLED, 
subsystemEvents.poll(impl.getSubsystemId(), 5000));
+                                       assertEvent(impl, State.RESOLVING, 
subsystemEvents.poll(impl.getSubsystemId(), 5000));
+                                       assertEvent(impl, State.RESOLVED, 
subsystemEvents.poll(impl.getSubsystemId(), 5000));
+                                       assertEvent(impl, State.STARTING, 
subsystemEvents.poll(impl.getSubsystemId(), 5000));
+                                       assertEvent(impl, State.ACTIVE, 
subsystemEvents.poll(impl.getSubsystemId(), 5000));
+                                       return null;
+                               }
+                       },
+                       new Callable<Void>() {
+                               @Override
+                               public Void call() throws Exception {
+                                       api.start();
+                                       assertEvent(api, State.INSTALLED, 
subsystemEvents.poll(api.getSubsystemId(), 5000));
+                                       assertEvent(api, State.RESOLVING, 
subsystemEvents.poll(api.getSubsystemId(), 5000));
+                                       assertEvent(api, State.RESOLVED, 
subsystemEvents.poll(api.getSubsystemId(), 5000));
+                                       assertEvent(api, State.STARTING, 
subsystemEvents.poll(api.getSubsystemId(), 5000));
+                                       assertEvent(api, State.ACTIVE, 
subsystemEvents.poll(api.getSubsystemId(), 5000));
+                                       return null;
+                               }
+                       }
+               };
+               List<Future<Void>> startFutures = 
executor.invokeAll(Arrays.asList(startCallables));
+               startFutures.get(0).get();
+               startFutures.get(1).get();
+               startFutures.get(2).get();
+       }
+       
+       @Test
+       public void testAutoInstallDependenciesComposite() throws Exception {
+               Subsystem root = getRootSubsystem();
+               Subsystem b = installSubsystem(
+                               root,
+                               "b", 
+                               new SubsystemArchiveBuilder()
+                                               .symbolicName("b")
+                                               
.type(SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE 
+                                                               + ';' 
+                                                               + 
AriesProvisionDependenciesDirective.RESOLVE.toString())
+                                               .content("a;version=\"[0,0]\"")
+                                               .exportPackage("a")
+                                               .importPackage("b")
+                                               .bundle(
+                                                               "a", 
+                                                               new 
BundleArchiveBuilder()
+                                                               
.symbolicName("a")
+                                                               
.importPackage("b")
+                                                               
.exportPackage("a")
+                                                               .build())
+                                               .bundle(
+                                                               "b", 
+                                                               new 
BundleArchiveBuilder()
+                                                               
.symbolicName("b")
+                                                               
.exportPackage("b")
+                                                               .build())
+                                               .build(),
+                               false
+               );
+               uninstallableSubsystems.add(b);
+               try {
+                       Subsystem a = installSubsystem(
+                                       root,
+                                       "a", 
+                                       new SubsystemArchiveBuilder()
+                                                       .symbolicName("a")
+                                                       
.type(SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION)
+                                                       .bundle(
+                                                                       "a", 
+                                                                       new 
BundleArchiveBuilder()
+                                                                       
.symbolicName("a")
+                                                                       
.importPackage("a")
+                                                                       
.build())
+                                                       .build(),
+                                       true
+                       );
+                       uninstallableSubsystems.add(a);
+                       assertState(EnumSet.of(State.INSTALLED, 
State.RESOLVED), b);
+               }
+               catch (Exception e) {
+                       e.printStackTrace();
+                       fail("Subsystem should have installed");
+               }
+       }
+       
+       @Test
+       public void testAutoInstallDependenciesFeature() throws Exception {
+               Subsystem root = getRootSubsystem();
+               Subsystem shared = installSubsystem(
+                               root,
+                               "shared", 
+                               new SubsystemArchiveBuilder()
+                                               .symbolicName("shared")
+                                               
.type(SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE 
+                                                               + ';' 
+                                                               + 
AriesProvisionDependenciesDirective.RESOLVE.toString()
+                                                               + ';'
+                                                               + 
SubsystemConstants.PROVISION_POLICY_DIRECTIVE
+                                                               + ":="
+                                                               + 
SubsystemConstants.PROVISION_POLICY_ACCEPT_DEPENDENCIES)
+                                               .build(),
+                               false
+               );
+               uninstallableSubsystems.add(shared);
+               startSubsystem(shared, false);
+               Subsystem b = installSubsystem(
+                               shared,
+                               "b", 
+                               new SubsystemArchiveBuilder()
+                                               .symbolicName("b")
+                                               
.type(SubsystemConstants.SUBSYSTEM_TYPE_FEATURE)
+                                               .content("a")
+                                               .bundle(
+                                                               "a", 
+                                                               new 
BundleArchiveBuilder()
+                                                               
.symbolicName("a")
+                                                               
.importPackage("b")
+                                                               
.exportPackage("a")
+                                                               .build())
+                                               .bundle(
+                                                               "b", 
+                                                               new 
BundleArchiveBuilder()
+                                                               
.symbolicName("b")
+                                                               
.exportPackage("b")
+                                                               .build())
+                                               .build(),
+                               false
+               );
+               try {
+                       installSubsystem(
+                                       shared,
+                                       "a", 
+                                       new SubsystemArchiveBuilder()
+                                                       .symbolicName("a")
+                                                       
.type(SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION
+                                                                       + ';' 
+                                                                       + 
AriesProvisionDependenciesDirective.INSTALL.toString())
+                                                       .bundle(
+                                                                       "a", 
+                                                                       new 
BundleArchiveBuilder()
+                                                                       
.symbolicName("a")
+                                                                       
.importPackage("a")
+                                                                       
.build())
+                                                       .build(),
+                                       true
+                       );
+                       assertState(EnumSet.of(State.INSTALLED, 
State.RESOLVED), b);
+               }
+               catch (Exception e) {
+                       e.printStackTrace();
+                       fail("Subsystem should have installed");
+               }
+       }
 }

Modified: 
aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/util/SubsystemArchiveBuilder.java
URL: 
http://svn.apache.org/viewvc/aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/util/SubsystemArchiveBuilder.java?rev=1727424&r1=1727423&r2=1727424&view=diff
==============================================================================
--- 
aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/util/SubsystemArchiveBuilder.java
 (original)
+++ 
aries/trunk/subsystem/subsystem-itests/src/test/java/org/apache/aries/subsystem/itests/util/SubsystemArchiveBuilder.java
 Thu Jan 28 19:27:49 2016
@@ -49,6 +49,10 @@ public class SubsystemArchiveBuilder {
                return header(Constants.EXPORT_PACKAGE, value);
        }
        
+       public SubsystemArchiveBuilder exportService(String value) {
+               return header(SubsystemConstants.SUBSYSTEM_EXPORTSERVICE, 
value);
+       }
+       
        public SubsystemArchiveBuilder file(String name, InputStream value) {
                bundle.add(name, value);
                return this;
@@ -63,14 +67,22 @@ public class SubsystemArchiveBuilder {
                return header(Constants.IMPORT_PACKAGE, value);
        }
        
-       public SubsystemArchiveBuilder requireBundle(String value) {
-               return header(Constants.REQUIRE_BUNDLE, value);
+       public SubsystemArchiveBuilder importService(String value) {
+               return header(SubsystemConstants.SUBSYSTEM_IMPORTSERVICE, 
value);
        }
        
        public SubsystemArchiveBuilder provideCapability(String value) {
                return header(Constants.PROVIDE_CAPABILITY, value);
        }
        
+       public SubsystemArchiveBuilder requireBundle(String value) {
+               return header(Constants.REQUIRE_BUNDLE, value);
+       }
+       
+       public SubsystemArchiveBuilder requireCapability(String value) {
+               return header(Constants.REQUIRE_CAPABILITY, value);
+       }
+       
        public SubsystemArchiveBuilder subsystem(String name, InputStream 
value) {
                return file(name + ESA_EXTENSION, value);
        }


Reply via email to