/*******************************************************************************
 * Copyright (c) 1997, 2018 by ProSyst Software GmbH and others.
 * http://www.prosyst.com
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *    ProSyst Software GmbH - initial API and implementation
 *******************************************************************************/
package org.eclipse.equinox.ds.tests.tbc;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import org.eclipse.equinox.ds.tests.BundleInstaller;
import org.eclipse.equinox.ds.tests.DSTestsActivator;
import org.junit.Before;
import org.junit.Test;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.osgi.framework.Filter;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.PackagePermission;
import org.osgi.framework.ServicePermission;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.component.ComponentConstants;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.ComponentFactory;
import org.osgi.service.component.ComponentInstance;
import org.osgi.service.permissionadmin.PermissionAdmin;
import org.osgi.service.permissionadmin.PermissionInfo;
import org.osgi.util.tracker.ServiceTracker;

public class DSTest {

  private static final String NAMED_CLASS = "org.eclipse.equinox.ds.tests.tb4.NamedService";

  private static final String EXTENDED_CLASS = "org.eclipse.equinox.ds.tests.tb1.BindUnbindSuccessor";

  private static final String SAC_CLASS = "org.eclipse.equinox.ds.tests.tb1.impl.AnotherComponent";

  private static final String SC_CLASS = "org.eclipse.equinox.ds.tests.tb1.impl.BaseComp";

  private static final String DYN_SERVICE_CLASS = "org.eclipse.equinox.ds.tests.tb4.DynamicService";

  private static final String BSRC_CLASS = "org.eclipse.equinox.ds.tests.tb4.BoundReplacer";

  private static final String MBSRC_CLASS = "org.eclipse.equinox.ds.tests.tb4.AdvancedBounder";

  private static final String SECURITY_CLASS = "org.eclipse.equinox.ds.tests.tb5.impl.SecurityTester";

  private static final String BLOCK_ACTIVE_CLASS = "org.eclipse.equinox.ds.tests.tb2.impl.Blocker";

  private static final String BLOCK_BIND_CLASS = "org.eclipse.equinox.ds.tests.tb3.impl.BindBlocker";

  private static final String STATIC_CLASS = "org.eclipse.equinox.ds.tests.tb6.StaticComp";

  private static final String REFERENCED_CLASS = "org.eclipse.equinox.ds.tests.tb6.ReferencedComp";

  private static final String NS_CLASS = "org.eclipse.equinox.ds.tests.tbc.NamespaceProvider";

  private static final String COMP_OPTIONAL_100 = "org.eclipse.equinox.ds.tests.tb11.optionalNS100";

  private static final String COMP_OPTIONAL_110 = "org.eclipse.equinox.ds.tests.tb11.optionalNS110";

  private static final String COMP_REQUIRE_100 = "org.eclipse.equinox.ds.tests.tb11.requireNS100";

  private static final String COMP_REQUIRE_110 = "org.eclipse.equinox.ds.tests.tb11.requireNS110";

  private static final String COMP_IGNORE_100 = "org.eclipse.equinox.ds.tests.tb11.ignoreNS100";

  private static final String COMP_IGNORE_110 = "org.eclipse.equinox.ds.tests.tb11.ignoreNS110";

  private static final String COMP_NOTSET_100 = "org.eclipse.equinox.ds.tests.tb11.notsetNS100";

  private static final String COMP_NOTSET_110 = "org.eclipse.equinox.ds.tests.tb11.notsetNS110";

  private static final String MOD_NOTSET_NS100 = "org.eclipse.equinox.ds.tests.tb21.notsetNS100";

  private static final String MOD_NOTSET_NS110 = "org.eclipse.equinox.ds.tests.tb21.notsetNS110";

  private static final String MOD_NOARGS_NS100 = "org.eclipse.equinox.ds.tests.tb21.NoArgs100";

  private static final String MOD_NOARGS_NS110 = "org.eclipse.equinox.ds.tests.tb21.NoArgs110";

  private static final String MOD_CC_NS100 = "org.eclipse.equinox.ds.tests.tb21.CcNS100";

  private static final String MOD_CC_NS110 = "org.eclipse.equinox.ds.tests.tb21.CcNS110";

  private static final String MOD_BC_NS100 = "org.eclipse.equinox.ds.tests.tb21.BcNS100";

  private static final String MOD_BC_NS110 = "org.eclipse.equinox.ds.tests.tb21.BcNS110";

  private static final String MOD_MAP_NS100 = "org.eclipse.equinox.ds.tests.tb21.MapNS100";

  private static final String MOD_MAP_NS110 = "org.eclipse.equinox.ds.tests.tb21.MapNS110";

  private static final String MOD_CC_BC_MAP_NS100 = "org.eclipse.equinox.ds.tests.tb21.CcBcMapNS100";

  private static final String MOD_CC_BC_MAP_NS110 = "org.eclipse.equinox.ds.tests.tb21.CcBcMapNS110";

  private static final String MOD_NOT_EXIST_NS110 = "org.eclipse.equinox.ds.tests.tb21.NotExistNS110";

  private static final String MOD_THROW_EX_NS110 = "org.eclipse.equinox.ds.tests.tb21.ThrowExNS110";
  
  private static final String COMP_OPTIONAL = "org.eclipse.equinox.ds.tests.tb24.optional";
  
  private static final String COMP_REQUIRE = "org.eclipse.equinox.ds.tests.tb24.require";

  private static final String COMP_IGNORE = "org.eclipse.equinox.ds.tests.tb24.ignore";


  private static int timeout = 1000;

  private Bundle tb1;

  private ServiceTracker trackerNamedService;

  private ServiceTracker trackerNamedServiceFactory;

  private ServiceTracker trackerCM;

  private ServiceTracker trackerExtendedClass;

  private ServiceTracker trackerSAC;

  private ServiceTracker trackerSC;

  private ServiceTracker trackerDynService;

  private ServiceTracker trackerDynServiceFactory;

  private ServiceTracker trackerBSRC;

  private ServiceTracker trackerMBSRC;

  private ServiceTracker trackerSecurity;

  private ServiceTracker trackerBAS;

  private ServiceTracker trackerBBS;

  private ServiceTracker trackerStatic;

  private ServiceTracker trackerReferenced;

  private ServiceTracker trackerNS;

  private ServiceTracker trackerBoundServiceCounterFactory;

  private ServiceTracker trackerBoundServiceCounterHelperFactory;

  private ServiceTracker trackerStaticServiceCounterFactory;

  private ServiceTracker trackerBaseService;

  private Hashtable registeredServices = new Hashtable();

  private int scr_restart_timeout = 33000;

  private boolean synchronousBuild = false;

  private BundleInstaller installer;

  @Before
  public void setUp() throws Exception {
    DSTestsActivator.activateSCR();

    timeout = getSystemProperty("scr.test.timeout", timeout);
    scr_restart_timeout = getSystemProperty("scr.restart.timeout", scr_restart_timeout);

    String synchronousBuildProp = System.getProperty("equinox.ds.synchronous_build");
    synchronousBuild = (synchronousBuildProp == null) || !synchronousBuildProp.equalsIgnoreCase("false");

    clearConfigurations();
    // init trackers
    BundleContext bc = getContext();

    installer = new BundleInstaller("/scr_test/", bc);

    // install test bundles
    tb1 = installBundle("tb1");

    // start them
    tb1.start();
    waitBundleStart();

    trackerNamedService = new ServiceTracker(bc, NAMED_CLASS, null);
    Filter filter = bc.createFilter("(&(" + ComponentConstants.COMPONENT_FACTORY + '=' + NAMED_CLASS + ")("
        + Constants.OBJECTCLASS + '=' + ComponentFactory.class.getName() + "))");
    trackerNamedServiceFactory = new ServiceTracker(bc, filter, null);
    trackerCM = new ServiceTracker(bc, ConfigurationAdmin.class.getName(), null);
    trackerExtendedClass = new ServiceTracker(bc, EXTENDED_CLASS, null);
    trackerSAC = new ServiceTracker(bc, SAC_CLASS, null);
    trackerSC = new ServiceTracker(bc, SC_CLASS, null);
    trackerDynService = new ServiceTracker(bc, DYN_SERVICE_CLASS, null);
    filter = bc.createFilter("(&(" + ComponentConstants.COMPONENT_FACTORY + '=' + DYN_SERVICE_CLASS + ")("
        + Constants.OBJECTCLASS + '=' + ComponentFactory.class.getName() + "))");
    trackerDynServiceFactory = new ServiceTracker(bc, filter, null);
    trackerBSRC = new ServiceTracker(bc, BSRC_CLASS, null);
    trackerMBSRC = new ServiceTracker(bc, MBSRC_CLASS, null);
    trackerSecurity = new ServiceTracker(bc, SECURITY_CLASS, null);
    trackerBAS = new ServiceTracker(bc, BLOCK_ACTIVE_CLASS, null);
    trackerBBS = new ServiceTracker(bc, BLOCK_BIND_CLASS, null);
    trackerStatic = new ServiceTracker(bc, STATIC_CLASS, null);
    trackerReferenced = new ServiceTracker(bc, REFERENCED_CLASS, null);
    trackerNS = new ServiceTracker(bc, NS_CLASS, null);
    filter = bc.createFilter("(&(" + ComponentConstants.COMPONENT_FACTORY + '=' + "CountFactory" + ")("
        + Constants.OBJECTCLASS + '=' + ComponentFactory.class.getName() + "))");
    trackerBoundServiceCounterFactory = new ServiceTracker(bc, filter, null);
    filter = bc.createFilter("(&(" + ComponentConstants.COMPONENT_FACTORY + '=' + "CountHelperFactory" + ")("
        + Constants.OBJECTCLASS + '=' + ComponentFactory.class.getName() + "))");
    trackerBoundServiceCounterHelperFactory = new ServiceTracker(bc, filter, null);
    filter = bc.createFilter("(&(" + ComponentConstants.COMPONENT_FACTORY + '=' + "StaticServiceCountFactory" + ")("
        + Constants.OBJECTCLASS + '=' + ComponentFactory.class.getName() + "))");
    trackerStaticServiceCounterFactory = new ServiceTracker(bc, filter, null);
    trackerBaseService = new ServiceTracker(bc, PropertiesProvider.class.getName(), null);

    // start listening
    trackerNamedService.open(true);
    trackerNamedServiceFactory.open(true);
    trackerCM.open(true);
    trackerExtendedClass.open(true);
    trackerSAC.open(true);
    trackerSC.open(true);
    trackerDynService.open(true);
    trackerDynServiceFactory.open(true);
    trackerBSRC.open(true);
    trackerMBSRC.open(true);
    trackerSecurity.open(true);
    trackerBAS.open(true);
    trackerBBS.open(true);
    trackerStatic.open(true);
    trackerReferenced.open(true);
    trackerNS.open(true);
    trackerBoundServiceCounterFactory.open(true);
    trackerBoundServiceCounterHelperFactory.open(true);
    trackerStaticServiceCounterFactory.open(true);
    trackerBaseService.open(true);
  }

  /**
   * This methods takes care of the configurations related to this test
   * 
   * @throws IOException
   * @throws InvalidSyntaxException
   * @throws InterruptedException
   */
  private void clearConfigurations() throws IOException, InvalidSyntaxException {
    ServiceReference cmSR = getContext().getServiceReference(ConfigurationAdmin.class.getName());
    if (cmSR == null)
      return;
    ConfigurationAdmin cm = (ConfigurationAdmin) getContext().getService(cmSR);
    // clean configurations from previous tests
    // clean factory configs for named service
    clearConfiguration(cm, "(service.factoryPid=" + NAMED_CLASS + ")");
    // clean configs for named service
    clearConfiguration(cm, "(service.pid=" + NAMED_CLASS + ")");
    // clean configs for stand alone component
    clearConfiguration(cm, "(service.pid=" + SAC_CLASS + ")");
    // clean configs for optionalNS100
    clearConfiguration(cm, "(service.pid=" + COMP_OPTIONAL_100 + ")");
    clearConfiguration(cm, "(service.factoryPid=" + COMP_OPTIONAL_100 + ")");
    // clean configs for optionalNS110
    clearConfiguration(cm, "(service.pid=" + COMP_OPTIONAL_110 + ")");
    clearConfiguration(cm, "(service.factoryPid=" + COMP_OPTIONAL_110 + ")");
    // clean configs for requireNS100
    clearConfiguration(cm, "(service.pid=" + COMP_REQUIRE_100 + ")");
    clearConfiguration(cm, "(service.factoryPid=" + COMP_REQUIRE_100 + ")");
    // clean configs for requireNS110
    clearConfiguration(cm, "(service.pid=" + COMP_REQUIRE_110 + ")");
    clearConfiguration(cm, "(service.factoryPid=" + COMP_REQUIRE_110 + ")");
    // clean configs for ignoreNS100
    clearConfiguration(cm, "(service.pid=" + COMP_IGNORE_100 + ")");
    clearConfiguration(cm, "(service.factoryPid=" + COMP_IGNORE_100 + ")");
    // clean configs for ignoreNS110
    clearConfiguration(cm, "(service.pid=" + COMP_IGNORE_110 + ")");
    clearConfiguration(cm, "(service.factoryPid=" + COMP_IGNORE_110 + ")");
    // clean configs for notsetNS100
    clearConfiguration(cm, "(service.pid=" + COMP_NOTSET_100 + ")");
    clearConfiguration(cm, "(service.factoryPid=" + COMP_NOTSET_100 + ")");
    // clean configs for notsetNS110
    clearConfiguration(cm, "(service.pid=" + COMP_NOTSET_110 + ")");
    clearConfiguration(cm, "(service.factoryPid=" + COMP_NOTSET_110 + ")");

    clearConfiguration(cm, "(service.pid=" + MOD_NOTSET_NS100 + ")");
    clearConfiguration(cm, "(service.pid=" + MOD_NOTSET_NS110 + ")");
    clearConfiguration(cm, "(service.pid=" + MOD_NOARGS_NS100 + ")");
    clearConfiguration(cm, "(service.pid=" + MOD_NOARGS_NS110 + ")");
    clearConfiguration(cm, "(service.pid=" + MOD_CC_NS100 + ")");
    clearConfiguration(cm, "(service.pid=" + MOD_CC_NS110 + ")");
    clearConfiguration(cm, "(service.pid=" + MOD_BC_NS100 + ")");
    clearConfiguration(cm, "(service.pid=" + MOD_BC_NS110 + ")");
    clearConfiguration(cm, "(service.pid=" + MOD_MAP_NS100 + ")");
    clearConfiguration(cm, "(service.pid=" + MOD_MAP_NS110 + ")");
    clearConfiguration(cm, "(service.pid=" + MOD_CC_BC_MAP_NS100 + ")");
    clearConfiguration(cm, "(service.pid=" + MOD_CC_BC_MAP_NS110 + ")");
    clearConfiguration(cm, "(service.pid=" + MOD_NOT_EXIST_NS110 + ")");
    clearConfiguration(cm, "(service.pid=" + MOD_THROW_EX_NS110 + ")");

    clearConfiguration(cm, "(service.pid=" + COMP_IGNORE + ")");
    clearConfiguration(cm, "(service.pid=" + COMP_OPTIONAL + ")");
    clearConfiguration(cm, "(service.pid=" + COMP_REQUIRE + ")");
    getContext().ungetService(cmSR);
  }

  private void clearConfiguration(ConfigurationAdmin cm, String filter) throws IOException, InvalidSyntaxException {
    Configuration[] configs = cm.listConfigurations(filter);
    for (int i = 0; configs != null && i < configs.length; i++) {
      Configuration configuration = configs[i];
      configuration.delete();
    }
  }

  /**
   * @param propertyKey
   */
  private int getSystemProperty(String propertyKey, int defaultValue) {
    String propertyString = System.getProperty(propertyKey);
    int sleepTime = defaultValue;
    if (propertyString != null) {
      try {
        sleepTime = Integer.parseInt(propertyString);
      } catch (Exception e) {
        e.printStackTrace();
        System.out.println("Error while parsing sleep value! The default one will be used : " + defaultValue);
      }
      if (sleepTime < 100) {
        log("The sleep value is too low : " + sleepTime + " ! The default one will be used : " + defaultValue);
        return defaultValue;
      }
      return sleepTime;
    }
    return defaultValue;
  }

  /*
   * (non-Javadoc)
   * 
   * @see org.eclipse.equinox.ds.tests.tbc.DSTest.#tearDown()
   */
  public void tearDown() throws Exception {
    unregisterAllServices();

    trackerNamedService.close();
    trackerNamedServiceFactory.close();
    trackerExtendedClass.close();
    trackerSAC.close();
    trackerSC.close();
    trackerDynService.close();
    trackerDynServiceFactory.close();
    trackerBSRC.close();
    trackerMBSRC.close();
    trackerSecurity.close();
    trackerBAS.close();
    trackerBBS.close();
    trackerStatic.close();
    trackerReferenced.close();
    trackerNS.close();
    trackerBoundServiceCounterFactory.close();
    trackerBoundServiceCounterHelperFactory.close();
    trackerBaseService.close();

    if (installer != null) {
      BundleInstaller bi = installer;
      installer = null;
      bi.shutdown();
    }

    clearConfigurations();
  }

  @Test
  public void testBindUnbind() throws Exception {

    assertEquals("TestBundle1 must be running.", Bundle.ACTIVE, tb1.getState());

    Bundle tb1a = installBundle("tb1a");
    tb1a.start();
    waitBundleStart();
    ServiceReference sr1 = getServiceReference("org.eclipse.equinox.ds.tests.tb1a.Comp1");
    assertNotNull("Incorrect components should be ignored and the component Comp1 should be available", sr1);
    getContext().ungetService(sr1);
    uninstallBundle(tb1a);

    Object s = trackerExtendedClass.getService();
    assertNotNull("The BindUnbindSuccessor component should be available", s);

    assertTrue("The bind method on BindUnbindSuccessor component should be called to save the service reference",
        ((BoundTester) s).getBoundObjectsCount() > 0);

    // disable the referenced component to trigger unbind event
    ComponentManager enabler = (ComponentManager) s;
    enabler.enableComponent(SAC_CLASS, false);
    Thread.sleep(timeout);

    assertNull("The SAC component should be disabled (unavailable)", trackerSAC.getServiceReference());

    assertTrue("The unbind method on BindUnbindSuccessor component should be called to reset the service reference",
        ((BoundTester) s).getBoundObjectsCount() < 1);

    // enable the referenced component
    enabler = (ComponentManager) trackerExtendedClass.getService();
    enabler.enableComponent(SAC_CLASS, true);
    Thread.sleep(timeout);
    assertNotNull("The SAC component should be available", trackerSAC.getServiceReference());
  }

  @Test
  public void testUniqueComponentContext() throws Exception {
    Bundle bundle = installBundle("tb4");
    bundle.start();
    waitBundleStart();

    Hashtable props;
    ComponentFactory factory = (ComponentFactory) trackerNamedServiceFactory.getService();
    assertNotNull("The NamedService component factory should be available", factory);

    // create the first service
    props = new Hashtable();
    props.put("name", "hello");

    ComponentInstance ci1 = factory.newInstance(props);
    ComponentInstance ci2 = factory.newInstance(props);

    ComponentContextProvider cce1 = (ComponentContextProvider) ci1.getInstance();
    ComponentContextProvider cce2 = (ComponentContextProvider) ci2.getInstance();

    assertNotSame("The two instances created must be different", cce1, cce2);

    ComponentContext cc1 = cce1.getComponentContext();
    ComponentContext cc2 = cce2.getComponentContext();

    assertNotSame("The two component contexts must be not the same", cc1, cc2);

    uninstallBundle(bundle);
  }

  @Test
  public void testComponentContextMethods() throws Exception {

    Object extendedClass = trackerExtendedClass.getService();
    // check that the BindUnbindSuccessor component is available
    assertNotNull("BindUnbindSuccessor component should be available", extendedClass);

    ComponentContext ctxt = ((ComponentContextProvider) extendedClass).getComponentContext();
    assertNotNull("The BindUnbindSuccessor component should be activated properly", ctxt);

    assertNotNull("The AnotherComponent should be available before we disable it", trackerSAC.getServiceReferences());
    assertTrue("The AnotherComponent should be available before we disable it",
        trackerSAC.getServiceReferences().length > 0);
    assertNotNull("The Worker should be available before we disable it", trackerSC.getServiceReferences());
    assertTrue("The Worker should be available before we disable it", trackerSC.getServiceReferences().length > 0);

    try {
      ((ComponentManager) extendedClass).enableComponent("InvalidParameter", true); // test
      // for
      // disabling
      // unexistent
    } catch (Exception e) {
      // unexpected exception
      fail("Unexpected exception " + e.getMessage());
    }

    ((ComponentManager) extendedClass).enableComponent(SAC_CLASS, false);
    Thread.sleep(timeout); // let the SCR to unregister the service
    assertNull("The service must not be available after we had disabled the component (AnotherComponent)", trackerSAC
        .getServiceReferences());

    ((ComponentManager) extendedClass).enableComponent(SC_CLASS, false);
    Thread.sleep(timeout); // let the SCR to unregister the service
    assertNull("The service must not be available after we had disabled the component (Worker)", trackerSC
        .getServiceReferences());

    // *** test enableComponent() method
    ((ComponentManager) extendedClass).enableComponent(SAC_CLASS, true);
    Thread.sleep(timeout); // let the SCR to register the service
    assertNotNull("The service must be available after we had enabled the component", trackerSAC.getServiceReferences());
    assertTrue("The service must be available after we had enabled the component",
        trackerSAC.getServiceReferences().length > 0);

    ((ComponentManager) extendedClass).enableComponent(null, true);
    Thread.sleep(timeout);
    assertNotNull("The enableComponent() with passed null parameter, must enable the remaining disabled components",
        trackerSC.getServiceReferences());
    assertTrue("The enableComponent() with passed null parameter, must enable the remaining disabled components",
        trackerSC.getServiceReferences().length > 0);

    // *** test getBundleContext()
    BundleContextProvider sacBCE = (BundleContextProvider) trackerSAC.getService();
    assertNotNull("AnotherComponent should be available", sacBCE);
    assertSame("The two bundle context (this from the activator and from the ComponentContext object must be the same",
        sacBCE.getBundleContext(), ((ComponentContextProvider) extendedClass).getComponentContext().getBundleContext());

    // *** test getComponentInstance()
    Bundle bundle = installBundle("tb4");
    assertNotNull("Installing tb4.jar should succeed", bundle);
    bundle.start();
    waitBundleStart();

    Hashtable props;
    ComponentFactory factory = (ComponentFactory) trackerNamedServiceFactory.getService();
    assertNotNull("NamedService component factory should be available", factory);

    props = new Hashtable();
    props.put("name", "hello");

    ComponentInstance ci = factory.newInstance(props);
    assertNotNull("newInstance() should not return null", ci);
    ComponentContextProvider cce = (ComponentContextProvider) ci.getInstance();
    assertNotNull("getInstance() should not return null if we haven't disposed the component", cce);
    assertNotNull("the component instance should be initialized correctly", cce.getComponentContext());
    ComponentInstance ctxtInstance = cce.getComponentContext().getComponentInstance();
    assertSame(
        "The ComponentInstance object retrieved from the factory and from the ComponentContext must be the same", ci,
        ctxtInstance);
    // dispose the instance
    ci.dispose();
    assertNull("getInstance() should return null when disposed", ci.getInstance());

    // *** test getUsingBundle()
    ComponentContextProvider simpleComponentCCE = (ComponentContextProvider) trackerSC.getService();
    assertNotNull("Worker should be available", simpleComponentCCE);
    assertNotNull("Worker's context should be not null", simpleComponentCCE.getComponentContext());
    assertNotNull("At least this bundle (TBC) must be using the Worker service", simpleComponentCCE
        .getComponentContext().getUsingBundle());

    // *** test getProperties()
    Dictionary p = simpleComponentCCE.getComponentContext().getProperties();
    assertNotNull("Worker properties must be not null", p);
    assertEquals("The properties must contain the custom property defined in the component description", p
        .get("custom"), "customvalue");
    assertEquals("The properties must contain the component.name property", p.get(ComponentConstants.COMPONENT_NAME),
        SC_CLASS);
    assertNotNull("The properties must contain the component.id property", p.get(ComponentConstants.COMPONENT_ID));
    assertEquals("The component.id property must be of type java.lang.Long", p.get(ComponentConstants.COMPONENT_ID)
        .getClass().getName(), Long.class.getName());

    // *** test getServiceReference()
    ServiceReference ctxtServiceReference = ctxt.getServiceReference();
    ServiceReference bcServiceReference = trackerExtendedClass.getServiceReference();
    assertEquals("The two ServiceReference should be equal", ctxtServiceReference, bcServiceReference);

    // *** test locateService(String)
    Object locateSac = ctxt.locateService("StandAloneComp");
    assertNotNull("The locateService() method should return non-null object", locateSac);
    assertEquals("The object must implement " + SAC_CLASS, locateSac.getClass().getName(), SAC_CLASS);

    // test illegal call
    assertNull("Trying to get invalid reference should return null", ctxt.locateService("InvalidReference"));

    ((ComponentManager) extendedClass).enableComponent(SAC_CLASS, false); // disable
    // component
    // to
    // test
    // that
    // the
    // locateService()
    // don't
    // return
    // disabled
    // components
    Thread.sleep(timeout);

    assertEquals("Check that the component is correctly disabled", 0, countAvailableServices(trackerSAC));

    locateSac = ctxt.locateService("StandAloneComp");
    assertNull("The reference shouldn't be available with optional cardinality and disabled component", locateSac);

    ((ComponentManager) extendedClass).enableComponent(SAC_CLASS, true);
    Thread.sleep(timeout);
    assertTrue("Check that the component is correctly enabled", countAvailableServices(trackerSAC) > 0);

    // *** test locateServices(String)
    Object[] boundObjects = ctxt.locateServices("StandAloneComp");
    int boundCount = ((BoundTester) extendedClass).getBoundObjectsCount();
    assertNotNull("The returned array of bound services should not be null", boundObjects);
    assertEquals("The returned array of bound services should have the length equal to the internal count", boundCount,
        boundObjects.length);
    for (int i = 0; i < boundObjects.length; i++) {
      assertNotNull("There shouldn't be null element in the bound objects array (" + i + ")", boundObjects[i]);
    }

    assertNull("The locateServices() method should return null on invalid reference name", ctxt
        .locateServices("InvalidReference"));

    // *** test locateService(String, ServiceReference)
    assertTrue("There must be at least one bound element", ((BoundTester) extendedClass).getBoundObjectsCount() > 0);

    ServiceReference sr1 = ((BoundTester) extendedClass).getBoundServiceRef(0);
    assertNotNull("The ServiceReference bound to the BindUnbindSuccessor components should not be null", sr1);
    Object fromSR1 = ctxt.getBundleContext().getService(sr1);
    Object fromCtxt = ctxt.locateService("StandAloneComp", sr1);
    try {
      assertNotNull("The service object from BundleContext must not be null", fromSR1);
      assertNotNull("The service object from locateService() must not be null", fromCtxt);
      assertSame(
          "The two objects retrieved from BundleContext and from locateService(String, ServiceReference) method must be the same",
          fromSR1, fromCtxt);
    } finally {
      ctxt.getBundleContext().ungetService(sr1);
    }

// null is not allowed for ServiceReference
//    assertNull("locateService() must return null when passed ServiceReference is null", ctxt.locateService(
//        "StandAloneComp", null));

    assertNull("locateService() must return null when passed ServiceReference isn't bound to the component", ctxt
        .locateService("StandAloneComp", trackerExtendedClass.getServiceReference()));

    assertNull(
        "locateService() must return null when the referenceName isn't correct even if the service reference is correct",
        ctxt.locateService("InvalidReference", sr1));

    uninstallBundle(bundle);
  }

  @Test
  public void testPropertiesHandling() throws Exception {
    ConfigurationAdmin cm = (ConfigurationAdmin) trackerCM.getService();
    if (cm == null)
    	return; // cannot test without CM
    ServiceReference ref;

    // update the properties
    Hashtable props = new Hashtable(10);
    props.put("test.property.value", "setFromCM");
    props.put("test.property.list", "setFromCM");
    props.put("component.name", "setFromCM");
    props.put("component.id", Long.valueOf(-1));
    // the line below will create the configuration if it doesn't exists!
    // see CM api for details

    Configuration config = cm.getConfiguration(SAC_CLASS, null);
    assertNotNull("The Configuration object should be created if don't exist", config);
    config.update(props);

    // let SCR & CM to complete it's job
    Thread.sleep(timeout * 2);

    ref = trackerSAC.getServiceReference();
    // check the correctness of the properties
    assertNotNull("The AnotherComponent's reference should be available", ref);
    assertEquals("Properties not overriden from later ones should not be lost", "setFromFile", ref
        .getProperty("test.property.array"));
    assertEquals("Properties set through the CM should take precedence before those set from file", "setFromCM", ref
        .getProperty("test.property.value"));
    assertEquals("Properties overriden from later ones in definition should take precedence", "setFromDefinition", ref
        .getProperty("test.property.name"));
    assertEquals("Properties set through the CM should take precedence before those set from definition", "setFromCM",
        ref.getProperty("test.property.list"));
    assertEquals("Properties not overriden from later ones should not be lost", "setFromDefinition", ref
        .getProperty("test.property.cont"));
    assertEquals("Must not allow overriding the component.name property", SAC_CLASS, ref.getProperty("component.name"));
    assertNotNull("component.id property should be present", ref.getProperty(ComponentConstants.COMPONENT_ID));
    assertTrue("Must not allow overriding the component.id property", ((Long) ref.getProperty("component.id"))
        .longValue() > 0);

    Bundle bundle = installBundle("tb4");
    bundle.start();
    waitBundleStart();

    Configuration c = cm.getConfiguration(NAMED_CLASS, null);
    assertNotNull("The Configuration should be created properly", c);
    Hashtable cmProps = new Hashtable();
    cmProps.put("override.property.3", "setFromCM");
    c.update(cmProps);

    // let the config update reach the SCR
    Thread.sleep(timeout * 2);

    ComponentFactory factory = (ComponentFactory) trackerNamedServiceFactory.getService();
    assertNotNull("The NamedService ComponentFactory should be available", factory);

    Hashtable newProps = new Hashtable();
    newProps.put("override.property.1", "setFromMethod");
    newProps.put("override.property.2", "setFromMethod");
    newProps.put("override.property.3", "setFromMethod");
    newProps.put(ComponentConstants.COMPONENT_NAME, "setFromMethod");
    newProps.put(ComponentConstants.COMPONENT_ID, Long.valueOf(-1));
    newProps.put("name", "test");

    List<ComponentInstance> cis = new ArrayList<>();
    ComponentInstance ci = factory.newInstance(newProps);
    assertNotNull("newInstance() method shouldn't return null", ci);
    cis.add(ci);
    ci = factory.newInstance(newProps);
    assertNotNull("newInstance() method shouldn't return null", ci);
    cis.add(ci);
    ci = factory.newInstance(newProps);
    assertNotNull("newInstance() method shouldn't return null", ci);
    cis.add(ci);

    ServiceReference[] refs = trackerNamedService.getServiceReferences();
    boolean serviceFound = false;
    for (int i = 0; refs != null && i < refs.length; i++) {
      ServiceReference current = refs[i];
      if ("test".equals(current.getProperty("name"))) {
        serviceFound = true;
        assertEquals("Properties set through newInstance method must override those from definition", "setFromMethod",
            current.getProperty("override.property.1"));
        assertEquals("Properties set through newInstance method must override those from file", "setFromMethod",
            current.getProperty("override.property.2"));
        assertEquals("Properties set through newInstance method must override those from ConfigurationAdmin",
            "setFromMethod", current.getProperty("override.property.3"));
        assertEquals("Must not override " + ComponentConstants.COMPONENT_NAME, current
            .getProperty(ComponentConstants.COMPONENT_NAME), NAMED_CLASS);
        assertTrue("Must not override " + ComponentConstants.COMPONENT_ID, ((Long) current
            .getProperty(ComponentConstants.COMPONENT_ID)).longValue() > 0);
      }
    }
    assertTrue("Must have found service", serviceFound);

    for (ComponentInstance i :cis) {
    	i.dispose();
    }
    c.delete();
    //bundle.stop();

    factory = (ComponentFactory) trackerNamedServiceFactory.getService();
    ci = factory.newInstance(newProps);
    assertNotNull("newInstance() method shouldn't return null", ci);

    // test the conflict between factory and factoryPID
    c = cm.createFactoryConfiguration(NAMED_CLASS, null);
    assertNotNull("CM should not return null Configuration from createFactoryConfiguration()", c);
    c.update(cmProps);
    Thread.sleep(timeout);

    bundle.start();
    waitBundleStart();

 // TODO Equinox DS behaves differently than Felix SCR here. The specification is vague
 // Equinox DS will disable the component factory in this error case
 // Felix will keep the component factory enabled and ignore the CM factory configuration
    assertNotNull("The named service ComponentFactory should be available even when there is factory configuration for it",
        trackerNamedServiceFactory.getService());

    c.delete();
    Thread.sleep(timeout * 2);

    // create factory configs for Worker
    Configuration scConfig1 = cm.createFactoryConfiguration(SC_CLASS, null);
    Hashtable scProps1 = new Hashtable();
    scProps1.put("name", "instance1");
    scConfig1.update(scProps1);

    Configuration scConfig2 = cm.createFactoryConfiguration(SC_CLASS, null);
    Hashtable scProps2 = new Hashtable();
    scProps2.put("name", "instance2");
    scConfig2.update(scProps2);

    Thread.sleep(timeout * 2);

    try {// test factory configuration for normal component
      assertEquals("The Worker should have two instances", 2, countAvailableServices(trackerSC));
    } finally {
      scConfig1.delete();
      scConfig2.delete();
    }
    Thread.sleep(timeout * 2);

    assertEquals("The Worker should have one instance", 1, countAvailableServices(trackerSC));
    ServiceReference scRef = trackerSC.getServiceReference();
    assertNull("The Worker only instance shouldn't have \"name\" property", scRef.getProperty("name"));

    uninstallBundle(bundle);
  }

  @Test
  public void testBoundServiceReplacement() throws Exception {
    int beforeCount, afterCount;
    Hashtable mandatoryProperty = new Hashtable();
    mandatoryProperty.put("mandatory.property", "true");

    Bundle tb4 = installBundle("tb4");
    tb4.start();
    waitBundleStart();
    assertEquals("tb4.jar should be ACTIVE", Bundle.ACTIVE, tb4.getState());

    ComponentFactory namedFactory = (ComponentFactory) trackerNamedServiceFactory.getService();
    assertNotNull("NamedService component factory should be available", namedFactory);
    ComponentFactory dynFactory = (ComponentFactory) trackerDynServiceFactory.getService();
    assertNotNull("DynamicWorker component factory should be available", dynFactory);

    // create the mandatory elements
    ComponentInstance namedServiceInstance = namedFactory.newInstance((Dictionary) mandatoryProperty.clone());
    assertNotNull("NamedService component instance should not be null", namedServiceInstance);
    Object namedService = namedServiceInstance.getInstance();
    assertNotNull("NamedService should be created properly", namedService);
    ComponentInstance dynServiceInstance = dynFactory.newInstance((Dictionary) mandatoryProperty.clone());
    assertNotNull("DynamicWorker component instance should not be null", dynServiceInstance);
    Object dynService = dynServiceInstance.getInstance();
    assertNotNull("DynamicWorker should be created properly", dynService);

    Object bsrc = trackerBSRC.getService();
    assertNotNull("BoundReplacer should be available", bsrc);
    assertSame("NamedService bound should be our first instance", ((BoundMainProvider) bsrc)
        .getBoundService(BoundMainProvider.NAMED_SERVICE), namedService);
    assertSame("DynamicWorker bound should be our first instance", ((BoundMainProvider) bsrc)
        .getBoundService(BoundMainProvider.DYNAMIC_SERVICE), dynService);

    // provide second dynamic service
    ComponentInstance dynServiceInstance2 = dynFactory.newInstance((Dictionary) mandatoryProperty.clone());
    assertNotNull("Second DynamicWorker component instance should not be null", dynServiceInstance2);
    Object dynService2 = dynServiceInstance2.getInstance();
    assertNotNull("Second DynamicWorker instance should be available", dynService2);

    // reset the events
    ((DSEventsProvider) bsrc).resetEvents();
    // destroy the first instance of dynamic service
    dynServiceInstance.dispose();

    // check that service is replaced
    assertNotSame("The bound dynamic service shouldn't be our first instance", ((BoundMainProvider) bsrc)
        .getBoundService(BoundMainProvider.DYNAMIC_SERVICE), dynService);
    assertSame("The bound dynamic service should be our second instance", ((BoundMainProvider) bsrc)
        .getBoundService(BoundMainProvider.DYNAMIC_SERVICE), dynService2);

    // check the correct order of replacing
    DSEvent[] replacedBoundDynamicServicesEvents = ((DSEventsProvider) bsrc).getEvents();
    assertEquals("There should two events after we have disposed the bound service", 2,
        replacedBoundDynamicServicesEvents.length);
    assertEquals("The first event should be bind event", DSEvent.ACT_BOUND, replacedBoundDynamicServicesEvents[0]
        .getAction());
    assertSame("The first event should have associated object the second instance", dynService2,
        replacedBoundDynamicServicesEvents[0].getObject());

    assertEquals("The second event should be unbind event", DSEvent.ACT_UNBOUND, replacedBoundDynamicServicesEvents[1]
        .getAction());
    assertSame("The second event should have associated object the first instance", dynService,
        replacedBoundDynamicServicesEvents[1].getObject());

    // destroy and the second service
    dynServiceInstance2.dispose();

    // check that the inspected service is deactivated
    assertNull("The BoundReplacer should not be available as the destroyed service hasn't replacement", trackerBSRC
        .getService());

    // restore the BSRC
    assertNotNull("The DynamicWorker component instance should be created properly", dynFactory
        .newInstance((Dictionary) mandatoryProperty.clone()));

    Object bsrcObject = trackerBSRC.getService();
    assertNotNull("The BoundReplacer should be available again", bsrcObject);
    ComponentContext bsrcCtxt1 = ((ComponentContextProvider) bsrcObject).getComponentContext();
    assertNotNull("The BoundReplacer should be activated and ComponentContext available", bsrcCtxt1);

    // prepare second static service instance
    ComponentInstance namedServiceInstance2 = namedFactory.newInstance((Dictionary) mandatoryProperty.clone());
    assertNotNull("Second NamedService instance should be created properly", namedServiceInstance2);
    Object namedService2 = namedServiceInstance2.getInstance();
    assertNotNull("Second NamedService instance should be created properly", namedService2);

    // destroy the first instance
    beforeCount = countAvailableServices(trackerNamedService);
    namedServiceInstance.dispose();
    afterCount = countAvailableServices(trackerNamedService);
    assertEquals("The NamedService instance should be removed from the registry", beforeCount - 1, afterCount);

    // check that the BSRC has been reactivated
    Object bsrcObject2 = trackerBSRC.getService(); // the BSRC object can be
    // recreated
    assertNotNull("The BoundReplacer should not be null", bsrcObject2);
    ComponentContext bsrcCtxt2 = ((ComponentContextProvider) bsrcObject2).getComponentContext();
    assertNotNull("The second ComponentContext should not be null", bsrcCtxt2);
    assertNotSame("The second ComponentContext should be different than the first one", bsrcCtxt1, bsrcCtxt2);

    // destroy the second instance
    namedServiceInstance2.dispose();

    assertNull("The BSRC should be disabled", trackerBSRC.getService());

    uninstallBundle(tb4);
  }

  /**
   * Returns the number of available services for the passed tracker
   * 
   * @param tracker
   * @return
   */
  private int countAvailableServices(ServiceTracker tracker) {
    if (tracker == null)
      return -1;
    ServiceReference[] refs = tracker.getServiceReferences();
    return refs != null ? refs.length : 0;
  }

  @Test
  public void testBoundServiceReplacementOnModification() throws Exception {
    BundleContext bc = getContext();
    Hashtable initialProps = new Hashtable();
    Hashtable modifiedProps = new Hashtable();
    initialProps.put("mandatory.property", "true");
    modifiedProps.put("mandatory.property", "false");

    ServiceRegistration dynRegistration1 = registerService(DynamicWorker.class.getName(), new DynamicWorker(),
        (Dictionary) initialProps.clone());

    ServiceRegistration staticRegistration1 = registerService(StaticWorker.class.getName(), new StaticWorker(),
        (Dictionary) initialProps.clone());

    Bundle tb4 = installBundle("tb4");
    tb4.start();
    waitBundleStart();
    assertEquals("tb4.jar should be ACTIVE", Bundle.ACTIVE, tb4.getState());

    // assure the MBSRC is available
    assertTrue("The AdvancedBounder must be available", countAvailableServices(trackerMBSRC) > 0);
    Object bsrc = trackerMBSRC.getService();
    assertNotNull("MBSRC isntance should be not null", bsrc);

    // register the second instances
    ServiceRegistration dynRegistration2 = registerService(DynamicWorker.class.getName(), new DynamicWorker(),
        (Dictionary) initialProps.clone());

    // reset the bound services events
    ((DSEventsProvider) bsrc).resetEvents();
    // change the first instance of dynamic service
    dynRegistration1.setProperties(modifiedProps);

    Object instance1 = bc.getService(dynRegistration1.getReference());
    Object instance2 = bc.getService(dynRegistration2.getReference());
    try {
      // check that service is replaced
      assertNotSame("The bound dynamic service shouldn't be our first instance", ((BoundMainProvider) bsrc)
          .getBoundService(BoundMainProvider.DYNAMIC_SERVICE), instance1);
      assertSame("The bound dynamic service should be our second instance", ((BoundMainProvider) bsrc)
          .getBoundService(BoundMainProvider.DYNAMIC_SERVICE), instance2);

      // check the correct order of replacing
      DSEvent[] replacedBoundDynamicServicesEvents = ((DSEventsProvider) bsrc).getEvents();
      assertEquals("There should two events after we have disposed the bound service", 2,
          replacedBoundDynamicServicesEvents.length);

      assertEquals("The first event should be bind event", DSEvent.ACT_BOUND, replacedBoundDynamicServicesEvents[0]
          .getAction());
      assertSame("The first event should have associated object the second instance", instance2,
          replacedBoundDynamicServicesEvents[0].getObject());

      assertEquals("The second event should be unbind event", DSEvent.ACT_UNBOUND,
          replacedBoundDynamicServicesEvents[1].getAction());
      assertSame("The second event should have associated object the first instance", instance1,
          replacedBoundDynamicServicesEvents[1].getObject());

    } finally {
      bc.ungetService(dynRegistration1.getReference());
      bc.ungetService(dynRegistration2.getReference());
    }
    instance1 = instance2 = null;

    ComponentContext bsrcCtxt1 = ((ComponentContextProvider) bsrc).getComponentContext();
    assertNotNull("ComponentContext object should be available", bsrcCtxt1);

    ServiceRegistration staticRegistration2 = registerService(StaticWorker.class.getName(), new StaticWorker(),
        (Dictionary) initialProps.clone());
    // change the first instance
    staticRegistration1.setProperties((Dictionary) modifiedProps.clone());

    Object bsrcObject2 = trackerMBSRC.getService(); // the BSRC object can be
    // recreated
    assertNotNull("The BoundReplacer should not be null", bsrcObject2);
    ComponentContext bsrcCtxt2 = ((ComponentContextProvider) bsrcObject2).getComponentContext();
    assertNotNull("The second ComponentContext should not be null", bsrcCtxt2);
    assertNotSame("The second ComponentContext should be different than the first one", bsrcCtxt1, bsrcCtxt2);
    
    //test the modification of static service reference which is not bound
    unregisterService(staticRegistration1);
    unregisterService(staticRegistration2);
    assertTrue("The AdvancedBounder must not be available", countAvailableServices(trackerMBSRC) == 0);
    staticRegistration1 = registerService(StaticWorker.class.getName(), new StaticWorker(),
        (Dictionary) initialProps.clone());    
    // assure the MBSRC is available
    assertTrue("The AdvancedBounder must be available", countAvailableServices(trackerMBSRC) > 0);
    staticRegistration2 = registerService(StaticWorker.class.getName(), new StaticWorker(),
        (Dictionary) initialProps.clone());
    bsrcCtxt2 = ((ComponentContextProvider)trackerMBSRC.getService()).getComponentContext();
    //modify the service which is not bound
    staticRegistration2.setProperties((Dictionary) modifiedProps.clone());
    bsrcCtxt1 = ((ComponentContextProvider)trackerMBSRC.getService()).getComponentContext();
    //check if the component is reactivated when a service is modified but it is not bound to the component
    assertEquals("The component context must not be changed", bsrcCtxt2, bsrcCtxt1);
    
    // test the modification of static service reference which is bound. 
    // The service reference does still satisfy the component reference after the
    // modification
    unregisterService(staticRegistration1);
    unregisterService(staticRegistration2);
    assertTrue("The AdvancedBounder must not be available", countAvailableServices(trackerMBSRC) == 0);
    staticRegistration1 = registerService(StaticWorker.class.getName(), new StaticWorker(),
        (Dictionary) initialProps.clone());    
    // assure the MBSRC is available
    assertTrue("The AdvancedBounder must be available", countAvailableServices(trackerMBSRC) > 0);
    bsrcCtxt1 = ((ComponentContextProvider)trackerMBSRC.getService()).getComponentContext();
    //modify the service which is bound
    Hashtable modified = (Hashtable) initialProps.clone();
    modified.put("a", "a");
    staticRegistration1.setProperties(modified);
    bsrcCtxt2 = ((ComponentContextProvider)trackerMBSRC.getService()).getComponentContext();
    //check if the component is reactivated when a bound service is modified but still satisfies the component reference
    assertEquals("The component context must not be changed", bsrcCtxt1, bsrcCtxt2);
    
    // test the modification of dynamic service reference which is bound. 
    // The service reference does still satisfy the component reference after the
    // modification
    unregisterService(dynRegistration1);
    unregisterService(dynRegistration2);
    assertTrue("The AdvancedBounder must not be available", countAvailableServices(trackerMBSRC) == 0);
    dynRegistration1 = registerService(DynamicWorker.class.getName(), new DynamicWorker(),
        (Dictionary) initialProps.clone());    
    // assure the MBSRC is available
    assertTrue("The AdvancedBounder must be available", countAvailableServices(trackerMBSRC) > 0);
    bsrc = trackerMBSRC.getService();
    // reset the bound services events
    ((DSEventsProvider) bsrc).resetEvents();
    //modify the service which is bound
    modified = (Hashtable) initialProps.clone();
    modified.put("a", "a");
    dynRegistration1.setProperties(modified);
    assertEquals(
        "There must be no unbind/bind activity when modifying reference which still satsfies the component reference",
        0, ((DSEventsProvider) bsrc).getEvents().length);

    uninstallBundle(tb4);

    unregisterService(dynRegistration1);
    unregisterService(dynRegistration2);
    unregisterService(staticRegistration1);
    unregisterService(staticRegistration2);
  }

  @Test
  public void testSecurity() throws Exception {
    // the method below sets the permissions of a bundle before installing it
    // to simplify the test case
    if (System.getSecurityManager() == null) {
      // the security is off
      return;
    }
    BundleContext bc = getContext();
    ServiceReference padmRef = bc.getServiceReference(PermissionAdmin.class.getName());
    assertNotNull("Permission Admin service not available.", padmRef);

    PermissionAdmin padm = (PermissionAdmin) bc.getService(padmRef);
    assertNotNull("Permission Admin service should be available", padm);

    assertEquals("TestBundle1 must be running.", Bundle.ACTIVE, tb1.getState());

    PermissionInfo registerServiceInfo = new PermissionInfo(ServicePermission.class.getName(),
        "org.eclipse.equinox.ds.tests.tb5.impl.SecurityTester", ServicePermission.REGISTER);
    PermissionInfo getServiceInfo = new PermissionInfo(ServicePermission.class.getName(),
        "org.eclipse.equinox.ds.tests.tb1.impl.AnotherComponent", ServicePermission.GET);

    PermissionInfo importPackage = new PermissionInfo(PackagePermission.class.getName(),
        "org.eclipse.equinox.ds.tests.tb1.impl", PackagePermission.IMPORT);

    // install the bundle to get the location
    Bundle tb5 = installBundle("tb5");
    tb5.start();
    waitBundleStart();
    final String bundleLocation = tb5.getLocation();

    uninstallBundle(tb5);

    // do the test

    // set all permission needed for correct operation
    padm.setPermissions(bundleLocation, new PermissionInfo[] { registerServiceInfo, getServiceInfo, importPackage });

    // install
    tb5 = installBundle("tb5");
    tb5.start();
    waitBundleStart();
    assertEquals("The bundle location should be the same as the first one registered", bundleLocation, tb5
        .getLocation());

    // check that the component is available
    assertTrue("The SecurityTester should be present because all needed permissions are set",
        countAvailableServices(trackerSecurity) > 0);

    // uninstall
    uninstallBundle(tb5);

    // remove the register permission - the service shouldn't be available
    padm.setPermissions(bundleLocation, new PermissionInfo[] { importPackage, getServiceInfo });

    // install
    tb5 = installBundle("tb5");
    tb5.start();
    waitBundleStart();
    assertEquals("The bundle location should be the same as the first one registered", bundleLocation, tb5
        .getLocation());

    // check that the service is unavailable
    assertEquals("The SecurityTester shouldn't be present due to missing ServicePermission.REGISTER", 0,
        countAvailableServices(trackerSecurity));

    // uninstall
    uninstallBundle(tb5);

    // remove the get permission and bring back the register permission
    padm.setPermissions(bundleLocation, new PermissionInfo[] { importPackage, registerServiceInfo });

    // install
    tb5 = installBundle("tb5");
    tb5.start();
    waitBundleStart();
    assertEquals("The bundle location should be the same as the first one registered", bundleLocation, tb5
        .getLocation());

    // check that the component is unavailable
    assertEquals("The SecurityTester shouldn't be present due to missing ServicePermission.GET", 0,
        countAvailableServices(trackerSecurity));

    // uninstall
    uninstallBundle(tb5);

    // reset the permissions
    padm.setPermissions(bundleLocation, null);
    // release the PermissionAdmin service
    bc.ungetService(padmRef);
    padm = null;
  }

  @Test
  public void testImmediateComponents() throws Exception {
    Bundle tb4 = installBundle("tb4");
    tb4.start();
    waitBundleStart();

    // check that the ServiceProvider is registered
    assertNotNull("The ServiceProvider should be registered as service", getServiceReference(
        "org.eclipse.equinox.ds.tests.tb4.ServiceProvider"));
    // check that the ServiceProvider is activated
    assertTrue("The ServiceProvider should be activated", TestHelper.isActivatedServiceProvider());

    // check that the Stand Alone Component is activated
    assertTrue("The AnotherComponent should be activated", TestHelper.isActivatedStandAlone());

    uninstallBundle(tb4);
  }

  @Test
  public void testRowReference() throws Exception {
    final String TAIL_CLASS = "org.eclipse.equinox.ds.tests.tb4.Component3";
    final String MIDDLE_CLASS = "org.eclipse.equinox.ds.tests.tb4.Component2";
    final String HEAD_CLASS = "org.eclipse.equinox.ds.tests.tb4.Component1";

    Bundle tb4 = installBundle("tb4");
    tb4.start();
    waitBundleStart();

    // check that all the components are present
    assertTrue("The Component3 should be available", checkAvailability(TAIL_CLASS));
    assertTrue("The Component2 should be available", checkAvailability(MIDDLE_CLASS));
    assertTrue("The Component1 should be available", checkAvailability(HEAD_CLASS));

    BundleContext bc = getContext();
    // get ComponentContext
    ServiceReference cceRef = bc.getServiceReference("org.eclipse.equinox.ds.tests.tbc.ComponentContextProvider");
    assertNotNull("The GiveMeContext should be available", cceRef);
    assertEquals("The GiveMeContext should be the implementation present in tb4.jar",
        "org.eclipse.equinox.ds.tests.tb4.GiveMeContext", cceRef.getProperty("component.name"));

    ComponentContextProvider cce = (ComponentContextProvider) bc.getService(cceRef);
    assertNotNull("The service object should be retrieved correctly", cce);
    ComponentContext ctxt = cce.getComponentContext();
    assertNotNull("The ComponentContext object should not be null", ctxt);

    // disable the tail component
    ctxt.disableComponent(TAIL_CLASS);
    Thread.sleep(timeout);

    // check that no component is available
    assertTrue("The Component3 shouldn't be available", !checkAvailability(TAIL_CLASS));
    assertTrue("The Component2 shouldn't be available", !checkAvailability(MIDDLE_CLASS));
    assertTrue("The Component1 shouldn't be available", !checkAvailability(HEAD_CLASS));

    // enable the tail component
    ctxt.enableComponent(TAIL_CLASS);
    Thread.sleep(timeout);

    // check that the components are back online
    assertTrue("The Component3 should be available", checkAvailability(TAIL_CLASS));
    assertTrue("The Component2 should be available", checkAvailability(MIDDLE_CLASS));
    assertTrue("The Component1 should be available", checkAvailability(HEAD_CLASS));

    // release the GiveMeContext
    bc.ungetService(cceRef);

    // remove the bundle
    uninstallBundle(tb4);
  }

  private boolean checkAvailability(String service) {
    ServiceReference ref = getServiceReference(service);
    return ref != null;
  }

  private ServiceReference getServiceReference(String service) {
    BundleContext bc = getContext();
    try {
		ServiceReference[] refs = bc.getAllServiceReferences(service, null);
		return refs == null ? null : refs[0];
	} catch (InvalidSyntaxException e) {
		return null;
	}
  }

  private boolean checkFactoryAvailability(String factory) throws InvalidSyntaxException {
    BundleContext bc = getContext();
    ServiceReference[] refs = bc.getServiceReferences(ComponentFactory.class.getName(), "("
        + ComponentConstants.COMPONENT_FACTORY + "=" + factory + ")");
    return refs != null && refs.length > 0;
  }

  @Test
  public void testBlockingComponents() throws Exception {
    final Bundle tb2 = installBundle("tb2");
    final Bundle tb3 = installBundle("tb3");
    final Bundle tb4 = installBundle("tb4");

    new Thread() {
      @Override
      public void run() {
        try {
          tb2.start(); // start the blocking service
        } catch (BundleException e) {
        }
      }
    }.start();
    sleep0(scr_restart_timeout + timeout * 2);

    new Thread() {
      @Override
      public void run() {
        try {
          tb4.start(); // start the other
        } catch (BundleException e) {
        }
      }
    }.start();

    sleep0(timeout * 2); // sleep until the services are activated

    // check that the first service is missing, and the second is available
    assertEquals("The blocking service should not be available", 0, countAvailableServices(trackerBAS));
    assertTrue("The service in the bundle should be available",
        checkAvailability("org.eclipse.equinox.ds.tests.tbc.ComponentContextProvider"));

    tb2.stop();
    tb4.stop();

    // check that AnotherComponent is available
    assertTrue("The AnotherComponent should be available",
        checkAvailability("org.eclipse.equinox.ds.tests.tb1.impl.AnotherComponent"));

    // start the other blocking bundle
    new Thread() {
      @Override
      public void run() {
        try {
          tb3.start();
        } catch (BundleException e) {
        }
      }
    }.start();

    sleep0(scr_restart_timeout + timeout * 2);

    // start the non-blocking bundle
    new Thread() {
      @Override
      public void run() {
        try {
          tb4.start(); // start the other
        } catch (BundleException e) {
        }
      }
    }.start();

    sleep0(timeout * 2); // sleep until the services are activated

    assertEquals("The blocking service should not be available", 0, countAvailableServices(trackerBBS));
    assertTrue("The service in the bundle should be available",
        checkAvailability("org.eclipse.equinox.ds.tests.tbc.ComponentContextProvider"));

    uninstallBundle(tb2);
    uninstallBundle(tb3);
    uninstallBundle(tb4);
  }

  @Test
  public void testStaticPolicyBinding() throws Exception {
    Bundle tb6 = installBundle("tb6");
    tb6.start();
    waitBundleStart();

    // check initial conditions
    assertTrue("The StaticComp should be available", checkAvailability(STATIC_CLASS));
    assertTrue("The ReferencedComp shouldn't be available (disabled)", !checkAvailability(REFERENCED_CLASS));

    // reset the events list
    Object initialStatic = trackerStatic.getService();
    assertNotNull(STATIC_CLASS + " component should be non-null", initialStatic);
    ComponentContext initialCtxt = ((ComponentContextProvider) initialStatic).getComponentContext();
    ((DSEventsProvider) initialStatic).resetEvents();
    assertEquals("There shouldn't be bound service to StaticComp", 0, ((BoundTester) initialStatic)
        .getBoundObjectsCount());

    // enable the ReferencedComp
    initialCtxt.enableComponent(REFERENCED_CLASS);
    Thread.sleep(timeout);

    // check the availability after enablement
    assertTrue("The StaticComp should be available", checkAvailability(STATIC_CLASS));
    assertTrue("The ReferencedComp should be available", checkAvailability(REFERENCED_CLASS));

    Object enabledStatic = trackerStatic.getService();
    assertNotNull(STATIC_CLASS + " component should be non-null", enabledStatic);
    ComponentContext enabledCtxt = ((ComponentContextProvider) enabledStatic).getComponentContext();
    assertSame("The StaticComp must not have been restarted", initialCtxt, enabledCtxt);
    assertEquals("There should be no bound service to StaticComp", 0, ((BoundTester) enabledStatic)
        .getBoundObjectsCount());

    // disable the referenced component
    enabledCtxt.disableComponent(REFERENCED_CLASS);
    Thread.sleep(timeout);

    // check the availability
    assertTrue("The StaticComp should be available", checkAvailability(STATIC_CLASS));
    assertTrue("The ReferencedComp shouldn't be available", !checkAvailability(REFERENCED_CLASS));

    // check that the SCR did not restarted the component with static reference
    Object staticRefComp = trackerStatic.getService();
    assertNotNull(STATIC_CLASS + " component should be non-null", staticRefComp);
    ComponentContext ctxt = ((ComponentContextProvider) staticRefComp).getComponentContext();
    assertSame("The StaticComp must not have been restarted", enabledCtxt, ctxt);
    assertEquals("There should be none bound service to StaticComp", 0, ((BoundTester) staticRefComp)
        .getBoundObjectsCount());

    uninstallBundle(tb6);
  }

  @Test
  public void testCircularityHandling() throws Exception {
    Bundle tb7 = installBundle("tb7");
    tb7.start();
    waitBundleStart();

    final String UNBREAKABLE = "org.eclipse.equinox.ds.tests.tb7.UnbreakableCircuit";
    final String DYN_BREAKABLE = "org.eclipse.equinox.ds.tests.tb7.DynamicCircuit";
    final String STATIC_BREAKABLE = "org.eclipse.equinox.ds.tests.tb7.StaticCircuit";

    // check that the unbreakable circuit isn't available
    assertTrue("The first service from the unbreakable circularity shouldn't be available",
        !checkAvailability(UNBREAKABLE + "1"));
    assertTrue("The second service from the unbreakable circularity shouldn't be available",
        !checkAvailability(UNBREAKABLE + "2"));

    // check that the breakable circuit with dynamic optional reference is
    // available
    assertTrue("The first service from the breakable circularity with dynamic optional reference should be available",
        checkAvailability(DYN_BREAKABLE + "1"));
    assertTrue("The second service from the breakable circularity with dynamic optional reference should be available",
        checkAvailability(DYN_BREAKABLE + "2"));

    
    // check that the breakable circuit with dynamic optional reference has
    // bound correctly
    ServiceReference dynBreak1Ref = getServiceReference(DYN_BREAKABLE + "1");
    Object dynBreak1 = getContext().getService(dynBreak1Ref);
    ServiceReference dynBreak2Ref = getServiceReference(DYN_BREAKABLE + "2");
    Object dynBreak2 = getContext().getService(dynBreak2Ref);
    sleep0(timeout * 2); // sleep to allow the delayed bind for the broken reference cycle to be done
    try {
    	assertNotNull("The DynamicCircuit1 component should be available", dynBreak1);
    	assertEquals("The DynamicCircuit2 component should have one bound object", 1, ((BoundTester) dynBreak2)
    			.getBoundObjectsCount());
    	assertNotNull("The DynamicCircuit2 component should have one non-null bound object", ((BoundTester) dynBreak2)
    			.getBoundService(0));
    } finally {
      getContext().ungetService(dynBreak2Ref);
    }
    // check that the breakable circuit with static optional reference isn't
    // available
    assertTrue("The first service from the breakable circularity with static optional reference should be available",
        checkAvailability(STATIC_BREAKABLE + "1"));
    assertTrue("The second service from the breakable circularity with static optional reference should be available",
        checkAvailability(STATIC_BREAKABLE + "2"));

    // check that the optional reference isn't satisfied
    ServiceReference staticBreak2Ref = getServiceReference(STATIC_BREAKABLE + "2");
    Object staticBreak2 = getContext().getService(staticBreak2Ref);
    try {
		BoundTester tester = (BoundTester) staticBreak2;
		int count = tester.getBoundObjectsCount();
		String bound = "";
		for (int i = 0; i < count; i++) {
			bound += "Ref " + i + " = " + tester.getBoundService(i);
		}
		assertEquals("The StaticCircuit2 component shouldn't have bound objects: " + bound, 0, count);
    } finally {
      getContext().ungetService(staticBreak2Ref);
    }

    // check that the worker hasn't been blocked
    Bundle tb5 = installBundle("tb5");
    tb5.start();
    waitBundleStart();

    // check that the AnotherComponent service is available
    assertTrue("The AnotherComponent should be available",
        checkAvailability("org.eclipse.equinox.ds.tests.tb1.impl.AnotherComponent"));
    assertTrue("The service in TB5 should be available which means that the working thread of the SCR isn't blocked",
        checkAvailability("org.eclipse.equinox.ds.tests.tb5.impl.SecurityTester"));

    uninstallBundle(tb5);
    uninstallBundle(tb7);
  }

  // tests namespace handling in xml component description parser
  // TODO Felix handles improper XML differently, not sure this is spec'ed behavior.
  // disable @Test
  public void testNamespaceHandling() throws Exception {
    Bundle tb8 = installBundle("tb8");
    tb8.start();
    waitBundleStart();

    // check the root component handling
    assertTrue("The root1 component should be available", isNSComponentAvailable(101));
    assertTrue("The root2 component should be available", isNSComponentAvailable(102));
    assertTrue("The root3 component should not be available", !isNSComponentAvailable(103));
    assertTrue("The root4 component should be available", isNSComponentAvailable(104));
    assertTrue("The root5 component should not be available", !isNSComponentAvailable(105));
    // check the non root component handling
    assertTrue("The nonroot1 component should not be available", !isNSComponentAvailable(111));
    assertTrue("The nonroot2 component should be available", isNSComponentAvailable(112));
    assertTrue("The nonroot3 component should not be available", !isNSComponentAvailable(113));
    assertTrue("The nonroot4 component should be available", isNSComponentAvailable(114));
    assertTrue("The nonroot5 component should not be available", !isNSComponentAvailable(115));
    assertTrue("The nonroot6 component should be available", isNSComponentAvailable(116));
    assertTrue("The nonroot7 component should not be available", !isNSComponentAvailable(117));
    assertTrue("The nonroot8 component should not be available", !isNSComponentAvailable(118));
    assertTrue("The nonroot9 component should not be available", !isNSComponentAvailable(119));
    assertTrue("The nonroot10 component should not be available", !isNSComponentAvailable(120));
    assertTrue("The nonroot11 component should be available", isNSComponentAvailable(121));
    assertTrue("The nonroot12 component should not be available", !isNSComponentAvailable(122));
    assertTrue("The nonroot13 component should not be available", !isNSComponentAvailable(123));
    assertTrue("The nonroot14 component should be available", isNSComponentAvailable(124));
    assertTrue("The nonroot15 component should be available", isNSComponentAvailable(125));
    assertTrue("The nonroot16 component should be available", isNSComponentAvailable(126));
    assertTrue("The nonroot17 component should be available", isNSComponentAvailable(127));

    uninstallBundle(tb8);
  }

  private boolean isNSComponentAvailable(int nsid) {
    Object[] services = trackerNS.getServices();
    if (services == null) {
      return false;
    }
    for (Object service : services) {
        if (service instanceof NamespaceProvider) {
           NamespaceProvider s = (NamespaceProvider) service;
            if (s.getComponentNSID() == nsid) {
                return true;
            }
        }
    }

    return false;
  }

  // tests wildcard handling in mf (e.g. Service-Component: OSGI-INF/*.xml)
  @Test
  public void testWildcardHandling() throws Exception {
    Bundle tb9 = installBundle("tb9");
    tb9.start();
    waitBundleStart();

    final String WILD = "org.eclipse.equinox.ds.tests.tb9.Wildcard";

    // check that the both components are available
    assertTrue("The first Wildcard component should be available", checkAvailability(WILD + "1"));
    assertTrue("The second Wildcard component should be available", checkAvailability(WILD + "2"));

    uninstallBundle(tb9);
  }

  @Test
  public void testDynamicComponentFactoryServiceBinding() throws Exception {
    Bundle tb10 = installBundle("tb10");
    assertNotNull("Failed to install test bundle tb10.jar", tb10);
    tb10.start();
    waitBundleStart();

    // assert that the required components are available
    assertTrue("The referenced simple component should be available", checkAvailability(SC_CLASS));
    assertTrue("The referenced factory component should be available", checkFactoryAvailability("CountHelperFactory"));
    assertTrue("The dependent dynamic factory component should be available", checkFactoryAvailability("CountFactory"));

    // retrieve the helper factory
    ComponentFactory helperFactory = (ComponentFactory) trackerBoundServiceCounterHelperFactory.getService();
    assertNotNull("The helper factory must be retrievable", helperFactory);

    // retrieve the observed factory
    ComponentFactory observedFactory = (ComponentFactory) trackerBoundServiceCounterFactory.getService();
    assertNotNull("The observed factory must be retrievable", observedFactory);

    // instantiate observed components
    ComponentInstance observedInstance1 = observedFactory.newInstance(null);
    assertNotNull("Cannot instantiate component from observed factory [1]", observedInstance1);
    ComponentInstance observedInstance2 = observedFactory.newInstance(null);
    assertNotNull("Cannot instantiate component from observed factory [2]", observedInstance2);

    BoundCountProvider observedComponent1 = (BoundCountProvider) observedInstance1.getInstance();
    BoundCountProvider observedComponent2 = (BoundCountProvider) observedInstance2.getInstance();
    assertNotNull("The observed component must be non-null [1]", observedComponent1);
    assertNotNull("The observed component must be non-null [2]", observedComponent2);

    // check the bound services before instantiating helper factory components -
    // both components must have 1 service bound
    assertEquals("The bound service count must be 1 - only the simple component is available [1]", 1,
        observedComponent1.getBoundServiceCount(null));
    assertEquals("The bound service count must be 1 - only the simple component is available [2]", 1,
        observedComponent2.getBoundServiceCount(null));

    // instantiate three helper components and check the service count again
    for (int i = 0; i < 3; i++) {
      helperFactory.newInstance(null); // don't keep track of the created
      // instances, they will be disposed when
      // the bundle is stopped and uninstalled
    }

    // check the bound services count again - both components must have 4
    // services bound -> 1 simple component and 3 helper factory
    assertEquals("The bound service count must be 4 - 1 simple component, 3 helper factory components [1]", 4,
        observedComponent1.getBoundServiceCount(null));
    assertEquals("The bound service count must be 4 - 1 simple component, 3 helper factory components [2]", 4,
        observedComponent2.getBoundServiceCount(null));

    uninstallBundle(tb10);
  }

  @Test
  public void testStaticComponentFactoryServiceBinding() throws Exception {
    Bundle tb10 = installBundle("tb10");
    assertNotNull("Failed to install test bundle tb10.jar", tb10);
    tb10.start();
    waitBundleStart();

    // assert that the required components are available
    assertTrue("The referenced simple component should be available", checkAvailability(SC_CLASS));
    assertTrue("The referenced factory component should be available", checkFactoryAvailability("CountHelperFactory"));
    assertTrue("The dependent static factory component should be available",
        checkFactoryAvailability("StaticServiceCountFactory"));

    // retrieve the helper and observer factories
    ComponentFactory helperFactory = (ComponentFactory) trackerBoundServiceCounterHelperFactory.getService();
    ComponentFactory observedFactory = (ComponentFactory) trackerStaticServiceCounterFactory.getService();
    assertNotNull("The helper factory must be retrievable", helperFactory);
    assertNotNull("The observed factory must be retrievable", observedFactory);

    // instantiate observed components
    ComponentInstance observedInstance1 = observedFactory.newInstance(null);
    ComponentInstance observedInstance2 = observedFactory.newInstance(null);
    assertNotNull("Cannot instantiate component from observed factory [1]", observedInstance1);
    assertNotNull("Cannot instantiate component from observed factory [2]", observedInstance2);

    BoundCountProvider observedComponent1 = (BoundCountProvider) observedInstance1.getInstance();
    BoundCountProvider observedComponent2 = (BoundCountProvider) observedInstance2.getInstance();
    assertNotNull("The observed component must be non-null [1]", observedComponent1);
    assertNotNull("The observed component must be non-null [2]", observedComponent2);

    // check the bound services before instantiating helper factory components -
    // both components must have 1 service bound
    assertEquals("The bound service count must be 1 - only the simple component is available [1]", 1,
        observedComponent1.getBoundServiceCount(null));
    assertEquals("The bound service count must be 1 - only the simple component is available [2]", 1,
        observedComponent2.getBoundServiceCount(null));

    // instantiate three helper components and check the service count again
    for (int i = 0; i < 3; i++) {
      helperFactory.newInstance(null); // don't keep track of the created
      // instances, they will be disposed when
      // the bundle is stopped and uninstalled
    }

    // check whether the factory is the same, it shouldn't be disposed
    assertSame(observedFactory, trackerStaticServiceCounterFactory.getService());
    observedFactory = (ComponentFactory) trackerStaticServiceCounterFactory.getService();

    // check that the components are reinstantiated
    observedInstance1 = observedFactory.newInstance(null);
    observedInstance2 = observedFactory.newInstance(null);
    assertNotNull("Cannot instantiate new observed component instance [1]", observedInstance1);
    assertNotNull("Cannot instantiate new observed component instance [2]", observedInstance2);

    // get the new instances
    observedComponent1 = (BoundCountProvider) observedInstance1.getInstance();
    observedComponent2 = (BoundCountProvider) observedInstance2.getInstance();
    assertNotNull("The observed component instance must be non-null [1]", observedComponent1);
    assertNotNull("The observed component instance must be non-null [2]", observedComponent2);

    // check the bound services count again - both components must have 4
    // services bound -> 1 simple component and 3 helper factory
    assertEquals("The bound service count must be 4 - 1 simple component, 3 helper factory components [1]", 4,
        observedComponent1.getBoundServiceCount(null));
    assertEquals("The bound service count must be 4 - 1 simple component, 3 helper factory components [2]", 4,
        observedComponent2.getBoundServiceCount(null));

    uninstallBundle(tb10);
  }

  @Test
  public void testConfigurationPolicy() throws Exception {
    ConfigurationAdmin cm = (ConfigurationAdmin) trackerCM.getService();
    if (cm == null)
    	return;

    Bundle tb11 = installBundle("tb11");
    tb11.start();
    waitBundleStart();

    Hashtable props = new Hashtable(10);
    props.put("config.base.data", Integer.valueOf(1));

    // component notsetNS100 - property not set by Configuration Admin; XML NS
    // 1.0.0
    assertEquals("Configuration data should not be available for notsetNS100", 0, getBaseConfigData(COMP_NOTSET_100));
    // component notsetNS100 - property set by Configuration Admin; XML NS 1.0.0
    Configuration config = cm.getConfiguration(COMP_NOTSET_100, null);
    config.update(props);
    Thread.sleep(timeout * 2);
    assertEquals("Configuration data should be available for notsetNS100 and equal to 1", 1,
        getBaseConfigData(COMP_NOTSET_100));

    // component notsetNS110 - property not set by Configuration Admin; XML NS
    // 1.1.0
    assertEquals("Configuration data should not be available for notsetNS110", 0, getBaseConfigData(COMP_NOTSET_110));
    // component notsetNS110 - property set by Configuration Admin; XML NS 1.1.0
    config = cm.getConfiguration(COMP_NOTSET_110, null);
    config.update(props);
    Thread.sleep(timeout * 2);
    assertEquals("Configuration data should be available for notsetNS110 and equal to 1", 1,
        getBaseConfigData(COMP_NOTSET_110));

    // component optionalNS100 - property not set by Configuration Admin; XML NS
    // 1.0.0 - INVALID COMPONENT
    assertEquals("Component optionalNS100 should not be activated", -1, getBaseConfigData(COMP_OPTIONAL_100));
    // component optionalNS100 - property set by Configuration Admin; XML NS
    // 1.0.0 - INVALID COMPONENT
    config = cm.getConfiguration(COMP_OPTIONAL_100, null);
    config.update(props);
    Thread.sleep(timeout * 2);
    assertEquals("Component optionalNS100 should not be activated", -1, getBaseConfigData(COMP_OPTIONAL_100));

    // component optionalNS110 - property not set by Configuration Admin; XML NS
    // 1.1.0
    assertEquals("Configuration data should not be available for optionalNS110", 0,
        getBaseConfigData(COMP_OPTIONAL_110));
    // component optionalNS110 - property set by Configuration Admin; XML NS
    // 1.1.0
    config = cm.getConfiguration(COMP_OPTIONAL_110, null);
    config.update(props);
    Thread.sleep(timeout * 2);
    assertEquals("Configuration data should be available for optionalNS110 and equal to 1", 1,
        getBaseConfigData(COMP_OPTIONAL_110));

    // component requireNS100 - property not set by Configuration Admin; XML NS
    // 1.0.0 - INVALID COMPONENT
    assertEquals("Component requireNS100 should not be activated", -1, getBaseConfigData(COMP_REQUIRE_100));
    // component requireNS100 - property set by Configuration Admin; XML NS
    // 1.0.0 - INVALID COMPONENT
    config = cm.getConfiguration(COMP_REQUIRE_100, null);
    config.update(props);
    Thread.sleep(timeout * 2);
    assertEquals("Component requireNS100 should not be activated", -1, getBaseConfigData(COMP_REQUIRE_100));

    // component requireNS110 - property not set by Configuration Admin; XML NS
    // 1.1.0
    assertEquals("Configuration data should not be available for requireNS110, and it should not be satisfied", -1,
        getBaseConfigData(COMP_REQUIRE_110));
    // component requireNS110 - property set by Configuration Admin; XML NS
    // 1.1.0
    config = cm.getConfiguration(COMP_REQUIRE_110, null);
    config.update(props);
    Thread.sleep(timeout * 2);
    assertEquals("Configuration data should be available for requireNS110 and equal to 1", 1,
        getBaseConfigData(COMP_REQUIRE_110));

    // component ignoreNS100 - property not set by Configuration Admin; XML NS
    // 1.0.0 - INVALID COMPONENT
    assertEquals("Component ignoreNS100 should not be activated", -1, getBaseConfigData(COMP_IGNORE_100));
    // component ignoreNS100 - property set by Configuration Admin; XML NS 1.0.0
    // - INVALID COMPONENT
    config = cm.getConfiguration(COMP_IGNORE_100, null);
    config.update(props);
    Thread.sleep(timeout * 2);
    assertEquals("Component ignoreNS100 should not be activated", -1, getBaseConfigData(COMP_IGNORE_100));

    // component ignoreNS110 - property not set by Configuration Admin; XML NS
    // 1.1.0
    assertEquals("Configuration data should not be available for ignoreNS110, but it should be satisfied", 0,
        getBaseConfigData(COMP_IGNORE_110));
    // component ignoreNS110 - property set by Configuration Admin; XML NS 1.1.0
    config = cm.getConfiguration(COMP_IGNORE_110, null);
    config.update(props);
    Thread.sleep(timeout * 2);
    assertEquals("Configuration data should not be available for ignoreNS110, but it should be satisfied", 0,
        getBaseConfigData(COMP_IGNORE_110));

    uninstallBundle(tb11);
  }

  // tests configuration-policy for factory configuration objects
  @Test
  public void testConfigurationPolicyFactoryConf() throws Exception {
    ConfigurationAdmin cm = (ConfigurationAdmin) trackerCM.getService();
    if (cm == null)
    	return;

    Bundle tb11 = installBundle("tb11");
    tb11.start();
    waitBundleStart();

    Hashtable props = new Hashtable(10);
    props.put("config.base.data", Integer.valueOf(1));

    // component notsetNS100 - property not set by Configuration Admin; XML NS
    // 1.0.0
    assertEquals("Configuration data should not be available for notsetNS100", 0, getBaseConfigData(COMP_NOTSET_100));
    // component notsetNS100 - property set by Configuration Admin; XML NS 1.0.0
    Configuration config = cm.createFactoryConfiguration(COMP_NOTSET_100, null);
    config.update(props);
    Thread.sleep(timeout * 2);
    assertEquals("Configuration data should be available for notsetNS100 and equal to 1", 1,
        getBaseConfigData(COMP_NOTSET_100));

    // component notsetNS110 - property not set by Configuration Admin; XML NS
    // 1.1.0
    assertEquals("Configuration data should not be available for notsetNS110", 0, getBaseConfigData(COMP_NOTSET_110));
    // component notsetNS110 - property set by Configuration Admin; XML NS 1.1.0
    config = cm.createFactoryConfiguration(COMP_NOTSET_110, null);
    config.update(props);
    Thread.sleep(timeout * 2);
    assertEquals("Configuration data should be available for notsetNS110 and equal to 1", 1,
        getBaseConfigData(COMP_NOTSET_110));

    // component optionalNS100 - property not set by Configuration Admin; XML NS
    // 1.0.0 - INVALID COMPONENT
    assertEquals("Component optionalNS100 should not be activated", -1, getBaseConfigData(COMP_OPTIONAL_100));
    // component optionalNS100 - property set by Configuration Admin; XML NS
    // 1.0.0 - INVALID COMPONENT
    config = cm.createFactoryConfiguration(COMP_OPTIONAL_100, null);
    config.update(props);
    Thread.sleep(timeout * 2);
    assertEquals("Component optionalNS100 should not be activated", -1, getBaseConfigData(COMP_OPTIONAL_100));

    // component optionalNS110 - property not set by Configuration Admin; XML NS
    // 1.1.0
    assertEquals("Configuration data should not be available for optionalNS110", 0,
        getBaseConfigData(COMP_OPTIONAL_110));
    // component optionalNS110 - property set by Configuration Admin; XML NS
    // 1.1.0
    config = cm.createFactoryConfiguration(COMP_OPTIONAL_110, null);
    config.update(props);
    Thread.sleep(timeout * 2);
    assertEquals("Configuration data should be available for optionalNS110 and equal to 1", 1,
        getBaseConfigData(COMP_OPTIONAL_110));

    // component requireNS100 - property not set by Configuration Admin; XML NS
    // 1.0.0 - INVALID COMPONENT
    assertEquals("Component requireNS100 should not be activated", -1, getBaseConfigData(COMP_REQUIRE_100));
    // component requireNS100 - property set by Configuration Admin; XML NS
    // 1.0.0 - INVALID COMPONENT
    config = cm.createFactoryConfiguration(COMP_REQUIRE_100, null);
    config.update(props);
    Thread.sleep(timeout * 2);
    assertEquals("Component requireNS100 should not be activated", -1, getBaseConfigData(COMP_REQUIRE_100));

    // component requireNS110 - property not set by Configuration Admin; XML NS
    // 1.1.0
    assertEquals("Configuration data should not be available for requireNS110, and it should not be satisfied", -1,
        getBaseConfigData(COMP_REQUIRE_110));
    // component requireNS110 - property set by Configuration Admin; XML NS
    // 1.1.0
    config = cm.createFactoryConfiguration(COMP_REQUIRE_110, null);
    config.update(props);
    Thread.sleep(timeout * 2);
    assertEquals("Configuration data should be available for requireNS110 and equal to 1", 1,
        getBaseConfigData(COMP_REQUIRE_110));

    // component ignoreNS100 - property not set by Configuration Admin; XML NS
    // 1.0.0 - INVALID COMPONENT
    assertEquals("Component ignoreNS100 should not be activated", -1, getBaseConfigData(COMP_IGNORE_100));
    // component ignoreNS100 - property set by Configuration Admin; XML NS 1.0.0
    // - INVALID COMPONENT
    config = cm.createFactoryConfiguration(COMP_IGNORE_100, null);
    config.update(props);
    Thread.sleep(timeout * 2);
    assertEquals("Component ignoreNS100 should not be activated", -1, getBaseConfigData(COMP_IGNORE_100));

    // component ignoreNS110 - property not set by Configuration Admin; XML NS
    // 1.1.0
    assertEquals("Configuration data should not be available for ignoreNS110, but it should be satisfied", 0,
        getBaseConfigData(COMP_IGNORE_110));
    // component ignoreNS110 - property set by Configuration Admin; XML NS 1.1.0
    config = cm.createFactoryConfiguration(COMP_IGNORE_110, null);
    config.update(props);
    Thread.sleep(timeout * 2);
    assertEquals("Configuration data should not be available for ignoreNS110, but it should be satisfied", 0,
        getBaseConfigData(COMP_IGNORE_110));

    uninstallBundle(tb11);
  }

  @Test
  public void testActivateDeactivate() throws Exception {
    Bundle tb12 = installBundle("tb12");
    tb12.start();
    waitBundleStart();

    final String NOTSET_NS100 = "org.eclipse.equinox.ds.tests.tb12.notsetNS100";
    final String NOTSET_NS110 = "org.eclipse.equinox.ds.tests.tb12.notsetNS110";
    final String NOARGS_NS100 = "org.eclipse.equinox.ds.tests.tb12.NoArgs100";
    final String NOARGS_NS110 = "org.eclipse.equinox.ds.tests.tb12.NoArgs110";
    final String CC_NS100 = "org.eclipse.equinox.ds.tests.tb12.CcNS100";
    final String CC_NS110 = "org.eclipse.equinox.ds.tests.tb12.CcNS110";
    final String BC_NS100 = "org.eclipse.equinox.ds.tests.tb12.Bc100";
    final String BC_NS110 = "org.eclipse.equinox.ds.tests.tb12.Bc110";
    final String MAP_NS100 = "org.eclipse.equinox.ds.tests.tb12.MapNS100";
    final String MAP_NS110 = "org.eclipse.equinox.ds.tests.tb12.MapNS110";
    final String CC_BC_MAP_NS100 = "org.eclipse.equinox.ds.tests.tb12.CcBcMapNS100";
    final String CC_BC_MAP_NS110 = "org.eclipse.equinox.ds.tests.tb12.CcBcMapNS110";
    final String INT_NS110 = "org.eclipse.equinox.ds.tests.tb12.IntNS110";
    final String CC_BC_MAP_INT_NS110 = "org.eclipse.equinox.ds.tests.tb12.CcBcMapIntNS110";

    PropertiesProvider bs = getBaseService(NOTSET_NS100);
    ComponentContext cc = (bs instanceof ComponentContextProvider) ? ((ComponentContextProvider) bs)
        .getComponentContext() : null;
    assertNotNull("Component context should be available", cc);

    assertEquals("Activate method of " + NOTSET_NS100 + " should be called", 1 << 0, (1 << 0) & getBaseConfigData(bs));
    cc.disableComponent(NOTSET_NS100);
    Thread.sleep(timeout);
    assertEquals("Deactivate method of " + NOTSET_NS100 + " should be called", 1 << 1, (1 << 1) & getBaseConfigData(bs));

    bs = getBaseService(NOTSET_NS110);
    assertNotNull(bs);
    assertEquals("Activate method of " + NOTSET_NS110 + " should be called", 1 << 0, (1 << 0) & getBaseConfigData(bs));
    cc.disableComponent(NOTSET_NS110);
    Thread.sleep(timeout);
    assertEquals("Deactivate method of " + NOTSET_NS110 + " should be called", 1 << 1, (1 << 1) & getBaseConfigData(bs));

    bs = getBaseService(NOARGS_NS100); // INVALID COMPONENT FOR XML NS 1.0.0
    assertEquals("Component " + NOARGS_NS100 + " should not be activated", -1, getBaseConfigData(bs));

    bs = getBaseService(NOARGS_NS110);
    assertNotNull(bs);
    assertEquals("Activate method of " + NOARGS_NS110 + " should be called", 1 << 2, (1 << 2) & getBaseConfigData(bs));
    cc.disableComponent(NOARGS_NS110);
    Thread.sleep(timeout);
    assertEquals("Deactivate method of " + NOARGS_NS110 + " should be called", 1 << 3, (1 << 3) & getBaseConfigData(bs));

    bs = getBaseService(CC_NS100); // INVALID COMPONENT FOR XML NS 1.0.0
    assertEquals("Component " + CC_NS100 + " should not be activated", -1, getBaseConfigData(bs));

    bs = getBaseService(CC_NS110);
    assertNotNull(bs);
    assertEquals("Activate method of " + CC_NS110 + " should be called", 1 << 4, (1 << 4) & getBaseConfigData(bs));
    cc.disableComponent(CC_NS110);
    Thread.sleep(timeout);
    assertEquals("Deactivate method of " + CC_NS110 + " should be called", 1 << 5, (1 << 5) & getBaseConfigData(bs));

    bs = getBaseService(BC_NS100); // INVALID COMPONENT FOR XML NS 1.0.0
    assertEquals("Component " + BC_NS100 + " should not be activated", -1, getBaseConfigData(bs));

    bs = getBaseService(BC_NS110);
    assertNotNull(bs);
    assertEquals("Activate method of " + BC_NS110 + " should be called", 1 << 6, (1 << 6) & getBaseConfigData(bs));
    cc.disableComponent(BC_NS110);
    Thread.sleep(timeout);
    assertEquals("Deactivate method of " + BC_NS110 + " should be called", 1 << 7, (1 << 7) & getBaseConfigData(bs));

    bs = getBaseService(MAP_NS100); // INVALID COMPONENT FOR XML NS 1.0.0
    assertEquals("Component " + MAP_NS100 + " should not be activated", -1, getBaseConfigData(bs));

    bs = getBaseService(MAP_NS110);
    assertNotNull(bs);
    assertEquals("Activate method of " + MAP_NS110 + " should be called", 1 << 8, (1 << 8) & getBaseConfigData(bs));
    cc.disableComponent(MAP_NS110);
    Thread.sleep(timeout);
    assertEquals("Deactivate method of " + MAP_NS110 + " should be called", 1 << 9, (1 << 9) & getBaseConfigData(bs));

    bs = getBaseService(CC_BC_MAP_NS100); // INVALID COMPONENT FOR XML NS 1.0.0
    assertEquals("Component " + CC_BC_MAP_NS100 + " should not be activated", -1, getBaseConfigData(bs));

    bs = getBaseService(CC_BC_MAP_NS110);
    assertNotNull(bs);
    assertEquals("Activate method of " + CC_BC_MAP_NS110 + " should be called", 1 << 10, (1 << 10)
        & getBaseConfigData(bs));
    cc.disableComponent(CC_BC_MAP_NS110);
    Thread.sleep(timeout);
    assertEquals("Deactivate method of " + CC_BC_MAP_NS110 + " should be called", 1 << 11, (1 << 11)
        & getBaseConfigData(bs));

    bs = getBaseService(INT_NS110);
    assertNotNull(bs);
    cc.disableComponent(INT_NS110);
    Thread.sleep(timeout);
    assertEquals("Deactivate method of " + INT_NS110 + " should be called", 1 << 12, (1 << 12) & getBaseConfigData(bs));

    bs = getBaseService(CC_BC_MAP_INT_NS110);
    assertNotNull(bs);
    cc.disableComponent(CC_BC_MAP_INT_NS110);
    Thread.sleep(timeout);
    int data = getBaseConfigData(bs);
    assertEquals("Deactivate method of " + CC_BC_MAP_INT_NS110 + " should be called", 1 << 13, (1 << 13) & data);

    // // Testing Deactivation reasons ////
    assertEquals("Deactivation reason shall be DEACTIVATION_REASON_DISABLED", 1, 0xFF & (data >> 16));

    final String CONT_EXP = "org.eclipse.equinox.ds.tests.tb12.ContextExp";

    cc.enableComponent(CC_BC_MAP_INT_NS110);
    Thread.sleep(timeout);
    bs = getBaseService(CC_BC_MAP_INT_NS110);
    assertNotNull(bs);
    cc.disableComponent(CONT_EXP);
    Thread.sleep(timeout);
    assertEquals("Deactivation reason shall be DEACTIVATION_REASON_REFERENCE", 2, 0xFF & (getBaseConfigData(bs) >> 16));

    cc.enableComponent(CONT_EXP);
    Thread.sleep(timeout);

    bs = getBaseService(CC_BC_MAP_INT_NS110);
    assertNotNull(bs);
    ConfigurationAdmin cm = (ConfigurationAdmin) trackerCM.getService();
    if (cm == null)
    	return;
    Configuration config = cm.getConfiguration(CC_BC_MAP_INT_NS110, null);
    Dictionary properties = config.getProperties();
    if (properties == null) {
      properties = new Hashtable();
    }
    properties.put("configuration.dummy", "dummy");
    config.update(properties);
    Thread.sleep(timeout * 2);
    assertEquals("Deactivation reason shall be DEACTIVATION_REASON_CONFIGURATION_MODIFIED", 3,
        0xFF & (getBaseConfigData(bs) >> 16));

    cc.enableComponent(CC_BC_MAP_INT_NS110);
    Thread.sleep(timeout );

    bs = getBaseService(CC_BC_MAP_INT_NS110);
    assertNotNull(bs);
    config = cm.getConfiguration(CC_BC_MAP_INT_NS110, null);
    config.delete();
    Thread.sleep(timeout * 2);
    assertEquals("Deactivation reason shall be DEACTIVATION_REASON_CONFIGURATION_DELETED", 4,
        0xFF & (getBaseConfigData(bs) >> 16));

    cc.enableComponent(CC_BC_MAP_INT_NS110);
    Thread.sleep(timeout);

    cc.enableComponent(INT_NS110);
    Thread.sleep(timeout);

    bs = getBaseService(INT_NS110);
    assertNotNull(bs);
    ComponentContext ccIntNS110 = (bs instanceof ComponentContextProvider) ? ((ComponentContextProvider) bs)
        .getComponentContext() : null;
    assertNotNull("Component context should be available for " + INT_NS110, ccIntNS110);
    ccIntNS110.getComponentInstance().dispose();
    assertEquals("Deactivation reason shall be DEACTIVATION_REASON_DISPOSED", 5, 0xFF & (getBaseConfigData(bs) >> 16));

    bs = getBaseService(CC_BC_MAP_INT_NS110);
    assertNotNull(bs);
    tb12.stop();
    assertEquals("Deactivation reason shall be DEACTIVATION_REASON_BUNDLE_STOPPED", 6,
        0xFF & (getBaseConfigData(bs) >> 16));

    uninstallBundle(tb12);
  }

  @Test
  public void testBindUnbindParams() throws Exception {
    Bundle tb13 = installBundle("tb13");
    tb13.start();
    waitBundleStart();

    final String SR_NS100 = "org.eclipse.equinox.ds.tests.tb13.SrNS100";
    final String SR_NS110 = "org.eclipse.equinox.ds.tests.tb13.SrNS110";
    final String CE_NS100 = "org.eclipse.equinox.ds.tests.tb13.CeNS100";
    final String CE_NS110 = "org.eclipse.equinox.ds.tests.tb13.CeNS110";
    final String CE_MAP_NS100 = "org.eclipse.equinox.ds.tests.tb13.CeMapNS100";
    final String CE_MAP_NS110 = "org.eclipse.equinox.ds.tests.tb13.CeMapNS110";

    ServiceReference ref = getContext().getServiceReference(ComponentManager.class.getName());
    assertNotNull("Component Enabler Service Reference should be available", ref);
    ComponentManager enabler = (ComponentManager) getContext().getService(ref);
    assertNotNull("Component Enabler Service should be available", enabler);

    PropertiesProvider bs = getBaseService(SR_NS100);
    assertNotNull("Component " + SR_NS100 + " should be activated", bs);
    assertEquals("Bind method of " + SR_NS100 + " should be called", 1 << 0, (1 << 0) & getBaseConfigData(bs));
    enabler.enableComponent(SR_NS100, false);
    Thread.sleep(timeout);
    assertEquals("Unbind method of " + SR_NS100 + " should be called", 1 << 1, (1 << 1) & getBaseConfigData(bs));

    bs = getBaseService(SR_NS110);
    assertNotNull("Component " + SR_NS110 + " should be activated", bs);
    assertEquals("Bind method of " + SR_NS110 + " should be called", 1 << 0, (1 << 0) & getBaseConfigData(bs));
    enabler.enableComponent(SR_NS110, false);
    Thread.sleep(timeout);
    assertEquals("Unbind method of " + SR_NS110 + " should be called", 1 << 5, (1 << 5) & getBaseConfigData(bs));

    bs = getBaseService(CE_NS100);
    assertNotNull("Component " + CE_NS100 + " should be activated", bs);
    assertEquals("Bind method of " + CE_NS100 + " should be called", 1 << 2, (1 << 2) & getBaseConfigData(bs));
    enabler.enableComponent(CE_NS100, false);
    Thread.sleep(timeout);
    assertEquals("Unbind method of " + CE_NS100 + " should be called", 1 << 3, (1 << 3) & getBaseConfigData(bs));

    bs = getBaseService(CE_NS110);
    assertNotNull("Component " + CE_NS110 + " should be activated", bs);
    assertEquals("Bind method of " + CE_NS110 + " should be called", 1 << 2, (1 << 2) & getBaseConfigData(bs));
    enabler.enableComponent(CE_NS110, false);
    Thread.sleep(timeout);
    assertEquals("Unbind method of " + CE_NS110 + " should be called", 1 << 3, (1 << 3) & getBaseConfigData(bs));

    bs = getBaseService(CE_MAP_NS100);
    assertNotNull("Component " + CE_MAP_NS100 + " should be activated", bs);

    bs = getBaseService(CE_MAP_NS110);
    assertNotNull("Component " + CE_MAP_NS110 + " should be activated", bs);
    assertEquals("Bind method of " + CE_MAP_NS110 + " should be called", 1 << 4, (1 << 4) & getBaseConfigData(bs));
    enabler.enableComponent(CE_MAP_NS110, false);
    Thread.sleep(timeout);
    assertEquals("Unbind method of " + CE_MAP_NS110 + " should be called", 1 << 5, (1 << 5) & getBaseConfigData(bs));

    getContext().ungetService(ref);
    uninstallBundle(tb13);
  }

  @Test
  public void testOptionalNames() throws Exception {
    Bundle tb14 = installBundle("tb14");
    tb14.start();
    waitBundleStart();
    PropertiesProvider bs;

    final String OPT_NAME_100 = "org.eclipse.equinox.ds.tests.tb14.Optional";
    final String OPT_NAME_110 = "org.eclipse.equinox.ds.tests.tb14.Optional2";
    final String OPT_REF_100 = "org.eclipse.equinox.ds.tests.tb14.OptRef100";
    final String OPT_REF_110 = "org.eclipse.equinox.ds.tests.tb14.OptRef110";

    assertNull("Component " + OPT_NAME_100 + " should not be activated", getBaseService(OPT_NAME_100));
    assertNotNull("Component " + OPT_NAME_110 + " should be activated", getBaseService(OPT_NAME_110));

    assertNull("Component " + OPT_REF_100 + " should not be activated", getBaseService(OPT_REF_100));
    assertNotNull("Component " + OPT_REF_110 + " should be activated", bs = getBaseService(OPT_REF_110));

    ComponentContext cc = (bs instanceof ComponentContextProvider) ? ((ComponentContextProvider) bs)
        .getComponentContext() : null;
    assertNotNull("Component context should be available", cc);
    assertNotNull("Optional reference name should be set to interface attribute", cc
        .locateService(ComponentContextProvider.class.getName()));

    uninstallBundle(tb14);
  }

  @Test
  public void testDisposingMultipleDependencies() throws Exception {
    Bundle tb15 = installBundle("tb15");
    tb15.start();
    waitBundleStart();

    final String C1 = "org.eclipse.equinox.ds.tests.tb15.Component1";
    final String C2 = "org.eclipse.equinox.ds.tests.tb15.Component2";
    final String C3 = "org.eclipse.equinox.ds.tests.tb15.Component3";

    PropertiesProvider serviceC1 = getBaseService(C1);
    assertNotNull("Component " + C1 + " should be activated", serviceC1);
    PropertiesProvider serviceC2 = getBaseService(C2);
    assertNotNull("Component " + C2 + " should be activated", serviceC2);
    PropertiesProvider serviceC3 = getBaseService(C3);
    assertNotNull("Component " + C3 + " should be activated", serviceC3);

    ComponentContext cc = (serviceC1 instanceof ComponentContextProvider) ? ((ComponentContextProvider) serviceC1)
        .getComponentContext() : null;
    assertNotNull("Component context should be available", cc);

    cc.disableComponent(C1);
    Thread.sleep(timeout);

    assertEquals("Component " + C3 + " should be deactivated first", 0, getBaseConfigData(serviceC3));
    assertEquals("Component " + C2 + " should be deactivated second", 1, getBaseConfigData(serviceC2));
    assertEquals("Component " + C1 + " should be deactivated third", 2, getBaseConfigData(serviceC1));

    uninstallBundle(tb15);
  }

  @Test
  public void testReferenceTargetProperty() throws Exception {
    Bundle tb16 = installBundle("tb16");
    tb16.start();
    waitBundleStart();

    final String EXPOSER = "org.eclipse.equinox.ds.tests.tb16.Exposer";
    final String C1 = "org.eclipse.equinox.ds.tests.tb16.C1";
    final String C2 = "org.eclipse.equinox.ds.tests.tb16.C2";

    PropertiesProvider bs = getBaseService(EXPOSER);
    ComponentContext cc = (bs instanceof ComponentContextProvider) ? ((ComponentContextProvider) bs)
        .getComponentContext() : null;
    assertNotNull("Component context should be available", cc);

    PropertiesProvider serviceC2 = getBaseService(C2);
    // target property of referenced service of component Component2 should not
    // be satisfied
    assertNull("Component " + C2 + " should not be activated because of unsatisfied reference", serviceC2);

    cc.enableComponent(C1);
    Thread.sleep(timeout);
    assertNotNull("Component " + C1 + " should be available", getBaseService(C1));

    serviceC2 = getBaseService(C2);
    // target property of referenced service of component Component2 should now
    // be satisfied
    assertNotNull("Component " + C2 + " should be activated", serviceC2);

    uninstallBundle(tb16);
  }

  class OverloadManager extends Thread {
    private String compPrefix;
    private int firstComp;
    private int lastComp;

    public OverloadManager(String compPrefix, int first, int last) {
      this.compPrefix = compPrefix;
      this.firstComp = first;
      this.lastComp = last;
    }

    @Override
    public void run() {
      ConfigurationAdmin cm = (ConfigurationAdmin) trackerCM.getService();
      assertNotNull("The ConfigurationAdmin should be available", cm);

      try {
        for (int i = firstComp; i <= lastComp; i++) {
          Configuration config = cm.getConfiguration(compPrefix + i, null);
          Dictionary properties = config.getProperties();
          if (properties == null) {
            properties = new Hashtable();
          }
          properties.put("component.index", Integer.valueOf(i));
          config.update(properties);

          sleep0(100);
        }
      } catch (IOException e) {
        return;
      }
    }

    public boolean isAllComponentsRunning() {
      for (int i = firstComp; i <= lastComp; i++) {
        if (getBaseService(compPrefix + i) == null) {
          return false;
        }
      }
      return true;
    }
  }

  @Test
  public void testOverload() throws Exception {
    Bundle tb17 = installBundle("tb17");
    Bundle tb18 = installBundle("tb18");
    Bundle tb19 = installBundle("tb19");
    tb17.start();
    tb18.start();
    tb19.start();
    waitBundleStart();

    final String SCR = "org.eclipse.equinox.ds.tests";
    final int startComp = 1;
    final int lastComp = 10;
    final int OVERLOAD_TIMEOUT = 60000; // max time allowed for processing in ms

    OverloadManager manager17 = new OverloadManager(SCR + ".tb17.C", startComp, lastComp);
    OverloadManager manager18 = new OverloadManager(SCR + ".tb18.C", startComp, lastComp);
    OverloadManager manager19 = new OverloadManager(SCR + ".tb19.C", startComp, lastComp);

    long startTime = System.currentTimeMillis();
    manager17.start();
    manager18.start();
    manager19.start();

    manager17.join();
    manager18.join();
    manager19.join();

    // waiting SCR to process events
    int successCount = 0;
    while ((successCount < 5) && (System.currentTimeMillis() - startTime < OVERLOAD_TIMEOUT)) {
      Thread.sleep(100);
      if (!manager17.isAllComponentsRunning() || !manager18.isAllComponentsRunning()
          || !manager19.isAllComponentsRunning()) {
        successCount = 0;
        continue;
      }
      successCount++;
    }

    long elapsed = System.currentTimeMillis() - startTime;
    log("testOverload(): Overload processing finished for " + elapsed + " ms.");

    assertTrue("All components of tb17 should be activated", manager17.isAllComponentsRunning());
    assertTrue("All components of tb18 should be activated", manager18.isAllComponentsRunning());
    assertTrue("All components of tb19 should be activated", manager19.isAllComponentsRunning());

    uninstallBundle(tb17);
    uninstallBundle(tb18);
    uninstallBundle(tb19);
  }

  @Test
  public void testLazyBundles() throws Exception {
    Bundle tb20 = installBundle("tb20");
    // lazy bundle
    tb20.start(Bundle.START_ACTIVATION_POLICY);
    waitBundleStart();

    final String COMP = "org.eclipse.equinox.ds.tests.tb20.component";
    assertTrue("Provided service of Component " + COMP + " should be available.", trackerBaseService.size() > 0);
    uninstallBundle(tb20);
  }

  // Testing modified attribute for XML NS 1.0.0
  @Test
  public void testModified100() throws Exception {
    ConfigurationAdmin cm = (ConfigurationAdmin) trackerCM.getService();
    if (cm == null)
    	return;

    Bundle tb21 = installBundle("tb21");

    Hashtable props = new Hashtable(10);
    props.put("config.dummy.data", Integer.valueOf(1));
    cm.getConfiguration(MOD_NOTSET_NS100, null).update(props);
    cm.getConfiguration(MOD_NOARGS_NS100, null).update(props);
    cm.getConfiguration(MOD_CC_NS100, null).update(props);
    cm.getConfiguration(MOD_BC_NS100, null).update(props);
    cm.getConfiguration(MOD_MAP_NS100, null).update(props);
    cm.getConfiguration(MOD_CC_BC_MAP_NS100, null).update(props);

    Thread.sleep(timeout * 2);

    tb21.start();
    waitBundleStart();

    props.put("config.dummy.data", Integer.valueOf(2));
    Hashtable unsatisfyingProps = new Hashtable(10);
    unsatisfyingProps.put("ref.target", "(component.name=org.eclipse.equinox.ds.tests.tb21.unexisting.provider)");

    PropertiesProvider bs = getBaseService(MOD_NOTSET_NS100);
    assertNotNull(bs);
    cm.getConfiguration(MOD_NOTSET_NS100, null).update(props);
    Thread.sleep(timeout * 2);
    assertEquals("Modified method of " + MOD_NOTSET_NS100 + " should not be called", 0, (1 << 0)
        & getBaseConfigData(bs));
    assertEquals("Deactivate method of " + MOD_NOTSET_NS100 + " should be called", 1 << 7, (1 << 7)
        & getBaseConfigData(bs));
    bs = getBaseService(MOD_NOTSET_NS100);
    cm.getConfiguration(MOD_NOTSET_NS100, null).update(unsatisfyingProps);
    Thread.sleep(timeout * 2);
    assertEquals("Modified method of " + MOD_NOTSET_NS100 + " should not be called", 0, (1 << 0)
        & getBaseConfigData(bs));
    assertEquals("Deactivate method of " + MOD_NOTSET_NS100 + " should be called", 1 << 7, (1 << 7)
        & getBaseConfigData(bs));

    // INVALID COMPONENTS for XML NS 1.0.0 - modified attribute is set
    bs = getBaseService(MOD_NOARGS_NS100);
    assertEquals("Component " + MOD_NOARGS_NS100 + " should not be activated", null, bs);
    bs = getBaseService(MOD_CC_NS100);
    assertEquals("Component " + MOD_CC_NS100 + " should not be activated", null, bs);
    bs = getBaseService(MOD_BC_NS100);
    assertEquals("Component " + MOD_BC_NS100 + " should not be activated", null, bs);
    bs = getBaseService(MOD_MAP_NS100);
    assertEquals("Component " + MOD_MAP_NS100 + " should not be activated", null, bs);
    bs = getBaseService(MOD_CC_BC_MAP_NS100);
    assertEquals("Component " + MOD_CC_BC_MAP_NS100 + " should not be activated", null, bs);

    uninstallBundle(tb21);
  }

  // Testing modified attribute for XML NS 1.1.0
  @Test
  public void testModified110() throws Exception {
    ConfigurationAdmin cm = (ConfigurationAdmin) trackerCM.getService();
    if (cm == null)
    	return;

    Bundle tb21a = installBundle("tb21a");

    Hashtable props = new Hashtable(10);
    props.put("config.dummy.data", Integer.valueOf(1));
    cm.getConfiguration(MOD_NOTSET_NS110, null).update(props);
    cm.getConfiguration(MOD_NOARGS_NS110, null).update(props);
    cm.getConfiguration(MOD_CC_NS110, null).update(props);
    cm.getConfiguration(MOD_BC_NS110, null).update(props);
    cm.getConfiguration(MOD_MAP_NS110, null).update(props);
    cm.getConfiguration(MOD_CC_BC_MAP_NS110, null).update(props);

    Thread.sleep(timeout * 2);

    tb21a.start();
    waitBundleStart();

    props.put("config.dummy.data", Integer.valueOf(2));
    Hashtable unsatisfyingProps = new Hashtable(10);
    unsatisfyingProps.put("ref.target", "(component.name=org.eclipse.equinox.ds.tests.tb21.unexisting.provider)");

    PropertiesProvider bs = getBaseService(MOD_NOTSET_NS110);
    cm.getConfiguration(MOD_NOTSET_NS110, null).update(props);
    Thread.sleep(timeout * 2);
    assertEquals("Modified method of " + MOD_NOTSET_NS110 + " should not be called", 0, (1 << 0)
        & getBaseConfigData(bs));
    assertEquals("Deactivate method of " + MOD_NOTSET_NS110 + " should be called", 1 << 7, (1 << 7)
        & getBaseConfigData(bs));
    bs = getBaseService(MOD_NOTSET_NS110);
    cm.getConfiguration(MOD_NOTSET_NS110, null).update(unsatisfyingProps);
    Thread.sleep(timeout * 2);
    assertEquals("Modified method of " + MOD_NOTSET_NS110 + " should not be called", 0, (1 << 0)
        & getBaseConfigData(bs));
    assertEquals("Deactivate method of " + MOD_NOTSET_NS110 + " should be called", 1 << 7, (1 << 7)
        & getBaseConfigData(bs));
    // Re-activating
    bs = getBaseService(MOD_NOTSET_NS110);
    assertEquals("Activate method of " + MOD_NOTSET_NS110 + " should be called", 1 << 6, (1 << 6)
        & getBaseConfigData(bs));

    bs = getBaseService(MOD_NOARGS_NS110);
    cm.getConfiguration(MOD_NOARGS_NS110, null).update(props);
    Thread.sleep(timeout * 2);
    assertEquals("Modified method of " + MOD_NOARGS_NS110 + " should be called", 1 << 1, (1 << 1)
        & getBaseConfigData(bs));
    assertEquals("Deactivate method of " + MOD_NOARGS_NS110 + " should not be called", 0, (1 << 7)
        & getBaseConfigData(bs));
    cm.getConfiguration(MOD_NOARGS_NS110, null).update(unsatisfyingProps);
    Thread.sleep(timeout * 2);
    assertEquals("Deactivate method of " + MOD_NOARGS_NS110 + " should be called", 1 << 7, (1 << 7)
        & getBaseConfigData(bs));
    // Re-activating
    bs = getBaseService(MOD_NOARGS_NS110);
    assertEquals("Activate method of " + MOD_NOARGS_NS110 + " should be called", 1 << 6, (1 << 6)
        & getBaseConfigData(bs));

    bs = getBaseService(MOD_CC_NS110);
    cm.getConfiguration(MOD_CC_NS110, null).update(props);
    Thread.sleep(timeout * 2);
    assertEquals("Modified method of " + MOD_CC_NS110 + " should be called", 1 << 2, (1 << 2) & getBaseConfigData(bs));
    assertEquals("Deactivate method of " + MOD_CC_NS110 + " should not be called", 0, (1 << 7) & getBaseConfigData(bs));
    cm.getConfiguration(MOD_CC_NS110, null).update(unsatisfyingProps);
    Thread.sleep(timeout * 2);
    assertEquals("Deactivate method of " + MOD_CC_NS110 + " should be called", 1 << 7, (1 << 7) & getBaseConfigData(bs));
    // Re-activating
    bs = getBaseService(MOD_CC_NS110);
    assertEquals("Activate method of " + MOD_CC_NS110 + " should be called", 1 << 6, (1 << 6) & getBaseConfigData(bs));

    bs = getBaseService(MOD_BC_NS110);
    cm.getConfiguration(MOD_BC_NS110, null).update(props);
    Thread.sleep(timeout * 2);
    assertEquals("Modified method of " + MOD_BC_NS110 + " should be called", 1 << 3, (1 << 3) & getBaseConfigData(bs));
    assertEquals("Deactivate method of " + MOD_BC_NS110 + " should not be called", 0, (1 << 7) & getBaseConfigData(bs));
    cm.getConfiguration(MOD_BC_NS110, null).update(unsatisfyingProps);
    Thread.sleep(timeout * 2);
    assertEquals("Deactivate method of " + MOD_BC_NS110 + " should be called", 1 << 7, (1 << 7) & getBaseConfigData(bs));
    // Re-activating
    bs = getBaseService(MOD_BC_NS110);
    assertEquals("Activate method of " + MOD_BC_NS110 + " should be called", 1 << 6, (1 << 6) & getBaseConfigData(bs));

    bs = getBaseService(MOD_MAP_NS110);
    cm.getConfiguration(MOD_MAP_NS110, null).update(props);
    Thread.sleep(timeout * 2);
    assertEquals("Modified method of " + MOD_MAP_NS110 + " should be called", 1 << 4, (1 << 4) & getBaseConfigData(bs));
    assertEquals("Deactivate method of " + MOD_MAP_NS110 + " should not be called", 0, (1 << 7) & getBaseConfigData(bs));
    cm.getConfiguration(MOD_MAP_NS110, null).update(unsatisfyingProps);
    Thread.sleep(timeout * 2);
    assertEquals("Deactivate method of " + MOD_MAP_NS110 + " should be called", 1 << 7, (1 << 7)
        & getBaseConfigData(bs));
    // Re-activating
    bs = getBaseService(MOD_MAP_NS110);
    assertEquals("Activate method of " + MOD_MAP_NS110 + " should be called", 1 << 6, (1 << 6) & getBaseConfigData(bs));

    bs = getBaseService(MOD_CC_BC_MAP_NS110);
    cm.getConfiguration(MOD_CC_BC_MAP_NS110, null).update(props);
    Thread.sleep(timeout * 2);
    assertEquals("Modified method of " + MOD_CC_BC_MAP_NS110 + " should be called", 1 << 5, (1 << 5)
        & getBaseConfigData(bs));
    assertEquals("Deactivate method of " + MOD_CC_BC_MAP_NS110 + " should not be called", 0, (1 << 7)
        & getBaseConfigData(bs));
    cm.getConfiguration(MOD_CC_BC_MAP_NS110, null).update(unsatisfyingProps);
    Thread.sleep(timeout * 2);
    assertEquals("Deactivate method of " + MOD_CC_BC_MAP_NS110 + " should be called", 1 << 7, (1 << 7)
        & getBaseConfigData(bs));
    // Re-activating
    bs = getBaseService(MOD_CC_BC_MAP_NS110);
    assertEquals("Activate method of " + MOD_CC_BC_MAP_NS110 + " should be called", 1 << 6, (1 << 6)
        & getBaseConfigData(bs));

    uninstallBundle(tb21a);
  }

  // Testing modified attribute - special cases
  @Test
  public void testModifiedSpecialCases() throws Exception {
    ConfigurationAdmin cm = (ConfigurationAdmin) trackerCM.getService();
    if (cm == null)
    	return;

    Bundle tb21a = installBundle("tb21a");

    Hashtable props = new Hashtable(10);
    props.put("config.dummy.data", Integer.valueOf(1));
    cm.getConfiguration(MOD_CC_NS110, null).update(props);
    cm.getConfiguration(MOD_NOT_EXIST_NS110, null).update(props);
    cm.getConfiguration(MOD_THROW_EX_NS110, null).update(props);
    cm.getConfiguration(MOD_BC_NS110, null).update(props);
    Thread.sleep(timeout * 2);

    tb21a.start();
    waitBundleStart();

    // Verifying correctness of updated component properties
    PropertiesProvider bs = getBaseService(MOD_CC_NS110);
    props.put("config.dummy.data", Integer.valueOf(2));
    cm.getConfiguration(MOD_CC_NS110, null).update(props);
    Thread.sleep(timeout * 2);
    Object val = ((ComponentContextProvider) bs).getComponentContext().getProperties().get("config.dummy.data");
    assertEquals("Modified method of " + MOD_CC_NS110 + " should be called", 1 << 2, (1 << 2) & getBaseConfigData(bs));
    assertTrue("Component properties should be updated properly for " + MOD_CC_NS110, (Integer.valueOf(2)).equals(val));

    // Specified modified method doesn't exist, deactivate() should be called
    // instead of modified
    bs = getBaseService(MOD_NOT_EXIST_NS110);
    cm.getConfiguration(MOD_NOT_EXIST_NS110, null).update(props);
    Thread.sleep(timeout * 2);
    assertEquals("Deactivate method of " + MOD_NOT_EXIST_NS110 + " should be called", 1 << 7, (1 << 7)
        & getBaseConfigData(bs));
    // Re-activating
    bs = getBaseService(MOD_NOT_EXIST_NS110);
    assertEquals("Activate method of " + MOD_NOT_EXIST_NS110 + " should be called", 1 << 6, (1 << 6)
        & getBaseConfigData(bs));

    // Specified modified method throws exception. Normal workflow should
    // continue, deactivate() should not be called
    bs = getBaseService(MOD_THROW_EX_NS110);
    cm.getConfiguration(MOD_THROW_EX_NS110, null).update(props);
    Thread.sleep(timeout * 2);
    assertEquals("Deactivate method of " + MOD_THROW_EX_NS110 + " should not be called", 0, (1 << 7)
        & getBaseConfigData(bs));

    // Deleting component configuration
    bs = getBaseService(MOD_BC_NS110);
    cm.getConfiguration(MOD_BC_NS110, null).delete();
    Thread.sleep(timeout * 2);
    assertEquals("Modified method of " + MOD_BC_NS110 + " should not be called", 0, (1 << 5) & getBaseConfigData(bs));
    assertEquals("Deactivate method of " + MOD_BC_NS110 + " should be called", 1 << 7, (1 << 7) & getBaseConfigData(bs));
    // Re-activating
    bs = getBaseService(MOD_BC_NS110);
    assertEquals("Activate method of " + MOD_BC_NS110 + " should be called", 1 << 6, (1 << 6) & getBaseConfigData(bs));

    uninstallBundle(tb21a);
  }

  @Test
  public void testPrivateProperties() throws Exception {
    Bundle tb22 = installBundle("tb22");
    tb22.start();
    waitBundleStart();

    final String COMP = "org.eclipse.equinox.ds.tests.tb22.component";

    ServiceReference ref = trackerBaseService.getServiceReference();
    assertNotNull("Provided service of " + COMP + " should be available", ref);
    String[] keys = ref.getPropertyKeys();
    for (String key : keys) {
        assertTrue("Private properties should not be propagated", !key.startsWith("."));
    }

    uninstallBundle(tb22);
  }

  // Testing situation when bind method throws exception
  @Test
  public void testBindException() throws Exception {
    Bundle tb23 = installBundle("tb23");

    final String MANDATORY_REF_COMP = "org.eclipse.equinox.ds.tests.tb23.mandatory";
    final String OPTIONAL_REF_COMP = "org.eclipse.equinox.ds.tests.tb23.optional";
    tb23.start();
    waitBundleStart();

    PropertiesProvider bs = getBaseService(MANDATORY_REF_COMP);
    assertNotNull("Component " + MANDATORY_REF_COMP + " should not be activated", bs);
    bs = getBaseService(OPTIONAL_REF_COMP);
    assertEquals("Component " + OPTIONAL_REF_COMP + " should be activated", 1 << 2, (1 << 2) & getBaseConfigData(bs));

    uninstallBundle(tb23);
  }
  
  // Testing config admin appear/disappear situations
  @Test
  public void testConfigAdminOnOff() throws Exception {
    ConfigurationAdmin cm = (ConfigurationAdmin) trackerCM.getService();
    if (cm == null)
    	return;
    
    Hashtable props = new Hashtable(11);
    props.put("config.base.data", Integer.valueOf(1));
    //create the configurations for the test DS components
    Configuration config = cm.getConfiguration(COMP_OPTIONAL, null);
    config.update(props);
    config = cm.getConfiguration(COMP_REQUIRE, null);
    config.update(props);
    config = cm.getConfiguration(COMP_IGNORE, null);
    config.update(props);
    //wait for CM to process the configuration updates
    Thread.sleep(timeout * 2);
    
    //stop the config admin bundle
    Bundle cmBundle = trackerCM.getServiceReference().getBundle();
    cmBundle.stop();
    Bundle tb24 = installBundle("tb24");
    try {
      tb24.start();
      waitBundleStart();

      // component with optional configuration should be available and not initialized by configuration
      assertEquals("Component with optional configuration should be activated", 0, getBaseConfigData(COMP_OPTIONAL));
      // component with ignored configuration should be available and not initialized by configuration
      assertEquals("Component with ignored configuration should be activated", 0, getBaseConfigData(COMP_IGNORE));
      // component with required configuration should NOT be available
      assertEquals("Component with required configuration should NOT be activated", -1, getBaseConfigData(COMP_REQUIRE));
      
      //start again the config admin 
      cmBundle.start();
      
      //wait for processing components that depend on configurations
      Thread.sleep(timeout * 2);

      // component with optional configuration should be available and initialized by configuration
      assertEquals("Component with optional configuration should be activated and inited by configuration", 1, getBaseConfigData(COMP_OPTIONAL));
      // component with ignored configuration should be available and not initialized by configuration
      assertEquals("Component with ignored configuration should be activated", 0, getBaseConfigData(COMP_IGNORE));
      // component with required configuration should be available
      assertEquals("Component with required configuration should be activated", 1, getBaseConfigData(COMP_REQUIRE));
      
      //stop again the config admin 
      cmBundle.stop();

      /*The components should remain activated when Configuration Admin service disappears*/
      // component with optional configuration should be available and initialized by configuration
      assertEquals("Component with optional configuration should be activated", 1, getBaseConfigData(COMP_OPTIONAL));
      // component with ignored configuration should be available and not initialized by configuration
      assertEquals("Component with ignored configuration should be activated", 0, getBaseConfigData(COMP_IGNORE));
      // component with required configuration should be available
      assertEquals("Component with required configuration should be activated", 1, getBaseConfigData(COMP_REQUIRE));

    } finally {
      uninstallBundle(tb24);
      cmBundle.start();
    }
  }

  // Tests update of service properties
  @Test
  public void testServicePropertiesUpdate() throws Exception {
    Bundle tb25 = installBundle("tb25");
    ServiceRegistration sr = null;
    try {
      final String COMP_NAME = "org.eclipse.equinox.ds.tests.tb25.ServicePropertiesComp";
      final String PROP = "test.property";
      final String PROP_STATIC = "serviceUpdatedStatic";
      final String PROP_DYNAMIC = "serviceUpdatedDynamic";
      tb25.start();
      waitBundleStart();

      Hashtable props = new Hashtable();
      props.put("service.provider", "service.properties.update.test");
      props.put(PROP, Boolean.FALSE);

      // register service referenced by the component
      sr = registerService(PropertiesProvider.class.getName(), new DefaultPropertiesProvider(null),
          (Dictionary) props.clone());
      Thread.sleep(timeout);

      PropertiesProvider bs = getBaseService(COMP_NAME);
      assertNotNull("Component " + COMP_NAME + " should be activated", bs);

      // update service properties
      props.put(PROP, Boolean.TRUE);
      sr.setProperties((Dictionary) props.clone());
      Thread.sleep(timeout);

      bs = getBaseService(COMP_NAME);
      assertNotNull("Component " + COMP_NAME + " should still be active", bs);
      Dictionary compProps = bs.getProperties();
      assertNotNull("Component properties should be available.", compProps);
      assertEquals("Test property should be updated.", Boolean.TRUE, compProps.get(PROP));
      assertEquals("Updated method for static reference should be called.", Boolean.TRUE, compProps.get(PROP_STATIC));
      assertEquals("Updated method for dynamic reference should be called.", Boolean.TRUE, compProps.get(PROP_DYNAMIC));
    } finally {
      uninstallBundle(tb25);
      if (sr != null) {
        unregisterService(sr);
      }
    }
  }

  // Tests Reluctant policy option of service references
  @Test
  public void testPolicyOptionReluctant() throws Exception {
    Bundle tb25 = installBundle("tb25");
    ServiceRegistration sr = null;
    ServiceRegistration srLower = null;
    ServiceRegistration srHigher = null;
    try {
      final String COMP_NAME_STATIC = "org.eclipse.equinox.ds.tests.tb25.PolicyReluctantStaticComp";
      final String COMP_NAME_DYNAMIC = "org.eclipse.equinox.ds.tests.tb25.PolicyReluctantDynamicComp";
      final String PROP_BIND_01 = "bind01";
      final String PROP_BIND_11 = "bind11";
      final String PROP_BIND_0n = "bind0n";
      final String PROP_BIND_1n = "bind1n";
      final Integer RANK_1 = Integer.valueOf(1);
      final Integer RANK_2 = Integer.valueOf(2);
      final Integer RANK_3 = Integer.valueOf(3);
      tb25.start();
      waitBundleStart();

      Hashtable props = new Hashtable();
      props.put("service.provider", "reluctant.policy.option.test");
      props.put(Constants.SERVICE_RANKING, RANK_2);

      // register service referenced by the component
      sr = registerService(PropertiesProvider.class.getName(), new DefaultPropertiesProvider(null),
          (Dictionary) props.clone());
      Thread.sleep(timeout);

      PropertiesProvider bsStatic = getBaseService(COMP_NAME_STATIC);
      assertNotNull("Component " + COMP_NAME_STATIC + " should be activated", bsStatic);
      PropertiesProvider bsDynamic = getBaseService(COMP_NAME_DYNAMIC);
      assertNotNull("Component " + COMP_NAME_DYNAMIC + " should be activated", bsDynamic);

      // register service with lower ranking
      props.put(Constants.SERVICE_RANKING, RANK_1);
      srLower = registerService(PropertiesProvider.class.getName(), new DefaultPropertiesProvider(null),
          (Dictionary) props.clone());
      Thread.sleep(timeout);

      // check bound references
      bsStatic = getBaseService(COMP_NAME_STATIC);
      assertNotNull("Component " + COMP_NAME_STATIC + " should still be active", bsStatic);
      Dictionary compProps = bsStatic.getProperties();
      assertNotNull("Component properties should be available.", compProps);
      assertEqualElements("New target service with lower ranking should be ignored for static 0..1.", toList(RANK_2),
          (List) compProps.get(PROP_BIND_01));
      assertEqualElements("New target service with lower ranking should be ignored for static 1..1.", toList(RANK_2),
          (List) compProps.get(PROP_BIND_11));
      assertEqualElements("New target service with lower ranking should be ignored for static 0..n.", toList(RANK_2),
          (List) compProps.get(PROP_BIND_0n));
      assertEqualElements("New target service with lower ranking should be ignored for static 1..n.", toList(RANK_2),
          (List) compProps.get(PROP_BIND_1n));
      bsDynamic = getBaseService(COMP_NAME_DYNAMIC);
      assertNotNull("Component " + COMP_NAME_DYNAMIC + " should still be active", bsDynamic);
      compProps = bsDynamic.getProperties();
      assertNotNull("Component properties should be available.", compProps);
      assertEqualElements("New target service with lower ranking should be ignored for dynamic 0..1.", toList(RANK_2),
          (List) compProps.get(PROP_BIND_01));
      assertEqualElements("New target service with lower ranking should be ignored for dynamic 1..1.", toList(RANK_2),
          (List) compProps.get(PROP_BIND_11));
      assertEqualElements("New target service with lower ranking should be bound for dynamic 0..n.",
          toList(RANK_1, RANK_2), (List) compProps.get(PROP_BIND_0n));
      assertEqualElements("New target service with lower ranking should be bound for dynamic 1..n.",
          toList(RANK_1, RANK_2), (List) compProps.get(PROP_BIND_1n));

      // register service with higher ranking
      props.put(Constants.SERVICE_RANKING, RANK_3);
      srHigher = registerService(PropertiesProvider.class.getName(), new DefaultPropertiesProvider(null),
          (Dictionary) props.clone());
      Thread.sleep(timeout);

      // check bound references
      bsStatic = getBaseService(COMP_NAME_STATIC);
      assertNotNull("Component " + COMP_NAME_STATIC + " should still be active", bsStatic);
      compProps = bsStatic.getProperties();
      assertNotNull("Component properties should be available.", compProps);
      assertEqualElements("New target service with higher ranking should be ignored for static 0..1.", toList(RANK_2),
          (List) compProps.get(PROP_BIND_01));
      assertEqualElements("New target service with higher ranking should be ignored for static 1..1.", toList(RANK_2),
          (List) compProps.get(PROP_BIND_11));
      assertEqualElements("New target service with higher ranking should be ignored for static 0..n.", toList(RANK_2),
          (List) compProps.get(PROP_BIND_0n));
      assertEqualElements("New target service with higher ranking should be ignored for static 1..n.", toList(RANK_2),
          (List) compProps.get(PROP_BIND_1n));
      bsDynamic = getBaseService(COMP_NAME_DYNAMIC);
      assertNotNull("Component " + COMP_NAME_DYNAMIC + " should still be active", bsDynamic);
      compProps = bsDynamic.getProperties();
      assertNotNull("Component properties should be available.", compProps);
      assertEqualElements("New target service with higher ranking should be ignored for dynamic 0..1.", toList(RANK_2),
          (List) compProps.get(PROP_BIND_01));
      assertEqualElements("New target service with higher ranking should be ignored for dynamic 1..1.", toList(RANK_2),
          (List) compProps.get(PROP_BIND_11));
      assertEqualElements("New target service with higher ranking should be bound for dynamic 0..n.",
          toList(RANK_1, RANK_2, RANK_3), (List) compProps.get(PROP_BIND_0n));
      assertEqualElements("New target service with higher ranking should be bound for dynamic 1..n.",
          toList(RANK_1, RANK_2, RANK_3), (List) compProps.get(PROP_BIND_1n));
    } finally {
      uninstallBundle(tb25);
      if (sr != null) {
        unregisterService(sr);
      }
      if (srLower != null) {
        unregisterService(srLower);
      }
      if (srHigher != null) {
        unregisterService(srHigher);
      }
    }
  }

  // Tests Greedy policy option of service references
  @Test
  public void testPolicyOptionGreedy() throws Exception {
    Bundle tb25 = installBundle("tb25");
    ServiceRegistration sr = null;
    ServiceRegistration srLower = null;
    ServiceRegistration srHigher = null;
    try {
      final String COMP_NAME_STATIC = "org.eclipse.equinox.ds.tests.tb25.PolicyGreedyStaticComp";
      final String COMP_NAME_DYNAMIC = "org.eclipse.equinox.ds.tests.tb25.PolicyGreedyDynamicComp";
      final String PROP_BIND_01 = "bind01";
      final String PROP_BIND_11 = "bind11";
      final String PROP_BIND_0n = "bind0n";
      final String PROP_BIND_1n = "bind1n";
      final Integer RANK_1 = 1;
      final Integer RANK_2 = 2;
      final Integer RANK_3 = 3;
      tb25.start();
      waitBundleStart();

      Hashtable props = new Hashtable();
      props.put("service.provider", "greedy.policy.option.test");
      props.put(Constants.SERVICE_RANKING, RANK_2);

      // register service referenced by the component
      sr = registerService(PropertiesProvider.class.getName(), new DefaultPropertiesProvider(null),
          (Dictionary) props.clone());
      Thread.sleep(timeout);

      PropertiesProvider bsStatic = getBaseService(COMP_NAME_STATIC);
      assertNotNull("Component " + COMP_NAME_STATIC + " should be activated", bsStatic);
      PropertiesProvider bsDynamic = getBaseService(COMP_NAME_DYNAMIC);
      assertNotNull("Component " + COMP_NAME_DYNAMIC + " should be activated", bsDynamic);

      // register service with lower ranking
      props.put(Constants.SERVICE_RANKING, RANK_1);
      srLower = registerService(PropertiesProvider.class.getName(), new DefaultPropertiesProvider(null),
          (Dictionary) props.clone());
      Thread.sleep(timeout);

      // check bound references
      bsStatic = getBaseService(COMP_NAME_STATIC);
      assertNotNull("Component " + COMP_NAME_STATIC + " should still be active", bsStatic);
      Dictionary compProps = bsStatic.getProperties();
      assertNotNull("Component properties should be available.", compProps);
      assertEqualElements("New target service with lower ranking should be ignored for static 0..1.", toList(RANK_2),
          (List) compProps.get(PROP_BIND_01));
      assertEqualElements("New target service with lower ranking should be ignored for static 1..1.", toList(RANK_2),
          (List) compProps.get(PROP_BIND_11));
      assertEqualElements("New target service with lower ranking should be bound for static 0..n.",
          toList(RANK_1, RANK_2), (List) compProps.get(PROP_BIND_0n));
      assertEqualElements("New target service with lower ranking should be bound for static 1..n.",
          toList(RANK_1, RANK_2), (List) compProps.get(PROP_BIND_1n));
      bsDynamic = getBaseService(COMP_NAME_DYNAMIC);
      assertNotNull("Component " + COMP_NAME_DYNAMIC + " should still be active", bsDynamic);
      compProps = bsDynamic.getProperties();
      assertNotNull("Component properties should be available.", compProps);
      assertEqualElements("New target service with lower ranking should be ignored for dynamic 0..1.", toList(RANK_2),
          (List) compProps.get(PROP_BIND_01));
      assertEqualElements("New target service with lower ranking should be ignored for dynamic 1..1.", toList(RANK_2),
          (List) compProps.get(PROP_BIND_11));
      assertEqualElements("New target service with lower ranking should be bound for dynamic 0..n.",
          toList(RANK_1, RANK_2), (List) compProps.get(PROP_BIND_0n));
      assertEqualElements("New target service with lower ranking should be bound for dynamic 1..n.",
          toList(RANK_1, RANK_2), (List) compProps.get(PROP_BIND_1n));

      // register service with higher ranking
      props.put(Constants.SERVICE_RANKING, RANK_3);
      srHigher = registerService(PropertiesProvider.class.getName(), new DefaultPropertiesProvider(null),
          (Dictionary) props.clone());
      Thread.sleep(timeout);

      // check bound references
      bsStatic = getBaseService(COMP_NAME_STATIC);
      assertNotNull("Component " + COMP_NAME_STATIC + " should still be active", bsStatic);
      compProps = bsStatic.getProperties();
      assertNotNull("Component properties should be available.", compProps);
      assertEqualElements("New target service with higher ranking should be bound for static 0..1.", toList(RANK_3),
          (List) compProps.get(PROP_BIND_01));
      assertEqualElements("New target service with higher ranking should be bound for static 1..1.", toList(RANK_3),
          (List) compProps.get(PROP_BIND_11));
      assertEqualElements("New target service with higher ranking should be bound for static 0..n.",
          toList(RANK_1, RANK_2, RANK_3), (List) compProps.get(PROP_BIND_0n));
      assertEqualElements("New target service with higher ranking should be bound for static 1..n.",
          toList(RANK_1, RANK_2, RANK_3), (List) compProps.get(PROP_BIND_1n));
      bsDynamic = getBaseService(COMP_NAME_DYNAMIC);
      assertNotNull("Component " + COMP_NAME_DYNAMIC + " should still be active", bsDynamic);
      compProps = bsDynamic.getProperties();
      assertNotNull("Component properties should be available.", compProps);
      assertEqualElements("New target service with higher ranking should be bound for dynamic 0..1.",
          toList(RANK_2, RANK_3), (List) compProps.get(PROP_BIND_01));
      assertEqualElements("New target service with higher ranking should be bound for dynamic 1..1.",
          toList(RANK_2, RANK_3), (List) compProps.get(PROP_BIND_11));
      assertEqualElements("New target service with higher ranking should be bound for dynamic 0..n.",
          toList(RANK_1, RANK_2, RANK_3), (List) compProps.get(PROP_BIND_0n));
      assertEqualElements("New target service with higher ranking should be bound for dynamic 1..n.",
          toList(RANK_1, RANK_2, RANK_3), (List) compProps.get(PROP_BIND_1n));
    } finally {
      uninstallBundle(tb25);
      if (sr != null) {
        unregisterService(sr);
      }
      if (srLower != null) {
        unregisterService(srLower);
      }
      if (srHigher != null) {
        unregisterService(srHigher);
      }
    }
  }

  // Tests PID of Component configuration
  @Test
  public void testComponentConfigurationPID() throws Exception {
    ConfigurationAdmin cm = (ConfigurationAdmin) trackerCM.getService();
    if (cm == null) {
      return;
    }

    Bundle tb25 = installBundle("tb25");
    Configuration config = null;
    try {
      final String COMP_NAME = "org.eclipse.equinox.ds.tests.tb25.ConfigPIDComp";
      final String CONFIG_PID = "test.changed.configuration.pid";
      final String PROP = "test.property";

      // set component configuration
      config = cm.getConfiguration(CONFIG_PID, null);
      Dictionary configProperties = config.getProperties();
      if (configProperties == null) {
        configProperties = new Hashtable();
      }
      configProperties.put(PROP, Boolean.TRUE);
      config.update(configProperties);
      Thread.sleep(timeout);

      tb25.start();
      waitBundleStart();

      // check component properties
      PropertiesProvider bs = getBaseService(COMP_NAME);
      assertNotNull("Component " + COMP_NAME + " should be activated", bs);
      Dictionary compProps = bs.getProperties();
      assertNotNull("Component properties should be available.", compProps);
      assertEquals("Test property should be set.", Boolean.TRUE, compProps.get(PROP));
    } finally {
      uninstallBundle(tb25);
      if (config != null) {
        config.delete();
      }
    }
  }

  /**
   * Asserts that two lists contain equal elements (the order doesn't matter).
   */
  private static void assertEqualElements(String message, List list1, List list2) {
    if (list1 == null || list2 == null) {
      fail(message);
    }
    if (list1.size() != list2.size()) {
      fail(message);
    }
    List tmp = new ArrayList(list2);
    for (Object el : list1) {
      if (!tmp.contains(el)) {
        fail(message);
      }
      tmp.remove(el);
    }
  }

  private List toList(Object el) {
    List list = new ArrayList();
    list.add(el);
    return list;
  }

  private List toList(Object el1, Object el2) {
    List list = new ArrayList();
    list.add(el1);
    list.add(el2);
    return list;
  }

  private List toList(Object el1, Object el2, Object el3) {
    List list = new ArrayList();
    list.add(el1);
    list.add(el2);
    list.add(el3);
    return list;
  }

  /**
   * Searches for component with name componentName which provides
   * PropertiesProvider. Returns value of its "config.base.data" property.
   * 
   * @param componentName
   *          - the name of the component to get data
   * @return the value of property "config.base.data", provided by
   *         PropertiesProvider.getProperties(). Returned value is -1 when
   *         component which provides PropertiesProvider and has specified name
   *         is not activated. Returned value is 0 when component with specified
   *         name is active but doesn't have property "config.base.data".
   */
  private int getBaseConfigData(String componentName) {
    PropertiesProvider s = getBaseService(componentName);
    return getBaseConfigData(s);
  }

  private int getBaseConfigData(PropertiesProvider s) {
    Dictionary props = null;
    int value = -1;
    if (s != null) {
      value = 0;
      if ((props = s.getProperties()) != null) {
        Object prop = props.get("config.base.data");
        if (prop instanceof Integer) {
          value = ((Integer) prop).intValue();
        }
      }
    }
    return value;
  }

  private PropertiesProvider getBaseService(String componentName) {
    Object[] services = trackerBaseService.getServices();
    if (services == null) {
      return null;
    }
    for (Object service : services) {
        if (service instanceof PropertiesProvider) {
            PropertiesProvider s = (PropertiesProvider) service;
            Dictionary props = s.getProperties();
            if (props != null && ((String) props.get(ComponentConstants.COMPONENT_NAME)).equals(componentName)) {
                return s;
                }
            }
        }
    return null;
  }

  private BundleContext getContext() {
    return DSTestsActivator.getContext();
  }

  private Bundle installBundle(String bundle) throws BundleException {
    Bundle b = installer.installBundle(bundle);
    return b;
  }
  
  private Bundle installBundleAsDirectory(String bundle) throws Exception {
	  BundleContext context = getContext();
	  String location = installer.getBundleLocation(bundle);
	  String reference = "reference:";
	  if (location.startsWith(reference))
		  // Remove the "reference" protocol from the URL.
		  // (1) So that when running from a workspace, the test will modify a
		  // copy of the component.xml.
		  // (2) When running from a server, to get a readable input stream when 
		  // extracting the JAR into a directory.
		  location = location.substring(location.indexOf(':') + 1);
	  if (!location.endsWith(".jar"))
		  // If the bundle is already a directory, go ahead and install it in
		  // the typical fashion. Leave the "reference" protocol out.
		  return context.installBundle(location);
	  // The bundle is a JAR file and needs to be extracted and copied as a
	  // directory to the data storage area of the test harness bundle.
	  File file = context.getBundle().getDataFile(bundle);
	  try (ZipInputStream in = new ZipInputStream(new URL(location).openStream())) {
		  for (ZipEntry ze = in.getNextEntry(); ze != null; ze = in.getNextEntry()) {
			  String name = ze.getName();
			  // Is the entry a directory?
			  if (ze.isDirectory())
				  // If so, continue to the next entry. Directories will be
				  // created later.
				  continue;
			  // If not, the contents of the file must be copied.
			  File destination;
			  // Does the file entry contain a directory?
			  int index = name.lastIndexOf('/');
			  if (index == -1)
				  // If not, just create the destination file.
				  destination = new File(file, name);
			  else {
				  // If so, make sure the directory exists.
				  File dir = new File(file, name.substring(0, index));
				  dir.mkdirs();
				  // Then create the destination file.
				  destination = new File(dir, name.substring(index));
			  }
			  // Now copy the contents of the file entry to the destination.
			  byte[] bytes = new byte[1024];
			  int read;
			  try (FileOutputStream out = new FileOutputStream(destination)) {
				  while ((read = in.read(bytes)) != -1)
					  out.write(bytes, 0, read);
			  }
			  in.closeEntry();
		  }
	  }
	  // Add the "reference" protocol back when running from a server so the
	  // framework sees the modified component.xml in the bundle data storage.
	  return context.installBundle(reference + file.toURI());
  }

  private void uninstallBundle(Bundle bundle) throws BundleException {
    installer.uninstallBundle(bundle);
  }

  private ServiceRegistration registerService(String className, Object service, Dictionary props) {
    ServiceRegistration sr = getContext().registerService(className, service, props);

    registeredServices.put(service, sr);

    return sr;
  }

  private void unregisterService(Object service) {
    ServiceRegistration sr = (ServiceRegistration) registeredServices.get(service);
    if (sr != null) {
      sr.unregister();
      registeredServices.remove(service);
    }
  }

  private void unregisterService(ServiceRegistration reg) {
    Enumeration e = registeredServices.keys();
    while (e.hasMoreElements()) {
      Object service = e.nextElement();
      if (reg == null || registeredServices.get(service) == reg) {
        unregisterService(service);
      }
    }
  }

  private void unregisterAllServices() {
    Enumeration e = registeredServices.keys();
    while (e.hasMoreElements()) {
      Object service = e.nextElement();
      unregisterService(service);
    }
  }

  private void log(String msg) {
    // System.out.println("[Declarative Service TC] " + msg);
  }

  public void sleep0(long millisToSleep) {
    long start = System.currentTimeMillis();
    do {
      try {
        Thread.sleep(50);
      } catch (InterruptedException e) {
      }
    } while (System.currentTimeMillis() - start < millisToSleep);
  }

  /**
   * Waits for the processing of the bundle declarative components by SCR. In
   * case the building of the DS components is synchronous the method does not
   * wait
   */
  private void waitBundleStart() {
    if (!synchronousBuild) {
      sleep0(2 * timeout);
    }
  }
  
  /*
   * This test requires the bundle to be installed as a directory using a 
   * reference URL. Otherwise, necessary file updates will not occur, and the 
   * test will no longer be valid.
   */
  public void disableTestComponentDefinitionReloadedOnBundleUpdateInDevModeWhenUsingWildcardInServiceComponentHeader() throws Exception {
	  // Enable component caching in DS.
	  System.setProperty("equinox.ds.dbstore", Boolean.TRUE.toString());
	  // Set dev mode.
	  System.setProperty("osgi.checkConfiguration", Boolean.TRUE.toString());
	  
	  String serviceName = "org.eclipse.equinox.ds.tests.tb26.Component";
	  // component.xml = conmponent1.xml initially.
	  Bundle b = installBundleAsDirectory("tb26");
	  try {
		  b.start();
		  waitBundleStart();
		  
		  // The component service should be Component1.
		  BundleContext context = getContext();
		  ServiceReference<?> ref = getServiceReference(serviceName);
		  assertNotNull("Component service not registered on start", ref);
		  Object service = context.getService(ref);
		  Class clazz = service.getClass();
		  assertEquals("Wrong Component service", "org.eclipse.equinox.ds.tests.tb26.impl.Component1", clazz.getName());
		  
		  // Update component.xml with component2.xml.
		  clazz.getMethod("update", (Class[])null).invoke(service, (Object[])null);
		  
		  // Force a component update in DS.
		  b.stop();
		  b.start();
		  waitBundleStart();
		  
		  // The component service should now be Component2.
		  ref = context.getServiceReference(serviceName);
		  assertNotNull("Component service not registered on restart", ref);
		  service = context.getService(ref);
		  clazz = service.getClass();
		  assertEquals("Wrong component service", "org.eclipse.equinox.ds.tests.tb26.impl.Component2", clazz.getName());
	  }
	  finally {
		  uninstallBundle(b);
	  }
  }
}
