You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ma...@apache.org on 2009/12/04 10:23:05 UTC

svn commit: r887124 - in /felix/trunk/dependencymanager: core/src/main/java/org/apache/felix/dependencymanager/dependencies/ core/src/main/java/org/apache/felix/dependencymanager/impl/ test/src/test/java/org/apache/felix/dependencymanager/test/

Author: marrs
Date: Fri Dec  4 09:23:04 2009
New Revision: 887124

URL: http://svn.apache.org/viewvc?rev=887124&view=rev
Log:
added support and tests for resource dependencies that are shared between services

Modified:
    felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/ResourceDependency.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/ResourceDependencyTest.java
    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/ResourceDependency.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/ResourceDependency.java?rev=887124&r1=887123&r2=887124&view=diff
==============================================================================
--- felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/ResourceDependency.java (original)
+++ felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dependencymanager/dependencies/ResourceDependency.java Fri Dec  4 09:23:04 2009
@@ -21,12 +21,13 @@
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Properties;
 
 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.resources.Resource;
 import org.apache.felix.dependencymanager.resources.ResourceHandler;
 import org.osgi.framework.BundleContext;
@@ -44,11 +45,13 @@
     private boolean m_autoConfig;
     private final Logger m_logger;
     private String m_autoConfigInstance;
-    private DependencyService m_service;
+//    private DependencyService m_service;
+    protected List m_services = new ArrayList();
 	private boolean m_isRequired;
 	private String m_resourceFilter;
 	private Resource m_resource;
 	private Resource m_trackedResource;
+    private boolean m_isStarted;
 
 	
     public ResourceDependency(BundleContext context, Logger logger) {
@@ -70,101 +73,115 @@
 	}
 
 	public void start(DependencyService service) {
-		m_service = service;
-		Properties props = new Properties();
-		// TODO create constant for this key
-		props.setProperty("filter", m_resourceFilter);
-		m_registration = m_context.registerService(ResourceHandler.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.setProperty(Resource.FILTER, m_resourceFilter);
+	        m_registration = m_context.registerService(ResourceHandler.class.getName(), this, props);
+	    }
 	}
 
 	public void stop(DependencyService service) {
-		m_registration.unregister();
-		m_registration = null;
+	    boolean needsStopping = false;
+	    synchronized (this) {
+            if (m_services.size() == 1 && m_services.contains(service)) {
+                m_isStarted = false;
+                needsStopping = true;
+                m_services.remove(service);
+            }
+	    }
+	    if (needsStopping) {
+	        m_registration.unregister();
+	        m_registration = null;
+	    }
 	}
 
 	public void added(Resource resource) {
-		System.out.println("RD ADDED " + resource);
 		long counter;
+		Object[] services;
 		synchronized (this) {
 			m_resourceCounter++;
 			counter = m_resourceCounter;
 			m_resource = resource; // TODO this really sucks as a way to track a single resource
+			services = m_services.toArray();
 		}
-        if (counter == 1) {
-            m_service.dependencyAvailable(this);
-        }
-        else {
-            m_service.dependencyChanged(this);
-        }
-        // try to invoke callback, if specified, but only for optional dependencies
-        // because callbacks for required dependencies are handled differently
-        if (!isRequired()) {
-            invokeAdded(resource);
+        for (int i = 0; i < services.length; i++) {
+            DependencyService ds = (DependencyService) services[i];
+            if (counter == 1) {
+                ds.dependencyAvailable(this);
+                if (!isRequired()) {
+                    invokeAdded(ds, resource);
+                }
+            }
+            else {
+                ds.dependencyChanged(this);
+                invokeAdded(ds, resource);
+            }
         }
 	}
 
 	public void changed(Resource resource) {
-		invokeChanged(resource);
+        Object[] services;
+        synchronized (this) {
+            services = m_services.toArray();
+        }
+        for (int i = 0; i < services.length; i++) {
+            DependencyService ds = (DependencyService) services[i];
+            invokeChanged(ds, resource);
+        }
 	}
 
 	public void removed(Resource resource) {
 		long counter;
+		Object[] services;
 		synchronized (this) {
 			m_resourceCounter--;
 			counter = m_resourceCounter;
+			services = m_services.toArray();
 		}
-        if (counter == 0) {
-            m_service.dependencyUnavailable(this);
-        }
-        // try to invoke callback, if specified, but only for optional dependencies
-        // because callbacks for required dependencies are handled differently
-        if (!isRequired()) {
-            invokeRemoved(resource);
+        for (int i = 0; i < services.length; i++) {
+            DependencyService ds = (DependencyService) services[i];
+            if (counter == 0) {
+                ds.dependencyUnavailable(this);
+                if (!isRequired()) {
+                    invokeRemoved(ds, resource);
+                }
+            }
+            else {
+                ds.dependencyChanged(this);
+                invokeRemoved(ds, resource);
+            }
         }
 	}
 	
-    public void invokeAdded() {
-    	// TODO fixme
-        //invokeAdded(m_bundleInstance);
-    }
-
-    public void invokeAdded(Resource serviceInstance) {
-        Object[] callbackInstances = getCallbackInstances();
+    public void invokeAdded(DependencyService ds, Resource serviceInstance) {
+        Object[] callbackInstances = getCallbackInstances(ds);
         if ((callbackInstances != null) && (m_callbackAdded != null)) {
-                invokeCallbackMethod(callbackInstances, m_callbackAdded, serviceInstance);
+            invokeCallbackMethod(callbackInstances, m_callbackAdded, serviceInstance);
         }
     }
 
-    public void invokeChanged(Resource serviceInstance) {
-        Object[] callbackInstances = getCallbackInstances();
+    public void invokeChanged(DependencyService ds, Resource serviceInstance) {
+        Object[] callbackInstances = getCallbackInstances(ds);
         if ((callbackInstances != null) && (m_callbackChanged != null)) {
-//                if (m_reference == null) {
-//                    Thread.dumpStack();
-//                }
-                invokeCallbackMethod(callbackInstances, m_callbackChanged, serviceInstance);
+            invokeCallbackMethod(callbackInstances, m_callbackChanged, serviceInstance);
         }
     }
 
-    
-    public void invokeRemoved() {
-    	// TODO fixme
-        //invokeRemoved(m_bundleInstance);
-    }
-    
-    public void invokeRemoved(Resource serviceInstance) {
-        Object[] callbackInstances = getCallbackInstances();
+    public void invokeRemoved(DependencyService ds, Resource serviceInstance) {
+        Object[] callbackInstances = getCallbackInstances(ds);
         if ((callbackInstances != null) && (m_callbackRemoved != null)) {
-//                if (m_reference == null) {
-//                    Thread.dumpStack();
-//                }
-                invokeCallbackMethod(callbackInstances, m_callbackRemoved, serviceInstance);
+            invokeCallbackMethod(callbackInstances, m_callbackRemoved, serviceInstance);
         }
     }
 
-
-
-	
     /**
      * 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 
@@ -325,15 +342,14 @@
         }
         return false;
     }
-    private synchronized Object[] getCallbackInstances() {
-        Object[] callbackInstances = ((ServiceImpl) m_service).getCompositionInstances();
+    
+    private synchronized Object[] getCallbackInstances(DependencyService ds) {
         if (m_callbackInstance == null) {
-            return callbackInstances;
+            return ds.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;
     }
 
 	public ResourceDependency setResource(Resource resource) {

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=887124&r1=887123&r2=887124&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 Fri Dec  4 09:23:04 2009
@@ -365,11 +365,11 @@
                 m_isStarted = false;
                 needsStopping = true;
             }
+            m_services.remove(service);
         }
         if (needsStopping) {
             m_tracker.close();
             m_tracker = null;
-            m_services.remove(service);
         }
     }
 
@@ -389,7 +389,10 @@
     public void addedService(ServiceReference ref, Object service) {
         boolean makeAvailable = makeAvailable();
         
-        Object[] services = m_services.toArray();
+        Object[] services;
+        synchronized (this) {
+            services = m_services.toArray();
+        }
         for (int i = 0; i < services.length; i++) {
             DependencyService ds = (DependencyService) services[i];
             if (makeAvailable) {
@@ -419,7 +422,10 @@
         m_reference = ref;
         m_serviceInstance = service;
         
-        Object[] services = m_services.toArray();
+        Object[] services;
+        synchronized (this) {
+            services = m_services.toArray();
+        }
         for (int i = 0; i < services.length; i++) {
             DependencyService ds = (DependencyService) services[i];
             ds.dependencyChanged(this);
@@ -442,7 +448,10 @@
     public void removedService(ServiceReference ref, Object service) {
         boolean makeUnavailable = makeUnavailable();
         
-        Object[] services = m_services.toArray();
+        Object[] services;
+        synchronized (this) {
+            services = m_services.toArray();
+        }
         for (int i = 0; i < services.length; i++) {
             DependencyService ds = (DependencyService) services[i];
             if (makeUnavailable) {

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=887124&r1=887123&r2=887124&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 Fri Dec  4 09:23:04 2009
@@ -972,7 +972,7 @@
                 }
                 // for required dependencies, we invoke any callbacks here
                 if (bd.isRequired()) {
-                    bd.invokeAdded();
+                    bd.invokeAdded(this, bd.getResource());
                 }
             }
             else if (dependency instanceof ConfigurationDependency) {

Modified: felix/trunk/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/ResourceDependencyTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/ResourceDependencyTest.java?rev=887124&r1=887123&r2=887124&view=diff
==============================================================================
--- felix/trunk/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/ResourceDependencyTest.java (original)
+++ felix/trunk/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/ResourceDependencyTest.java Fri Dec  4 09:23:04 2009
@@ -25,7 +25,10 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Properties;
+import java.util.Map.Entry;
 
 import junit.framework.Assert;
 
@@ -76,6 +79,7 @@
     static class ResourceConsumer {
         private volatile int m_counter;
         public void add(Resource resource) {
+            System.out.println("RC:ADD " + resource);
             m_counter++;
             try {
                 resource.openStream();
@@ -85,6 +89,7 @@
             }
         }
         public void remove(Resource resource) {
+            System.out.println("RC:REMOVE " + resource);
             m_counter--;
         }
         public void ensure() {
@@ -95,6 +100,7 @@
     static class ResourceProvider {
         private volatile BundleContext m_context;
         private final Ensure m_ensure;
+        private final Map m_handlers = new HashMap();
         private StaticResource[] m_resources = {
             new StaticResource("test1.txt", "/test", "TestRepository") {
                 public InputStream openStream() throws IOException {
@@ -122,32 +128,50 @@
         
         public void add(ServiceReference ref, ResourceHandler handler) {
             String filterString = (String) ref.getProperty("filter");
+            Filter filter;
             try {
-                Filter filter = m_context.createFilter(filterString);
-                for (int i = 0; i < m_resources.length; i++) {
-                    if (filter.match(m_resources[i].getProperties())) {
-                        handler.added(m_resources[i]);
-                    }
-                }
+                filter = m_context.createFilter(filterString);
             }
             catch (InvalidSyntaxException e) {
-                e.printStackTrace();
+                Assert.fail("Could not create filter for resource handler: " + e);
+                return;
+            }
+            synchronized (m_handlers) {
+                m_handlers.put(handler, filter);
+            }
+            for (int i = 0; i < m_resources.length; i++) {
+                if (filter.match(m_resources[i].getProperties())) {
+                    handler.added(m_resources[i]);
+                }
             }
         }
 
         public void remove(ServiceReference ref, ResourceHandler handler) {
-            String filterString = (String) ref.getProperty("filter");
-            try {
-                Filter filter = m_context.createFilter(filterString);
-                for (int i = 0; i < m_resources.length; i++) {
-                    if (filter.match(m_resources[i].getProperties())) {
-                        handler.removed(m_resources[i]);
-                    }
+            Filter filter;
+            synchronized (m_handlers) {
+                filter = (Filter) m_handlers.remove(handler);
+            }
+            removeResources(handler, filter);
+        }
+
+        private void removeResources(ResourceHandler handler, Filter filter) {
+            for (int i = 0; i < m_resources.length; i++) {
+                if (filter.match(m_resources[i].getProperties())) {
+                    handler.removed(m_resources[i]);
                 }
             }
-            catch (InvalidSyntaxException e) {
-                e.printStackTrace();
+        }
+        
+        public void destroy() {
+            Entry[] handlers;
+            synchronized (m_handlers) {
+                handlers = (Entry[]) m_handlers.entrySet().toArray(new Entry[m_handlers.size()]);
+            }
+            for (int i = 0; i < handlers.length; i++) {
+                removeResources((ResourceHandler) handlers[i].getKey(), (Filter) handlers[i].getValue());
             }
+            
+            System.out.println("DESTROY..." + m_handlers.size());
         }
     }
     

Modified: 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=887124&r1=887123&r2=887124&view=diff
==============================================================================
--- felix/trunk/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/SharingDependenciesWithMultipleServicesTest.java (original)
+++ felix/trunk/dependencymanager/test/src/test/java/org/apache/felix/dependencymanager/test/SharingDependenciesWithMultipleServicesTest.java Fri Dec  4 09:23:04 2009
@@ -22,6 +22,8 @@
 import static org.ops4j.pax.exam.CoreOptions.options;
 import static org.ops4j.pax.exam.CoreOptions.provision;
 
+import java.io.IOException;
+import java.io.InputStream;
 import java.util.Dictionary;
 import java.util.Properties;
 
@@ -29,14 +31,20 @@
 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.ResourceDependency;
 import org.apache.felix.dependencymanager.dependencies.ServiceDependency;
 import org.apache.felix.dependencymanager.impl.Logger;
+import org.apache.felix.dependencymanager.resources.Resource;
+import org.apache.felix.dependencymanager.resources.ResourceHandler;
 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.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
 import org.osgi.service.cm.ConfigurationAdmin;
 import org.osgi.service.cm.ConfigurationException;
 import org.osgi.service.cm.ManagedService;
@@ -118,6 +126,27 @@
         m.remove(consumer1);
     }
     
+    @Test
+    public void testShareResourceDependencyWithMultipleServices(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
+        ResourceDependency dependency = m.createResourceDependency().setFilter("(" + Resource.REPOSITORY + "=TestRepository)").setRequired(true);
+        Service consumer1 = m.createService().setImplementation(new ResourceConsumer(e, 1)).add(dependency);
+        Service consumer2 = m.createService().setImplementation(new ResourceConsumer(e, 2)).add(dependency);
+        Service resourceProvider = m.createService().setImplementation(new ResourceProvider()).add(m.createServiceDependency().setService(ResourceHandler.class).setCallbacks("add", "remove"));;
+        m.add(resourceProvider);
+        m.add(consumer1);
+        e.waitForStep(1, 2000);
+        m.add(consumer2);
+        e.waitForStep(2, 2000);
+        m.remove(consumer2);
+        m.remove(consumer1);
+        m.remove(resourceProvider);
+    }
+    
+    
     static interface ServiceInterface {
         public void invoke(Runnable r);
     }
@@ -199,4 +228,93 @@
             m_ensure.step(m_step);
         }
     }
+    
+    static class ResourceConsumer {
+        private final Ensure m_ensure;
+        private int m_step;
+
+        public ResourceConsumer(Ensure e, int step) {
+            m_ensure = e;
+            m_step = step;
+        }
+        
+        public void start() {
+            m_ensure.step(m_step);
+        }
+    }
+    
+    static class ResourceProvider {
+        private volatile BundleContext m_context;
+        private StaticResource[] m_resources = {
+            new StaticResource("test1.txt", "/test", "TestRepository"),
+            new StaticResource("test2.txt", "/test", "TestRepository")
+        };
+
+        public void add(ServiceReference ref, ResourceHandler handler) {
+            String filterString = (String) ref.getProperty("filter");
+            try {
+                Filter filter = m_context.createFilter(filterString);
+                for (int i = 0; i < m_resources.length; i++) {
+                    if (filter.match(m_resources[i].getProperties())) {
+                        handler.added(m_resources[i]);
+                    }
+                }
+            }
+            catch (InvalidSyntaxException e) {
+                e.printStackTrace();
+            }
+        }
+
+        public void remove(ServiceReference ref, ResourceHandler handler) {
+            String filterString = (String) ref.getProperty("filter");
+            try {
+                Filter filter = m_context.createFilter(filterString);
+                for (int i = 0; i < m_resources.length; i++) {
+                    if (filter.match(m_resources[i].getProperties())) {
+                        handler.removed(m_resources[i]);
+                    }
+                }
+            }
+            catch (InvalidSyntaxException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+    
+    static class StaticResource implements Resource {
+        private String m_name;
+        private String m_path;
+        private String m_repository;
+
+        public StaticResource(String name, String path, String repository) {
+            m_name = name;
+            m_path = path;
+            m_repository = repository;
+        }
+
+        public String getName() {
+            return m_name;
+        }
+
+        public String getPath() {
+            return m_path;
+        }
+
+        public String getRepository() {
+            return m_repository;
+        }
+        
+        public Dictionary getProperties() {
+            return new Properties() {{
+                put(Resource.NAME, getName());
+                put(Resource.PATH, getPath());
+                put(Resource.REPOSITORY, getRepository());
+            }};
+        }
+
+        public InputStream openStream() throws IOException {
+            return null;
+        }
+    }
+
 }