Fix FELIX-4231 Provide service binding interceptors

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1523996 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/runtime/core-it/ipojo-core-service-dependency-interceptor-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/components/FooConsumer.java b/ipojo/runtime/core-it/ipojo-core-service-dependency-interceptor-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/components/FooConsumer.java
index 349cb3c..11ce322 100644
--- a/ipojo/runtime/core-it/ipojo-core-service-dependency-interceptor-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/components/FooConsumer.java
+++ b/ipojo/runtime/core-it/ipojo-core-service-dependency-interceptor-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/components/FooConsumer.java
@@ -21,8 +21,10 @@
 
 import org.apache.felix.ipojo.annotations.*;
 import org.apache.felix.ipojo.runtime.core.test.services.CheckService;
+import org.apache.felix.ipojo.runtime.core.test.services.Enhanced;
 import org.apache.felix.ipojo.runtime.core.test.services.FooService;
 
+import java.util.Dictionary;
 import java.util.Map;
 import java.util.Properties;
 
@@ -33,21 +35,24 @@
 @Provides
 public class FooConsumer implements CheckService {
 
-    @Requires(id= "foo", policy = "dynamic-priority")
+    @Requires(id= "foo", policy = "dynamic-priority", proxy = false)
     private FooService foo;
 
     private Map<String, Object> props;
 
     @Override
     public boolean check() {
-        return foo != null;
+        return foo.foo();
     }
 
     @Override
-    public Properties getProps() {
+    public Dictionary getProps() {
         Properties properties =  new Properties();
         properties.put("props", props);
         properties.put("grade", foo.getGrade());
+        if (foo instanceof Enhanced) {
+            properties.put("enhanced", ((Enhanced) foo).enhance());
+        }
         return properties;
     }
 
diff --git a/ipojo/runtime/core-it/ipojo-core-service-dependency-interceptor-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/interceptors/EnhancingBindingInterceptor.java b/ipojo/runtime/core-it/ipojo-core-service-dependency-interceptor-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/interceptors/EnhancingBindingInterceptor.java
new file mode 100644
index 0000000..69a5382
--- /dev/null
+++ b/ipojo/runtime/core-it/ipojo-core-service-dependency-interceptor-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/interceptors/EnhancingBindingInterceptor.java
@@ -0,0 +1,77 @@
+/*
+ * 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.ipojo.runtime.core.test.interceptors;
+
+import org.apache.felix.ipojo.annotations.Component;
+import org.apache.felix.ipojo.annotations.Provides;
+import org.apache.felix.ipojo.annotations.ServiceProperty;
+import org.apache.felix.ipojo.dependency.interceptors.DefaultDependencyInterceptor;
+import org.apache.felix.ipojo.dependency.interceptors.ServiceBindingInterceptor;
+import org.apache.felix.ipojo.runtime.core.test.services.Enhanced;
+import org.apache.felix.ipojo.util.DependencyModel;
+import org.osgi.framework.ServiceReference;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Arrays;
+import java.util.HashMap;
+
+/**
+ * A binding interceptor enhancing the service object.
+ */
+@Component(immediate = true)
+@Provides
+public class EnhancingBindingInterceptor extends DefaultDependencyInterceptor implements ServiceBindingInterceptor {
+
+    @ServiceProperty
+    private String target;
+
+    private HashMap<ServiceReference, Object> deps = new HashMap<ServiceReference, Object>();
+
+    @Override
+    public <S> S getService(DependencyModel dependency, ServiceReference<S> reference, S service) {
+        S proxy =  (S) Proxy.newProxyInstance(this.getClass().getClassLoader(),
+                new Class[]{dependency.getSpecification(), Enhanced.class}, new Interceptor(service));
+        deps.put(reference, proxy);
+        return proxy;
+    }
+
+    @Override
+    public <S> void ungetService(DependencyModel dependency, ServiceReference<S> reference) {
+        deps.remove(reference);
+    }
+
+    private class Interceptor implements InvocationHandler {
+
+        private final Object service;
+
+        public Interceptor(Object service) {
+            this.service = service;
+        }
+
+        @Override
+        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+            if (method.getName().equals("enhance")) {
+                return "yo!";
+            }
+            return method.invoke(service, args);
+        }
+    }
+}
diff --git a/ipojo/runtime/core-it/ipojo-core-service-dependency-interceptor-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/interceptors/ProxyBindingInterceptor.java b/ipojo/runtime/core-it/ipojo-core-service-dependency-interceptor-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/interceptors/ProxyBindingInterceptor.java
new file mode 100644
index 0000000..79a623d
--- /dev/null
+++ b/ipojo/runtime/core-it/ipojo-core-service-dependency-interceptor-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/interceptors/ProxyBindingInterceptor.java
@@ -0,0 +1,100 @@
+/*
+ * 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.ipojo.runtime.core.test.interceptors;
+
+import org.apache.felix.ipojo.annotations.Component;
+import org.apache.felix.ipojo.annotations.Provides;
+import org.apache.felix.ipojo.annotations.ServiceProperty;
+import org.apache.felix.ipojo.dependency.interceptors.DefaultDependencyInterceptor;
+import org.apache.felix.ipojo.dependency.interceptors.ServiceBindingInterceptor;
+import org.apache.felix.ipojo.runtime.core.test.services.CheckService;
+import org.apache.felix.ipojo.util.DependencyModel;
+import org.osgi.framework.ServiceReference;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.*;
+
+/**
+ * A binding interceptor generating a proxy to monitor the invocations.
+ */
+@Component
+@Provides
+public class ProxyBindingInterceptor extends DefaultDependencyInterceptor implements ServiceBindingInterceptor, CheckService {
+
+    @ServiceProperty
+    private String target;
+
+    private HashMap<ServiceReference, Object> deps = new HashMap<ServiceReference, Object>();
+    private Dictionary data = new Hashtable();
+
+
+    private void increment(String key) {
+        if (data.get(key) == null) {
+            data.put(key, 1);
+        } else {
+            data.put(key, (Integer) data.get(key) + 1);
+        }
+    }
+
+    @Override
+    public <S> S getService(DependencyModel dependency, ServiceReference<S> reference, S service) {
+        S proxy =  (S) Proxy.newProxyInstance(this.getClass().getClassLoader(),
+                new Class[]{dependency.getSpecification()}, new Interceptor(service));
+        deps.put(reference, proxy);
+        increment("bound");
+        return proxy;
+    }
+
+    @Override
+    public <S> void ungetService(DependencyModel dependency, ServiceReference<S> reference) {
+        deps.remove(reference);
+        increment("unbound");
+    }
+
+    @Override
+    public boolean check() {
+        return true;
+    }
+
+    @Override
+    public Dictionary getProps() {
+        return data;
+    }
+
+    private class Interceptor implements InvocationHandler {
+
+        private final Object service;
+
+        public Interceptor(Object service) {
+            this.service = service;
+        }
+
+        @Override
+        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+            increment(method.getName());
+
+            if (method.getName().equals("toString")) {
+                return this.toString();
+            }
+            return method.invoke(service, args);
+        }
+    }
+}
diff --git a/ipojo/runtime/core-it/ipojo-core-service-dependency-interceptor-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/services/CheckService.java b/ipojo/runtime/core-it/ipojo-core-service-dependency-interceptor-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/services/CheckService.java
index 610c62d..2ed95ff 100644
--- a/ipojo/runtime/core-it/ipojo-core-service-dependency-interceptor-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/services/CheckService.java
+++ b/ipojo/runtime/core-it/ipojo-core-service-dependency-interceptor-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/services/CheckService.java
@@ -19,7 +19,7 @@
 
 package org.apache.felix.ipojo.runtime.core.test.services;
 
-import java.util.Properties;
+import java.util.Dictionary;
 
 public interface CheckService {
     
@@ -27,6 +27,6 @@
 	
 	public boolean check();
 	
-	public Properties getProps();
+	public Dictionary getProps();
 
 }
diff --git a/ipojo/runtime/core-it/ipojo-core-service-dependency-interceptor-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/services/Enhanced.java b/ipojo/runtime/core-it/ipojo-core-service-dependency-interceptor-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/services/Enhanced.java
new file mode 100644
index 0000000..32333c4
--- /dev/null
+++ b/ipojo/runtime/core-it/ipojo-core-service-dependency-interceptor-test/src/main/java/org/apache/felix/ipojo/runtime/core/test/services/Enhanced.java
@@ -0,0 +1,27 @@
+/*
+ * 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.ipojo.runtime.core.test.services;
+
+/**
+ * An interface added on the fly to the service object.
+ */
+public interface Enhanced {
+
+    public String enhance();
+}
diff --git a/ipojo/runtime/core-it/ipojo-core-service-dependency-interceptor-test/src/test/java/org/apache/felix/ipojo/runtime/core/test/dependencies/TestBindingInterceptors.java b/ipojo/runtime/core-it/ipojo-core-service-dependency-interceptor-test/src/test/java/org/apache/felix/ipojo/runtime/core/test/dependencies/TestBindingInterceptors.java
new file mode 100644
index 0000000..b20ad3e
--- /dev/null
+++ b/ipojo/runtime/core-it/ipojo-core-service-dependency-interceptor-test/src/test/java/org/apache/felix/ipojo/runtime/core/test/dependencies/TestBindingInterceptors.java
@@ -0,0 +1,177 @@
+/*
+ * 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.ipojo.runtime.core.test.dependencies;
+
+import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.runtime.core.test.services.CheckService;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.ServiceReference;
+
+import java.util.Properties;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+/**
+ * Checks binding interceptors.
+ */
+public class TestBindingInterceptors extends Common {
+
+    private ComponentInstance provider;
+
+    @Before
+    public void setUp() {
+        provider = ipojoHelper.createComponentInstance("org.apache.felix.ipojo.runtime.core.test" +
+                ".components.FooProvider");
+    }
+
+    @Test
+    public void testProxyBindingInterceptorBeforeInstanceCreation() {
+        // Create the interceptor
+        Properties configuration = new Properties();
+        configuration.put("target", "(dependency.id=foo)");
+        ipojoHelper.createComponentInstance("org.apache.felix.ipojo.runtime.core.test.interceptors" +
+                ".ProxyBindingInterceptor", configuration);
+
+        // Create the FooConsumer
+        ComponentInstance instance = ipojoHelper.createComponentInstance("org.apache.felix.ipojo.runtime.core.test" +
+                ".components" +
+                ".FooConsumer");
+
+        ServiceReference ref = osgiHelper.waitForService(CheckService.class.getName(),
+                "(instance.name=" + instance.getInstanceName() + ")",
+                1000, true);
+        CheckService check = (CheckService) osgiHelper.getServiceObject(ref);
+
+        assertThat(check.check());
+
+        // Extract monitored data
+        CheckService checkService = osgiHelper.getServiceObject(CheckService.class,
+                "(factory.name=org.apache.felix.ipojo.runtime.core.test.interceptors.ProxyBindingInterceptor)");
+
+        assertThat(checkService).isNotNull();
+        assertThat(checkService.getProps().get("bound")).isEqualTo(1);
+        assertThat(checkService.getProps().get("foo")).isEqualTo(1);
+
+        provider.dispose();
+
+        assertThat(checkService.getProps().get("bound")).isEqualTo(1);
+        assertThat(checkService.getProps().get("unbound")).isEqualTo(1);
+    }
+
+    /**
+     * Same as previous but the interceptor arrives after the instance.
+     */
+    @Test
+    public void testProxyBindingInterceptorAfterInstanceCreation() {
+        // Create the FooConsumer
+        ComponentInstance instance = ipojoHelper.createComponentInstance("org.apache.felix.ipojo.runtime.core.test" +
+                ".components" +
+                ".FooConsumer");
+
+        ServiceReference ref = osgiHelper.waitForService(CheckService.class.getName(),
+                "(instance.name=" + instance.getInstanceName() + ")",
+                1000, true);
+        CheckService check = (CheckService) osgiHelper.getServiceObject(ref);
+
+        assertThat(check.check());
+
+        // Create the interceptor
+        Properties configuration = new Properties();
+        configuration.put("target", "(dependency.id=foo)");
+        ipojoHelper.createComponentInstance("org.apache.felix.ipojo.runtime.core.test.interceptors" +
+                ".ProxyBindingInterceptor", configuration);
+
+        // Extract monitored data
+        CheckService checkService = osgiHelper.getServiceObject(CheckService.class,
+                "(factory.name=org.apache.felix.ipojo.runtime.core.test.interceptors.ProxyBindingInterceptor)");
+
+        // Nothing was intercepted.
+        assertThat(checkService).isNotNull();
+        assertThat(checkService.getProps().get("bound")).isNull();
+        assertThat(checkService.getProps().get("foo")).isNull();
+
+        // Force rebinding.
+        provider.stop();
+        provider.start();
+
+        check.check();
+
+        // Things should have been intercepted
+        assertThat(checkService).isNotNull();
+        assertThat(checkService.getProps().get("bound")).isEqualTo(1);
+        assertThat(checkService.getProps().get("foo")).isEqualTo(1);
+
+        provider.dispose();
+
+        // Two unget calls, as we intercepted the first one (provider.stop()).
+        assertThat(checkService.getProps().get("unbound")).isEqualTo(2);
+    }
+
+    /**
+     * Checks that two interceptors are called sequentially.
+     */
+    @Test
+    public void testWithTwoInterceptors() {
+        // First, only one interceptor.
+
+        // Create the interceptor
+        Properties configuration = new Properties();
+        configuration.put("target", "(dependency.id=foo)");
+        ipojoHelper.createComponentInstance("org.apache.felix.ipojo.runtime.core.test.interceptors" +
+                ".ProxyBindingInterceptor", configuration);
+
+
+        // Create the FooConsumer
+        ComponentInstance instance = ipojoHelper.createComponentInstance("org.apache.felix.ipojo.runtime.core.test" +
+                ".components" +
+                ".FooConsumer");
+
+        ServiceReference ref = osgiHelper.waitForService(CheckService.class.getName(),
+                "(instance.name=" + instance.getInstanceName() + ")",
+                1000, true);
+        CheckService check = (CheckService) osgiHelper.getServiceObject(ref);
+
+        assertThat(check.check());
+
+        // Create the second interceptor, but it's too late to modify the first binding.
+        configuration = new Properties();
+        configuration.put("target", "(dependency.id=foo)");
+        ipojoHelper.createComponentInstance("org.apache.felix.ipojo.runtime.core.test.interceptors" +
+                ".EnhancingBindingInterceptor", configuration);
+
+        assertThat(check.getProps().get("enhanced")).isNull();
+
+        // Extract monitored data
+        CheckService checkService = osgiHelper.getServiceObject(CheckService.class,
+                "(factory.name=org.apache.felix.ipojo.runtime.core.test.interceptors.ProxyBindingInterceptor)");
+
+        assertThat(checkService).isNotNull();
+        assertThat(checkService.getProps().get("bound")).isEqualTo(1);
+        assertThat(checkService.getProps().get("foo")).isEqualTo(1);
+
+        // Force re-binding.
+        provider.stop();
+        provider.start();
+
+        assertThat(check.getProps().get("enhanced")).isNotNull();
+    }
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/dependency/impl/ServiceReferenceManager.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/dependency/impl/ServiceReferenceManager.java
index 21f9f22..96e5ed7 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/dependency/impl/ServiceReferenceManager.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/dependency/impl/ServiceReferenceManager.java
@@ -20,6 +20,7 @@
 package org.apache.felix.ipojo.dependency.impl;
 
 import org.apache.felix.ipojo.Factory;
+import org.apache.felix.ipojo.dependency.interceptors.ServiceBindingInterceptor;
 import org.apache.felix.ipojo.dependency.interceptors.ServiceRankingInterceptor;
 import org.apache.felix.ipojo.dependency.interceptors.ServiceTrackingInterceptor;
 import org.apache.felix.ipojo.dependency.interceptors.TransformedServiceReference;
@@ -71,10 +72,20 @@
      */
     private ServiceRankingInterceptor m_rankingInterceptor;
     /**
-     * Service interceptor tracker.
+     * Service Ranking Interceptor trackers.
      */
     private Tracker m_rankingInterceptorTracker;
+
+    /**
+     * Service Tracking Interceptor trackers.
+     */
     private Tracker m_trackingInterceptorTracker;
+
+    /**
+     * Service Binding Interceptor trackers.
+     */
+    private Tracker m_bindingInterceptorTracker;
+
     /**
      * The set of tracking interceptors.
      * TODO this set should be sorted according to the OSGi ranking policy.
@@ -84,6 +95,13 @@
             LinkedList<ServiceTrackingInterceptor>();
 
     /**
+     * The set of binding interceptors.
+     * TODO this set should be sorted according to the OSGi ranking policy.
+     */
+    private LinkedList<ServiceBindingInterceptor> m_bindingInterceptors = new
+            LinkedList<ServiceBindingInterceptor>();
+
+    /**
      * Creates the service reference manager.
      *
      * @param dep        the dependency
@@ -191,6 +209,42 @@
                     }
                 });
         m_rankingInterceptorTracker.open();
+
+        m_bindingInterceptorTracker = new Tracker(m_dependency.getBundleContext(),
+                ServiceBindingInterceptor.class.getName(),
+                new TrackerCustomizer() {
+
+                    public boolean addingService(ServiceReference reference) {
+                        return DependencyProperties.match(reference, m_dependency);
+                    }
+
+                    public void addedService(ServiceReference reference) {
+                        ServiceBindingInterceptor interceptor = (ServiceBindingInterceptor) m_bindingInterceptorTracker
+                                .getService(reference);
+                        if (interceptor != null) {
+                            addBindingInterceptor(interceptor);
+                        } else {
+                            m_dependency.getComponentInstance().getFactory().getLogger().log(Log.ERROR,
+                                    "Cannot retrieve the interceptor object from service reference " + reference
+                                            .getProperty(Constants.SERVICE_ID) + " - " + reference.getProperty
+                                            (Factory.INSTANCE_NAME_PROPERTY));
+                        }
+                    }
+
+                    public void modifiedService(ServiceReference reference, Object service) {
+                        // Not supported.
+                    }
+
+                    public void removedService(ServiceReference reference, Object service) {
+                        if (service != null && service instanceof ServiceBindingInterceptor &&
+                                m_bindingInterceptors.contains(service)
+                                ) {
+                            removeBindingInterceptor((ServiceBindingInterceptor) service);
+                        }
+                    }
+                }
+        );
+        m_bindingInterceptorTracker.open();
     }
 
     private void addTrackingInterceptor(ServiceTrackingInterceptor interceptor) {
@@ -220,6 +274,53 @@
         m_dependency.onChange(changeset);
     }
 
+    private void addBindingInterceptor(ServiceBindingInterceptor interceptor) {
+        // A new interceptor arrives, open it.
+        // Binding interceptor cannot modify existing bindings.
+        try {
+            m_dependency.acquireWriteLockIfNotHeld();
+            m_bindingInterceptors.add(interceptor);
+            interceptor.open(m_dependency);
+        } finally {
+            m_dependency.releaseWriteLockIfHeld();
+        }
+    }
+
+    private void removeBindingInterceptor(ServiceBindingInterceptor interceptor) {
+        try {
+            m_dependency.acquireWriteLockIfNotHeld();
+            m_bindingInterceptors.remove(interceptor);
+            interceptor.close(m_dependency);
+        } finally {
+            m_dependency.releaseWriteLockIfHeld();
+        }
+    }
+
+    public Object weavingServiceBinding(DependencyModel.ServiceBindingHolder sbh) {
+        Object svc = sbh.service;
+        try {
+            m_dependency.acquireReadLockIfNotHeld();
+            for (ServiceBindingInterceptor interceptor : m_bindingInterceptors) {
+                // Interceptor are not allowed to return null.
+                svc = interceptor.getService(m_dependency, sbh.reference, svc);
+            }
+        } finally {
+            m_dependency.releaseReadLockIfHeld();
+        }
+        return svc;
+    }
+
+    public void unweavingServiceBinding(DependencyModel.ServiceBindingHolder sbh) {
+        try {
+            m_dependency.acquireReadLockIfNotHeld();
+            for (ServiceBindingInterceptor interceptor : m_bindingInterceptors) {
+                interceptor.ungetService(m_dependency, sbh.reference);
+            }
+        } finally {
+            m_dependency.releaseReadLockIfHeld();
+        }
+    }
+
     private ChangeSet computeChangesInMatchingServices() {
         if (m_dependency.getTracker() == null || m_dependency.getTracker().getServiceReferences() == null) {
             // Tracker closed, no problem
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/dependency/interceptors/ServiceBindingInterceptor.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/dependency/interceptors/ServiceBindingInterceptor.java
new file mode 100644
index 0000000..af7c049
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/dependency/interceptors/ServiceBindingInterceptor.java
@@ -0,0 +1,70 @@
+/*
+ * 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.ipojo.dependency.interceptors;
+
+import org.apache.felix.ipojo.util.DependencyModel;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * A service to modify / monitor the service bindings.
+ * This service is notified every time the dependency is weaving a new service bindings on un-weaves an existing one.
+ *
+ * Several binding interceptors can be plugged to the same service dependency. In this case,
+ * a chain is created where all interceptor are called in a sequence.
+ *
+ * A binding interceptor cannot modify the existing bindings.
+ *
+ * Obviously an interceptor can be plugged to several dependencies.
+ *
+ * @since 1.10.2
+ */
+public interface ServiceBindingInterceptor extends DependencyInterceptor {
+
+    /**
+     * Notification method when a dependency is weaving a new service binding.
+     * The interceptor can modify the service object. It must <strong>never</strong> return a {@code null} object,
+     * but the receive service object if it does not want to do anything with the service object.
+     *
+     * When the interceptor <em>modifies</em> the service object, the returned object <strong>must</strong> be
+     * compatible with the dependency specification.
+     *
+     * The received service object may already have been <em>wrapped</em> by binding interceptors called before the
+     * current one.
+     *
+     * @param dependency the dependency
+     * @param reference the service reference bound
+     * @param service the service object
+     * @param <S> the service specification
+     * @return the service object to be injected within the component. Must never be {@code null}.
+     */
+    public <S> S getService(DependencyModel dependency, ServiceReference<S> reference, S service);
+
+    /**
+     * Notification method when a dependency is un-weaving a service binding.
+     * The interceptor must released all objects related to this service binding.
+     *
+     * @param dependency the dependency
+     * @param reference the unbound service reference
+     * @param <S> the service specification
+     */
+    public <S> void ungetService(DependencyModel dependency, ServiceReference<S> reference);
+
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/dependency/interceptors/ServiceRankingInterceptor.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/dependency/interceptors/ServiceRankingInterceptor.java
index 2cf1689..e349c1b 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/dependency/interceptors/ServiceRankingInterceptor.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/dependency/interceptors/ServiceRankingInterceptor.java
@@ -31,6 +31,8 @@
  *
  * This interceptors is called to compute the selected set of services from the matching set,
  * i.e. the set of services that matching the filter (actually accepted by the tracking interceptors).
+ *
+ * @since 1.10.1
  */
 public interface ServiceRankingInterceptor extends DependencyInterceptor {
 
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/dependency/interceptors/ServiceTrackingInterceptor.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/dependency/interceptors/ServiceTrackingInterceptor.java
index 1885478..5a7a316 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/dependency/interceptors/ServiceTrackingInterceptor.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/dependency/interceptors/ServiceTrackingInterceptor.java
@@ -31,7 +31,10 @@
  * a chain is created where all interceptor can influence the next one. If the dependency has a filter,
  * a tracking interceptor using this filter is the last interceptor of the chain.
  *
- * Obviously an interceptor can be plugged to several interceptors.
+ * Obviously an interceptor can be plugged to several dependencies. Conversely, several tracking interceptor can be
+ * plugged to one dependency.
+ *
+ * @since 1.10.1
  */
 public interface ServiceTrackingInterceptor extends DependencyInterceptor {
 
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/Dependency.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/Dependency.java
index 53188d2..570471a 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/Dependency.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/Dependency.java
@@ -697,7 +697,7 @@
                     } else {
                         //  Use a reflective construction to avoid class cast exception. This method allows setting the component type.
                         Object[] objs = (Object[]) Array.newInstance(getSpecification(), refs.length);
-                        for (int i = 0; refs != null && i < refs.length; i++) {
+                        for (int i = 0; i < refs.length; i++) {
                             ServiceReference ref = refs[i];
                             objs[i] = getService(ref);
                         }
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandlerDescription.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandlerDescription.java
index 720d7f1..b5cf947 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandlerDescription.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/dependency/DependencyHandlerDescription.java
@@ -18,9 +18,6 @@
  */
 package org.apache.felix.ipojo.handlers.dependency;
 
-import java.util.Iterator;
-import java.util.List;
-
 import org.apache.felix.ipojo.Factory;
 import org.apache.felix.ipojo.architecture.HandlerDescription;
 import org.apache.felix.ipojo.metadata.Attribute;
@@ -29,6 +26,8 @@
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceReference;
 
+import java.util.List;
+
 /**
  * Dependency Handler Description.
  * 
@@ -40,26 +39,6 @@
      * Dependencies managed by the dependency handler.
      */
     private DependencyDescription[] m_dependencies = new DependencyDescription[0];
-    
-    // TODO Define the DependencyStateListener Interface (in ipojo utils)
-    
-    // TODO Add the list of listener.
-    
-    // TODO Add register listener method.
-    
-    // TODO Add unregister listener method.
-    
-    // TODO Implement the validate method.
-    
-    // TODO Implement the invalidate method.
-    
-    // TODO Implement the onServiceArrival method.
-    
-    // TODO Implement the onServiceDeparture method.
-    
-    // TODO Implement the onServiceBound method.
-    
-    // TODO Implement the onServiceUnbound method.
 
     /**
      * Creates the Dependency Handler description.
@@ -71,7 +50,6 @@
         m_dependencies = new DependencyDescription[deps.length];
         for (int i = 0; i < m_dependencies.length; i++) {
             m_dependencies[i] = new DependencyDescription(deps[i]);
-            //TODO Register callback there on the dependency model.
         }
     }
 
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/DependencyModel.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/DependencyModel.java
index 0ff5614..1040787 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/DependencyModel.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/DependencyModel.java
@@ -128,7 +128,7 @@
      * This map stores service object, and so is able to handle

      * iPOJO custom policies.

      */

-    private Map<ServiceReference, Object> m_serviceObjects = new HashMap<ServiceReference, Object>();

+    private Map<ServiceReference, ServiceBindingHolder> m_serviceObjects = new HashMap<ServiceReference, ServiceBindingHolder>();

     /**

      * The current list of bound services.

      */

@@ -269,15 +269,16 @@
      * The method is called while holding the exclusive lock.

      */

     private void ungetAllServices() {

-        for (Map.Entry<ServiceReference, Object> entry : m_serviceObjects.entrySet()) {

+        for (Map.Entry<ServiceReference, ServiceBindingHolder> entry : m_serviceObjects.entrySet()) {

             ServiceReference ref = entry.getKey();

-            Object svc = entry.getValue();

+            ServiceBindingHolder sbh = entry.getValue();

             if (m_tracker != null) {

                 m_tracker.ungetService(ref);

             }

-            if (svc instanceof IPOJOServiceFactory) {

-                ((IPOJOServiceFactory) svc).ungetService(m_instance, svc);

+            if (sbh.factory != null) {

+                sbh.factory.ungetService(m_instance, sbh.service);

             }

+            m_serviceReferenceManager.unweavingServiceBinding(sbh);

         }

         m_serviceObjects.clear();

     }

@@ -790,12 +791,12 @@
             for (ServiceReference ref : arrivals) {

                 onServiceArrival(ref);

                 // Notify service binding to listeners

-                notifyListeners(DependencyEventType.BINDING, ref, m_serviceObjects.get(ref));

+                notifyListeners(DependencyEventType.BINDING, ref, m_serviceObjects.get(ref).service);

             }

             for (ServiceReference ref : departures) {

                 onServiceDeparture(ref);

                 // Notify service unbinding to listeners

-                notifyListeners(DependencyEventType.UNBINDING, ref, m_serviceObjects.get(ref));

+                notifyListeners(DependencyEventType.UNBINDING, ref, m_serviceObjects.get(ref).service);

             }

         } finally {

             releaseReadLockIfHeld();

@@ -912,6 +913,12 @@
             return null;

         }

 

+        // If we already have the service object, just return it.

+        if (m_serviceObjects.containsKey(ref)) {

+            return m_serviceObjects.get(ref).service;

+        }

+

+        ServiceBindingHolder holder = null;

         Object svc = m_tracker.getService(ref);

         IPOJOServiceFactory factory = null;

 

@@ -919,20 +926,23 @@
             factory = (IPOJOServiceFactory) svc;

             svc = factory.getService(m_instance);

         }

+        holder = new ServiceBindingHolder(ref, factory, svc);

+

+        svc = m_serviceReferenceManager.weavingServiceBinding(holder);

 

         if (store) {

             try {

                 acquireWriteLockIfNotHeld();

-                if (factory != null) {

-                    m_serviceObjects.put(ref, factory);

+                // The service object may have been modified by the interceptor, update the holder

+                if (svc != holder.service) {

+                    m_serviceObjects.put(ref, new ServiceBindingHolder(ref, factory, svc));

                 } else {

-                    m_serviceObjects.put(ref, svc);

+                    m_serviceObjects.put(ref, holder);

                 }

             } finally {

                 releaseWriteLockIfHeld();

             }

         }

-

         return svc;

     }

 

@@ -943,18 +953,21 @@
      */

     public void ungetService(ServiceReference ref) {

         m_tracker.ungetService(ref);

-        Object obj;

+        ServiceBindingHolder sbh;

         try {

             acquireWriteLockIfNotHeld();

-            obj = m_serviceObjects.remove(ref);

+            sbh = m_serviceObjects.remove(ref);

         } finally {

             releaseWriteLockIfHeld();

         }

 

         // Call the callback outside the lock.

-        if (obj != null && obj instanceof IPOJOServiceFactory) {

-            ((IPOJOServiceFactory) obj).ungetService(m_instance, obj);

+        if (sbh != null && sbh.factory != null) {

+            sbh.factory.ungetService(m_instance, sbh.service);

         }

+

+        m_serviceReferenceManager.unweavingServiceBinding(sbh);

+

     }

 

     public ContextSourceManager getContextSourceManager() {

@@ -988,11 +1001,11 @@
                         m_state = BROKEN;

 

                         // We are going to call callbacks, releasing the lock.

-                        Object svc = m_serviceObjects.get(ref);

+                        ServiceBindingHolder sbh = m_serviceObjects.get(ref);

                         releaseWriteLockIfHeld();

 

                         // Notify listeners

-                        notifyListeners(DependencyEventType.UNBINDING, ref, svc);

+                        notifyListeners(DependencyEventType.UNBINDING, ref, sbh.service);

                         notifyListeners(DependencyEventType.DEPARTURE, ref, null);

 

                         invalidate();  // This will invalidate the instance.

@@ -1072,7 +1085,7 @@
             }

 

             // Before leaving the protected region, copy used services.

-            Map<ServiceReference, Object> services = new HashMap<ServiceReference, Object>(m_serviceObjects);

+            Map<ServiceReference, ServiceBindingHolder> services = new HashMap<ServiceReference, ServiceBindingHolder>(m_serviceObjects);

 

             // Leaving the locked region to invoke callbacks

             releaseWriteLockIfHeld();

@@ -1080,14 +1093,24 @@
             for (ServiceReference ref : departures) {

                 onServiceDeparture(ref);

                 // Notify service unbinding to listeners

-                Object svc = services.get(ref);

-                notifyListeners(DependencyEventType.UNBINDING, ref, svc);

+                final ServiceBindingHolder sbh = services.get(ref);

+                if (sbh != null) {

+                    notifyListeners(DependencyEventType.UNBINDING, ref, sbh.service);

+                } else {

+                    notifyListeners(DependencyEventType.UNBINDING, ref, null);

+                }

+                // Unget the service reference.

+                ungetService(ref);

             }

             for (ServiceReference ref : arrivals) {

                 onServiceArrival(ref);

                 // Notify service binding to listeners

-                Object svc = services.get(ref);

-                notifyListeners(DependencyEventType.BINDING, ref, svc);

+                final ServiceBindingHolder sbh = services.get(ref);

+                if (sbh != null) {

+                    notifyListeners(DependencyEventType.BINDING, ref, sbh.service);

+                } else {

+                    notifyListeners(DependencyEventType.BINDING, ref, null);

+                }

             }

             // Do we have a modified service ?

             if (set.modified != null && m_boundServices.contains(set.modified)) {

@@ -1229,4 +1252,26 @@
             m_listeners.clear();

         }

     }

+

+    /**

+     * Service binding structure.

+     */

+    public class ServiceBindingHolder {

+

+        public final Object service;

+        public final IPOJOServiceFactory factory;

+        public final ServiceReference reference;

+

+        private ServiceBindingHolder(ServiceReference reference, IPOJOServiceFactory factory, Object service) {

+            this.service = service;

+            this.factory = factory;

+            this.reference = reference;

+        }

+

+        private ServiceBindingHolder(ServiceReference reference, Object service) {

+            this.service = service;

+            this.factory = null;

+            this.reference = reference;

+        }

+    }

 }