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 2006/03/25 21:46:21 UTC

svn commit: r388822 [2/2] - in /incubator/felix/trunk/org.apache.felix.dependencymanager: ./ src/ src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/ src/main/java/org/apache/felix/ src/main/java/org/apache/felix/dependencymanager/

Added: incubator/felix/trunk/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceTracker.java
URL: http://svn.apache.org/viewcvs/incubator/felix/trunk/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceTracker.java?rev=388822&view=auto
==============================================================================
--- incubator/felix/trunk/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceTracker.java (added)
+++ incubator/felix/trunk/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceTracker.java Sat Mar 25 12:46:19 2006
@@ -0,0 +1,847 @@
+/*
+ *   Copyright 2006 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.dependencymanager;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * TODO copied this from the OSGi specification, but it's not clear if that
+ * is allowed or not, for now I modified as little as possible but I might
+ * integrate only the parts I want as soon as this code is finished. Perhaps
+ * it would be better to borrow the Knopflerfish implementation here.
+ * 
+ * @author Marcel Offermans
+ */
+public class ServiceTracker implements ServiceTrackerCustomizer
+{
+    /**
+	 * Bundle context this <tt>ServiceTracker</tt> object is tracking against.
+	 */
+    protected final BundleContext context;
+
+    /**
+	 * Filter specifying search criteria for the services to track.
+	 * @since 1.1
+	 */
+    protected final Filter filter;
+
+    /**
+	 * Tracked services: <tt>ServiceReference</tt> object -> customized Object
+	 * and <tt>ServiceListener</tt> object
+	 */
+    private Tracked tracked;
+
+    /** <tt>ServiceTrackerCustomizer</tt> object for this tracker. */
+    private ServiceTrackerCustomizer customizer;
+
+    /**
+	 * Create a <tt>ServiceTracker</tt> object on the specified <tt>ServiceReference</tt> object.
+	 *
+	 * <p>The service referenced by the specified <tt>ServiceReference</tt> object
+	 * will be tracked by this <tt>ServiceTracker</tt> object.
+	 *
+	 * @param context   <tt>BundleContext</tt> object against which the tracking is done.
+	 * @param reference <tt>ServiceReference</tt> object for the service to be tracked.
+	 * @param customizer The customizer object to call when services are
+	 * added, modified, or removed in this <tt>ServiceTracker</tt> object.
+	 * If customizer is <tt>null</tt>, then this <tt>ServiceTracker</tt> object will be used
+	 * as the <tt>ServiceTrackerCustomizer</tt> object and the <tt>ServiceTracker</tt>
+	 * object will call the <tt>ServiceTrackerCustomizer</tt> methods on itself.
+	 */
+    public ServiceTracker(BundleContext context, ServiceReference reference,
+                          ServiceTrackerCustomizer customizer)
+    {
+        this.context = context;
+        this.customizer = (customizer == null) ? this : customizer;
+
+        try
+        {
+            this.filter = context.createFilter("("+Constants.SERVICE_ID+"="+reference.getProperty(Constants.SERVICE_ID).toString()+")");
+        }
+        catch (InvalidSyntaxException e)
+        {
+            throw new RuntimeException("unexpected InvalidSyntaxException: "+e.getMessage());
+        }
+    }
+
+    /**
+	 * Create a <tt>ServiceTracker</tt> object on the specified class name.
+	 *
+	 * <p>Services registered under the specified class name will be tracked
+	 * by this <tt>ServiceTracker</tt> object.
+	 *
+	 * @param context   <tt>BundleContext</tt> object against which the tracking is done.
+	 * @param clazz     Class name of the services to be tracked.
+	 * @param customizer The customizer object to call when services are
+	 * added, modified, or removed in this <tt>ServiceTracker</tt> object.
+	 * If customizer is <tt>null</tt>, then this <tt>ServiceTracker</tt> object will be used
+	 * as the <tt>ServiceTrackerCustomizer</tt> object and the <tt>ServiceTracker</tt> object
+	 * will call the <tt>ServiceTrackerCustomizer</tt> methods on itself.
+	 */
+    public ServiceTracker(BundleContext context, String clazz,
+                          ServiceTrackerCustomizer customizer)
+    {
+        this.context = context;
+        this.customizer = (customizer == null) ? this : customizer;
+
+        try
+        {
+            this.filter = context.createFilter("("+Constants.OBJECTCLASS+"="+clazz+")");
+        }
+        catch (InvalidSyntaxException e)
+        {
+            throw new RuntimeException("unexpected InvalidSyntaxException: "+e.getMessage());
+        }
+
+        if (clazz == null)
+        {
+            throw new NullPointerException();
+        }
+    }
+
+    /**
+	 * Create a <tt>ServiceTracker</tt> object on the specified <tt>Filter</tt> object.
+	 *
+	 * <p>Services which match the specified <tt>Filter</tt> object will be tracked
+	 * by this <tt>ServiceTracker</tt> object.
+	 *
+	 * @param context   <tt>BundleContext</tt> object against which the tracking is done.
+	 * @param filter    <tt>Filter</tt> object to select the services to be tracked.
+	 * @param customizer The customizer object to call when services are
+	 * added, modified, or removed in this <tt>ServiceTracker</tt> object.
+	 * If customizer is null, then this <tt>ServiceTracker</tt> object will be used
+	 * as the <tt>ServiceTrackerCustomizer</tt> object and the <tt>ServiceTracker</tt>
+	 * object will call the <tt>ServiceTrackerCustomizer</tt> methods on itself.
+	 * @since 1.1
+	 */
+    public ServiceTracker(BundleContext context, Filter filter,
+                          ServiceTrackerCustomizer customizer)
+    {
+        this.context = context;
+        this.filter = filter;
+        this.customizer = (customizer == null) ? this : customizer;
+
+        if ((context == null) || (filter == null))
+        {
+            throw new NullPointerException();
+        }
+    }
+
+    /**
+	 * Open this <tt>ServiceTracker</tt> object and begin tracking services.
+	 *
+	 * <p>Services which match the search criteria specified when
+	 * this <tt>ServiceTracker</tt> object was created are now tracked
+	 * by this <tt>ServiceTracker</tt> object.
+	 *
+	 * @throws java.lang.IllegalStateException if the <tt>BundleContext</tt>
+	 * object with which this <tt>ServiceTracker</tt> object was created is no longer valid.
+	 */
+    public synchronized void open()
+    {
+        if (tracked == null)
+        {
+            tracked = new Tracked(customizer, filter);
+
+            ServiceReference[] references;
+
+            synchronized (tracked)
+            {
+                context.addServiceListener(tracked);
+
+                try
+                {
+                    references = context.getServiceReferences(null, filter.toString());
+                }
+                catch (InvalidSyntaxException e)
+                {
+                    throw new RuntimeException("unexpected InvalidSyntaxException");
+                }
+            }
+
+            /* Call tracked outside of synchronized region */
+            if (references != null)
+            {
+                int size = references.length;
+
+                for (int i=0; i < size; i++)
+                {
+                    ServiceReference reference = references[i];
+
+                    /* if the service is still registered */
+                    if (reference.getBundle() != null)
+                    {
+                        tracked.track(reference);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+	 * Close this <tt>ServiceTracker</tt> object.
+	 *
+	 * <p>This method should be called when this <tt>ServiceTracker</tt> object
+	 * should end the tracking of services.
+	 */
+
+    public synchronized void close()
+    {
+        if (tracked != null)
+        {
+            tracked.close();
+
+            ServiceReference references[] = getServiceReferences();
+
+            Tracked outgoing = tracked;
+            tracked = null;
+
+            try
+            {
+                context.removeServiceListener(outgoing);
+            }
+            catch (IllegalStateException e)
+            {
+                /* In case the context was stopped. */
+            }
+
+            if (references != null)
+            {
+                for (int i = 0; i < references.length; i++)
+                {
+                    outgoing.untrack(references[i]);
+                }
+            }
+        }
+    }
+
+    /**
+	 * Properly close this <tt>ServiceTracker</tt> object when finalized.
+	 * This method calls the <tt>close</tt> method to close this <tt>ServiceTracker</tt> object
+	 * if it has not already been closed.
+	 *
+	 */
+    protected void finalize() throws Throwable
+    {
+        close();
+    }
+
+    /**
+	 * Default implementation of the <tt>ServiceTrackerCustomizer.addingService</tt> method.
+	 *
+	 * <p>This method is only called when this <tt>ServiceTracker</tt> object
+	 * has been constructed with a <tt>null ServiceTrackerCustomizer</tt> argument.
+	 *
+	 * The default implementation returns the result of
+	 * calling <tt>getService</tt>, on the
+	 * <tt>BundleContext</tt> object with which this <tt>ServiceTracker</tt> object was created,
+	 * passing the specified <tt>ServiceReference</tt> object.
+	 * <p>This method can be overridden in a subclass to customize
+	 * the service object to be tracked for the service
+	 * being added. In that case, take care not
+	 * to rely on the default implementation of removedService that will unget the service.
+	 *
+	 * @param reference Reference to service being added to this
+	 * <tt>ServiceTracker</tt> object.
+	 * @return The service object to be tracked for the service
+	 * added to this <tt>ServiceTracker</tt> object.
+	 * @see ServiceTrackerCustomizer
+	 */
+    public Object addingService(ServiceReference reference)
+    {
+        return context.getService(reference);
+    }
+    public void addedService(ServiceReference ref, Object service) {
+        // do nothing
+    }
+    
+    /**
+	 * Default implementation of the <tt>ServiceTrackerCustomizer.modifiedService</tt> method.
+	 *
+	 * <p>This method is only called when this <tt>ServiceTracker</tt> object
+	 * has been constructed with a <tt>null ServiceTrackerCustomizer</tt> argument.
+	 *
+	 * The default implementation does nothing.
+	 *
+	 * @param reference Reference to modified service.
+	 * @param service The service object for the modified service.
+	 * @see ServiceTrackerCustomizer
+	 */
+    public void modifiedService(ServiceReference reference, Object service)
+    {
+    }
+
+    /**
+	 * Default implementation of the <tt>ServiceTrackerCustomizer.removedService</tt> method.
+	 *
+	 * <p>This method is only called when this <tt>ServiceTracker</tt> object
+	 * has been constructed with a <tt>null ServiceTrackerCustomizer</tt> argument.
+	 *
+	 * The default implementation
+	 * calls <tt>ungetService</tt>, on the
+	 * <tt>BundleContext</tt> object with which this <tt>ServiceTracker</tt> object was created,
+	 * passing the specified <tt>ServiceReference</tt> object.
+	 * <p>This method can be overridden in a subclass. If the default
+	 * implementation of <tt>addingService</tt> method was used, this method must unget the service.
+	 *
+	 * @param reference Reference to removed service.
+	 * @param object The service object for the removed service.
+	 * @see ServiceTrackerCustomizer
+	 */
+    public void removedService(ServiceReference reference, Object object)
+    {
+        context.ungetService(reference);
+    }
+
+    /**
+	 * Wait for at least one service to be tracked by this <tt>ServiceTracker</tt> object.
+	 * <p>It is strongly recommended that <tt>waitForService</tt> is not used
+	 * during the calling of the <tt>BundleActivator</tt> methods. <tt>BundleActivator</tt> methods are
+	 * expected to complete in a short period of time.
+	 *
+	 * @param timeout time interval in milliseconds to wait.  If zero,
+	 * the method will wait indefinately.
+	 * @return Returns the result of <tt>getService()</tt>.
+	 * @throws IllegalArgumentException If the value of timeout is
+	 * negative.
+	 */
+    public Object waitForService(long timeout) throws InterruptedException
+    {
+        if (timeout < 0)
+        {
+            throw new IllegalArgumentException("timeout value is negative");
+        }
+
+        Object object = getService();
+
+        while (object == null)
+        {
+            Tracked tracked = this.tracked;     /* use local var since we are not synchronized */
+
+            if (tracked == null)    /* if ServiceTracker is not open */
+            {
+                return null;
+            }
+
+            synchronized (tracked)
+            {
+                if (tracked.size() == 0)
+                {
+                    tracked.wait(timeout);
+                }
+            }
+
+            object = getService();
+
+            if (timeout > 0)
+            {
+                return object;
+            }
+        }
+
+        return object;
+    }
+
+    /**
+	 * Return an array of <tt>ServiceReference</tt> objects for all services
+	 * being tracked by this <tt>ServiceTracker</tt> object.
+	 *
+	 * @return Array of <tt>ServiceReference</tt> objects or <tt>null</tt> if no service
+	 * are being tracked.
+	 */
+    public ServiceReference[] getServiceReferences()
+    {
+        Tracked tracked = this.tracked;     /* use local var since we are not synchronized */
+
+        if (tracked == null)    /* if ServiceTracker is not open */
+        {
+            return null;
+        }
+
+        synchronized (tracked)
+        {
+            int size = tracked.size();
+
+            if (size == 0)
+            {
+                return null;
+            }
+
+            ServiceReference references[] = new ServiceReference[size];
+
+            Enumeration trackedServiceRefs = tracked.keys();
+
+            for (int i = 0; i < size; i++)
+            {
+                references[i] = (ServiceReference)trackedServiceRefs.nextElement();
+            }
+
+            return references;
+        }
+    }
+
+    /**
+	 * Return an array of service objects for all services
+	 * being tracked by this <tt>ServiceTracker</tt> object.
+	 *
+	 * @return Array of service objects or <tt>null</tt> if no service
+	 * are being tracked.
+	 */
+    public Object[] getServices()
+    {
+        Tracked tracked = this.tracked;     /* use local var since we are not synchronized */
+
+        if (tracked == null)    /* if ServiceTracker is not open */
+        {
+            return null;
+        }
+
+        synchronized (tracked)
+        {
+            int size = tracked.size();
+
+            if (size == 0)
+            {
+                return null;
+            }
+
+            Object objects[] = new Object[size];
+
+            Enumeration trackedServices = tracked.elements();
+
+            for (int i = 0; i < size; i++)
+            {
+                objects[i] = trackedServices.nextElement();
+            }
+
+            return objects;
+        }
+    }
+
+    /**
+	 * Returns a <tt>ServiceReference</tt> object for one of the services
+	 * being tracked by this <tt>ServiceTracker</tt> object.
+	 *
+	 * <p>If multiple services are being tracked, the service
+	 * with the highest ranking (as specified in its <tt>service.ranking</tt> property) is
+	 * returned.
+	 *
+	 * <p>If there is a tie in ranking, the service with the lowest
+	 * service ID (as specified in its <tt>service.id</tt> property); that is,
+	 * the service that was registered first is returned.
+	 * <p>This is the same algorithm used by <tt>BundleContext.getServiceReference</tt>.
+	 *
+	 * @return <tt>ServiceReference</tt> object or <tt>null</tt> if no service is being tracked.
+	 * @since 1.1
+	 */
+    public ServiceReference getServiceReference()
+    {
+        ServiceReference[] references = getServiceReferences();
+
+        int length = (references == null) ? 0 : references.length;
+
+        if (length > 0)         /* if a service is being tracked */
+        {
+            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];
+        }
+
+        return null;
+    }
+
+    /**
+	 * Returns the service object for the specified <tt>ServiceReference</tt> object
+	 * if the referenced service is
+	 * being tracked by this <tt>ServiceTracker</tt> object.
+	 *
+	 * @param reference Reference to the desired service.
+	 * @return Service object or <tt>null</tt> if the service referenced by the
+	 * specified <tt>ServiceReference</tt> object is not being tracked.
+	 */
+    public Object getService(ServiceReference reference)
+    {
+        Tracked tracked = this.tracked;     /* use local var since we are not synchronized */
+
+        if (tracked == null)    /* if ServiceTracker is not open */
+        {
+            return null;
+        }
+
+        return tracked.get(reference);
+    }
+
+    /**
+	 * Returns a service object for one of the services
+	 * being tracked by this <tt>ServiceTracker</tt> object.
+	 *
+	 * <p>If any services are being tracked, this method returns the result
+	 * of calling <tt>getService(getServiceReference())</tt>.
+	 *
+	 * @return Service object or <tt>null</tt> if no service is being tracked.
+	 */
+    public Object getService()
+    {
+        ServiceReference reference = getServiceReference();
+
+        if (reference != null)
+        {
+            return getService(reference);
+        }
+
+        return null;
+    }
+
+    /**
+	 * Remove a service from this <tt>ServiceTracker</tt> object.
+	 *
+	 * The specified service will be removed from this
+	 * <tt>ServiceTracker</tt> object.
+	 * If the specified service was being tracked then the
+	 * <tt>ServiceTrackerCustomizer.removedService</tt> method will be
+	 * called for that service.
+	 *
+	 * @param reference Reference to the service to be removed.
+	 */
+    public void remove(ServiceReference reference)
+    {
+        Tracked tracked = this.tracked;     /* use local var since we are not synchronized */
+
+        if (tracked == null)    /* if ServiceTracker is not open */
+        {
+            return;
+        }
+
+        tracked.untrack(reference);
+    }
+
+    /**
+	 * Return the number of services being tracked by this <tt>ServiceTracker</tt> object.
+	 *
+	 * @return Number of services being tracked.
+	 */
+
+    public int size()
+    {
+        Tracked tracked = this.tracked;     /* use local var since we are not synchronized */
+
+        if (tracked == null)    /* if ServiceTracker is not open */
+        {
+            return 0;
+        }
+
+        return tracked.size();
+    }
+
+    /**
+	 * Returns the tracking count for this <tt>ServiceTracker</tt> object.
+	 *
+	 * The tracking count is initialized to 0 when this
+	 * <tt>ServiceTracker</tt> object is opened. Every time a service is
+	 * added or removed from this <tt>ServiceTracker</tt> object
+	 * the tracking count is incremented.
+	 *
+	 * <p>The tracking count can
+	 * be used to determine if this <tt>ServiceTracker</tt> object
+	 * has added or removed a service by comparing a tracking count value
+	 * previously collected with the current tracking count value. If the value
+	 * has not changed, then no service has been added or removed from
+	 * this <tt>ServiceTracker</tt> object
+	 * since the previous tracking count was collected.
+	 *
+	 * @since 1.2
+	 * @return The tracking count for this <tt>ServiceTracker</tt> object
+	 * or -1 if this <tt>ServiceTracker</tt> object is not open.
+	 */
+    public int getTrackingCount()
+    {
+        Tracked tracked = this.tracked;     /* use local var since we are not synchronized */
+
+        if (tracked == null)    /* if ServiceTracker is not open */
+        {
+            return -1;
+        }
+
+        return tracked.getTrackingCount();
+    }
+
+    /**
+	 * Inner class to track the services.
+	 * This class is a hashtable mapping <tt>ServiceReference</tt> object -> customized Object.
+	 * This class also implements the <tt>ServiceListener</tt> interface for the tracker.
+	 * This is not a public class. It is only for use by the implementation
+	 * of the <tt>ServiceTracker</tt> class.
+	 *
+	 */
+    static class Tracked extends Hashtable implements ServiceListener {
+        private ServiceTrackerCustomizer customizer;    /** ServiceTrackerCustomizer object for this tracker. */
+        private Filter filter;      /** The filter used to track */
+        private Vector adding;      /** list of ServiceReferences currently being added */
+        private boolean closed;     /** true if the tracked object is closed */
+        private int trackingCount;  /** modification count */
+
+
+        /**
+		 * Tracked constructor.
+		 *
+		 * @param customizer Customizer object from parent <tt>ServiceTracker</tt> object.
+		 * @param filter <tt>Filter</tt> object from parent <tt>ServiceTracker</tt> object.
+		 */
+        protected Tracked(ServiceTrackerCustomizer customizer, Filter filter)
+        {
+            super();
+            this.customizer = customizer;
+            this.filter = filter;
+            closed = false;
+            trackingCount = 0;
+            adding = new Vector(10, 10);
+        }
+
+        /**
+		 * Called by the parent <tt>ServiceTracker</tt> object when it is closed.
+		 */
+        protected void close()
+        {
+            closed = true;
+        }
+
+        /**
+		 * Called by the parent <tt>ServiceTracker</tt> object to get
+		 * the modification count.
+		 *
+		 * @since 1.2
+		 * @return modification count.
+		 */
+        protected int getTrackingCount()
+        {
+            return trackingCount;
+        }
+
+        /**
+		 * <tt>ServiceListener</tt> method for the <tt>ServiceTracker</tt> class.
+		 * This method must NOT be synchronized to avoid deadlock potential.
+		 *
+		 * @param event <tt>ServiceEvent</tt> object from the framework.
+		 */
+        public void serviceChanged(ServiceEvent event)
+        {
+            /* Check if we had a delayed call (which could happen when we close). */
+            if (closed)
+            {
+                return;
+            }
+
+            ServiceReference reference = event.getServiceReference();
+
+            switch (event.getType())
+            {
+                case ServiceEvent.REGISTERED:
+                case ServiceEvent.MODIFIED:
+                    if (filter.match(reference))
+                    {
+                        track(reference);
+                        /* If the customizer throws an unchecked exception, it is safe to let it propagate */
+                    }
+                    else
+                    {
+                        untrack(reference);
+                        /* If the customizer throws an unchecked exception, it is safe to let it propagate */
+                    }
+
+                    break;
+
+                case ServiceEvent.UNREGISTERING:
+                    untrack(reference);
+                    /* If the customizer throws an unchecked exception, it is safe to let it propagate */
+
+                    break;
+            }
+        }
+
+        /**
+		 * Begin to track the referenced service.
+		 *
+		 * @param reference Reference to a service to be tracked.
+		 */
+        protected void track(ServiceReference reference)
+        {
+            Object object = get(reference);
+
+            if (object != null)     /* we are already tracking the service */
+            {
+                /* Call customizer outside of synchronized region */
+                customizer.modifiedService(reference, object);
+                /* If the customizer throws an unchecked exception, it is safe to let it propagate */
+
+                return;
+            }
+
+            synchronized (this)
+            {
+                if (adding.indexOf(reference, 0) != -1) /* if this service is already
+                                                         * in the process of being added. */
+                {
+                    return;
+                }
+
+                adding.addElement(reference);   /* mark this service is being added */
+            }
+
+            boolean becameUntracked = false;
+
+            /* Call customizer outside of synchronized region */
+            try
+            {
+                object = customizer.addingService(reference);
+                /* If the customizer throws an unchecked exception, it will propagate after the finally */
+            }
+            finally
+            {
+                boolean needToCallback = false;
+                synchronized (this)
+                {
+                    if (adding.removeElement(reference))    /* if the service was not untracked
+                                                             * during the customizer callback */
+                    {
+                        if (object != null)
+                        {
+                            put(reference, object);
+
+                            trackingCount++;            /* increment modification count */
+
+                            notifyAll();
+                            
+                            // Marrs: extra callback added, will be invoked after the synchronized block
+                            needToCallback = true;
+                        }
+                    }
+                    else
+                    {
+                        becameUntracked = true;
+                    }
+                }
+                if (needToCallback) {
+                    customizer.addedService(reference, object);
+                }
+            }
+
+            /* The service became untracked during
+			 * the customizer callback.
+			 */
+            if (becameUntracked)
+            {
+                /* Call customizer outside of synchronized region */
+                customizer.removedService(reference, object);
+                /* If the customizer throws an unchecked exception, it is safe to let it propagate */
+            }
+        }
+
+        /**
+		 * Discontinue tracking the referenced service.
+		 *
+		 * @param reference Reference to the tracked service.
+		 */
+        protected void untrack(ServiceReference reference)
+        {
+            Object object;
+
+            synchronized (this)
+            {
+                if (adding.removeElement(reference)) /* if the service is in the process
+                                                      * of being added */
+                {
+                    return;                          /* in case the service is untracked
+                                                      * while in the process of adding */
+                }
+
+                object = this.remove(reference);     /* must remove from tracker before calling
+                                                      * customizer callback */
+
+                if (object == null)             /* are we actually tracking the service */
+                {
+                    return;
+                }
+
+                trackingCount++;                /* increment modification count */
+            }
+
+            /* Call customizer outside of synchronized region */
+            customizer.removedService(reference, object);
+            /* If the customizer throws an unchecked exception, it is safe to let it propagate */
+        }
+    }
+}

Added: incubator/felix/trunk/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceTrackerCustomizer.java
URL: http://svn.apache.org/viewcvs/incubator/felix/trunk/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceTrackerCustomizer.java?rev=388822&view=auto
==============================================================================
--- incubator/felix/trunk/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceTrackerCustomizer.java (added)
+++ incubator/felix/trunk/org.apache.felix.dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceTrackerCustomizer.java Sat Mar 25 12:46:19 2006
@@ -0,0 +1,33 @@
+/*
+ *   Copyright 2006 The Apache Software Foundation
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+package org.apache.felix.dependencymanager;
+
+import org.osgi.framework.ServiceReference;
+
+/**
+ * TODO modified version of a normal service tracker customizer, this one has an
+ * extra callback "addedservice" that is invoked after the service has been added
+ * to the tracker (and therefore is accessible through the tracker API)
+ * 
+ * @author Marcel Offermans
+ */
+public interface ServiceTrackerCustomizer {
+    public Object addingService(ServiceReference ref);
+    public void addedService(ServiceReference ref, Object service);
+    public void modifiedService(ServiceReference ref, Object service);
+    public void removedService(ServiceReference ref, Object service);
+}