FELIX-658 Optionally skip stopping unaffected bundles when updating a deployment package

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@682377 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/deploymentadmin/service/pom.xml b/deploymentadmin/service/pom.xml
index 8988720..1869d09 100644
--- a/deploymentadmin/service/pom.xml
+++ b/deploymentadmin/service/pom.xml
@@ -33,14 +33,13 @@
     <dependency>
       <groupId>${pom.groupId}</groupId>
       <artifactId>org.osgi.core</artifactId>
-      <version>1.1.0-SNAPSHOT</version>
+      <version>1.0.0</version>
       <scope>provided</scope>
     </dependency>
     <dependency>
       <groupId>${pom.groupId}</groupId>
       <artifactId>org.osgi.compendium</artifactId>
       <version>1.1.0-SNAPSHOT</version>
-      <scope>provided</scope>
     </dependency>
     <dependency>
       <groupId>${pom.groupId}</groupId>
@@ -63,8 +62,8 @@
             <Bundle-Name>Apache Felix Deployment Admin</Bundle-Name>
             <Bundle-Description>A bundle that implements the Deployment Admin.</Bundle-Description>
             <Bundle-Vendor>The Apache Software Foundation</Bundle-Vendor>
-            <Private-Package>org.apache.felix.deploymentadmin.spi</Private-Package>
-            <Export-Package>org.apache.felix.deploymentadmin,org.osgi.service.deploymentadmin.*;version="1.0.0"</Export-Package>
+            <Private-Package>org.apache.felix.deploymentadmin.*</Private-Package>
+            <Export-Package>org.osgi.service.deploymentadmin.*;version="1.0"</Export-Package>
           </instructions>
         </configuration>
       </plugin>
diff --git a/deploymentadmin/service/src/main/java/org/apache/felix/deploymentadmin/spi/StopBundleCommand.java b/deploymentadmin/service/src/main/java/org/apache/felix/deploymentadmin/spi/StopBundleCommand.java
index ee56373..8500f75 100644
--- a/deploymentadmin/service/src/main/java/org/apache/felix/deploymentadmin/spi/StopBundleCommand.java
+++ b/deploymentadmin/service/src/main/java/org/apache/felix/deploymentadmin/spi/StopBundleCommand.java
@@ -19,6 +19,7 @@
 package org.apache.felix.deploymentadmin.spi;
 
 import org.apache.felix.deploymentadmin.AbstractDeploymentPackage;
+import org.apache.felix.deploymentadmin.BundleInfoImpl;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleException;
 import org.osgi.service.deploymentadmin.BundleInfo;
@@ -27,6 +28,12 @@
 
 /**
  * Command that stops all bundles described in the target deployment package of a deployment session.
+ * 
+ * By spec every single bundle of the target package should be stopped, even if this is not strictly necessary 
+ * because of bundles being unaffected by an update. To be able to skip the stopping of unaffected bundles the 
+ * following system property can be defined: <code>org.apache.felix.deploymentadmin.stopunaffectedbundle</code>.
+ * If this property has value <code>false</code> (case insensitive) then unaffected bundles will not be stopped, 
+ * in all other cases the bundles will be stopped according to the OSGi specification.
  */
 public class StopBundleCommand extends Command {
 
@@ -37,8 +44,13 @@
             if (isCancelled()) {
                 throw new DeploymentException(DeploymentException.CODE_CANCELLED);
             }
-            Bundle bundle = target.getBundle(bundleInfos[i].getSymbolicName());
+            String symbolicName = bundleInfos[i].getSymbolicName();
+			Bundle bundle = target.getBundle(symbolicName);
             if (bundle != null) {
+            	String stopUnaffectedBundle = System.getProperty("org.apache.felix.deploymentadmin.stopunaffectedbundle", "true");
+        		if (stopUnaffectedBundle.equalsIgnoreCase("false") && omitBundleStop(session, symbolicName)) {
+        			continue;
+        		}
                 addRollback(new StartBundleRunnable(bundle));
                 try {
                     bundle.stop();
@@ -48,12 +60,34 @@
                 }
             }
             else {
-            	session.getLog().log(LogService.LOG_WARNING, "Could not stop bundle '" + bundleInfos[i].getSymbolicName() + "' because it was not defined int he framework");
+            	session.getLog().log(LogService.LOG_WARNING, "Could not stop bundle '" + symbolicName + "' because it was not defined int he framework");
             }
         }
     }
 
-    private class StartBundleRunnable implements Runnable {
+    /**
+     * Determines whether stopping a bundle is strictly needed.
+     * 
+     * @param session The current deployment session.
+     * @param symbolicName The symbolic name of the bundle to inspect.
+     * 
+     * @return Returns <code>true</code> if <code>Constants.DEPLOYMENTPACKAGE_MISSING</code> is true for the specified bundle in the
+     * source deployment package or if the version of the bundle is the same in both source and target deployment package. Returns 
+     * <code>false</code> otherwise.
+     */
+    private boolean omitBundleStop(DeploymentSessionImpl session, String symbolicName) {
+    	boolean result = false;
+		BundleInfoImpl sourceBundleInfo = session.getSourceAbstractDeploymentPackage().getBundleInfoByName(symbolicName);
+		BundleInfoImpl targetBundleInfo = session.getSourceAbstractDeploymentPackage().getBundleInfoByName(symbolicName);
+		boolean fixPackageMissing = sourceBundleInfo != null  && sourceBundleInfo.isMissing();
+		boolean sameVersion = (targetBundleInfo != null && sourceBundleInfo != null && targetBundleInfo.getVersion().equals(sourceBundleInfo.getVersion()));
+		if (fixPackageMissing || sameVersion) {
+			result = true;
+		}
+		return result;
+	}
+
+	private class StartBundleRunnable implements Runnable {
 
         private final Bundle m_bundle;