Several smaller bugfixes and performance updates for resources. Added a test that demonstrates how to create a custom dependency (and did some refactorings to make that easier).

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@983639 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/core/pom.xml b/dependencymanager/core/pom.xml
index 3114a2d..8d0b40f 100644
--- a/dependencymanager/core/pom.xml
+++ b/dependencymanager/core/pom.xml
@@ -55,9 +55,9 @@
             <Export-Package>org.apache.felix.dm;version="3.0.0"</Export-Package>
             <Import-Package>!org.apache.felix.dm,*</Import-Package>
 	        <Private-Package>org.apache.felix.dm.impl,org.apache.felix.dm.impl.dependencies,org.apache.felix.dm.impl.tracker,org.apache.felix.dm.impl.metatype</Private-Package>
-	        <!-- Uncomment this line to include source code in the bundle.
+	        <!-- Uncomment this line to include source code in the bundle.-->
             <Include-Resource>src/main/java</Include-Resource>
-            -->
+            <!-- -->
           </instructions>
         </configuration>
       </plugin>
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/Dependency.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/Dependency.java
index f79f304..8388eeb 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/Dependency.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/Dependency.java
@@ -20,7 +20,6 @@
 
 import java.util.Dictionary;
 
-import org.apache.felix.dm.impl.dependencies.DependencyService;
 
 /**
  * Generic dependency for a service. A dependency can be required or not.
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyActivation.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyActivation.java
new file mode 100644
index 0000000..e15fd92
--- /dev/null
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyActivation.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm;
+
+public interface DependencyActivation {
+  public void start(DependencyService service);
+  public void stop(DependencyService service);
+}
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/DependencyService.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyService.java
similarity index 93%
rename from dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/DependencyService.java
rename to dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyService.java
index 0b8f37a..74a7fa4 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/DependencyService.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyService.java
@@ -16,10 +16,8 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.felix.dm.impl.dependencies;
+package org.apache.felix.dm;
 
-import org.apache.felix.dm.Dependency;
-import org.apache.felix.dm.Service;
 
 public interface DependencyService {
     /**
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/ResourceDependency.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/ResourceDependency.java
index aa76552..431003c 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/ResourceDependency.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/ResourceDependency.java
@@ -21,7 +21,7 @@
 import java.net.URL;
 
 
-public interface ResourceDependency extends Dependency, ResourceHandler {
+public interface ResourceDependency extends Dependency, ServiceComponentDependency, ResourceHandler {
     /**
      * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
      * dependency is added or removed. When you specify callbacks, the auto configuration 
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/ResourceHandler.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/ResourceHandler.java
index 9b758ca..ad83d39 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/ResourceHandler.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/ResourceHandler.java
@@ -26,6 +26,8 @@
 public interface ResourceHandler {
     /** Name of the property that's used to describe the filter condition for a resource. */
     public static final String FILTER = "filter";
+    /** Exact URL that this handler is looking for. Can be used instead of a filter to be very explicit about the resource you're looking for. */
+    public static final String URL = "url";
     
     /** The host part of the URL. */
     public static final String HOST = "host";
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/ResourceUtil.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/ResourceUtil.java
index 04d849e..ebb1c55 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/ResourceUtil.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/ResourceUtil.java
@@ -1,3 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
 package org.apache.felix.dm;
 
 import java.net.URL;
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/Service.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/Service.java
index 66397f5..b7bfd24 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/Service.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/Service.java
@@ -238,4 +238,39 @@
 	 * Returns the dependency manager associated with this service.
 	 */
 	public DependencyManager getDependencyManager();
+
+	/**
+	 * Configures auto configuration of injected classes in the service instance.
+	 * The following injections are currently performed, unless you explicitly
+	 * turn them off:
+	 * <dl>
+	 * <dt>BundleContext</dt><dd>the bundle context of the bundle</dd>
+     * <dt>ServiceRegistration</dt><dd>the service registration used to register your service</dd>
+     * <dt>DependencyManager</dt><dd>the dependency manager instance</dd>
+     * <dt>Service</dt><dd>the service instance of the dependency manager</dd>
+	 * </dl>
+	 * 
+	 * @param clazz the class (from the list above)
+	 * @param autoConfig <code>false</code> to turn off auto configuration
+	 */
+    public Service setAutoConfig(Class clazz, boolean autoConfig);
+    
+    /**
+     * Configures auto configuration of injected classes in the service instance.
+     * 
+     * @param clazz the class (from the list above)
+     * @param instanceName the name of the instance to inject the class into
+     * @see setAutoConfig(Class, boolean)
+     */
+    public Service setAutoConfig(Class clazz, String instanceName);
+
+    /**
+     * Returns the status of auto configuration of the specified class.
+     */
+    public boolean getAutoConfig(Class clazz);
+    
+    /**
+     * Returns the instance variable name of auto configuration of the specified class.
+     */
+    public String getAutoConfigInstance(Class clazz);
 }
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/AbstractDecorator.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/AbstractDecorator.java
index 09c7cc8..c889744 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/AbstractDecorator.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/AbstractDecorator.java
@@ -30,7 +30,9 @@
 import org.apache.felix.dm.Service;
 import org.apache.felix.dm.ServiceStateListener;
 import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.cm.ConfigurationException;
 
 public abstract class AbstractDecorator  {
@@ -233,4 +235,21 @@
         }
         m_services.clear();
     }    
+    
+    public void configureAutoConfigState(Service target, Service source) {
+        configureAutoConfigState(target, source, BundleContext.class);
+        configureAutoConfigState(target, source, ServiceRegistration.class);
+        configureAutoConfigState(target, source, DependencyManager.class);
+        configureAutoConfigState(target, source, Service.class);
+    }
+
+    private void configureAutoConfigState(Service target, Service source, Class clazz) {
+        String name = source.getAutoConfigInstance(clazz);
+        if (name != null) {
+            target.setAutoConfig(clazz, name);
+        }
+        else {
+            target.setAutoConfig(clazz, source.getAutoConfig(clazz));
+        }
+    }
 }
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/AdapterServiceImpl.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/AdapterServiceImpl.java
index 725c704..e4f6b25 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/AdapterServiceImpl.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/AdapterServiceImpl.java
@@ -85,6 +85,8 @@
                      .setService(m_adapteeInterface, ref)
                      .setRequired(true));
             
+            configureAutoConfigState(service, m_service);
+            
             for (int i = 0; i < dependencies.size(); i++) {
                 service.add(((Dependency) dependencies.get(i)).createCopy());
             }
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/AspectServiceImpl.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/AspectServiceImpl.java
index 2076ad4..26196bb 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/AspectServiceImpl.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/AspectServiceImpl.java
@@ -88,6 +88,8 @@
                 .setCallbacks(m_callbackObject, m_init, m_start, m_stop, m_destroy) // if not set, no effect
                 .add(getAspectDependency());
             
+            configureAutoConfigState(service, m_service);
+            
             for (int i = 0; i < dependencies.size(); i++) {
                 service.add(((Dependency) dependencies.get(i)).createCopy());
             }
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/FilterService.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/FilterService.java
index 896f529..05a1a9f 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/FilterService.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/FilterService.java
@@ -260,4 +260,22 @@
     public DependencyManager getDependencyManager() {
         return m_service.getDependencyManager();
     }
+
+    public Service setAutoConfig(Class clazz, boolean autoConfig) {
+        m_service.setAutoConfig(clazz, autoConfig);
+        return this;
+    }
+
+    public Service setAutoConfig(Class clazz, String instanceName) {
+        m_service.setAutoConfig(clazz, instanceName);
+        return this;
+    }
+    
+    public boolean getAutoConfig(Class clazz) {
+        return m_service.getAutoConfig(clazz);
+    }
+    
+    public String getAutoConfigInstance(Class clazz) {
+        return m_service.getAutoConfigInstance(clazz);
+    };
 }
\ No newline at end of file
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/ServiceImpl.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/ServiceImpl.java
index d72c18c..9e1966b 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/ServiceImpl.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/ServiceImpl.java
@@ -33,13 +33,13 @@
 import java.util.Properties;
 
 import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyActivation;
 import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.DependencyService;
 import org.apache.felix.dm.Service;
 import org.apache.felix.dm.ServiceComponent;
 import org.apache.felix.dm.ServiceComponentDependency;
 import org.apache.felix.dm.ServiceStateListener;
-import org.apache.felix.dm.impl.dependencies.DependencyActivation;
-import org.apache.felix.dm.impl.dependencies.DependencyService;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceRegistration;
 
@@ -768,13 +768,24 @@
     	}
     }
 
-    public void setAutoConfig(Class clazz, boolean autoConfig) {
+    public synchronized Service setAutoConfig(Class clazz, boolean autoConfig) {
         m_autoConfig.put(clazz, Boolean.valueOf(autoConfig));
+        return this;
     }
     
-    public void setAutoConfig(Class clazz, String instanceName) {
+    public synchronized Service setAutoConfig(Class clazz, String instanceName) {
         m_autoConfig.put(clazz, Boolean.valueOf(instanceName != null));
         m_autoConfigInstance.put(clazz, instanceName);
+        return this;
+    }
+    
+    public boolean getAutoConfig(Class clazz) {
+        Boolean result = (Boolean) m_autoConfig.get(clazz);
+        return (result != null && result.booleanValue());
+    }
+    
+    public String getAutoConfigInstance(Class clazz) {
+        return (String) m_autoConfigInstance.get(clazz);
     }
     
     private void configureService(State state) {
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/BundleDependencyImpl.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/BundleDependencyImpl.java
index 0482cec..07b90bb 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/BundleDependencyImpl.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/BundleDependencyImpl.java
@@ -26,6 +26,7 @@
 
 import org.apache.felix.dm.BundleDependency;
 import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyService;
 import org.apache.felix.dm.ServiceComponentDependency;
 import org.apache.felix.dm.impl.DefaultNullObject;
 import org.apache.felix.dm.impl.InvocationUtil;
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ConfigurationDependencyImpl.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ConfigurationDependencyImpl.java
index 267cd85..3ffb0d4 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ConfigurationDependencyImpl.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ConfigurationDependencyImpl.java
@@ -28,6 +28,8 @@
 
 import org.apache.felix.dm.ConfigurationDependency;
 import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyActivation;
+import org.apache.felix.dm.DependencyService;
 import org.apache.felix.dm.PropertyMetaData;
 import org.apache.felix.dm.ServiceComponentDependency;
 import org.apache.felix.dm.impl.InvocationUtil;
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/DependencyActivation.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/DependencyActivation.java
deleted file mode 100644
index 189e0a0..0000000
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/DependencyActivation.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package org.apache.felix.dm.impl.dependencies;
-
-public interface DependencyActivation {
-  public void start(DependencyService service);
-  public void stop(DependencyService service);
-}
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/DependencyBase.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/DependencyBase.java
index 70217c5..a3a1f7a 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/DependencyBase.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/DependencyBase.java
@@ -1,6 +1,7 @@
 package org.apache.felix.dm.impl.dependencies;
 
 import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyActivation;
 import org.apache.felix.dm.impl.Logger;
 
 public abstract class DependencyBase implements Dependency, DependencyActivation {
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ResourceDependencyImpl.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ResourceDependencyImpl.java
index df09ddc..d074404 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ResourceDependencyImpl.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ResourceDependencyImpl.java
@@ -26,6 +26,8 @@
 import java.util.Properties;
 
 import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyActivation;
+import org.apache.felix.dm.DependencyService;
 import org.apache.felix.dm.ResourceDependency;
 import org.apache.felix.dm.ResourceHandler;
 import org.apache.felix.dm.Service;
@@ -95,10 +97,14 @@
 	        }
 	    }
 	    if (needsStarting) {
-	        Properties props = null;
-	        if (m_resourceFilter != null) {
+	        Dictionary props = null;
+	        if (m_trackedResource != null) {
+                props = new Properties();
+                props.put(ResourceHandler.URL, m_trackedResource);
+	        }
+	        else if (m_resourceFilter != null) {
 	            props = new Properties();
-	            props.setProperty(ResourceHandler.FILTER, m_resourceFilter);
+	            props.put(ResourceHandler.FILTER, m_resourceFilter);
 	        }
 	        m_registration = m_context.registerService(ResourceHandler.class.getName(), this, props);
 	    }
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ServiceDependencyImpl.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ServiceDependencyImpl.java
index 76a3306..2e87c95 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ServiceDependencyImpl.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ServiceDependencyImpl.java
@@ -32,6 +32,7 @@
 import java.util.Set;
 
 import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyService;
 import org.apache.felix.dm.ServiceComponentDependency;
 import org.apache.felix.dm.ServiceDependency;
 import org.apache.felix.dm.impl.DefaultNullObject;
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/TemporalServiceDependencyImpl.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/TemporalServiceDependencyImpl.java
index 3a54c4b..6aeafc3 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/TemporalServiceDependencyImpl.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/TemporalServiceDependencyImpl.java
@@ -16,6 +16,7 @@
 import java.lang.reflect.Proxy;
 
 import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyService;
 import org.apache.felix.dm.TemporalServiceDependency;
 import org.apache.felix.dm.impl.Logger;
 import org.osgi.framework.BundleContext;
diff --git a/dependencymanager/test/src/test/java/org/apache/felix/dm/test/CustomDependencyTest.java b/dependencymanager/test/src/test/java/org/apache/felix/dm/test/CustomDependencyTest.java
new file mode 100644
index 0000000..83081aa
--- /dev/null
+++ b/dependencymanager/test/src/test/java/org/apache/felix/dm/test/CustomDependencyTest.java
@@ -0,0 +1,202 @@
+package org.apache.felix.dm.test;
+
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.provision;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.List;
+
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyActivation;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.DependencyService;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.Configuration;
+import org.ops4j.pax.exam.junit.JUnit4TestRunner;
+import org.osgi.framework.BundleContext;
+
+
+@RunWith(JUnit4TestRunner.class)
+public class CustomDependencyTest extends Base {
+    @Configuration
+    public static Option[] configuration() {
+        return options(
+            provision(
+                mavenBundle().groupId("org.osgi").artifactId("org.osgi.compendium").version("4.1.0"),
+                mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.dependencymanager").versionAsInProject()
+            )
+        );
+    }    
+
+    @Test
+    public void testCustomDependency(BundleContext context) {
+        Ensure e = new Ensure();
+        DependencyManager dm = new DependencyManager(context);
+        
+        // create a toggle that can be used to turn on/off our custom dependency
+        Toggle toggle = new Toggle();
+        
+        // create a service that has our custom dependency as its only dependency
+        dm.add(dm.createService()
+            .setImplementation(new ServiceImpl(e))
+            .add(new CustomDependency(toggle))
+            );
+        
+        // make the toggle, therefore the dependency, therefore the service available
+        toggle.setAvailable(true);
+        e.waitForStep(1, 1000);
+        
+        // make the toggle unavailable again
+        toggle.setAvailable(false);
+        e.waitForStep(2, 1000);
+    }
+    
+    /** A toggle implementation that invokes a callback on every change. */
+    public static class Toggle {
+        private boolean m_isAvailable;
+        private Runnable m_runnable;
+        
+        public boolean isAvailable() {
+            return m_isAvailable;
+        }
+        public void setAvailable(boolean isAvailable) {
+            boolean changed = m_isAvailable != isAvailable;
+            m_isAvailable = isAvailable;
+            Runnable r = m_runnable;
+            if (r != null && changed) {
+                r.run();
+            }
+        }
+        public void setRunnable(Runnable runnable) {
+            m_runnable = runnable;
+        }
+    }
+    
+    /** Our custom dependency, which is less configurable than most, but that's okay for this test. */
+    public static class CustomDependency implements Dependency, DependencyActivation, Runnable {
+        private final Toggle m_toggle;
+        private final List m_services = new ArrayList();
+
+        public CustomDependency(Toggle toggle) {
+            m_toggle = toggle;
+        }
+        
+        public Dependency createCopy() {
+            return new CustomDependency(m_toggle);
+        }
+
+        public Object getAutoConfigInstance() {
+            return "" + m_toggle.isAvailable();
+        }
+
+        public String getAutoConfigName() {
+            return null;
+        }
+
+        public Class getAutoConfigType() {
+            return String.class;
+        }
+
+        public Dictionary getProperties() {
+            return null;
+        }
+        
+        public void run() {
+            // invoked on every change
+            if (m_toggle.isAvailable()) {
+                Object[] services = m_services.toArray();
+                for (int i = 0; i < services.length; i++) {
+                    DependencyService ds = (DependencyService) services[i];
+                    ds.dependencyAvailable(this);
+                    if (!isRequired()) {
+                        invokeAdded(ds);
+                    }
+                }
+            }
+            else {
+                Object[] services = m_services.toArray();
+                for (int i = 0; i < services.length; i++) {
+                    DependencyService ds = (DependencyService) services[i];
+                    ds.dependencyUnavailable(this);
+                    if (!isRequired()) {
+                        invokeRemoved(ds);
+                    }
+                }
+            }
+        }
+
+        public void invokeAdded(DependencyService service) {
+            invoke(service, "added");
+        }
+
+        public void invokeRemoved(DependencyService service) {
+            invoke(service, "removed");
+        }
+        
+        public void invoke(DependencyService dependencyService, String name) {
+            if (name != null) {
+                dependencyService.invokeCallbackMethod(getCallbackInstances(dependencyService), name,
+                  new Class[][] {{String.class}, {Object.class}, {}},
+                  new Object[][] {{getAutoConfigInstance()}, {getAutoConfigInstance()}, {}}
+                );
+            }
+        }
+        
+        private synchronized Object[] getCallbackInstances(DependencyService dependencyService) {
+            return dependencyService.getCompositionInstances();
+        }
+
+        public boolean isAutoConfig() {
+            return true;
+        }
+
+        public boolean isAvailable() {
+            return m_toggle.isAvailable();
+        }
+
+        public boolean isInstanceBound() {
+            return false;
+        }
+
+        public boolean isPropagated() {
+            return false;
+        }
+
+        public boolean isRequired() {
+            return true;
+        }
+
+        public void start(DependencyService service) {
+            synchronized (this) {
+                m_services.add(service);
+            }
+            m_toggle.setRunnable(this);
+        }
+
+        public void stop(DependencyService service) {
+            synchronized (this) {
+                m_services.remove(service);
+            }
+            m_toggle.setRunnable(null);
+        }
+    }
+    
+    public static class ServiceImpl {
+        private final Ensure m_e;
+        public ServiceImpl(Ensure e) {
+            m_e = e;
+        }
+        public void init() {
+            System.out.println("init");
+            m_e.step(1);
+        }
+        public void destroy() {
+            System.out.println("destroy");
+            m_e.step(2);
+        }
+    }
+}