Author: marrs
Date: Tue Dec  1 08:37:10 2009
New Revision: 885687

URL: http://svn.apache.org/viewvc?rev=885687&view=rev
Log:
adapted service, configuration and bundle dependencies to allow them to be 
added to more than one service so you can share them efficiently, added basic 
tests for those scenarios

Added:
    
felix/trunk/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/SharingDependenciesWithMultipleServicesTest.java
Modified:
    
felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/BundleDependency.java
    
felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/ConfigurationDependency.java
    
felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/ServiceDependency.java
    
felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/impl/ServiceImpl.java
    
felix/trunk/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/BundleDependencyTest.java

Modified: 
felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/BundleDependency.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/BundleDependency.java?rev=885687&r1=885686&r2=885687&view=diff
==============================================================================
--- 
felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/BundleDependency.java
 (original)
+++ 
felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/BundleDependency.java
 Tue Dec  1 08:37:10 2009
@@ -18,12 +18,13 @@
  */
 package org.apache.felix.dependencymanager.dependencies;
 
+import java.util.ArrayList;
 import java.util.Dictionary;
+import java.util.List;
 
 import org.apache.felix.dependencymanager.Dependency;
 import org.apache.felix.dependencymanager.DependencyService;
 import org.apache.felix.dependencymanager.impl.Logger;
-import org.apache.felix.dependencymanager.impl.ServiceImpl;
 import 
org.apache.felix.dependencymanager.management.ServiceComponentDependency;
 import org.apache.felix.dependencymanager.tracker.BundleTracker;
 import org.apache.felix.dependencymanager.tracker.BundleTrackerCustomizer;
@@ -38,7 +39,7 @@
        private boolean m_isStarted;
        private BundleTracker m_tracker;
        private int m_stateMask = Bundle.INSTALLED | Bundle.RESOLVED | 
Bundle.ACTIVE;
-       private DependencyService m_service;
+       private List m_services = new ArrayList();
        private boolean m_isAvailable;
        
     private Object m_callbackInstance;
@@ -66,32 +67,47 @@
 
 
     public void start(DependencyService service) {
+        boolean needsStarting = false;
                synchronized (this) {
-                       if (m_isStarted) {
-                               throw new IllegalStateException("Dependency was 
already started." + getName());
-                       }
-                       m_service = service;
-                       m_tracker = new BundleTracker(m_context, m_stateMask, 
this);
-                       m_isStarted = true;
+                   m_services.add(service);
+                   if (!m_isStarted) {
+                       m_tracker = new BundleTracker(m_context, m_stateMask, 
this);
+                       m_isStarted = true;
+                       needsStarting = true;
+                   }
+               }
+               if (needsStarting) {
+                   m_tracker.open();
                }
-               m_tracker.open();
-               System.out.println("START BD " + m_tracker);
        }
 
        public void stop(DependencyService service) {
+           boolean needsStopping = false;
         synchronized (this) {
-            if (!m_isStarted) {
-                throw new IllegalStateException("Dependency was not started.");
+            if (m_services.size() == 1 && m_services.contains(service)) {
+                m_isStarted = false;
+                needsStopping = true;
             }
-            m_isStarted = false;
         }
-        m_tracker.close();
-        m_tracker = null;
+        if (needsStopping) {
+            m_tracker.close();
+            m_tracker = null;
+            m_services.remove(service);
+        }            
        }
 
        public String getName() {
-               // TODO Auto-generated method stub
-               return null;
+        StringBuilder sb = new StringBuilder();
+        sb.append(m_bundleInstance.getSymbolicName());
+        sb.append(' ');
+        sb.append(m_bundleInstance.getVersion());
+        sb.append(' ');
+        sb.append(Integer.toString(m_stateMask, 2));
+        if (m_filter != null) {
+            sb.append(' ');
+            sb.append(m_filter.toString());
+        }
+        return sb.toString();
        }
 
        public int getState() {
@@ -100,12 +116,10 @@
        }
 
        public String getType() {
-               // TODO Auto-generated method stub
-               return null;
+               return "bundle";
        }
 
        public Object addingBundle(Bundle bundle, BundleEvent event) {
-               System.out.println("ADDING " + bundle + " " + event);
                // if we don't like a bundle, we could reject it here by 
returning null
                if (m_bundleId >= 0 && m_bundleId != bundle.getBundleId()) {
                        return null;
@@ -113,9 +127,7 @@
                Filter filter = m_filter;
                if (filter != null) {
                        Dictionary headers = bundle.getHeaders();
-//                     System.out.println("HEADERS: " + headers);
                        if (!m_filter.match(headers)) {
-                           System.out.println("NO MATCH: " + bundle);
                                return null;
                        }
                }
@@ -123,39 +135,49 @@
        }
        
        public void addedBundle(Bundle bundle, BundleEvent event, Object 
object) {
-               System.out.println("ADDED " + bundle + " " + event);
-        if (makeAvailable()) {
-            m_service.dependencyAvailable(this);
-            if (!isRequired()) {
-                invokeAdded(bundle);
-            }
-        }
-        else {
-            m_service.dependencyChanged(this);
-            invokeAdded(bundle);
-        }
+           boolean makeAvailable = makeAvailable();
+           Object[] services = m_services.toArray();
+           for (int i = 0; i < services.length; i++) {
+               DependencyService ds = (DependencyService) services[i];
+               if (makeAvailable) {
+                   ds.dependencyAvailable(this);
+                   if (!isRequired()) {
+                       invokeAdded(ds, bundle);
+                   }
+               }
+               else {
+                   ds.dependencyChanged(this);
+                   invokeAdded(ds, bundle);
+               }
+           }
        }
 
        public void modifiedBundle(Bundle bundle, BundleEvent event, Object 
object) {
-               System.out.println("MODIFIED " + bundle + " " + event);
-        m_service.dependencyChanged(this);
-        // only invoke the changed callback if the service itself is "active"
-        if (m_service.isRegistered()) {
-            invokeChanged(bundle);
+               Object[] services = m_services.toArray();
+        for (int i = 0; i < services.length; i++) {
+            DependencyService ds = (DependencyService) services[i];
+            ds.dependencyChanged(this);
+            if (ds.isRegistered()) {
+                invokeChanged(ds, bundle);
+            }
         }
        }
 
        public void removedBundle(Bundle bundle, BundleEvent event, Object 
object) {
-               System.out.println("REMOVED " + bundle + " " + event);
-        if (makeUnavailable()) {
-            m_service.dependencyUnavailable(this);
-            if (!isRequired()) {
-                invokeRemoved(bundle);
+               boolean makeUnavailable = makeUnavailable();
+        Object[] services = m_services.toArray();
+        for (int i = 0; i < services.length; i++) {
+            DependencyService ds = (DependencyService) services[i];
+            if (makeUnavailable) {
+                ds.dependencyUnavailable(this);
+                if (!isRequired()) {
+                    invokeRemoved(ds, bundle);
+                }
+            }
+            else {
+                ds.dependencyChanged(this);
+                invokeRemoved(ds, bundle);
             }
-        }
-        else {
-            m_service.dependencyChanged(this);
-            invokeRemoved(bundle);
         }
        }
        
@@ -170,18 +192,13 @@
     private synchronized boolean makeUnavailable() {
         if ((isAvailable()) && (m_tracker.getTrackingCount() == 0)) {
             m_isAvailable = false;
-
             return true;
         }
         return false;
     }
     
-    public void invokeAdded() {
-        invokeAdded(m_bundleInstance);
-    }
-    
-    public void invokeAdded(Bundle service) {
-        Object[] callbackInstances = getCallbackInstances();
+    public void invokeAdded(DependencyService dependencyService, Bundle 
service) {
+        Object[] callbackInstances = getCallbackInstances(dependencyService);
         if ((callbackInstances != null) && (m_callbackAdded != null)) {
             invokeCallbackMethod(callbackInstances, m_callbackAdded, 
                 new Class[][] {{Bundle.class}, {Object.class}, {}},
@@ -190,8 +207,8 @@
         }
     }
 
-    public void invokeChanged(Bundle service) {
-        Object[] callbackInstances = getCallbackInstances();
+    public void invokeChanged(DependencyService dependencyService, Bundle 
service) {
+        Object[] callbackInstances = getCallbackInstances(dependencyService);
         if ((callbackInstances != null) && (m_callbackChanged != null)) {
             invokeCallbackMethod(callbackInstances, m_callbackChanged, 
                 new Class[][] {{Bundle.class}, {Object.class}, {}},
@@ -200,12 +217,8 @@
         }
     }
     
-    public void invokeRemoved() {
-        invokeRemoved(m_bundleInstance);
-    }
-    
-    public void invokeRemoved(Bundle service) {
-        Object[] callbackInstances = getCallbackInstances();
+    public void invokeRemoved(DependencyService dependencyService, Bundle 
service) {
+        Object[] callbackInstances = getCallbackInstances(dependencyService);
         if ((callbackInstances != null) && (m_callbackRemoved != null)) {
             invokeCallbackMethod(callbackInstances, m_callbackRemoved,
               new Class[][] {{Bundle.class}, {Object.class}, {}},
@@ -213,17 +226,16 @@
             );
         }
     }
-    
-    private synchronized Object[] getCallbackInstances() {
-        Object[] callbackInstances = ((ServiceImpl) 
m_service).getCompositionInstances();
+
+    private synchronized Object[] getCallbackInstances(DependencyService 
dependencyService) {
         if (m_callbackInstance == null) {
-            return callbackInstances;
+            return dependencyService.getCompositionInstances();
+        }
+        else {
+            return new Object[] { m_callbackInstance };
         }
-        Object[] res = new Object[callbackInstances.length + 1];
-        res[0] = m_callbackInstance; //this could also be extended to an 
array...?
-        System.arraycopy(callbackInstances, 0, res, 1, 
callbackInstances.length);
-        return res;
     }
+    
     /**
      * 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 

Modified: 
felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/ConfigurationDependency.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/ConfigurationDependency.java?rev=885687&r1=885686&r2=885687&view=diff
==============================================================================
--- 
felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/ConfigurationDependency.java
 (original)
+++ 
felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/ConfigurationDependency.java
 Tue Dec  1 08:37:10 2009
@@ -20,8 +20,12 @@
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.util.ArrayList;
 import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.List;
 import java.util.Properties;
+import java.util.Set;
 
 import org.apache.felix.dependencymanager.Dependency;
 import org.apache.felix.dependencymanager.DependencyService;
@@ -57,11 +61,13 @@
        private BundleContext m_context;
        private String m_pid;
        private ServiceRegistration m_registration;
-       private volatile DependencyService m_service;
+    protected List m_services = new ArrayList();
        private Dictionary m_settings;
        private boolean m_propagate;
        private final Logger m_logger;
     private String m_callback;
+    private boolean m_isStarted;
+       private final Set m_updateInvokedCache = new HashSet();
        
        public ConfigurationDependency(BundleContext context, Logger logger) {
                m_context = context;
@@ -99,15 +105,34 @@
        }
        
        public void start(DependencyService service) {
-               m_service = service;
-               Properties props = new Properties();
-               props.put(Constants.SERVICE_PID, m_pid);
-               m_registration = 
m_context.registerService(ManagedService.class.getName(), this, props);
+           boolean needsStarting = false;
+           synchronized (this) {
+               m_services.add(service);
+               if (!m_isStarted) {
+                   m_isStarted = true;
+                needsStarting = true;
+               }
+           }
+           if (needsStarting) {
+               Properties props = new Properties();
+               props.put(Constants.SERVICE_PID, m_pid);
+               m_registration = 
m_context.registerService(ManagedService.class.getName(), this, props);
+           }
        }
 
        public void stop(DependencyService service) {
-               m_registration.unregister();
-               m_service = null;
+        boolean needsStopping = false;
+        synchronized (this) {
+            if (m_services.size() == 1 && m_services.contains(service)) {
+                m_isStarted = false;
+                needsStopping = true;
+            }
+        }
+        if (needsStopping) {
+            m_registration.unregister();
+            m_registration = null;
+            m_services.remove(service);
+        }
        }
 
         public Dependency setCallback(String callback) {
@@ -116,36 +141,71 @@
        }
 
        public void updated(Dictionary settings) throws ConfigurationException {
-               // if non-null settings come in, we have to instantiate the 
service and
-               // apply these settings
-               m_service.initService();
-               Object service = m_service.getService();
-                               
-               Dictionary oldSettings = null; 
+           m_updateInvokedCache.clear();
+           
+           Dictionary oldSettings = null; 
+           synchronized (this) {
+               oldSettings = m_settings;
+           }
+           
+           if (oldSettings == null && settings == null) {
+               // CM has started but our configuration is not still present in 
the CM database: ignore
+               return;
+           }
+
+           Object[] services = m_services.toArray();
+        for (int i = 0; i < services.length; i++) {
+            DependencyService ds = (DependencyService) services[i];
+            // if non-null settings come in, we have to instantiate the 
service and
+            // apply these settings
+            ds.initService();
+            Object service = ds.getService();
+
+            if (service != null) {
+                invokeUpdate(ds, service, settings);
+            }
+            else {
+                m_logger.log(Logger.LOG_ERROR, "Service " + ds + " with 
configuration dependency " + this + " could not be instantiated.");
+                return;
+            }
+        }
+
                synchronized (this) {
-                       oldSettings = m_settings;
-               }
-               
-               if (oldSettings == null && settings == null) {
-              // CM has started but our configuration is not still present in 
the CM database: ignore
-              return;
+                       m_settings = settings;
                }
                
-        if (service != null) {
-               String callback = (m_callback == null) ? "updated" : m_callback;
-               Method m;
-                       try {
-                               m = 
service.getClass().getDeclaredMethod(callback, new Class[] { Dictionary.class 
});
-                               m.setAccessible(true);
-                               // if exception is thrown here, what does that 
mean for the
-                               // state of this dependency? how smart do we 
want to be??
-                               // it's okay like this, if the new settings 
contain errors, we
-                               // remain in the state we were, assuming that 
any error causes
-                               // the "old" configuration to stay in effect.
-                               // CM will log any thrown exceptions.
-                               m.invoke(service, new Object[] { settings });
-                       } 
-               catch (InvocationTargetException e) {
+        for (int i = 0; i < services.length; i++) {
+            DependencyService ds = (DependencyService) services[i];
+            // If these settings did not cause a configuration exception, we 
determine if they have 
+            // caused the dependency state to change
+            if ((oldSettings == null) && (settings != null)) {
+                ds.dependencyAvailable(this);
+            }
+            if ((oldSettings != null) && (settings == null)) {
+                ds.dependencyUnavailable(this);
+            }
+            if ((oldSettings != null) && (settings != null)) {
+                ds.dependencyChanged(this);
+            }
+        }
+       }
+
+    public void invokeUpdate(DependencyService ds, Object service, Dictionary 
settings) throws ConfigurationException {
+        if (m_updateInvokedCache.add(ds)) {
+            String callback = (m_callback == null) ? "updated" : m_callback;
+            Method m;
+            try {
+                m = service.getClass().getDeclaredMethod(callback, new Class[] 
{ Dictionary.class });
+                m.setAccessible(true);
+                // if exception is thrown here, what does that mean for the
+                // state of this dependency? how smart do we want to be??
+                // it's okay like this, if the new settings contain errors, we
+                // remain in the state we were, assuming that any error causes
+                // the "old" configuration to stay in effect.
+                // CM will log any thrown exceptions.
+                m.invoke(service, new Object[] { settings });
+            } 
+            catch (InvocationTargetException e) {
                 // The component has thrown an exception during it's callback 
invocation.
                 if (e.getTargetException() instanceof ConfigurationException) {
                     // the callback threw an OSGi ConfigurationException: just 
re-throw it.
@@ -153,35 +213,15 @@
                 }
                 else {
                     // wrap the callback exception into a 
ConfigurationException.
-                    throw new ConfigurationException(null, "Service " + 
m_service + " with " + this.toString() + " could not be updated", 
e.getTargetException());
+                    throw new ConfigurationException(null, "Service " + ds + " 
with " + this.toString() + " could not be updated", e.getTargetException());
                 }
             }
             catch (Throwable t) {
                 // wrap any other exception as a ConfigurationException.
-                throw new ConfigurationException(null, "Service " + m_service 
+ " with " + this.toString() + " could not be updated", t);
+                throw new ConfigurationException(null, "Service " + ds + " 
with " + this.toString() + " could not be updated", t);
             }
         }
-        else {
-            m_logger.log(Logger.LOG_ERROR, "Service " + m_service + " with 
configuration dependency " + this + " could not be instantiated.");
-            return;
-        }
-
-               // If these settings did not cause a configuration exception, 
we determine if they have 
-               // caused the dependency state to change
-               synchronized (this) {
-                       m_settings = settings;
-               }
-
-               if ((oldSettings == null) && (settings != null)) {
-                       m_service.dependencyAvailable(this);
-               }
-               if ((oldSettings != null) && (settings == null)) {
-                       m_service.dependencyUnavailable(this);
-               }
-               if ((oldSettings != null) && (settings != null)) {
-                       m_service.dependencyChanged(this);
-               }
-       }
+    }
 
        /**
         * Sets the <code>service.pid</code> of the configuration you
@@ -205,7 +245,7 @@
        }
        
        private void ensureNotActive() {
-               if (m_service != null) {
+               if (m_services != null && m_services.size() > 0) {
                  throw new IllegalStateException("Cannot modify state while 
active.");
                }
     }

Modified: 
felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/ServiceDependency.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/ServiceDependency.java?rev=885687&r1=885686&r2=885687&view=diff
==============================================================================
--- 
felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/ServiceDependency.java
 (original)
+++ 
felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/ServiceDependency.java
 Tue Dec  1 08:37:10 2009
@@ -361,8 +361,7 @@
     public void stop(DependencyService service) {
         boolean needsStopping = false;
         synchronized (this) {
-            m_services.remove(service);
-            if (m_services.size() == 0) {
+            if (m_services.size() == 1 && m_services.contains(service)) {
                 m_isStarted = false;
                 needsStopping = true;
             }
@@ -370,6 +369,7 @@
         if (needsStopping) {
             m_tracker.close();
             m_tracker = null;
+            m_services.remove(service);
         }
     }
 
@@ -718,6 +718,7 @@
         StringBuilder sb = new StringBuilder();
         sb.append(m_trackedServiceName.getName());
         if (m_trackedServiceFilterUnmodified != null) {
+            sb.append(' ');
             sb.append(m_trackedServiceFilterUnmodified);
         }
         return sb.toString();

Modified: 
felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/impl/ServiceImpl.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/impl/ServiceImpl.java?rev=885687&r1=885686&r2=885687&view=diff
==============================================================================
--- 
felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/impl/ServiceImpl.java
 (original)
+++ 
felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/impl/ServiceImpl.java
 Tue Dec  1 08:37:10 2009
@@ -48,6 +48,7 @@
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.cm.ConfigurationException;
 
 /**
  * Service implementation.
@@ -910,6 +911,16 @@
         else {
             instances = new Object[] { m_serviceInstance };
         }
+        // TODO remove this test code; there are definitely cases where some 
instances in this array can be
+        // null, but it's not always harmful (in fact it's not possible to 
determine that here), this also happens
+        // when you start tracking required dependencies... it's probably safe 
not to include these null's in the
+        // array in the first place
+//        for (int i = 0; i < instances.length; i++) {
+//            if (instances[i] == null) {
+//                System.out.println("GetCompositionInstances had a null 
instance at index " + i + " dumping stack:");
+//                Thread.dumpStack();
+//            }
+//        }
         return instances;
     }
 
@@ -947,12 +958,13 @@
                     else {
                         // for optional services, we do an "ad-hoc" lookup to 
inject the service if it is
                         // already available even though the tracker has not 
yet been started
+                        
                         // TODO !!! configureImplementation(sd.getInterface(), 
sd.lookupService(), sd.getAutoConfigName());
                     }
                 }
                 // for required dependencies, we invoke any callbacks here
                 if (bd.isRequired()) {
-                    bd.invokeAdded();
+                    bd.invokeAdded(this, bd.getBundle());
                 }
             }
             else if (dependency instanceof ResourceDependency) {
@@ -964,6 +976,7 @@
                     else {
                         // for optional services, we do an "ad-hoc" lookup to 
inject the service if it is
                         // already available even though the tracker has not 
yet been started
+                        
                         // TODO !!! configureImplementation(sd.getInterface(), 
sd.lookupService(), sd.getAutoConfigName());
                     }
                 }
@@ -972,6 +985,19 @@
                     bd.invokeAdded();
                 }
             }
+            else if (dependency instanceof ConfigurationDependency) {
+                ConfigurationDependency cd = (ConfigurationDependency) 
dependency;
+                // for configuration dependencies, we invoke updated
+                try {
+                    cd.invokeUpdate(this, this.getService(), 
cd.getConfiguration());
+                }
+                catch (ConfigurationException e) {
+                    // if this happens, it's definitely an inconsistency
+                    // when sharing configuration dependencies between 
services, all implementations
+                    // should accept the same configurations
+                    e.printStackTrace();
+                }
+            }
         }
     }
 

Modified: 
felix/trunk/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/BundleDependencyTest.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/BundleDependencyTest.java?rev=885687&r1=885686&r2=885687&view=diff
==============================================================================
--- 
felix/trunk/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/BundleDependencyTest.java
 (original)
+++ 
felix/trunk/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/BundleDependencyTest.java
 Tue Dec  1 08:37:10 2009
@@ -55,7 +55,7 @@
     }
     
     static class Consumer {
-        private int m_count = 0;
+        private volatile int m_count = 0;
 
         public void add(Bundle b) {
             Assert.assertNotNull("bundle instance must not be null", b);
@@ -71,7 +71,7 @@
         }
         
         public void doubleCheck() {
-            Assert.assertTrue("all bundles we found should have been removed 
again", m_count == 0);
+            Assert.assertEquals("all bundles we found should have been removed 
again", 0, m_count);
         }
     }
     

Added: 
felix/trunk/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/SharingDependenciesWithMultipleServicesTest.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/SharingDependenciesWithMultipleServicesTest.java?rev=885687&view=auto
==============================================================================
--- 
felix/trunk/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/SharingDependenciesWithMultipleServicesTest.java
 (added)
+++ 
felix/trunk/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/SharingDependenciesWithMultipleServicesTest.java
 Tue Dec  1 08:37:10 2009
@@ -0,0 +1,184 @@
+package org.apache.felix.dependencymanager.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.Dictionary;
+import java.util.Properties;
+
+import org.apache.felix.dependencymanager.DependencyManager;
+import org.apache.felix.dependencymanager.Service;
+import org.apache.felix.dependencymanager.dependencies.BundleDependency;
+import org.apache.felix.dependencymanager.dependencies.ConfigurationDependency;
+import org.apache.felix.dependencymanager.dependencies.ServiceDependency;
+import org.apache.felix.dependencymanager.impl.Logger;
+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;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+
+...@runwith(JUnit4TestRunner.class)
+public class SharingDependenciesWithMultipleServicesTest {
+    @Configuration
+    public static Option[] configuration() {
+        return options(
+            provision(
+                
mavenBundle().groupId("org.osgi").artifactId("org.osgi.compendium").version("4.2.0"),
+                
mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.dependencymanager").versionAsInProject(),
+                
mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.configadmin").version("1.2.4")
+            )
+        );
+    }    
+    
+    @Test
+    public void testShareServiceDependencyWithMultipleServices(BundleContext 
context) {
+        DependencyManager m = new DependencyManager(context, new 
Logger(context));
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        // create a service provider and consumer
+        Service provider = m.createService().setImplementation(new 
ServiceProvider()).setInterface(ServiceInterface.class.getName(), null);
+        ServiceDependency dependency = 
m.createServiceDependency().setService(ServiceInterface.class).setRequired(true);
+        Service consumer1 = m.createService().setImplementation(new 
ServiceConsumer(e, 1)).add(dependency);
+        Service consumer2 = m.createService().setImplementation(new 
ServiceConsumer(e, 4)).add(dependency);
+        
+        m.add(provider);
+        m.add(consumer1);
+        e.waitForStep(3, 2000);
+        m.add(consumer2);
+        e.waitForStep(6, 2000);
+        m.remove(provider);
+        m.remove(consumer1);
+        m.remove(consumer2);
+    }
+    
+    @Test
+    public void 
testShareConfigurationDependencyWithMultipleServices(BundleContext context) {
+        DependencyManager m = new DependencyManager(context, new 
Logger(context));
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        // create a service provider and consumer
+        Service provider = m.createService().setImplementation(new 
ConfigurationProvider(e)).add(m.createServiceDependency().setService(ConfigurationAdmin.class).setRequired(true));
+        ConfigurationDependency dependency = 
m.createConfigurationDependency().setPid("test");
+        Service consumer1 = m.createService().setImplementation(new 
ConfigurationConsumer(e, 2)).add(dependency);
+        Service consumer2 = m.createService().setImplementation(new 
ConfigurationConsumer(e, 3)).add(dependency);
+        
+        // add the configuration provider that should publish the 
configuration as step 1
+        m.add(provider);
+        // add the first consumer, and wait until its updated() method is 
invoked
+        m.add(consumer1);
+        e.waitForStep(2, 2000);
+        // add the second consumer, and wait until its updated() method is 
invoked
+        m.add(consumer2);
+        e.waitForStep(3, 2000);
+        // break down the test again
+        m.remove(provider);
+        m.remove(consumer1);
+        m.remove(consumer2);
+    }
+    
+    @Test
+    public void testShareBundleDependencyWithMultipleServices(BundleContext 
context) {
+        DependencyManager m = new DependencyManager(context, new 
Logger(context));
+        // helper class that ensures certain steps get executed in sequence
+        Ensure e = new Ensure();
+        // create a service provider and consumer
+        BundleDependency dependency = 
m.createBundleDependency().setFilter("(Bundle-SymbolicName=org.apache.felix.dependencymanager)").setRequired(true);
+        Service consumer1 = m.createService().setImplementation(new 
BundleConsumer(e, 1)).add(dependency);
+        Service consumer2 = m.createService().setImplementation(new 
BundleConsumer(e, 2)).add(dependency);
+        
+        m.add(consumer1);
+        e.waitForStep(1, 2000);
+        m.add(consumer2);
+        e.waitForStep(2, 2000);
+        m.remove(consumer2);
+        m.remove(consumer1);
+    }
+    
+    static interface ServiceInterface {
+        public void invoke(Runnable r);
+    }
+
+    static class ServiceProvider implements ServiceInterface {
+        public void invoke(Runnable r) {
+            r.run();
+        }
+    }
+    
+    static class ServiceConsumer implements Runnable {
+        private volatile ServiceInterface m_service;
+        private final Ensure m_ensure;
+        private int m_step;
+
+        public ServiceConsumer(Ensure e, int step) {
+            m_ensure = e;
+            m_step = step;
+        }
+
+        public void start() {
+            Thread t = new Thread(this);
+            t.start();
+        }
+
+        public void run() {
+            m_ensure.step(m_step);
+            m_service.invoke(new Runnable() { public void run() { 
m_ensure.step(m_step + 1); } });
+            m_ensure.step(m_step + 2);
+        }
+    }
+    
+    static class ConfigurationConsumer implements ManagedService {
+        private final Ensure m_ensure;
+        private int m_step;
+
+        public ConfigurationConsumer(Ensure e, int step) {
+            m_ensure = e;
+            m_step = step;
+        }
+
+        public void updated(Dictionary properties) throws 
ConfigurationException {
+            if (properties != null) {
+                m_ensure.step(m_step);
+            }
+        }
+    }
+    
+    static class ConfigurationProvider {
+        private final Ensure m_ensure;
+        private volatile ConfigurationAdmin m_configAdmin;
+        
+        public ConfigurationProvider(Ensure ensure) {
+            m_ensure = ensure;
+        }
+        
+        public void init() {
+            try {
+                org.osgi.service.cm.Configuration conf = 
m_configAdmin.getConfiguration("test", null);
+                conf.update(new Properties() {{ put("testkey", "testvalue"); 
}} );
+                m_ensure.step(1);
+            }
+            catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+    
+    static class BundleConsumer {
+        private final Ensure m_ensure;
+        private int m_step;
+
+        public BundleConsumer(Ensure e, int step) {
+            m_ensure = e;
+            m_step = step;
+        }
+        
+        public void start() {
+            m_ensure.step(m_step);
+        }
+    }
+}


Reply via email to