/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.felix.deploymentadmin.spi;

import org.apache.felix.deploymentadmin.AbstractDeploymentPackage;
import org.apache.felix.deploymentadmin.BundleInfoImpl;
import org.osgi.framework.Bundle;
import org.osgi.service.deploymentadmin.BundleInfo;
import org.osgi.service.deploymentadmin.DeploymentException;
import org.osgi.service.log.LogService;

/**
 * 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 {

    protected void doExecute(DeploymentSessionImpl session) throws Exception {
        LogService log = session.getLog();

        AbstractDeploymentPackage target = session.getTargetAbstractDeploymentPackage();
        BundleInfo[] bundleInfos = target.getOrderedBundleInfos();
        for (int i = 0; i < bundleInfos.length; i++) {
            if (isCancelled()) {
                throw new DeploymentException(CODE_CANCELLED);
            }

            String symbolicName = bundleInfos[i].getSymbolicName();
            Bundle bundle = target.getBundle(symbolicName);
            if (bundle != null) {
                if (omitBundleStop(session, symbolicName)) {
                    continue;
                }
                if (isFragmentBundle(bundle)) {
                    log.log(LogService.LOG_INFO, "Skipping fragment bundle '" + symbolicName + "'");
                }
                else {
                    addRollback(new StartBundleRunnable(session, bundle));
                    try {
                        bundle.stop();
                    }
                    catch (Exception e) {
                        log.log(LogService.LOG_WARNING, "Could not stop bundle '" + symbolicName + "'", e);
                    }
                }
            }
            else {
                log.log(LogService.LOG_WARNING, "Could not stop bundle '" + symbolicName + "' because it was not present in the framework");
            }
        }
    }

    /**
     * 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 stopUnaffectedBundle = session.getConfiguration().isStopUnaffectedBundles();

        boolean result = stopUnaffectedBundle;
        BundleInfoImpl sourceBundleInfo = session.getSourceAbstractDeploymentPackage().getBundleInfoByName(symbolicName);
        BundleInfoImpl targetBundleInfo = session.getTargetAbstractDeploymentPackage().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 static class StartBundleRunnable extends AbstractAction {
        private final DeploymentSessionImpl m_session;

        private final Bundle m_bundle;

        public StartBundleRunnable(DeploymentSessionImpl session, Bundle bundle) {
            m_session = session;
            m_bundle = bundle;
        }

        protected void doRun() throws Exception {
            m_bundle.start();
        }

        protected void onFailure(Exception e) {
            m_session.getLog().log(LogService.LOG_WARNING, "Failed to start bundle '" + m_bundle.getSymbolicName() + "'", e);
        }
    }
}

@@ -4026,16 +4026,17 @@
 edBundle
+s
  = sessi
@@ -4084,25 +4084,24 @@
 dles();%0A
-%0A
         
 boolean 
@@ -4096,46 +4096,138 @@
     
-boolean result = stopUnaffectedBundle;
+if (stopUnaffectedBundles) %7B%0A            // Default behavior: stop all bundles (see spec)... %0A            return false;%0A        %7D%0A
 %0A   
@@ -4459,32 +4459,41 @@
 (symbolicName);%0A
+        %0A
         boolean 
@@ -4727,24 +4727,25 @@
 on()));%0A
+%0A
         
 if (fixP
@@ -4736,18 +4736,22 @@
         
-if
+return
  (fixPac
@@ -4781,69 +4781,8 @@
 ion)
- %7B%0A            result = true;%0A        %7D%0A        return result
 ;%0A  

Solved a small logical error in the stop command: