You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by fm...@apache.org on 2011/10/20 14:39:38 UTC

svn commit: r1186765 [2/2] - in /felix/trunk/configadmin: ./ src/main/java/org/apache/felix/cm/impl/ src/main/java/org/osgi/ src/main/java/org/osgi/service/ src/main/java/org/osgi/service/cm/ src/test/java/org/apache/felix/cm/integration/

Added: felix/trunk/configadmin/src/main/java/org/osgi/service/cm/ConfigurationPermission.java
URL: http://svn.apache.org/viewvc/felix/trunk/configadmin/src/main/java/org/osgi/service/cm/ConfigurationPermission.java?rev=1186765&view=auto
==============================================================================
--- felix/trunk/configadmin/src/main/java/org/osgi/service/cm/ConfigurationPermission.java (added)
+++ felix/trunk/configadmin/src/main/java/org/osgi/service/cm/ConfigurationPermission.java Thu Oct 20 12:39:38 2011
@@ -0,0 +1,638 @@
+/*
+ * Copyright (c) OSGi Alliance (2004, 2011). All Rights Reserved.
+ *
+ * Licensed 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.osgi.service.cm;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamField;
+import java.security.BasicPermission;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Indicates a bundle's authority to configure bundles or be updated by
+ * Configuration Admin.
+ *
+ * @ThreadSafe
+ * @version $Id: 0d700c494f2dc2bbe05165bf5c79fe185c9f0a4a $
+ * @since 1.2
+ */
+
+public final class ConfigurationPermission extends BasicPermission {
+    static final long               serialVersionUID    = 5716868734811965383L;
+    /**
+     * Provides permission to create new configurations for other bundles as
+     * well as manipulate them. The action string {@value #CONFIGURE}.
+     */
+    public final static String      CONFIGURE           = "configure";
+
+    /**
+     * The permission to be updated, that is, act as a Managed Service or
+     * Managed Service Factory. The action string {@value #TARGET}.
+     *
+     * @since 1.4
+     */
+    public final static String      TARGET              = "target";
+
+    private final static int        ACTION_CONFIGURE    = 0x00000001;
+    private final static int        ACTION_TARGET       = 0x00000002;
+    private final static int        ACTION_ALL          = ACTION_CONFIGURE
+                                                                | ACTION_TARGET;
+    final static int                ACTION_NONE         = 0;
+
+    /**
+     * The actions mask.
+     */
+    transient int                   action_mask;
+
+    /**
+     * The actions in canonical form.
+     *
+     * @serial
+     */
+    private volatile String         actions             = null;
+
+    /**
+     * Parsed name if it includes wildcards: "*"
+     */
+    private transient List substrings;
+
+    /**
+     * Create a new ConfigurationPermission.
+     *
+     * @param name Name of the permission. Wildcards ({@code '*'}) are allowed
+     *        in the name. During {@link #implies(Permission)}, the name is
+     *        matched to the requested permission using the substring matching
+     *        rules used by {@link Filter}s.
+     * @param actions Comma separated list of {@link #CONFIGURE},
+     *        {@link #TARGET}.
+     */
+
+    public ConfigurationPermission(String name, String actions) {
+        this(name, parseActions(actions));
+    }
+
+    /**
+     * Package private constructor used by ConfigurationPermissionCollection.
+     *
+     * @param name location string
+     * @param mask action mask
+     */
+    ConfigurationPermission(String name, int mask) {
+        super(name);
+        setTransients(mask);
+    }
+
+    /**
+     * Called by constructors and when deserialized.
+     *
+     * @param mask action mask
+     */
+    private void setTransients(int mask) {
+        if ((mask == ACTION_NONE) || ((mask & ACTION_ALL) != mask)) {
+            throw new IllegalArgumentException("invalid action string");
+        }
+        action_mask = mask;
+        substrings = parseSubstring(getName());
+    }
+
+    /**
+     * Parse action string into action mask.
+     *
+     * @param actions Action string.
+     * @return action mask.
+     */
+    private static int parseActions(String actions) {
+        boolean seencomma = false;
+
+        int mask = ACTION_NONE;
+
+        if (actions == null) {
+            return mask;
+        }
+
+        char[] a = actions.toCharArray();
+
+        int i = a.length - 1;
+        if (i < 0)
+            return mask;
+
+        while (i != -1) {
+            char c;
+
+            // skip whitespace
+            while ((i != -1)
+                    && ((c = a[i]) == ' ' || c == '\r' || c == '\n'
+                            || c == '\f' || c == '\t'))
+                i--;
+
+            // check for the known strings
+            int matchlen;
+
+            if (i >= 5 && (a[i - 5] == 't' || a[i - 5] == 'T')
+                    && (a[i - 4] == 'a' || a[i - 4] == 'A')
+                    && (a[i - 3] == 'r' || a[i - 3] == 'R')
+                    && (a[i - 2] == 'g' || a[i - 2] == 'G')
+                    && (a[i - 1] == 'e' || a[i - 1] == 'E')
+                    && (a[i] == 't' || a[i] == 'T')) {
+                matchlen = 6;
+                mask |= ACTION_TARGET;
+
+            }
+            else
+                if (i >= 8 && (a[i - 8] == 'c' || a[i - 8] == 'C')
+                        && (a[i - 7] == 'o' || a[i - 7] == 'O')
+                        && (a[i - 6] == 'n' || a[i - 6] == 'N')
+                        && (a[i - 5] == 'f' || a[i - 5] == 'F')
+                        && (a[i - 4] == 'i' || a[i - 4] == 'I')
+                        && (a[i - 3] == 'g' || a[i - 3] == 'G')
+                        && (a[i - 2] == 'u' || a[i - 2] == 'U')
+                        && (a[i - 1] == 'r' || a[i - 1] == 'R')
+                        && (a[i] == 'e' || a[i] == 'E')) {
+                    matchlen = 9;
+                    mask |= ACTION_CONFIGURE;
+
+                }
+                else {
+                    // parse error
+                    throw new IllegalArgumentException("invalid actions: "
+                            + actions);
+                }
+
+            // make sure we didn't just match the tail of a word
+            // like "ackbarftarget". Also, skip to the comma.
+            seencomma = false;
+            while (i >= matchlen && !seencomma) {
+                switch (a[i - matchlen]) {
+                    case ',' :
+                        seencomma = true;
+                        /* FALLTHROUGH */
+                    case ' ' :
+                    case '\r' :
+                    case '\n' :
+                    case '\f' :
+                    case '\t' :
+                        break;
+                    default :
+                        throw new IllegalArgumentException(
+                                "invalid permission: " + actions);
+                }
+                i--;
+            }
+
+            // point i at the location of the comma minus one (or -1).
+            i -= matchlen;
+        }
+
+        if (seencomma) {
+            throw new IllegalArgumentException("invalid actions: " + actions);
+        }
+
+        return mask;
+    }
+
+    /**
+     * Parse the name for wildcard processing.
+     *
+     * @param name The name of the permission.
+     * @return {@code null} is the name has no wildcards or a
+     *         {@code List<String>} where element is a substring to match or
+     *         null for {@code '*'}.
+     */
+    private static List parseSubstring(String name) {
+        if (name.indexOf('*') < 0) {
+            return null;
+        }
+        char[] chars = name.toCharArray();
+        StringBuffer sb = new StringBuffer(chars.length);
+
+        List sub = new ArrayList(10);
+
+        for (int pos = 0; pos < chars.length; pos++) {
+            char c = chars[pos];
+
+            switch (c) {
+                case '*' : {
+                    if (sb.length() > 0) {
+                        sub.add(sb.toString());
+                    }
+                    sb.setLength(0);
+                    sub.add(null);
+                    break;
+                }
+
+                case '\\' : {
+                    pos++;
+                    if (pos < chars.length) {
+                        c = chars[pos];
+                    }
+                    /* fall through into default */
+                }
+
+                default : {
+                    sb.append(c);
+                    break;
+                }
+            }
+        }
+        if (sb.length() > 0) {
+            sub.add(sb.toString());
+        }
+
+        int size = sub.size();
+
+        if (size == 0) {
+            return null;
+        }
+
+        if (size == 1) {
+            if (sub.get(0) != null) {
+                return null;
+            }
+        }
+        return sub;
+    }
+
+    /**
+     * Determines if a {@code ConfigurationPermission} object "implies" the
+     * specified permission.
+     *
+     * @param p The target permission to check.
+     * @return {@code true} if the specified permission is implied by this
+     *         object; {@code false} otherwise.
+     */
+
+    public boolean implies(Permission p) {
+        if (!(p instanceof ConfigurationPermission)) {
+            return false;
+        }
+        ConfigurationPermission requested = (ConfigurationPermission) p;
+        return implies0(requested, ACTION_NONE);
+    }
+
+    /**
+     * Internal implies method. Used by the implies and the permission
+     * collection implies methods.
+     *
+     * @param requested The requested ConfigurationPermission which has already
+     *        be validated as a proper argument.
+     * @param effective The effective actions with which to start.
+     * @return {@code true} if the specified permission is implied by this
+     *         object; {@code false} otherwise.
+     */
+    boolean implies0(ConfigurationPermission requested, int effective) {
+        /* check actions first - much faster */
+        effective |= action_mask;
+        final int desired = requested.action_mask;
+        if ((effective & desired) != desired) {
+            return false;
+        }
+        String requestedName = requested.getName();
+        if (substrings == null) {
+            return getName().equals(requestedName);
+        }
+        for (int i = 0, pos = 0, size = substrings.size(); i < size; i++) {
+            String substr = (String) substrings.get(i);
+
+            if (i + 1 < size) /* if this is not that last substr */{
+                if (substr == null) /* * */{
+                    String substr2 = (String) substrings.get(i + 1);
+
+                    if (substr2 == null) /* ** */
+                        continue; /* ignore first star */
+                    /* xxx */
+                    int index = requestedName.indexOf(substr2, pos);
+                    if (index == -1) {
+                        return false;
+                    }
+
+                    pos = index + substr2.length();
+                    if (i + 2 < size) // if there are more
+                        // substrings, increment
+                        // over the string we just
+                        // matched; otherwise need
+                        // to do the last substr
+                        // check
+                        i++;
+                }
+                else /* xxx */{
+                    int len = substr.length();
+                    if (requestedName.regionMatches(pos, substr, 0, len)) {
+                        pos += len;
+                    }
+                    else {
+                        return false;
+                    }
+                }
+            }
+            else /* last substr */{
+                if (substr == null) /* * */{
+                    return true;
+                }
+                /* xxx */
+                return requestedName.endsWith(substr);
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Determines the equality of two {@code ConfigurationPermission} objects.
+     * <p>
+     * Two {@code ConfigurationPermission} objects are equal.
+     *
+     * @param obj The object being compared for equality with this object.
+     * @return {@code true} if {@code obj} is equivalent to this
+     *         {@code ConfigurationPermission}; {@code false} otherwise.
+     */
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+
+        if (!(obj instanceof ConfigurationPermission)) {
+            return false;
+        }
+
+        ConfigurationPermission cp = (ConfigurationPermission) obj;
+
+        return (action_mask == cp.action_mask)
+                && getName().equals(cp.getName());
+    }
+
+    /**
+     * Returns the hash code value for this object.
+     *
+     * @return Hash code value for this object.
+     */
+
+    public int hashCode() {
+        int h = 31 * 17 + getName().hashCode();
+        h = 31 * h + getActions().hashCode();
+        return h;
+    }
+
+    /**
+     * Returns the canonical string representation of the
+     * {@code ConfigurationPermission} actions.
+     *
+     * <p>
+     * Always returns present {@code ConfigurationPermission} actions in the
+     * following order: {@value #CONFIGURE}, {@value #TARGET}
+     *
+     * @return Canonical string representation of the
+     *         {@code ConfigurationPermission} actions.
+     */
+    public String getActions() {
+        String result = actions;
+        if (result == null) {
+            StringBuffer sb = new StringBuffer();
+            boolean comma = false;
+
+            int mask = action_mask;
+            if ((mask & ACTION_CONFIGURE) == ACTION_CONFIGURE) {
+                sb.append(CONFIGURE);
+                comma = true;
+            }
+
+            if ((mask & ACTION_TARGET) == ACTION_TARGET) {
+                if (comma)
+                    sb.append(',');
+                sb.append(TARGET);
+            }
+
+            actions = result = sb.toString();
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns a new {@code PermissionCollection} object suitable for storing
+     * {@code ConfigurationPermission}s.
+     *
+     * @return A new {@code PermissionCollection} object.
+     */
+    public PermissionCollection newPermissionCollection() {
+        return new ConfigurationPermissionCollection();
+    }
+
+    /**
+     * WriteObject is called to save the state of this permission object to a
+     * stream. The actions are serialized, and the superclass takes care of the
+     * name.
+     */
+    private synchronized void writeObject(java.io.ObjectOutputStream s)
+            throws IOException {
+        // Write out the actions. The superclass takes care of the name
+        // call getActions to make sure actions field is initialized
+        if (actions == null)
+            getActions();
+        s.defaultWriteObject();
+    }
+
+    /**
+     * readObject is called to restore the state of this permission from a
+     * stream.
+     */
+    private synchronized void readObject(java.io.ObjectInputStream s)
+            throws IOException, ClassNotFoundException {
+        // Read in the data, then initialize the transients
+        s.defaultReadObject();
+        setTransients(parseActions(actions));
+    }
+}
+
+/**
+ * Stores a set of {@code ConfigurationPermission} permissions.
+ *
+ * @see java.security.Permission
+ * @see java.security.Permissions
+ * @see java.security.PermissionCollection
+ */
+final class ConfigurationPermissionCollection extends PermissionCollection {
+    static final long                               serialVersionUID    = -6917638867081695839L;
+    /**
+     * Collection of permissions.
+     *
+     * @serial
+     * @GuardedBy this
+     */
+    private Map    permissions;
+
+    /**
+     * Boolean saying if "*" is in the collection.
+     *
+     * @serial
+     * @GuardedBy this
+     */
+    private boolean                                 all_allowed;
+
+    /**
+     * Creates an empty {@code ConfigurationPermissionCollection} object.
+     *
+     */
+    public ConfigurationPermissionCollection() {
+        permissions = new HashMap();
+        all_allowed = false;
+    }
+
+    /**
+     * Adds the specified permission to the
+     * {@code ConfigurationPermissionCollection}. The key for the hash is the
+     * interface name of the service.
+     *
+     * @param permission The {@code Permission} object to add.
+     *
+     * @exception IllegalArgumentException If the permission is not an
+     *            {@code ConfigurationPermission}.
+     *
+     * @exception SecurityException If this ConfigurationPermissionCollection
+     *            object has been marked read-only.
+     */
+
+    public void add(Permission permission) {
+        if (!(permission instanceof ConfigurationPermission)) {
+            throw new IllegalArgumentException("invalid permission: "
+                    + permission);
+        }
+
+        if (isReadOnly())
+            throw new SecurityException("attempt to add a Permission to a "
+                    + "readonly PermissionCollection");
+
+        final ConfigurationPermission cp = (ConfigurationPermission) permission;
+        final String name = cp.getName();
+        synchronized (this) {
+            Map pc = permissions;
+            final ConfigurationPermission existing = (ConfigurationPermission) pc.get(name);
+            if (existing != null) {
+                final int oldMask = existing.action_mask;
+                final int newMask = cp.action_mask;
+                if (oldMask != newMask) {
+                    pc.put(name, new ConfigurationPermission(name, oldMask
+                            | newMask));
+                }
+            }
+            else {
+                pc.put(name, cp);
+            }
+
+            if (!all_allowed) {
+                if (name.equals("*")) {
+                    all_allowed = true;
+                }
+            }
+        }
+    }
+
+    /**
+     * Determines if the specified permissions implies the permissions expressed
+     * in {@code permission}.
+     *
+     * @param permission The Permission object to compare with this
+     *        {@code ConfigurationPermission} object.
+     * @return {@code true} if {@code permission} is a proper subset of a
+     *         permission in the set; {@code false} otherwise.
+     */
+    public boolean implies(Permission permission) {
+        if (!(permission instanceof ConfigurationPermission)) {
+            return false;
+        }
+        final ConfigurationPermission requested = (ConfigurationPermission) permission;
+        int effective = ConfigurationPermission.ACTION_NONE;
+
+        Collection perms;
+        synchronized (this) {
+            Map pc = permissions;
+            /* short circuit if the "*" Permission was added */
+            if (all_allowed) {
+                ConfigurationPermission cp = (ConfigurationPermission) pc.get("*");
+                if (cp != null) {
+                    effective |= cp.action_mask;
+                    final int desired = requested.action_mask;
+                    if ((effective & desired) == desired) {
+                        return true;
+                    }
+                }
+            }
+            perms = pc.values();
+        }
+        /* iterate one by one over permissions */
+        for (Iterator permI = perms.iterator(); permI.hasNext(); ) {
+            ConfigurationPermission perm = (ConfigurationPermission) permI.next();
+            if (perm.implies0(requested, effective)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns an enumeration of all {@code ConfigurationPermission} objects in
+     * the container.
+     *
+     * @return Enumeration of all {@code ConfigurationPermission} objects.
+     */
+    public synchronized Enumeration elements() {
+        List all = new ArrayList(permissions.values());
+        return Collections.enumeration(all);
+    }
+
+    /* serialization logic */
+    private static final ObjectStreamField[]    serialPersistentFields  = {
+            new ObjectStreamField("hasElement", Boolean.TYPE),
+            new ObjectStreamField("permissions", HashMap.class),
+            new ObjectStreamField("all_allowed", Boolean.TYPE)          };
+
+    private synchronized void writeObject(ObjectOutputStream out)
+            throws IOException {
+        ObjectOutputStream.PutField pfields = out.putFields();
+        pfields.put("hasElement", false);
+        pfields.put("permissions", permissions);
+        pfields.put("all_allowed", all_allowed);
+        out.writeFields();
+    }
+
+    private synchronized void readObject(java.io.ObjectInputStream in)
+            throws IOException, ClassNotFoundException {
+        ObjectInputStream.GetField gfields = in.readFields();
+        boolean hasElement = gfields.get("hasElement", false);
+        if (hasElement) { // old format
+            permissions = new HashMap();
+            permissions.put("*", new ConfigurationPermission("*",
+                    ConfigurationPermission.CONFIGURE));
+            all_allowed = true;
+        }
+        else {
+            permissions = (HashMap) gfields
+                    .get("permissions",
+                            new HashMap());
+            all_allowed = gfields.get("all_allowed", false);
+        }
+    }
+}

Modified: felix/trunk/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationBaseTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationBaseTest.java?rev=1186765&r1=1186764&r2=1186765&view=diff
==============================================================================
--- felix/trunk/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationBaseTest.java (original)
+++ felix/trunk/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationBaseTest.java Thu Oct 20 12:39:38 2011
@@ -40,6 +40,12 @@ import org.osgi.service.cm.Configuration
 public class ConfigurationBaseTest extends ConfigurationTestBase
 {
 
+    static
+    {
+        // uncomment to enable debugging of this test class
+        // paxRunnerVmOption = DEBUG_VM_OPTION;
+    }
+
     @Test
     public void test_basic_configuration_configure_then_start() throws BundleException, IOException
     {

Modified: felix/trunk/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationBindingTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationBindingTest.java?rev=1186765&r1=1186764&r2=1186765&view=diff
==============================================================================
--- felix/trunk/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationBindingTest.java (original)
+++ felix/trunk/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationBindingTest.java Thu Oct 20 12:39:38 2011
@@ -39,6 +39,13 @@ import org.osgi.service.cm.Configuration
 public class ConfigurationBindingTest extends ConfigurationTestBase
 {
 
+    static
+    {
+        // uncomment to enable debugging of this test class
+        // paxRunnerVmOption = DEBUG_VM_OPTION;
+    }
+
+
      @Test
     public void test_configuration_unbound_on_uninstall() throws BundleException
     {
@@ -445,6 +452,7 @@ public class ConfigurationBindingTest ex
 
         // remove the static binding and assert still bound
         config.setBundleLocation( null );
+        delay();
         TestCase.assertEquals( location, config.getBundleLocation() );
 
         // uninstall bundle and assert configuration unbound
@@ -580,33 +588,13 @@ public class ConfigurationBindingTest ex
         // ==> configuration is bound to locationB
         TestCase.assertEquals( locationB, config.getBundleLocation() );
 
-        /*
-         * According to BJ Hargrave configuration is not re-dispatched
-         * due to setting the bundle location.
-         * <p>
-         * Therefore, we have two sets one with re-dispatch expectation and
-         * one without re-dispatch expectation.
-         */
-        if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
-        {
-            // ==> configuration removed from service ms1
-            TestCase.assertNull( testerA1.props );
-            TestCase.assertEquals( 2, testerA1.numManagedServiceUpdatedCalls );
-
-            // ==> configuration supplied to the service ms2
-            TestCase.assertNotNull( testerB1.props );
-            TestCase.assertEquals( 1, testerB1.numManagedServiceUpdatedCalls );
-        }
-        else
-        {
-            // ==> configuration remains for service ms1
-            TestCase.assertNotNull( testerA1.props );
-            TestCase.assertEquals( 1, testerA1.numManagedServiceUpdatedCalls );
-
-            // ==> configuration not supplied to the service ms2
-            TestCase.assertNull( testerB1.props );
-            TestCase.assertEquals( 0, testerB1.numManagedServiceUpdatedCalls );
-        }
+        // ==> configuration removed from service ms1
+        TestCase.assertNull( testerA1.props );
+        TestCase.assertEquals( 2, testerA1.numManagedServiceUpdatedCalls );
+
+        // ==> configuration supplied to the service ms2
+        TestCase.assertNotNull( testerB1.props );
+        TestCase.assertEquals( 1, testerB1.numManagedServiceUpdatedCalls );
     }
 
 
@@ -650,33 +638,13 @@ public class ConfigurationBindingTest ex
         // ==> configuration is bound to locationB
         TestCase.assertEquals( locationB, config.getBundleLocation() );
 
-        /*
-         * According to BJ Hargrave configuration is not re-dispatched
-         * due to setting the bundle location.
-         * <p>
-         * Therefore, we have two sets one with re-dispatch expectation and
-         * one without re-dispatch expectation.
-         */
-        if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
-        {
-            // ==> configuration removed from service ms1
-            TestCase.assertNull( testerA1.props );
-            TestCase.assertEquals( 2, testerA1.numManagedServiceUpdatedCalls );
-
-            // ==> configuration supplied to the service ms2
-            TestCase.assertNotNull( testerB1.props );
-            TestCase.assertEquals( 1, testerB1.numManagedServiceUpdatedCalls );
-        }
-        else
-        {
-            // ==> configuration remains for service ms1
-            TestCase.assertNotNull( testerA1.props );
-            TestCase.assertEquals( 1, testerA1.numManagedServiceUpdatedCalls );
-
-            // ==> configuration not supplied to the service ms2
-            TestCase.assertNull( testerB1.props );
-            TestCase.assertEquals( 0, testerB1.numManagedServiceUpdatedCalls );
-        }
+        // ==> configuration removed from service ms1
+        TestCase.assertNull( testerA1.props );
+        TestCase.assertEquals( 2, testerA1.numManagedServiceUpdatedCalls );
+
+        // ==> configuration supplied to the service ms2
+        TestCase.assertNotNull( testerB1.props );
+        TestCase.assertEquals( 1, testerB1.numManagedServiceUpdatedCalls );
 
         // 6. Update configuration now
         config.update();
@@ -684,7 +652,7 @@ public class ConfigurationBindingTest ex
 
         // ==> configuration supplied to the service ms2
         TestCase.assertNotNull( testerB1.props );
-        TestCase.assertEquals( 1, testerB1.numManagedServiceUpdatedCalls );
+        TestCase.assertEquals( 2, testerB1.numManagedServiceUpdatedCalls );
     }
 
 
@@ -726,35 +694,14 @@ public class ConfigurationBindingTest ex
         // ==> configuration is bound to locationB
         TestCase.assertEquals( locationB, config.getBundleLocation() );
 
-        /*
-         * According to BJ Hargrave configuration is not re-dispatched
-         * due to setting the bundle location.
-         * <p>
-         * Therefore, we have two sets one with re-dispatch expectation and
-         * one without re-dispatch expectation.
-         */
-        if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
-        {
-            // ==> configuration removed from service ms1
-            TestCase.assertNull( testerA1.configs.get( pid ));
-            TestCase.assertEquals( 1, testerA1.numManagedServiceFactoryUpdatedCalls );
-            TestCase.assertEquals( 1, testerA1.numManagedServiceFactoryDeleteCalls );
-
-            // ==> configuration supplied to the service ms2
-            TestCase.assertNotNull( testerB1.configs.get( pid ) );
-            TestCase.assertEquals( 1, testerB1.numManagedServiceFactoryUpdatedCalls );
-        }
-        else
-        {
-            // ==> configuration not removed from service ms1
-            TestCase.assertNotNull( testerA1.configs.get( pid ));
-            TestCase.assertEquals( 1, testerA1.numManagedServiceFactoryUpdatedCalls );
-            TestCase.assertEquals( 0, testerA1.numManagedServiceFactoryDeleteCalls );
-
-            // ==> configuration not supplied to the service ms2
-            TestCase.assertNull( testerB1.configs.get( pid ) );
-            TestCase.assertEquals( 0, testerB1.numManagedServiceFactoryUpdatedCalls );
-        }
+        // ==> configuration removed from service ms1
+        TestCase.assertNull( testerA1.configs.get( pid ));
+        TestCase.assertEquals( 1, testerA1.numManagedServiceFactoryUpdatedCalls );
+        TestCase.assertEquals( 1, testerA1.numManagedServiceFactoryDeleteCalls );
+
+        // ==> configuration supplied to the service ms2
+        TestCase.assertNotNull( testerB1.configs.get( pid ) );
+        TestCase.assertEquals( 1, testerB1.numManagedServiceFactoryUpdatedCalls );
 
         // 6. Update configuration now
         config.update();
@@ -762,7 +709,7 @@ public class ConfigurationBindingTest ex
 
         // ==> configuration supplied to the service ms2
         TestCase.assertNotNull( testerB1.configs.get( pid ) );
-        TestCase.assertEquals( 1, testerB1.numManagedServiceFactoryUpdatedCalls );
+        TestCase.assertEquals( 2, testerB1.numManagedServiceFactoryUpdatedCalls );
    }
 
 
@@ -808,35 +755,14 @@ public class ConfigurationBindingTest ex
         // ==> configuration is bound to locationB
         TestCase.assertEquals( locationB, config.getBundleLocation() );
 
-        /*
-         * According to BJ Hargrave configuration is not re-dispatched
-         * due to setting the bundle location.
-         * <p>
-         * Therefore, we have two sets one with re-dispatch expectation and
-         * one without re-dispatch expectation.
-         */
-        if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
-        {
-            // ==> configuration removed from service ms1
-            TestCase.assertNull( testerA1.configs.get( pid ));
-            TestCase.assertEquals( 1, testerA1.numManagedServiceFactoryUpdatedCalls );
-            TestCase.assertEquals( 1, testerA1.numManagedServiceFactoryDeleteCalls );
-
-            // ==> configuration supplied to the service ms2
-            TestCase.assertNotNull( testerB1.configs.get( pid ) );
-            TestCase.assertEquals( 1, testerB1.numManagedServiceFactoryUpdatedCalls );
-        }
-        else
-        {
-            // ==> configuration not removed from service ms1
-            TestCase.assertNotNull( testerA1.configs.get( pid ));
-            TestCase.assertEquals( 1, testerA1.numManagedServiceFactoryUpdatedCalls );
-            TestCase.assertEquals( 0, testerA1.numManagedServiceFactoryDeleteCalls );
-
-            // ==> configuration not supplied to the service ms2
-            TestCase.assertNull( testerB1.configs.get( pid ) );
-            TestCase.assertEquals( 0, testerB1.numManagedServiceFactoryUpdatedCalls );
-        }
+        // ==> configuration removed from service ms1
+        TestCase.assertNull( testerA1.configs.get( pid ));
+        TestCase.assertEquals( 1, testerA1.numManagedServiceFactoryUpdatedCalls );
+        TestCase.assertEquals( 1, testerA1.numManagedServiceFactoryDeleteCalls );
+
+        // ==> configuration supplied to the service ms2
+        TestCase.assertNotNull( testerB1.configs.get( pid ) );
+        TestCase.assertEquals( 1, testerB1.numManagedServiceFactoryUpdatedCalls );
 
         // 6. Update configuration now
         config.update();
@@ -844,7 +770,7 @@ public class ConfigurationBindingTest ex
 
         // ==> configuration supplied to the service ms2
         TestCase.assertNotNull( testerB1.configs.get( pid ) );
-        TestCase.assertEquals( 1, testerB1.numManagedServiceFactoryUpdatedCalls );
+        TestCase.assertEquals( 2, testerB1.numManagedServiceFactoryUpdatedCalls );
     }
 
 
@@ -887,31 +813,12 @@ public class ConfigurationBindingTest ex
         bundleA.uninstall();
         delay();
 
-        /*
-         * According to BJ Hargrave configuration is not re-dispatched
-         * due to setting the bundle location.
-         * <p>
-         * Therefore, we have two sets one with re-dispatch expectation and
-         * one without re-dispatch expectation.
-         */
-        if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
-        {
-            // ==> configuration is bound to locationB
-            TestCase.assertEquals( locationB, config.getBundleLocation() );
-
-            // ==> configuration supplied to the service ms2
-            TestCase.assertNotNull( testerB1.props );
-            TestCase.assertEquals( 1, testerB1.numManagedServiceUpdatedCalls );
-        }
-        else
-        {
-            // ==> configuration is unbound
-            TestCase.assertNull( config.getBundleLocation() );
-
-            // ==> configuration not supplied to the service ms2
-            TestCase.assertNull( testerB1.props );
-            TestCase.assertEquals( 0, testerB1.numManagedServiceUpdatedCalls );
-        }
+        // ==> configuration is bound to locationB
+        TestCase.assertEquals( locationB, config.getBundleLocation() );
+
+        // ==> configuration supplied to the service ms2
+        TestCase.assertNotNull( testerB1.props );
+        TestCase.assertEquals( 1, testerB1.numManagedServiceUpdatedCalls );
 
         // 6. Update configuration now
         config.update();
@@ -919,7 +826,7 @@ public class ConfigurationBindingTest ex
 
         // ==> configuration supplied to the service ms2
         TestCase.assertNotNull( testerB1.props );
-        TestCase.assertEquals( 1, testerB1.numManagedServiceUpdatedCalls );
+        TestCase.assertEquals( 2, testerB1.numManagedServiceUpdatedCalls );
     }
 
 
@@ -962,31 +869,12 @@ public class ConfigurationBindingTest ex
         bundleA.uninstall();
         delay();
 
-        /*
-         * According to BJ Hargrave configuration is not re-dispatched
-         * due to setting the bundle location.
-         * <p>
-         * Therefore, we have two sets one with re-dispatch expectation and
-         * one without re-dispatch expectation.
-         */
-        if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
-        {
-            // ==> configuration is bound to locationB
-            TestCase.assertEquals( locationB, config.getBundleLocation() );
-
-            // ==> configuration supplied to the service ms2
-            TestCase.assertNotNull( testerB1.configs.get( pid ) );
-            TestCase.assertEquals( 1, testerB1.numManagedServiceFactoryUpdatedCalls );
-        }
-        else
-        {
-            // ==> configuration is unbound
-            TestCase.assertNull( config.getBundleLocation() );
-
-            // ==> configuration not supplied to the service ms2
-            TestCase.assertNull( testerB1.configs.get( pid ) );
-            TestCase.assertEquals( 0, testerB1.numManagedServiceFactoryUpdatedCalls );
-        }
+        // ==> configuration is bound to locationB
+        TestCase.assertEquals( locationB, config.getBundleLocation() );
+
+        // ==> configuration supplied to the service ms2
+        TestCase.assertNotNull( testerB1.configs.get( pid ) );
+        TestCase.assertEquals( 1, testerB1.numManagedServiceFactoryUpdatedCalls );
 
         // 6. Update configuration now
         config.update();
@@ -994,6 +882,6 @@ public class ConfigurationBindingTest ex
 
         // ==> configuration supplied to the service ms2
         TestCase.assertNotNull( testerB1.configs.get( pid ) );
-        TestCase.assertEquals( 1, testerB1.numManagedServiceFactoryUpdatedCalls );
+        TestCase.assertEquals( 2, testerB1.numManagedServiceFactoryUpdatedCalls );
     }
 }

Modified: felix/trunk/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationTestBase.java
URL: http://svn.apache.org/viewvc/felix/trunk/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationTestBase.java?rev=1186765&r1=1186764&r2=1186765&view=diff
==============================================================================
--- felix/trunk/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationTestBase.java (original)
+++ felix/trunk/configadmin/src/test/java/org/apache/felix/cm/integration/ConfigurationTestBase.java Thu Oct 20 12:39:38 2011
@@ -29,7 +29,9 @@ import java.io.InputStream;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.util.Dictionary;
+import java.util.HashSet;
 import java.util.Hashtable;
+import java.util.Set;
 
 import junit.framework.AssertionFailedError;
 import junit.framework.TestCase;
@@ -60,24 +62,6 @@ import org.osgi.util.tracker.ServiceTrac
 public abstract class ConfigurationTestBase
 {
 
-    /**
-     * There is currently an open issue in the specification in whether a
-     * call to Configuration.setBundleLocation() might trigger a configuration
-     * update or not.
-     * We have test cases in our integration test suite for both cases. To
-     * enable the respective tests set this field accordingly:
-     * <dl>
-     * <dt>false</dt>
-     * <dd>Expect configuration to <b>NOT</b> be redispatched. That is existing
-     * configurations are kept and other services are not updated</dd>
-     * <dt>true</dt>
-     * <dd>Expect configuration to be redispatched. That is existing configuration
-     * is revoked (update(null) or delete calls) and new matching services are
-     * updated.</dd>
-     * </dl>
-     */
-    public static final boolean REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION = false;
-
     // the name of the system property providing the bundle file to be installed and tested
     protected static final String BUNDLE_JAR_SYS_PROP = "project.bundle.file";
 
@@ -99,6 +83,8 @@ public abstract class ConfigurationTestB
 
     protected ServiceTracker configAdminTracker;
 
+    private Set<String> configurations = new HashSet<String>();
+
     protected static final String PROP_NAME = "theValue";
     protected static final Dictionary<String, String> theConfig;
 
@@ -121,6 +107,7 @@ public abstract class ConfigurationTestB
         }
 
         final Option[] base = options(
+            /* CoreOptions.allFrameworks(), */
             provision(
                 CoreOptions.bundle( bundleFile.toURI().toString() ),
                 mavenBundle( "org.ops4j.pax.swissbox", "pax-swissbox-tinybundles", "1.0.0" )
@@ -148,6 +135,11 @@ public abstract class ConfigurationTestB
             bundle.uninstall();
         }
 
+        for ( String pid : configurations )
+        {
+            deleteConfig( pid );
+        }
+
         configAdminTracker.close();
         configAdminTracker = null;
     }
@@ -299,7 +291,7 @@ public abstract class ConfigurationTestB
         final ConfigurationAdmin ca = getConfigurationAdmin();
         try
         {
-            final Configuration config = ca.createFactoryConfiguration( factoryPid, null );
+            final Configuration config = ca.createFactoryConfiguration( factoryPid, location );
             if ( withProps )
             {
                 config.update( theConfig );
@@ -345,6 +337,7 @@ public abstract class ConfigurationTestB
         final ConfigurationAdmin ca = getConfigurationAdmin();
         try
         {
+            configurations.remove( pid );
             final Configuration config = ca.getConfiguration( pid );
             config.delete();
         }
@@ -366,6 +359,7 @@ public abstract class ConfigurationTestB
             {
                 for ( Configuration configuration : configs )
                 {
+                    configurations.remove( configuration.getPid() );
                     configuration.delete();
                 }
             }

Modified: felix/trunk/configadmin/src/test/java/org/apache/felix/cm/integration/MultiServiceFactoryPIDTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/configadmin/src/test/java/org/apache/felix/cm/integration/MultiServiceFactoryPIDTest.java?rev=1186765&r1=1186764&r2=1186765&view=diff
==============================================================================
--- felix/trunk/configadmin/src/test/java/org/apache/felix/cm/integration/MultiServiceFactoryPIDTest.java (original)
+++ felix/trunk/configadmin/src/test/java/org/apache/felix/cm/integration/MultiServiceFactoryPIDTest.java Thu Oct 20 12:39:38 2011
@@ -39,6 +39,11 @@ import org.osgi.service.cm.Configuration
 @RunWith(JUnit4TestRunner.class)
 public class MultiServiceFactoryPIDTest extends ConfigurationTestBase
 {
+    static
+    {
+        // uncomment to enable debugging of this test class
+        // paxRunnerVmOption = DEBUG_VM_OPTION;
+    }
 
     @Test
     public void test_two_services_same_pid_in_same_bundle_configure_before_registration() throws BundleException
@@ -61,7 +66,7 @@ public class MultiServiceFactoryPIDTest 
 
         // assert activater has configuration (two calls, one per pid)
         TestCase.assertNotNull( "Expect Properties after Service Registration", tester.configs.get( pid ) );
-        TestCase.assertEquals( "Expect a single update call", 2, tester.numManagedServiceFactoryUpdatedCalls );
+        TestCase.assertEquals( "Expect a single update call", 1, tester.numManagedServiceFactoryUpdatedCalls );
 
         TestCase.assertEquals( bundle.getLocation(), config.getBundleLocation() );
 
@@ -165,23 +170,10 @@ public class MultiServiceFactoryPIDTest 
 
             delay();
 
-            /*
-             * According to BJ Hargrave configuration is not re-dispatched
-             * due to setting the bundle location.
-             * <p>
-             * Therefore, we have two sets one with re-dispatch expectation and
-             * one without re-dispatch expectation.
-             */
-            if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
-            {
-                // expect configuration reassigned
-                TestCase.assertEquals( bundle2.getLocation(), config.getBundleLocation() );
-            }
-            else
-            {
-                // expected configuration unbound
-                TestCase.assertNull( config.getBundleLocation() );
-            }
+            // expect configuration reassigned
+            TestCase.assertEquals( bundle2.getLocation(), config.getBundleLocation() );
+            TestCase.assertNotNull( "Expect Properties after Configuration Redispatch", tester2.configs.get( pid ) );
+            TestCase.assertEquals( "Expect update call after Configuration Redispatch", 1, tester2.numManagedServiceFactoryUpdatedCalls );
 
             // remove the configuration for good
             deleteConfig( pid );
@@ -230,78 +222,27 @@ public class MultiServiceFactoryPIDTest 
             delay();
 
             TestCase.assertEquals( factoryPid, config.getFactoryPid() );
-            TestCase.assertNotNull( config.getBundleLocation() );
 
-            if ( bundle.getLocation().equals( config.getBundleLocation() ) )
-            {
-                // configuration assigned to the first bundle
-                TestCase.assertNotNull( "Expect Properties after Service Registration", tester.configs.get( pid ) );
-                TestCase.assertEquals( "Expect a single update call", 1, tester.numManagedServiceFactoryUpdatedCalls );
-
-                TestCase.assertTrue( "Expect Properties after Service Registration", tester2.configs.isEmpty() );
-                TestCase.assertEquals( "Expect a single update call", 0, tester2.numManagedServiceFactoryUpdatedCalls );
-
-                bundle.uninstall();
-                bundle = null;
-
-                delay();
-
-                /*
-                 * According to BJ Hargrave configuration is not re-dispatched
-                 * due to setting the bundle location.
-                 * <p>
-                 * Therefore, we have two sets one with re-dispatch expectation and
-                 * one without re-dispatch expectation.
-                 */
-                if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
-                {
-                    // expect configuration reassigned
-                    TestCase.assertEquals( bundle2.getLocation(), config.getBundleLocation() );
-                }
-                else
-                {
-                    // expected configuration unbound
-                    TestCase.assertNull( config.getBundleLocation() );
-                }
-            }
-            else if ( bundle2.getLocation().equals( config.getBundleLocation() ) )
-            {
-                // configuration assigned to the second bundle
-                // assert activater has configuration (two calls, one per pid)
-                TestCase.assertNotNull( "Expect Properties after Service Registration", tester2.configs.get( pid ) );
-                TestCase.assertEquals( "Expect a single update call", 1, tester2.numManagedServiceFactoryUpdatedCalls );
+            TestCase.assertEquals(
+                "Configuration must be bound to second bundle because the service has higher ranking",
+                bundle.getLocation(), config.getBundleLocation() );
 
-                TestCase.assertTrue( "Expect Properties after Service Registration", tester.configs.isEmpty() );
-                TestCase.assertEquals( "Expect a single update call", 0, tester.numManagedServiceFactoryUpdatedCalls );
+            // configuration assigned to the first bundle
+            TestCase.assertNotNull( "Expect Properties after Service Registration", tester.configs.get( pid ) );
+            TestCase.assertEquals( "Expect a single update call", 1, tester.numManagedServiceFactoryUpdatedCalls );
 
-                bundle2.uninstall();
-                bundle2 = null;
+            TestCase.assertTrue( "Expect Properties after Service Registration", tester2.configs.isEmpty() );
+            TestCase.assertEquals( "Expect a single update call", 0, tester2.numManagedServiceFactoryUpdatedCalls );
+
+            bundle.uninstall();
+            bundle = null;
 
-                delay();
+            delay();
 
-                /*
-                 * According to BJ Hargrave configuration is not re-dispatched
-                 * due to setting the bundle location.
-                 * <p>
-                 * Therefore, we have two sets one with re-dispatch expectation and
-                 * one without re-dispatch expectation.
-                 */
-                if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
-                {
-                    // expect configuration reassigned
-                    TestCase.assertEquals( bundle.getLocation(), config.getBundleLocation() );
-                }
-                else
-                {
-                    // expected configuration unbound
-                    TestCase.assertNull( config.getBundleLocation() );
-                }
-            }
-            else
-            {
-                // configuration assigned to some other bundle ... fail
-                TestCase.fail( "Configuration assigned to unexpected bundle " + config.getBundleLocation() );
-            }
+            // expect configuration reassigned
+            TestCase.assertEquals( bundle2.getLocation(), config.getBundleLocation() );
+            TestCase.assertNotNull( "Expect Properties after Configuration Redispatch", tester2.configs.get( pid ) );
+            TestCase.assertEquals( "Expect a single update call after Configuration Redispatch", 1, tester2.numManagedServiceFactoryUpdatedCalls );
 
             // remove the configuration for good
             deleteConfig( pid );

Modified: felix/trunk/configadmin/src/test/java/org/apache/felix/cm/integration/MultiServicePIDTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/configadmin/src/test/java/org/apache/felix/cm/integration/MultiServicePIDTest.java?rev=1186765&r1=1186764&r2=1186765&view=diff
==============================================================================
--- felix/trunk/configadmin/src/test/java/org/apache/felix/cm/integration/MultiServicePIDTest.java (original)
+++ felix/trunk/configadmin/src/test/java/org/apache/felix/cm/integration/MultiServicePIDTest.java Thu Oct 20 12:39:38 2011
@@ -39,6 +39,11 @@ import org.osgi.service.cm.Configuration
 @RunWith(JUnit4TestRunner.class)
 public class MultiServicePIDTest extends ConfigurationTestBase
 {
+    static
+    {
+        // uncomment to enable debugging of this test class
+        // paxRunnerVmOption = DEBUG_VM_OPTION;
+    }
 
     @Test
     public void test_two_services_same_pid_in_same_bundle_configure_before_registration() throws BundleException
@@ -150,7 +155,7 @@ public class MultiServicePIDTest extends
 
             // expect first activator to have received properties
 
-            // assert first bundle has configuration (two calls, one per srv)
+            // assert first bundle has configuration (one calls, one per srv)
             TestCase.assertNotNull( "Expect Properties after Service Registration", tester.props );
             TestCase.assertEquals( "Expect a single update call", 1, tester.numManagedServiceUpdatedCalls );
 
@@ -166,23 +171,16 @@ public class MultiServicePIDTest extends
 
             delay();
 
-            /*
-             * According to BJ Hargrave configuration is not re-dispatched
-             * due to setting the bundle location.
-             * <p>
-             * Therefore, we have two sets one with re-dispatch expectation and
-             * one without re-dispatch expectation.
-             */
-            if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
-            {
-                // expect configuration reassigned
-                TestCase.assertEquals( bundle2.getLocation(), config.getBundleLocation() );
-            }
-            else
-            {
-                // expected configuration unbound
-                TestCase.assertNull( config.getBundleLocation() );
-            }
+            // after uninstallation, the configuration is redispatched
+            // due to the dynamic binding being removed
+
+            // expect configuration reassigned
+            TestCase.assertEquals( bundle2.getLocation(), config.getBundleLocation() );
+
+            // assert second bundle now has the configuration
+            TestCase.assertNotNull( "Expect Properties after Configuration redispatch", tester2.props );
+            TestCase.assertEquals( "Expect a single update call after Configuration redispatch", 1,
+                tester2.numManagedServiceUpdatedCalls );
 
             // remove the configuration for good
             deleteConfig( pid );
@@ -231,78 +229,33 @@ public class MultiServicePIDTest extends
 
             final Configuration config = getConfiguration( pid );
             TestCase.assertEquals( pid, config.getPid() );
-            TestCase.assertNotNull( config.getBundleLocation() );
 
-            if ( bundle.getLocation().equals( config.getBundleLocation() ) )
-            {
-                // configuration assigned to the first bundle
-                TestCase.assertNotNull( "Expect Properties after Service Registration", tester.props );
-                TestCase.assertEquals( "Expect a single update call", 2, tester.numManagedServiceUpdatedCalls );
-
-                TestCase.assertNull( "Expect Properties after Service Registration", tester2.props );
-                TestCase.assertEquals( "Expect a single update call", 1, tester2.numManagedServiceUpdatedCalls );
-
-                bundle.uninstall();
-                bundle = null;
-
-                delay();
-
-                /*
-                 * According to BJ Hargrave configuration is not re-dispatched
-                 * due to setting the bundle location.
-                 * <p>
-                 * Therefore, we have two sets one with re-dispatch expectation and
-                 * one without re-dispatch expectation.
-                 */
-                if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
-                {
-                    // expect configuration reassigned
-                    TestCase.assertEquals( bundle2.getLocation(), config.getBundleLocation() );
-                }
-                else
-                {
-                    // expected configuration unbound
-                    TestCase.assertNull( config.getBundleLocation() );
-                }
-            }
-            else if ( bundle2.getLocation().equals( config.getBundleLocation() ) )
-            {
-                // configuration assigned to the second bundle
-                // assert activater has configuration (two calls, one per pid)
-                TestCase.assertNotNull( "Expect Properties after Service Registration", tester2.props );
-                TestCase.assertEquals( "Expect a single update call", 2, tester2.numManagedServiceUpdatedCalls );
+            TestCase.assertEquals(
+                "Configuration must be bound to first bundle because the service has higher ranking",
+                bundle.getLocation(), config.getBundleLocation() );
+
+            // configuration assigned to the first bundle
+            TestCase.assertNotNull( "Expect Properties after Service Registration", tester.props );
+            TestCase.assertEquals( "Expect a single update call", 2, tester.numManagedServiceUpdatedCalls );
 
-                TestCase.assertNull( "Expect Properties after Service Registration", tester.props );
-                TestCase.assertEquals( "Expect a single update call", 1, tester.numManagedServiceUpdatedCalls );
+            TestCase.assertNull( "Expect Properties after Service Registration", tester2.props );
+            TestCase.assertEquals( "Expect a single update call", 1, tester2.numManagedServiceUpdatedCalls );
 
-                bundle2.uninstall();
-                bundle2 = null;
+            bundle.uninstall();
+            bundle = null;
 
-                delay();
+            delay();
 
-                /*
-                 * According to BJ Hargrave configuration is not re-dispatched
-                 * due to setting the bundle location.
-                 * <p>
-                 * Therefore, we have two sets one with re-dispatch expectation and
-                 * one without re-dispatch expectation.
-                 */
-                if ( REDISPATCH_CONFIGURATION_ON_SET_BUNDLE_LOCATION )
-                {
-                    // expect configuration reassigned
-                    TestCase.assertEquals( bundle.getLocation(), config.getBundleLocation() );
-                }
-                else
-                {
-                    // expected configuration unbound
-                    TestCase.assertNull( config.getBundleLocation() );
-                }
-            }
-            else
-            {
-                // configuration assigned to some other bundle ... fail
-                TestCase.fail( "Configuration assigned to unexpected bundle " + config.getBundleLocation() );
-            }
+            // after uninstallation, the configuration is redispatched
+            // due to the dynamic binding being removed
+
+            // expect configuration reassigned
+            TestCase.assertEquals( bundle2.getLocation(), config.getBundleLocation() );
+
+            // assert second bundle now has the configuration
+            TestCase.assertNotNull( "Expect Properties after Configuration redispatch", tester2.props );
+            TestCase.assertEquals( "Expect a single update call after Configuration redispatch", 2,
+                tester2.numManagedServiceUpdatedCalls );
 
             // remove the configuration for good
             deleteConfig( pid );