You are viewing a plain text version of this content. The canonical link for it is here.
Posted to scm@geronimo.apache.org by gn...@apache.org on 2009/04/20 13:19:48 UTC

svn commit: r766653 - in /geronimo/sandbox/blueprint: blueprint-core/src/main/java/org/apache/geronimo/blueprint/context/ blueprint-itests/src/test/java/org/apache/geronimo/blueprint/itests/ blueprint-sample/src/main/java/org/apache/geronimo/blueprint/...

Author: gnodet
Date: Mon Apr 20 11:19:48 2009
New Revision: 766653

URL: http://svn.apache.org/viewvc?rev=766653&view=rev
Log:
Commit work on UnaryServiceReference

Added:
    geronimo/sandbox/blueprint/blueprint-itests/src/test/java/org/apache/geronimo/blueprint/itests/TestModuleContext.java
      - copied, changed from r766585, geronimo/sandbox/blueprint/blueprint-itests/src/test/java/org/apache/geronimo/blueprint/itests/Test.java
    geronimo/sandbox/blueprint/blueprint-itests/src/test/java/org/apache/geronimo/blueprint/itests/TestReferences.java
    geronimo/sandbox/blueprint/blueprint-sample/src/main/java/org/apache/geronimo/blueprint/sample/BindingListener.java
    geronimo/sandbox/blueprint/blueprint-sample/src/main/java/org/apache/geronimo/blueprint/sample/FooRegistrationListener.java   (contents, props changed)
      - copied, changed from r766585, geronimo/sandbox/blueprint/blueprint-sample/src/main/java/org/apache/geronimo/blueprint/sample/FooListener.java
    geronimo/sandbox/blueprint/blueprint-sample/src/main/java/org/apache/geronimo/blueprint/sample/InterfaceA.java
Removed:
    geronimo/sandbox/blueprint/blueprint-itests/src/test/java/org/apache/geronimo/blueprint/itests/Test.java
    geronimo/sandbox/blueprint/blueprint-sample/src/main/java/org/apache/geronimo/blueprint/sample/FooListener.java
Modified:
    geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/context/BundleDelegatingClassLoader.java
    geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/context/Instanciator.java
    geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/context/ModuleContextImpl.java
    geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/context/ReferenceServiceRecipe.java
    geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/context/ReflectionUtils.java
    geronimo/sandbox/blueprint/blueprint-sample/src/main/resources/OSGI-INF/blueprint/config.xml

Modified: geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/context/BundleDelegatingClassLoader.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/context/BundleDelegatingClassLoader.java?rev=766653&r1=766652&r2=766653&view=diff
==============================================================================
--- geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/context/BundleDelegatingClassLoader.java (original)
+++ geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/context/BundleDelegatingClassLoader.java Mon Apr 20 11:19:48 2009
@@ -33,9 +33,15 @@
 public class BundleDelegatingClassLoader extends ClassLoader {
 
     private final Bundle bundle;
+    private final ClassLoader classLoader;
 
     public BundleDelegatingClassLoader(Bundle bundle) {
+        this(bundle, null);
+    }
+
+    public BundleDelegatingClassLoader(Bundle bundle, ClassLoader classLoader) {
         this.bundle = bundle;
+        this.classLoader = classLoader;
     }
 
     protected Class findClass(String name) throws ClassNotFoundException {
@@ -43,7 +49,11 @@
     }
 
     protected URL findResource(String name) {
-        return bundle.getResource(name);
+        URL resource = findResource(name);
+        if (classLoader != null && resource == null) {
+            resource = classLoader.getResource(name);
+        }
+        return resource;
     }
 
     protected Enumeration findResources(String name) throws IOException {
@@ -51,7 +61,16 @@
     }
 
     protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
-        Class clazz = findClass(name);
+        Class clazz;
+        try {
+            clazz = findClass(name);
+        }
+        catch (ClassNotFoundException cnfe) {
+            if (classLoader != null)
+                clazz = classLoader.loadClass(name);
+            else
+                throw cnfe;
+        }
         if (resolve) {
             resolveClass(clazz);
         }

Modified: geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/context/Instanciator.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/context/Instanciator.java?rev=766653&r1=766652&r2=766653&view=diff
==============================================================================
--- geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/context/Instanciator.java (original)
+++ geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/context/Instanciator.java Mon Apr 20 11:19:48 2009
@@ -58,6 +58,7 @@
 import org.osgi.service.blueprint.reflect.Value;
 import org.osgi.service.blueprint.reflect.UnaryServiceReferenceComponentMetadata;
 import org.osgi.service.blueprint.reflect.CollectionBasedServiceReferenceComponentMetadata;
+import org.osgi.service.blueprint.reflect.BindingListenerMetadata;
 
 /**
  * TODO: javadoc
@@ -148,8 +149,8 @@
             recipe.allow(Option.PRIVATE_PROPERTIES);
             recipe.setName(component.getName());
             recipe.setProperty("moduleContext", moduleContext);
-            LocalComponentMetadata exportedComponent = getServiceComponent(serviceExport.getExportedComponent());
-            if (LocalComponentMetadata.SCOPE_BUNDLE.equals(exportedComponent.getScope())) {
+            LocalComponentMetadata exportedComponent = getLocalServiceComponent(serviceExport.getExportedComponent());
+            if (exportedComponent != null && LocalComponentMetadata.SCOPE_BUNDLE.equals(exportedComponent.getScope())) {
                 Recipe exportedComponentRecipe = createRecipe(exportedComponent);
                 recipe.setProperty("service", new BundleScopeServiceFactory(moduleContext, exportedComponentRecipe));
             } else {
@@ -171,8 +172,20 @@
             }
             return recipe;
         } else if (component instanceof UnaryServiceReferenceComponentMetadata) {
-            // TODO
-            throw new IllegalStateException("Unsupported component type " + component.getClass());
+            UnaryServiceReferenceComponentMetadata metadata = (UnaryServiceReferenceComponentMetadata) component;
+            CollectionRecipe cr = null;
+            if (metadata.getBindingListeners() != null) {
+                cr = new CollectionRecipe(ArrayList.class);;
+                for (BindingListenerMetadata listener : (Collection<BindingListenerMetadata>) metadata.getBindingListeners()) {
+                    cr.add(createRecipe(listener));
+                }
+            }
+            ReferenceServiceRecipe recipe = new ReferenceServiceRecipe(moduleContext,
+                                                                       moduleContext.getSender(),
+                                                                       metadata,
+                                                                       cr);
+            recipe.setName(component.getName());
+            return recipe;
         } else if (component instanceof CollectionBasedServiceReferenceComponentMetadata) {
             // TODO
             throw new IllegalStateException("Unsupported component type " + component.getClass());
@@ -188,17 +201,29 @@
         recipe.setProperty("metadata", listener);
         return recipe;
     }
-    
-    private LocalComponentMetadata getServiceComponent(Value value) throws Exception {
+
+    private Recipe createRecipe(BindingListenerMetadata listener) throws Exception {
+        ObjectRecipe recipe = new ObjectRecipe(ReferenceServiceRecipe.Listener.class);
+        recipe.allow(Option.PRIVATE_PROPERTIES);
+        recipe.setProperty("listener", getValue(listener.getListenerComponent(), null));
+        recipe.setProperty("metadata", listener);
+        return recipe;
+    }
+
+    private LocalComponentMetadata getLocalServiceComponent(Value value) throws Exception {
+        ComponentMetadata metadata = null;
         if (value instanceof ReferenceValue) {
             ReferenceValue ref = (ReferenceValue) value;
             ComponentDefinitionRegistry registry = getComponentDefinitionRegistry();
-            return (LocalComponentMetadata) registry.getComponentDefinition(ref.getComponentName());
+            metadata = registry.getComponentDefinition(ref.getComponentName());
         } else if (value instanceof ComponentValue) {
             ComponentValue comp = (ComponentValue) value;
-            return (LocalComponentMetadata) comp.getComponentMetadata();
+            metadata = comp.getComponentMetadata();
+        }
+        if (metadata instanceof LocalComponentMetadata) {
+            return (LocalComponentMetadata) metadata;
         } else {
-            throw new RuntimeException("Unexpected component value: " + value);
+            return null;
         }
     }
     

Modified: geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/context/ModuleContextImpl.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/context/ModuleContextImpl.java?rev=766653&r1=766652&r2=766653&view=diff
==============================================================================
--- geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/context/ModuleContextImpl.java (original)
+++ geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/context/ModuleContextImpl.java Mon Apr 20 11:19:48 2009
@@ -77,6 +77,10 @@
         this.componentDefinitionRegistry = new ComponentDefinitionRegistryImpl();
     }
 
+    public ModuleContextEventSender getSender() {
+        return sender;
+    }
+
     private void checkDirectives() {
         Bundle bundle = bundleContext.getBundle();
         Dictionary headers = bundle.getHeaders();
@@ -109,7 +113,7 @@
 
             // TODO: handle scopes and such
             Map instances = objectGraph.createAll(new ArrayList<String>(componentDefinitionRegistry.getComponentDefinitionNames()));
-            System.out.println(instances);
+            //System.out.println(instances);
 
             registerAllServices();
             
@@ -126,9 +130,8 @@
             sender.sendWaiting(this, e.getServiceObjectClass(), e.getServiceFilter());
             // TODO: wait for dependency
         } catch (Exception e) {
-            e.printStackTrace();
-            // TODO: pass the exception to the event
             sender.sendFailure(this, e);
+            e.printStackTrace(); // TODO: log failure
         }
     }
 

Modified: geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/context/ReferenceServiceRecipe.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/context/ReferenceServiceRecipe.java?rev=766653&r1=766652&r2=766653&view=diff
==============================================================================
--- geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/context/ReferenceServiceRecipe.java (original)
+++ geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/context/ReferenceServiceRecipe.java Mon Apr 20 11:19:48 2009
@@ -19,68 +19,108 @@
 package org.apache.geronimo.blueprint.context;
 
 import java.lang.reflect.Type;
+import java.lang.reflect.Method;
 import java.util.List;
 import java.util.ArrayList;
 import java.util.Set;
+import java.util.Collection;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Collections;
 
 import org.apache.xbean.recipe.AbstractRecipe;
 import org.apache.xbean.recipe.ConstructionException;
+import org.apache.xbean.recipe.Recipe;
+import org.apache.xbean.recipe.CollectionRecipe;
+import org.apache.xbean.recipe.ExecutionContext;
 import org.apache.geronimo.blueprint.LifeCycle;
 import org.apache.geronimo.blueprint.BlueprintConstants;
+import org.apache.geronimo.blueprint.ModuleContextEventSender;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.FrameworkUtil;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.Constants;
-import org.osgi.framework.Filter;
-import org.osgi.service.blueprint.reflect.ServiceReferenceComponentMetadata;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.service.blueprint.reflect.UnaryServiceReferenceComponentMetadata;
+import org.osgi.service.blueprint.reflect.BindingListenerMetadata;
 import org.osgi.service.blueprint.context.ServiceUnavailableException;
+import org.osgi.service.blueprint.context.ModuleContext;
 import org.osgi.util.tracker.ServiceTracker;
 import org.osgi.util.tracker.ServiceTrackerCustomizer;
 import net.sf.cglib.proxy.ProxyRefDispatcher;
 import net.sf.cglib.proxy.Enhancer;
+import net.sf.cglib.proxy.Dispatcher;
 
 /**
- * A recipe to create an OSGi service reference
+ * A recipe to create an unary OSGi service reference.
+ *
+ * TODO: check synchronization / thread safety
  *
  * @author <a href="mailto:dev@geronimo.apache.org">Apache Geronimo Project</a>
  * @version $Rev: 760378 $, $Date: 2009-03-31 11:31:38 +0200 (Tue, 31 Mar 2009) $
  */
-public class ReferenceServiceRecipe extends AbstractRecipe implements ProxyRefDispatcher, LifeCycle, ServiceTrackerCustomizer {
+public class ReferenceServiceRecipe extends AbstractRecipe implements Dispatcher, LifeCycle, ServiceListener {
 
-    private final BundleContext bundleContext;
+    private final ModuleContext moduleContext;
+    private final ModuleContextEventSender sender;
     private final UnaryServiceReferenceComponentMetadata metadata;
-    private ServiceTracker tracker;
-    private Class type;
+    private final CollectionRecipe listenersRecipe;
+    private Class proxyClass;
     private String filter;
+    private List<Listener> listeners;
 
-    public ReferenceServiceRecipe(BundleContext bundleContext, UnaryServiceReferenceComponentMetadata metadata) {
-        this.bundleContext = bundleContext;
+    private volatile ServiceReference trackedServiceReference;
+    private volatile Object trackedService;
+    private final Object monitor = new Object();
+
+    public ReferenceServiceRecipe(ModuleContext moduleContext,
+                                  ModuleContextEventSender sender,
+                                  UnaryServiceReferenceComponentMetadata metadata,
+                                  CollectionRecipe listenersRecipe) {
+        this.moduleContext = moduleContext;
+        this.sender = sender;
         this.metadata = metadata;
+        this.listenersRecipe = listenersRecipe;
     }
 
     protected Object internalCreate(Type expectedType, boolean lazyRefAllowed) throws ConstructionException {
-        // TODO: bindingListeners
         // TODO: serviceAvailabilitySpecification
         try {
-            ClassLoader classLoader = new BundleDelegatingClassLoader(bundleContext.getBundle());
+            ClassLoader classLoader = new BundleDelegatingClassLoader(moduleContext.getBundleContext().getBundle(),
+                                                                      getClass().getClassLoader());
             Enhancer e = new Enhancer();
             e.setClassLoader(classLoader);
-            type = getTargetClass(classLoader);
-            e.setSuperclass(type);
+            e.setSuperclass(getTargetClass(classLoader));
             e.setInterfaces(getInterfaces(classLoader));
             e.setInterceptDuringConstruction(false);
             e.setCallback(this);
             e.setUseFactory(false);
             Object obj = e.create();
+            proxyClass = obj.getClass();
+
+            if (listenersRecipe != null) {
+                listeners = (List<Listener>) listenersRecipe.create(classLoader);
+                for (Listener listener : listeners) {
+                    listener.init(proxyClass);
+                }
+            } else {
+                listeners = Collections.emptyList();
+            }
 
             filter = getOsgiFilter();
-            tracker = new ServiceTracker(bundleContext, FrameworkUtil.createFilter(filter), this);
-            tracker.open();
+            moduleContext.getBundleContext().addServiceListener(this, filter);
+            retrack();
+
+            if (getName() != null) {
+                ExecutionContext.getContext().addObject(getName(), obj);
+            }
 
             return obj;
-        } catch (Exception e) {
-            throw new ConstructionException(e);
+        } catch (Throwable t) {
+            t.printStackTrace();
+            throw new ConstructionException(t);
         }
     }
 
@@ -134,6 +174,10 @@
         for (String name : (Set<String>) metadata.getInterfaceNames()) {
             Class clazz = classLoader.loadClass(name);
             if (!clazz.isInterface()) {
+                if (root == Object.class) {
+                    root = clazz;
+                    continue;
+                }
                 // Check that all classes are in the same hierarchy
                 for (Class p = clazz; p != Object.class; p = p.getSuperclass()) {
                     if (p == root) {
@@ -156,14 +200,18 @@
         return true;
     }
 
-    public Object loadObject(Object proxy) throws Exception {
-        Object svc = tracker.getService();
+    public Object loadObject() throws Exception {
+        Object svc = trackedService;
         if (svc == null && metadata.getTimeout() > 0) {
-            // TODO: send WAIT event
-            svc = tracker.waitForService(metadata.getTimeout());
+            Set<String> interfaces = (Set<String>) metadata.getInterfaceNames();
+            sender.sendWaiting(moduleContext, interfaces.toArray(new String[interfaces.size()]), filter);
+            synchronized (monitor) {
+                monitor.wait(metadata.getTimeout());
+            }
+            svc = trackedService;
         }
         if (svc == null) {
-            throw new ServiceUnavailableException("Timeout expired when waiting for OSGi service", type, filter);
+            throw new ServiceUnavailableException("Timeout expired when waiting for OSGi service", proxyClass.getSuperclass(), filter);
         }
         return svc;
     }
@@ -172,22 +220,186 @@
     }
 
     public void destroy() throws Exception {
-        ServiceTracker t = tracker;
-        tracker = null;
-        if (t != null) {
-            t.close();
+        moduleContext.getBundleContext().removeServiceListener(this);
+    }
+
+    private ServiceReference getBestServiceReference(ServiceReference[] references) {
+        int length = (references == null) ? 0 : references.length;
+        if (length == 0) { /* if no service is being tracked */
+            return null;
+        }
+        int index = 0;
+        if (length > 1) { /* if more than one service, select highest ranking */
+            int rankings[] = new int[length];
+            int count = 0;
+            int maxRanking = Integer.MIN_VALUE;
+            for (int i = 0; i < length; i++) {
+                Object property = references[i].getProperty(Constants.SERVICE_RANKING);
+                int ranking = (property instanceof Integer) ? ((Integer) property).intValue() : 0;
+                rankings[i] = ranking;
+                if (ranking > maxRanking) {
+                    index = i;
+                    maxRanking = ranking;
+                    count = 1;
+                } else {
+                    if (ranking == maxRanking) {
+                        count++;
+                    }
+                }
+            }
+            if (count > 1) { /* if still more than one service, select lowest id */
+                long minId = Long.MAX_VALUE;
+                for (int i = 0; i < length; i++) {
+                    if (rankings[i] == maxRanking) {
+                        long id = ((Long) (references[i].getProperty(Constants.SERVICE_ID))).longValue();
+                        if (id < minId) {
+                            index = i;
+                            minId = id;
+                        }
+                    }
+                }
+            }
         }
+        return references[index];
     }
 
-    public Object addingService(ServiceReference reference) {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    private void retrack() {
+        try {
+            ServiceReference[] refs = moduleContext.getBundleContext().getServiceReferences(null, filter);
+            ServiceReference ref = getBestServiceReference(refs);
+            if (ref != null) {
+                bind(ref);
+            } else {
+                unbind();
+            }
+        } catch (InvalidSyntaxException e) {
+            // Ignore, should never happen
+        }
     }
 
-    public void modifiedService(ServiceReference reference, Object service) {
-        //To change body of implemented methods use File | Settings | File Templates.
+    private void track(ServiceReference ref) {
+        if (trackedServiceReference == null) {
+            bind(ref);
+        } else {
+            Object property = trackedServiceReference.getProperty(Constants.SERVICE_RANKING);
+            int trackedRanking = (property instanceof Integer) ? ((Integer) property).intValue() : 0;
+            property = ref.getProperty(Constants.SERVICE_RANKING);
+            int newRanking = (property instanceof Integer) ? ((Integer) property).intValue() : 0;
+            if (trackedRanking > newRanking) {
+                return;
+            } else if (trackedRanking == newRanking) {
+                long trackedId = ((Long) (trackedServiceReference.getProperty(Constants.SERVICE_ID))).longValue();
+                long newId = ((Long) (ref.getProperty(Constants.SERVICE_ID))).longValue();
+                if (trackedId < newId) {
+                    return;
+                }
+            }
+            bind(ref);
+        }
     }
 
-    public void removedService(ServiceReference reference, Object service) {
-        //To change body of implemented methods use File | Settings | File Templates.
+    private void bind(ServiceReference ref) {
+        synchronized (monitor) {
+            if (trackedServiceReference != null) {
+                moduleContext.getBundleContext().ungetService(trackedServiceReference);
+            }
+            trackedServiceReference = ref;
+            trackedService = moduleContext.getBundleContext().getService(trackedServiceReference);
+            for (Listener listener : listeners) {
+                listener.bind(trackedServiceReference, trackedService);
+            }
+        }
+    }
+
+    private void unbind() {
+        synchronized (monitor) {
+            if (trackedServiceReference != null) {
+                for (Listener listener : listeners) {
+                    listener.unbind(trackedServiceReference, trackedService);
+                }
+                moduleContext.getBundleContext().ungetService(trackedServiceReference);
+                trackedServiceReference = null;
+                trackedService = null;
+            }
+        }
+    }
+
+    public void serviceChanged(ServiceEvent event) {
+        int eventType = event.getType();
+        ServiceReference ref = event.getServiceReference();
+        switch (eventType) {
+            case ServiceEvent.REGISTERED:
+            case ServiceEvent.MODIFIED:
+                track(ref);
+                break;
+            case ServiceEvent.UNREGISTERING:
+                retrack();
+                break;
+        }
+    }
+
+    public static class Listener {
+
+        private Object listener;
+        private BindingListenerMetadata metadata;
+        private List<Method> bindMethodsOneArg;
+        private List<Method> bindMethodsTwoArgs;
+        private List<Method> unbindMethodsOneArg;
+        private List<Method> unbindMethodsTwoArgs;
+
+        public void init(Class proxyClass) {
+            Class listenerClass = listener.getClass();
+            Class[] oneArgParams = new Class[] { ServiceReference.class };
+            Class[] twoArgsParams = new Class[] { proxyClass, Map.class };
+            String bindName = metadata.getBindMethodName();
+            if (bindName != null) {
+                bindMethodsOneArg = ReflectionUtils.findCompatibleMethods(listenerClass, bindName, oneArgParams);
+                bindMethodsTwoArgs = ReflectionUtils.findCompatibleMethods(listenerClass, bindName, twoArgsParams);
+            } else {
+                bindMethodsOneArg = Collections.emptyList();
+                bindMethodsTwoArgs = Collections.emptyList();
+            }
+            String unbindName = metadata.getUnbindMethodName();
+            if (unbindName != null) {
+                unbindMethodsOneArg = ReflectionUtils.findCompatibleMethods(listenerClass, unbindName, oneArgParams);
+                unbindMethodsTwoArgs = ReflectionUtils.findCompatibleMethods(listenerClass, unbindName, twoArgsParams);
+            } else {
+                unbindMethodsOneArg = Collections.emptyList();
+                unbindMethodsTwoArgs = Collections.emptyList();
+            }
+        }
+
+        public void bind(ServiceReference reference, Object service) {
+            invokeMethods(bindMethodsOneArg, bindMethodsTwoArgs, reference, service);
+        }
+
+        public void unbind(ServiceReference reference, Object service) {
+            invokeMethods(unbindMethodsOneArg, unbindMethodsTwoArgs, reference, service);
+        }
+
+        private void invokeMethods(List<Method> oneArgMethods, List<Method> twoArgsMethods, ServiceReference reference, Object service) {
+            for (Method method : oneArgMethods) {
+                try {
+                    method.invoke(listener, reference);
+                } catch (Exception e) {
+                    e.printStackTrace(); // TODO: log
+                }
+            }
+            Map<String, Object> props = null;
+            for (Method method : twoArgsMethods) {
+                if (props == null) {
+                    props = new HashMap<String, Object>();
+                    for (String name : reference.getPropertyKeys()) {
+                        props.put(name, reference.getProperty(name));
+                    }
+                }
+                try {
+                    method.invoke(listener, service, props);
+                } catch (Exception e) {
+                    e.printStackTrace(); // TODO: log
+                }
+            }
+        }
+
     }
 }

Modified: geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/context/ReflectionUtils.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/context/ReflectionUtils.java?rev=766653&r1=766652&r2=766653&view=diff
==============================================================================
--- geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/context/ReflectionUtils.java (original)
+++ geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/context/ReflectionUtils.java Mon Apr 20 11:19:48 2009
@@ -20,6 +20,8 @@
 
 import java.lang.reflect.Method;
 import java.util.Set;
+import java.util.List;
+import java.util.ArrayList;
 
 /**
  * TODO: javadoc
@@ -48,15 +50,15 @@
         return classes;
     }
     
-    public static Method findMethod(Class clazz, String name, Class[] paramTypes) {    
+    public static Method findMethod(Class clazz, String name, Class[] paramTypes) {
         try {
             return clazz.getMethod(name, paramTypes);
         } catch (NoSuchMethodException e) {
-            return findCompatibileMethod(clazz, name, paramTypes);
+            return findCompatibleMethod(clazz, name, paramTypes);
         }
     }
 
-    public static Method findCompatibileMethod(Class clazz, String name, Class[] paramTypes) {
+    public static Method findCompatibleMethod(Class clazz, String name, Class[] paramTypes) {
         Method[] methods = clazz.getMethods();
         for (Method method :  methods) {
             Class[] methodParams = method.getParameterTypes();
@@ -71,5 +73,23 @@
             }
         }
         return null;
-    }        
+    }
+
+    public static List<Method> findCompatibleMethods(Class clazz, String name, Class[] paramTypes) {
+        List<Method> methods = new ArrayList<Method>();
+        for (Method method : clazz.getMethods()) {
+            Class[] methodParams = method.getParameterTypes();
+            if (name.equals(method.getName()) && methodParams.length == paramTypes.length) {
+                boolean assignable = true;
+                for (int i = 0; i < paramTypes.length && assignable; i++) {
+                    assignable = methodParams[i].isAssignableFrom(paramTypes[i]);
+                }
+                if (assignable) {
+                    methods.add(method);
+                }
+            }
+        }
+        return methods;
+    }
+
 }

Copied: geronimo/sandbox/blueprint/blueprint-itests/src/test/java/org/apache/geronimo/blueprint/itests/TestModuleContext.java (from r766585, geronimo/sandbox/blueprint/blueprint-itests/src/test/java/org/apache/geronimo/blueprint/itests/Test.java)
URL: http://svn.apache.org/viewvc/geronimo/sandbox/blueprint/blueprint-itests/src/test/java/org/apache/geronimo/blueprint/itests/TestModuleContext.java?p2=geronimo/sandbox/blueprint/blueprint-itests/src/test/java/org/apache/geronimo/blueprint/itests/TestModuleContext.java&p1=geronimo/sandbox/blueprint/blueprint-itests/src/test/java/org/apache/geronimo/blueprint/itests/Test.java&r1=766585&r2=766653&rev=766653&view=diff
==============================================================================
--- geronimo/sandbox/blueprint/blueprint-itests/src/test/java/org/apache/geronimo/blueprint/itests/Test.java (original)
+++ geronimo/sandbox/blueprint/blueprint-itests/src/test/java/org/apache/geronimo/blueprint/itests/TestModuleContext.java Mon Apr 20 11:19:48 2009
@@ -19,16 +19,22 @@
 package org.apache.geronimo.blueprint.itests;
 
 import java.net.URLDecoder;
+import java.util.Properties;
+import java.util.Hashtable;
 
 import org.apache.servicemix.kernel.testing.support.AbstractIntegrationTest;
 import org.apache.geronimo.blueprint.sample.Foo;
 import org.apache.geronimo.blueprint.sample.Bar;
+import org.apache.geronimo.blueprint.sample.InterfaceA;
 import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.Constants;
 import org.osgi.service.blueprint.context.ModuleContext;
+import org.osgi.service.blueprint.context.ServiceUnavailableException;
 import org.springframework.core.io.Resource;
 import org.springframework.util.Assert;
 
-public class Test extends AbstractIntegrationTest {
+public class TestModuleContext extends AbstractIntegrationTest {
 
     public void test() throws Exception {
         Resource res = locateBundle(getBundle("org.apache.geronimo", "blueprint-sample"));
@@ -61,6 +67,10 @@
         }
     }
 
+    public void testUnaryReference() throws Exception {
+
+    }
+
     /**
 	 * The manifest to use for the "virtual bundle" created
 	 * out of the test classes and resources in this project

Added: geronimo/sandbox/blueprint/blueprint-itests/src/test/java/org/apache/geronimo/blueprint/itests/TestReferences.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/blueprint/blueprint-itests/src/test/java/org/apache/geronimo/blueprint/itests/TestReferences.java?rev=766653&view=auto
==============================================================================
--- geronimo/sandbox/blueprint/blueprint-itests/src/test/java/org/apache/geronimo/blueprint/itests/TestReferences.java (added)
+++ geronimo/sandbox/blueprint/blueprint-itests/src/test/java/org/apache/geronimo/blueprint/itests/TestReferences.java Mon Apr 20 11:19:48 2009
@@ -0,0 +1,144 @@
+/*
+ * 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.geronimo.blueprint.itests;
+
+import java.net.URLDecoder;
+import java.util.Properties;
+import java.util.Hashtable;
+
+import org.apache.servicemix.kernel.testing.support.AbstractIntegrationTest;
+import org.apache.geronimo.blueprint.sample.Foo;
+import org.apache.geronimo.blueprint.sample.Bar;
+import org.apache.geronimo.blueprint.sample.InterfaceA;
+import org.apache.geronimo.blueprint.sample.BindingListener;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.Constants;
+import org.osgi.service.blueprint.context.ModuleContext;
+import org.osgi.service.blueprint.context.ServiceUnavailableException;
+import org.springframework.core.io.Resource;
+import org.springframework.util.Assert;
+
+public class TestReferences extends AbstractIntegrationTest {
+
+    public void testUnaryReference() throws Exception {
+        ModuleContext moduleContext = getOsgiService(ModuleContext.class, 5000);
+        assertNotNull(moduleContext);
+
+        BindingListener listener = (BindingListener) moduleContext.getComponent("bindingListener");
+        assertNull(listener.getA());
+        assertNull(listener.getReference());
+
+        InterfaceA a = (InterfaceA) moduleContext.getComponent("ref2");
+        try {
+            a.hello("world");
+            fail("A ServiceUnavailableException should have been thrown");
+        } catch (ServiceUnavailableException e) {
+            // Ignore, expected
+        }
+
+        ServiceRegistration reg1 = bundleContext.registerService(InterfaceA.class.getName(), new InterfaceA() {
+            public String hello(String msg) {
+                return "Hello " + msg + "!";
+            }
+        }, null);
+        assertNotNull(listener.getA());
+        assertNotNull(listener.getReference());
+        assertEquals("Hello world!", a.hello("world"));
+
+        Hashtable props = new Hashtable();
+        props.put(Constants.SERVICE_RANKING, Integer.valueOf(1));
+        ServiceRegistration reg2 = bundleContext.registerService(InterfaceA.class.getName(), new InterfaceA() {
+            public String hello(String msg) {
+                return "Good morning " + msg + "!";
+            }
+        }, props);
+        assertNotNull(listener.getA());
+        assertNotNull(listener.getReference());
+        assertEquals("Good morning world!", a.hello("world"));
+
+        reg2.unregister();
+        assertNotNull(listener.getA());
+        assertNotNull(listener.getReference());
+        assertEquals("Hello world!", a.hello("world"));
+
+        reg1.unregister();
+        assertNull(listener.getA());
+        assertNull(listener.getReference());
+        try {
+            a.hello("world");
+            fail("A ServiceUnavailableException should have been thrown");
+        } catch (ServiceUnavailableException e) {
+            // Ignore, expected
+        }
+    }
+
+    /**
+	 * The manifest to use for the "virtual bundle" created
+	 * out of the test classes and resources in this project
+	 *
+	 * This is actually the boilerplate manifest with one additional
+	 * import-package added. We should provide a simpler customization
+	 * point for such use cases that doesn't require duplication
+	 * of the entire manifest...
+	 */
+	protected String getManifestLocation() {
+		return "classpath:org/apache/geronimo/blueprint/MANIFEST.MF";
+	}
+
+	/**
+	 * The location of the packaged OSGi bundles to be installed
+	 * for this test. Values are Spring resource paths. The bundles
+	 * we want to use are part of the same multi-project maven
+	 * build as this project is. Hence we use the localMavenArtifact
+	 * helper method to find the bundles produced by the package
+	 * phase of the maven build (these tests will run after the
+	 * packaging phase, in the integration-test phase).
+	 *
+	 * JUnit, commons-logging, spring-core and the spring OSGi
+	 * test bundle are automatically included so do not need
+	 * to be specified here.
+	 */
+	protected String[] getTestBundlesNames() {
+        return new String[] {
+                getBundle("org.apache.geronimo", "blueprint-bundle"),
+                getBundle("org.apache.geronimo", "blueprint-sample"),
+		};
+	}
+
+    private Bundle installBundle(Resource location) throws Exception {
+        Assert.notNull(bundleContext);
+        Assert.notNull(location);
+        if (logger.isDebugEnabled())
+            logger.debug("Installing bundle from location " + location.getDescription());
+
+        String bundleLocation;
+
+        try {
+            bundleLocation = URLDecoder.decode(location.getURL().toExternalForm(), "UTF-8");
+        }
+        catch (Exception ex) {
+            // the URL cannot be created, fall back to the description
+            bundleLocation = location.getDescription();
+        }
+
+        return bundleContext.installBundle(bundleLocation, location.getInputStream());
+    }
+
+}
\ No newline at end of file

Added: geronimo/sandbox/blueprint/blueprint-sample/src/main/java/org/apache/geronimo/blueprint/sample/BindingListener.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/blueprint/blueprint-sample/src/main/java/org/apache/geronimo/blueprint/sample/BindingListener.java?rev=766653&view=auto
==============================================================================
--- geronimo/sandbox/blueprint/blueprint-sample/src/main/java/org/apache/geronimo/blueprint/sample/BindingListener.java (added)
+++ geronimo/sandbox/blueprint/blueprint-sample/src/main/java/org/apache/geronimo/blueprint/sample/BindingListener.java Mon Apr 20 11:19:48 2009
@@ -0,0 +1,59 @@
+/**
+ *  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.geronimo.blueprint.sample;
+
+import java.util.Map;
+import java.io.Serializable;
+
+import org.osgi.framework.ServiceReference;
+
+public class BindingListener {
+
+    private InterfaceA a;
+    private Map props;
+    private ServiceReference reference;
+
+    public InterfaceA getA() {
+        return a;
+    }
+
+    public Map getProps() {
+        return props;
+    }
+
+    public ServiceReference getReference() {
+        return reference;
+    }
+
+    public void bind(InterfaceA a, Map props) {
+        this.a = a;
+        this.props = props;
+    }
+
+    public void bind(ServiceReference ref) {
+        this.reference = ref;
+    }
+
+    public void unbind(InterfaceA a, Map props) {
+        this.a = null;
+        this.props = null;
+    }
+
+    public void unbind(ServiceReference ref) {
+        this.reference = null;
+    }
+}

Copied: geronimo/sandbox/blueprint/blueprint-sample/src/main/java/org/apache/geronimo/blueprint/sample/FooRegistrationListener.java (from r766585, geronimo/sandbox/blueprint/blueprint-sample/src/main/java/org/apache/geronimo/blueprint/sample/FooListener.java)
URL: http://svn.apache.org/viewvc/geronimo/sandbox/blueprint/blueprint-sample/src/main/java/org/apache/geronimo/blueprint/sample/FooRegistrationListener.java?p2=geronimo/sandbox/blueprint/blueprint-sample/src/main/java/org/apache/geronimo/blueprint/sample/FooRegistrationListener.java&p1=geronimo/sandbox/blueprint/blueprint-sample/src/main/java/org/apache/geronimo/blueprint/sample/FooListener.java&r1=766585&r2=766653&rev=766653&view=diff
==============================================================================
--- geronimo/sandbox/blueprint/blueprint-sample/src/main/java/org/apache/geronimo/blueprint/sample/FooListener.java (original)
+++ geronimo/sandbox/blueprint/blueprint-sample/src/main/java/org/apache/geronimo/blueprint/sample/FooRegistrationListener.java Mon Apr 20 11:19:48 2009
@@ -19,7 +19,7 @@
 import java.io.Serializable;
 import java.util.Map;
 
-public class FooListener {
+public class FooRegistrationListener {
         
     public void serviceRegistered(Serializable foo, Map props) {
         System.out.println("Service registration notification: " + foo + " " + props);

Propchange: geronimo/sandbox/blueprint/blueprint-sample/src/main/java/org/apache/geronimo/blueprint/sample/FooRegistrationListener.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/sandbox/blueprint/blueprint-sample/src/main/java/org/apache/geronimo/blueprint/sample/FooRegistrationListener.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/sandbox/blueprint/blueprint-sample/src/main/java/org/apache/geronimo/blueprint/sample/FooRegistrationListener.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/sandbox/blueprint/blueprint-sample/src/main/java/org/apache/geronimo/blueprint/sample/InterfaceA.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/blueprint/blueprint-sample/src/main/java/org/apache/geronimo/blueprint/sample/InterfaceA.java?rev=766653&view=auto
==============================================================================
--- geronimo/sandbox/blueprint/blueprint-sample/src/main/java/org/apache/geronimo/blueprint/sample/InterfaceA.java (added)
+++ geronimo/sandbox/blueprint/blueprint-sample/src/main/java/org/apache/geronimo/blueprint/sample/InterfaceA.java Mon Apr 20 11:19:48 2009
@@ -0,0 +1,22 @@
+/**
+ *  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.geronimo.blueprint.sample;
+
+public interface InterfaceA {
+
+    String hello(String msg);
+}

Modified: geronimo/sandbox/blueprint/blueprint-sample/src/main/resources/OSGI-INF/blueprint/config.xml
URL: http://svn.apache.org/viewvc/geronimo/sandbox/blueprint/blueprint-sample/src/main/resources/OSGI-INF/blueprint/config.xml?rev=766653&r1=766652&r2=766653&view=diff
==============================================================================
--- geronimo/sandbox/blueprint/blueprint-sample/src/main/resources/OSGI-INF/blueprint/config.xml (original)
+++ geronimo/sandbox/blueprint/blueprint-sample/src/main/resources/OSGI-INF/blueprint/config.xml Mon Apr 20 11:19:48 2009
@@ -35,16 +35,24 @@
     <component id="converter2" class="org.apache.geronimo.blueprint.sample.CurrencyTypeConverter"/>
 
     <service ref="foo" auto-export="all-classes">
-        <registration-listener ref="fooListener"         
-                               registration-method="serviceRegistered"
-                               unregistration-method="serviceUnregistered"/>
-                                   
         <service-properties>
             <entry key="key" value="value"/>
         </service-properties>
+        <registration-listener ref="fooRegistrationListener"
+                               registration-method="serviceRegistered"
+                               unregistration-method="serviceUnregistered"/>
     </service>
     
-    <component id="fooListener" class="org.apache.geronimo.blueprint.sample.FooListener"/>
-    
+    <component id="fooRegistrationListener" class="org.apache.geronimo.blueprint.sample.FooRegistrationListener"/>
+
+    <reference id="ref" interface="org.apache.geronimo.blueprint.sample.Foo">
+    </reference>
+
+    <reference id="ref2" interface="org.apache.geronimo.blueprint.sample.InterfaceA" timeout="100">
+        <listener bind-method="bind" unbind-method="unbind" ref="bindingListener" />
+    </reference>
+
+    <component id="bindingListener" class="org.apache.geronimo.blueprint.sample.BindingListener"/>
+
 </components>