FELIX-4305: All aspect dependency callbacks are proxied in order to propagate original service properties modification.
Added setServiceProperties used to update current aspect service properties.


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1551348 13f79535-47bb-0310-9956-ffa450edef68
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 08d6a3a..871691b 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
@@ -19,15 +19,22 @@
 package org.apache.felix.dm.impl;
 
 import java.util.ArrayList;
+import java.util.Dictionary;
 import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Properties;
+import java.util.Set;
 
 import org.apache.felix.dm.Component;
 import org.apache.felix.dm.ComponentStateListener;
 import org.apache.felix.dm.Dependency;
 import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.DependencyService;
 import org.apache.felix.dm.ServiceDependency;
+import org.apache.felix.dm.impl.dependencies.ServiceDependencyImpl;
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceReference;
 
@@ -38,10 +45,23 @@
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
 public class AspectServiceImpl extends FilterService {
+    private final String m_add;
+    private final String m_change;
+    private final String m_remove;
+    private final String m_swap;
+    private final String m_aspectFilter;
+    private final int m_ranking; // the aspect ranking
+
     public AspectServiceImpl(DependencyManager dm, Class aspectInterface, String aspectFilter, int ranking, String autoConfig, String add, String change, String remove, String swap)
     { 
         super(dm.createComponent()); // This service will be filtered by our super class, allowing us to take control.
-        m_component.setImplementation(new AspectImpl(aspectInterface, aspectFilter, ranking, autoConfig, add, change, remove, swap))
+        m_ranking = ranking;
+        m_add = add;
+        m_change = change;
+        m_remove = remove;
+        m_swap = swap;
+        m_aspectFilter = aspectFilter;
+        m_component.setImplementation(new AspectImpl(aspectInterface, autoConfig))
              .add(dm.createServiceDependency()
                   .setService(aspectInterface, createDependencyFilterForAspect(aspectFilter))
                   .setAutoConfig(false)
@@ -59,53 +79,84 @@
         }        
     }
     
+    private Properties getServiceProperties(ServiceReference ref) {
+        Properties props = new Properties();
+        String[] keys = ref.getPropertyKeys();
+        for (int i = 0; i < keys.length; i++) {
+            String key = keys[i];
+            if (key.equals(Constants.SERVICE_ID) || key.equals(Constants.SERVICE_RANKING) || key.equals(DependencyManager.ASPECT) || key.equals(Constants.OBJECTCLASS)) {
+                // do not copy these
+            }
+            else {
+                props.put(key, ref.getProperty(key));
+            }
+        }
+        if (m_serviceProperties != null) {
+            Enumeration e = m_serviceProperties.keys();
+            while (e.hasMoreElements()) {
+                Object key = e.nextElement();
+                props.put(key, m_serviceProperties.get(key));
+            }
+        }
+        // finally add our aspect property
+        props.put(DependencyManager.ASPECT, ref.getProperty(Constants.SERVICE_ID));
+        // and the ranking
+        props.put(Constants.SERVICE_RANKING, Integer.valueOf(m_ranking));
+        return props;
+    }
+
     /**
      * This class is the Aspect Implementation. It will create the actual Aspect Service, and
      * will use the Aspect Service parameters provided by our enclosing class.
      */
     class AspectImpl extends AbstractDecorator {
-        private final Class m_aspectInterface; // the service decorated by this aspect
-        private final String m_aspectFilter; // the service filter decorated by this aspect
-        private final int m_ranking; // the aspect ranking
         private final String m_autoConfig; // the aspect impl field name where to inject decorated service
-        private final String m_add;
-        private final String m_change;
-        private final String m_remove;
-        private final String m_swap;
+        private final Class m_aspectInterface; // the service decorated by this aspect
       
-        public AspectImpl(Class aspectInterface, String aspectFilter, int ranking, String autoConfig, String add, String change, String remove, String swap) {
+        public AspectImpl(Class aspectInterface, String autoConfig) {
             m_aspectInterface = aspectInterface;
-            m_aspectFilter = aspectFilter;
-            m_ranking = ranking;
             m_autoConfig = autoConfig;
-            m_add = add;
-            m_change = change;
-            m_remove = remove;
-            m_swap = swap;
         }
         
+        /**
+         * Creates an aspect implementation component for a new original service.
+         * @param param First entry contains the ref to the original service
+         */
         public Component createService(Object[] params) {
-            List dependencies = m_component.getDependencies();
-            // remove our internal dependency
-            dependencies.remove(0);
-            // replace it with one that points to the specific service that just was passed in
-            Properties serviceProperties = getServiceProperties(params);
-            String[] serviceInterfaces = getServiceInterfaces();
+            // Get the new original service reference.
             ServiceReference ref = (ServiceReference) params[0];
-            ServiceDependency dependency = m_manager.createServiceDependency().setService(m_aspectInterface, createAspectFilter(ref)).setRequired(true);
+            List dependencies = m_component.getDependencies();
+            // remove our internal dependency, replace it with one that points to the specific service that just was passed in.
+            dependencies.remove(0);
+            Properties serviceProperties = getServiceProperties(ref);
+            String[] serviceInterfaces = getServiceInterfaces();
+            
+            ServiceDependency aspectDependency = (ServiceDependencyImpl) 
+                    m_manager.createServiceDependency().setService(m_aspectInterface, createAspectFilter(ref)).setRequired(true);
+
+//            aspectDependency.setDebug("AspectDependency#" + 
+//                    (m_serviceImpl instanceof Class ? (((Class) m_serviceImpl).getSimpleName()) : m_serviceImpl));
+            
+            aspectDependency.setCallbacks(new CallbackProxy(aspectDependency, ref), 
+                            m_add != null ? "addAspect" : null, 
+                            "changeAspect", // We have to propagate in case aspect does not have a change callback
+                            m_remove != null ? "removeAspect" : null, 
+                            m_swap != null ? "swapAspect" : null);
+            
             if (m_autoConfig != null) {
-                dependency.setAutoConfig(m_autoConfig);
+                aspectDependency.setAutoConfig(m_autoConfig);
+            } else if (m_add == null && m_change == null && m_remove == null && m_swap == null) {
+                // Since we have set callbacks, we must reactivate setAutoConfig because user has not specified any callbacks.
+                aspectDependency.setAutoConfig(true);
             }
-            if (m_add != null || m_change != null || m_remove != null || m_swap != null) {
-                dependency.setCallbacks(m_add, m_change, m_remove, m_swap);
-            }
+            
             Component service = m_manager.createComponent()
                 .setInterface(serviceInterfaces, serviceProperties)
                 .setImplementation(m_serviceImpl)
                 .setFactory(m_factory, m_factoryCreateMethod) // if not set, no effect
                 .setComposition(m_compositionInstance, m_compositionMethod) // if not set, no effect
                 .setCallbacks(m_callbackObject, m_init, m_start, m_stop, m_destroy) // if not set, no effect
-                .add(dependency);
+                .add(aspectDependency);
             
             configureAutoConfigState(service, m_component);
             
@@ -119,33 +170,23 @@
             return service;                
         }
         
-        private Properties getServiceProperties(Object[] params) {
-            ServiceReference ref = (ServiceReference) params[0]; 
-            Properties props = new Properties();
-            String[] keys = ref.getPropertyKeys();
-            for (int i = 0; i < keys.length; i++) {
-                String key = keys[i];
-                if (key.equals(Constants.SERVICE_ID) || key.equals(Constants.SERVICE_RANKING) || key.equals(DependencyManager.ASPECT) || key.equals(Constants.OBJECTCLASS)) {
-                    // do not copy these
-                }
-                else {
-                    props.put(key, ref.getProperty(key));
-                }
+        /**
+         * Modify some specific aspect service properties.
+         */
+        public void setServiceProperties(Dictionary props) {
+            Map services = super.getServices();
+            Iterator it = services.entrySet().iterator();
+            while (it.hasNext()) {
+                Map.Entry entry = (Map.Entry) it.next();
+                ServiceReference originalServiceRef = (ServiceReference) entry.getKey();
+                Component c = (Component) entry.getValue();
+                // m_serviceProperties is already set to the new service properties; and the getServiceProperties will
+                // merge m_serviceProperties with the original service properties.
+                Dictionary newProps = getServiceProperties(originalServiceRef);                
+                c.setServiceProperties(newProps);
             }
-            if (m_serviceProperties != null) {
-                Enumeration e = m_serviceProperties.keys();
-                while (e.hasMoreElements()) {
-                    Object key = e.nextElement();
-                    props.put(key, m_serviceProperties.get(key));
-                }
-            }
-            // finally add our aspect property
-            props.put(DependencyManager.ASPECT, ref.getProperty(Constants.SERVICE_ID));
-            // and the ranking
-            props.put(Constants.SERVICE_RANKING, Integer.valueOf(m_ranking));
-            return props;
         }
-        
+                
         private String[] getServiceInterfaces() {
             List serviceNames = new ArrayList();
             // Of course, we provide the aspect interface.
@@ -170,4 +211,43 @@
             return "Aspect for " + m_aspectInterface + ((m_aspectFilter != null) ? " with filter " + m_aspectFilter : "");
         }
     }
+    
+    class CallbackProxy {
+        private final ServiceDependencyImpl m_aspectDependency;
+        private final ServiceReference m_originalServiceRef;
+
+        CallbackProxy(ServiceDependency aspectDependency, ServiceReference originalServiceRef) {
+            m_aspectDependency = (ServiceDependencyImpl) aspectDependency;
+            m_originalServiceRef = originalServiceRef;
+        }
+
+        private void addAspect(Component c, ServiceReference ref, Object service) {
+            // Just forward "add" service dependency callback.
+            m_aspectDependency.invoke(c.getCompositionInstances(), (DependencyService) c, ref, service, m_add);
+        }
+
+        private void changeAspect(Component c, ServiceReference ref, Object service) {
+            // Invoke "change" service dependency callback
+            if (m_change != null) {
+                m_aspectDependency.invoke(c.getCompositionInstances(), (DependencyService) c, ref, service, m_change);
+            }
+            // Propagate change to immediate higher aspect, or to client using our aspect.
+            // We always propagate our own properties, and the ones from the original service, but we don't inherit
+            // from lower ranked aspect service properties.
+            Dictionary props = getServiceProperties(m_originalServiceRef);
+            c.setServiceProperties(props);
+        }
+
+        private void removeAspect(Component c, ServiceReference ref, Object service) {
+            // Just forward "remove" service dependency callback.
+            m_aspectDependency.invoke(c.getCompositionInstances(), (DependencyService) c, ref, service, m_remove);
+        }
+
+        private void swapAspect(Component c, ServiceReference prevRef, Object prev, ServiceReference currRef,
+                                Object curr) {
+            // Just forward "swap" service dependency callback.
+            m_aspectDependency.invokeSwappedCallback(c.getCompositionInstances(), (DependencyService) c, prevRef, prev,
+                    currRef, curr, m_swap);
+        }
+    }
 }