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/02/11 23:25:37 UTC

svn commit: r743536 - in /felix/trunk/dependencymanager: ./ src/main/java/org/apache/felix/dependencymanager/ src/main/java/org/osgi/ src/main/java/org/osgi/util/ src/main/java/org/osgi/util/tracker/

Author: marrs
Date: Wed Feb 11 22:25:36 2009
New Revision: 743536

URL: http://svn.apache.org/viewvc?rev=743536&view=rev
Log:
Moved the customized OSGi classes to a different package to make them more easily recognizable. Copied the same include resource mechanism the framework uses to ensure the customized classes get picked instead the ones in the compendium.

Added:
    felix/trunk/dependencymanager/src/main/java/org/osgi/
    felix/trunk/dependencymanager/src/main/java/org/osgi/util/
    felix/trunk/dependencymanager/src/main/java/org/osgi/util/tracker/
    felix/trunk/dependencymanager/src/main/java/org/osgi/util/tracker/ServiceTracker.java
    felix/trunk/dependencymanager/src/main/java/org/osgi/util/tracker/ServiceTrackerCustomizer.java
Modified:
    felix/trunk/dependencymanager/pom.xml
    felix/trunk/dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceDependency.java

Modified: felix/trunk/dependencymanager/pom.xml
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/pom.xml?rev=743536&r1=743535&r2=743536&view=diff
==============================================================================
--- felix/trunk/dependencymanager/pom.xml (original)
+++ felix/trunk/dependencymanager/pom.xml Wed Feb 11 22:25:36 2009
@@ -58,7 +58,8 @@
             <Bundle-Vendor>The Apache Software Foundation</Bundle-Vendor>
             <Export-Package>org.apache.felix.dependencymanager</Export-Package>
             <Import-Package>!org.apache.felix.dependencymanager,*</Import-Package>
-            <Include-Resource>META-INF/LICENCE=LICENSE,META-INF/NOTICE=NOTICE</Include-Resource>
+            <Include-Resource>META-INF/LICENSE=LICENSE,META-INF/NOTICE=NOTICE,org/osgi/util/tracker/=target/clas
+ses/org/osgi/util/tracker</Include-Resource>
           </instructions>
         </configuration>
       </plugin>

Modified: felix/trunk/dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceDependency.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceDependency.java?rev=743536&r1=743535&r2=743536&view=diff
==============================================================================
--- felix/trunk/dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceDependency.java (original)
+++ felix/trunk/dependencymanager/src/main/java/org/apache/felix/dependencymanager/ServiceDependency.java Wed Feb 11 22:25:36 2009
@@ -29,6 +29,8 @@
 import org.osgi.framework.Constants;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
 
 /**
  * Service dependency that can track an OSGi service.

Added: felix/trunk/dependencymanager/src/main/java/org/osgi/util/tracker/ServiceTracker.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/src/main/java/org/osgi/util/tracker/ServiceTracker.java?rev=743536&view=auto
==============================================================================
--- felix/trunk/dependencymanager/src/main/java/org/osgi/util/tracker/ServiceTracker.java (added)
+++ felix/trunk/dependencymanager/src/main/java/org/osgi/util/tracker/ServiceTracker.java Wed Feb 11 22:25:36 2009
@@ -0,0 +1,1153 @@
+/*
+ * 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.osgi.util.tracker;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.LinkedList;
+
+import org.osgi.framework.AllServiceListener;
+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;
+
+/**
+ * A modified <code>ServiceTracker</code> class simplifies using services 
+ * from the Framework's service registry. This class is used internally
+ * by the dependency manager. It is based on the OSGi R4.1 sources, which
+ * are made available under the same ASF 2.0 license.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceTracker implements ServiceTrackerCustomizer {
+    /* set this to true to compile in debug messages */
+    static final boolean                DEBUG           = false;
+    /**
+     * Bundle context against which this <code>ServiceTracker</code> object is
+     * tracking.
+     */
+    protected final BundleContext       context;
+    /**
+     * Filter specifying search criteria for the services to track.
+     * 
+     * @since 1.1
+     */
+    protected final Filter              filter;
+    /**
+     * <code>ServiceTrackerCustomizer</code> object for this tracker.
+     */
+    final ServiceTrackerCustomizer      customizer;
+    /**
+     * Filter string for use when adding the ServiceListener. If this field is
+     * set, then certain optimizations can be taken since we don't have a user
+     * supplied filter.
+     */
+    final String                        listenerFilter;
+    /**
+     * Class name to be tracked. If this field is set, then we are tracking by
+     * class name.
+     */
+    private final String                trackClass;
+    /**
+     * Reference to be tracked. If this field is set, then we are tracking a
+     * single ServiceReference.
+     */
+    private final ServiceReference      trackReference;
+    /**
+     * Tracked services: <code>ServiceReference</code> object -> customized
+     * Object and <code>ServiceListener</code> object
+     */
+    private volatile Tracked            tracked;
+    /**
+     * Modification count. This field is initialized to zero by open, set to -1
+     * by close and incremented by modified.
+     * 
+     * This field is volatile since it is accessed by multiple threads.
+     */
+    private volatile int                trackingCount   = -1;
+    /**
+     * Cached ServiceReference for getServiceReference.
+     * 
+     * This field is volatile since it is accessed by multiple threads.
+     */
+    private volatile ServiceReference   cachedReference;
+    /**
+     * Cached service object for getService.
+     * 
+     * This field is volatile since it is accessed by multiple threads.
+     */
+    private volatile Object             cachedService;
+
+    /**
+     * Create a <code>ServiceTracker</code> object on the specified
+     * <code>ServiceReference</code> object.
+     * 
+     * <p>
+     * The service referenced by the specified <code>ServiceReference</code>
+     * object will be tracked by this <code>ServiceTracker</code> object.
+     * 
+     * @param context <code>BundleContext</code> object against which the
+     *        tracking is done.
+     * @param reference <code>ServiceReference</code> object for the service
+     *        to be tracked.
+     * @param customizer The customizer object to call when services are added,
+     *        modified, or removed in this <code>ServiceTracker</code> object.
+     *        If customizer is <code>null</code>, then this
+     *        <code>ServiceTracker</code> object will be used as the
+     *        <code>ServiceTrackerCustomizer</code> object and the
+     *        <code>ServiceTracker</code> object will call the
+     *        <code>ServiceTrackerCustomizer</code> methods on itself.
+     */
+    public ServiceTracker(BundleContext context, ServiceReference reference,
+            ServiceTrackerCustomizer customizer) {
+        this.context = context;
+        this.trackReference = reference;
+        this.trackClass = null;
+        this.customizer = (customizer == null) ? this : customizer;
+        this.listenerFilter = "(" + Constants.SERVICE_ID + "=" + reference.getProperty(Constants.SERVICE_ID).toString() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+        try {
+            this.filter = context.createFilter(listenerFilter);
+        }
+        catch (InvalidSyntaxException e) { // we could only get this exception
+            // if the ServiceReference was
+            // invalid
+            throw new IllegalArgumentException(
+                    "unexpected InvalidSyntaxException: " + e.getMessage()); //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * Create a <code>ServiceTracker</code> object on the specified class
+     * name.
+     * 
+     * <p>
+     * Services registered under the specified class name will be tracked by
+     * this <code>ServiceTracker</code> object.
+     * 
+     * @param context <code>BundleContext</code> 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 <code>ServiceTracker</code> object.
+     *        If customizer is <code>null</code>, then this
+     *        <code>ServiceTracker</code> object will be used as the
+     *        <code>ServiceTrackerCustomizer</code> object and the
+     *        <code>ServiceTracker</code> object will call the
+     *        <code>ServiceTrackerCustomizer</code> methods on itself.
+     */
+    public ServiceTracker(BundleContext context, String clazz,
+            ServiceTrackerCustomizer customizer) {
+        this.context = context;
+        this.trackReference = null;
+        this.trackClass = clazz;
+        this.customizer = (customizer == null) ? this : customizer;
+        this.listenerFilter = "(" + Constants.OBJECTCLASS + "=" + clazz.toString() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+        try {
+            this.filter = context.createFilter(listenerFilter);
+        }
+        catch (InvalidSyntaxException e) { // we could only get this exception
+            // if the clazz argument was
+            // malformed
+            throw new IllegalArgumentException(
+                    "unexpected InvalidSyntaxException: " + e.getMessage()); //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * Create a <code>ServiceTracker</code> object on the specified
+     * <code>Filter</code> object.
+     * 
+     * <p>
+     * Services which match the specified <code>Filter</code> object will be
+     * tracked by this <code>ServiceTracker</code> object.
+     * 
+     * @param context <code>BundleContext</code> object against which the
+     *        tracking is done.
+     * @param filter <code>Filter</code> object to select the services to be
+     *        tracked.
+     * @param customizer The customizer object to call when services are added,
+     *        modified, or removed in this <code>ServiceTracker</code> object.
+     *        If customizer is null, then this <code>ServiceTracker</code>
+     *        object will be used as the <code>ServiceTrackerCustomizer</code>
+     *        object and the <code>ServiceTracker</code> object will call the
+     *        <code>ServiceTrackerCustomizer</code> methods on itself.
+     * @since 1.1
+     */
+    public ServiceTracker(BundleContext context, Filter filter,
+            ServiceTrackerCustomizer customizer) {
+        this.context = context;
+        this.trackReference = null;
+        this.trackClass = null;
+        this.listenerFilter = null;
+        this.filter = filter;
+        this.customizer = (customizer == null) ? this : customizer;
+        if ((context == null) || (filter == null)) { // we throw a NPE here
+            // to
+            // be consistent with the
+            // other constructors
+            throw new NullPointerException();
+        }
+    }
+
+    /**
+     * Open this <code>ServiceTracker</code> object and begin tracking
+     * services.
+     * 
+     * <p>
+     * This method calls <code>open(false)</code>.
+     * 
+     * @throws java.lang.IllegalStateException if the <code>BundleContext</code>
+     *         object with which this <code>ServiceTracker</code> object was
+     *         created is no longer valid.
+     * @see #open(boolean)
+     */
+    public void open() {
+        open(false);
+    }
+
+    /**
+     * Open this <code>ServiceTracker</code> object and begin tracking
+     * services.
+     * 
+     * <p>
+     * Services which match the search criteria specified when this
+     * <code>ServiceTracker</code> object was created are now tracked by this
+     * <code>ServiceTracker</code> object.
+     * 
+     * @param trackAllServices If <code>true</code>, then this
+     *        <code>ServiceTracker</code> will track all matching services
+     *        regardless of class loader accessibility. If <code>false</code>,
+     *        then this <code>ServiceTracker</code> will only track matching
+     *        services which are class loader accessibile to the bundle whose
+     *        <code>BundleContext</code> is used by this
+     *        <code>ServiceTracker</code>.
+     * @throws java.lang.IllegalStateException if the <code>BundleContext</code>
+     *         object with which this <code>ServiceTracker</code> object was
+     *         created is no longer valid.
+     * @since 1.3
+     */
+    public synchronized void open(boolean trackAllServices) {
+        if (tracked != null) {
+            return;
+        }
+        if (DEBUG) {
+            System.out.println("ServiceTracker.open: " + filter); //$NON-NLS-1$
+        }
+        tracked = trackAllServices ? new AllTracked() : new Tracked();
+        trackingCount = 0;
+        synchronized (tracked) {
+            try {
+                context.addServiceListener(tracked, listenerFilter);
+                ServiceReference[] references;
+                if (listenerFilter == null) { // user supplied filter
+                    references = getInitialReferences(trackAllServices, null,
+                            filter.toString());
+                }
+                else { // constructor supplied filter
+                    if (trackClass == null) {
+                        references = new ServiceReference[] {trackReference};
+                    }
+                    else {
+                        references = getInitialReferences(trackAllServices,
+                                trackClass, null);
+                    }
+                }
+
+                tracked.setInitialServices(references); // set tracked with
+                // the initial
+                // references
+            }
+            catch (InvalidSyntaxException e) {
+                throw new RuntimeException(
+                        "unexpected InvalidSyntaxException: " + e.getMessage()); //$NON-NLS-1$
+            }
+        }
+        /* Call tracked outside of synchronized region */
+        tracked.trackInitialServices(); // process the initial references
+    }
+
+    /**
+     * Returns the list of initial <code>ServiceReference</code> objects that
+     * will be tracked by this <code>ServiceTracker</code> object.
+     * 
+     * @param trackAllServices If true, use getAllServiceReferences.
+     * @param trackClass the class name with which the service was registered,
+     *        or null for all services.
+     * @param filterString the filter criteria or null for all services.
+     * @return the list of initial <code>ServiceReference</code> objects.
+     * @throws InvalidSyntaxException if the filter uses an invalid syntax.
+     */
+    private ServiceReference[] getInitialReferences(boolean trackAllServices,
+            String trackClass, String filterString)
+            throws InvalidSyntaxException {
+        if (trackAllServices) {
+            return context.getAllServiceReferences(trackClass, filterString);
+        }
+        else {
+            return context.getServiceReferences(trackClass, filterString);
+        }
+    }
+
+    /**
+     * Close this <code>ServiceTracker</code> object.
+     * 
+     * <p>
+     * This method should be called when this <code>ServiceTracker</code>
+     * object should end the tracking of services.
+     */
+    public synchronized void close() {
+        if (tracked == null) {
+            return;
+        }
+        if (DEBUG) {
+            System.out.println("ServiceTracker.close: " + filter); //$NON-NLS-1$
+        }
+        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]);
+            }
+        }
+        trackingCount = -1;
+        if (DEBUG) {
+            if ((cachedReference == null) && (cachedService == null)) {
+                System.out
+                        .println("ServiceTracker.close[cached cleared]: " + filter); //$NON-NLS-1$
+            }
+        }
+    }
+
+    /**
+     * Default implementation of the
+     * <code>ServiceTrackerCustomizer.addingService</code> method.
+     * 
+     * <p>
+     * This method is only called when this <code>ServiceTracker</code> object
+     * has been constructed with a <code>null ServiceTrackerCustomizer</code>
+     * argument.
+     * 
+     * The default implementation returns the result of calling
+     * <code>getService</code>, on the <code>BundleContext</code> object
+     * with which this <code>ServiceTracker</code> object was created, passing
+     * the specified <code>ServiceReference</code> 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
+     *        <code>ServiceTracker</code> object.
+     * @return The service object to be tracked for the service added to this
+     *         <code>ServiceTracker</code> object.
+     * @see ServiceTrackerCustomizer
+     */
+    public Object addingService(ServiceReference reference) {
+        return context.getService(reference);
+    }
+
+    /**
+     * Default implementation of the
+     * <code>ServiceTrackerCustomizer.modifiedService</code> method.
+     * 
+     * <p>
+     * This method is only called when this <code>ServiceTracker</code> object
+     * has been constructed with a <code>null ServiceTrackerCustomizer</code>
+     * 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
+     * <code>ServiceTrackerCustomizer.removedService</code> method.
+     * 
+     * <p>
+     * This method is only called when this <code>ServiceTracker</code> object
+     * has been constructed with a <code>null ServiceTrackerCustomizer</code>
+     * argument.
+     * 
+     * The default implementation calls <code>ungetService</code>, on the
+     * <code>BundleContext</code> object with which this
+     * <code>ServiceTracker</code> object was created, passing the specified
+     * <code>ServiceReference</code> object.
+     * <p>
+     * This method can be overridden in a subclass. If the default
+     * implementation of <code>addingService</code> method was used, this
+     * method must unget the service.
+     * 
+     * @param reference Reference to removed service.
+     * @param service The service object for the removed service.
+     * @see ServiceTrackerCustomizer
+     */
+    public void removedService(ServiceReference reference, Object service) {
+        context.ungetService(reference);
+    }
+
+    /**
+     * Wait for at least one service to be tracked by this
+     * <code>ServiceTracker</code> object.
+     * <p>
+     * It is strongly recommended that <code>waitForService</code> is not used
+     * during the calling of the <code>BundleActivator</code> methods.
+     * <code>BundleActivator</code> 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 <code>getService()</code>.
+     * @throws InterruptedException If another thread has interrupted the
+     *         current thread.
+     * @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"); //$NON-NLS-1$
+        }
+        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 <code>ServiceReference</code> objects for all
+     * services being tracked by this <code>ServiceTracker</code> object.
+     * 
+     * @return Array of <code>ServiceReference</code> objects or
+     *         <code>null</code> 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 length = tracked.size();
+            if (length == 0) {
+                return null;
+            }
+            ServiceReference[] references = new ServiceReference[length];
+            Enumeration keys = tracked.keys();
+            for (int i = 0; i < length; i++) {
+                references[i] = (ServiceReference) keys.nextElement();
+            }
+            return references;
+        }
+    }
+
+    /**
+     * Returns a <code>ServiceReference</code> object for one of the services
+     * being tracked by this <code>ServiceTracker</code> object.
+     * 
+     * <p>
+     * If multiple services are being tracked, the service with the highest
+     * ranking (as specified in its <code>service.ranking</code> property) is
+     * returned.
+     * 
+     * <p>
+     * If there is a tie in ranking, the service with the lowest service ID (as
+     * specified in its <code>service.id</code> property); that is, the
+     * service that was registered first is returned.
+     * <p>
+     * This is the same algorithm used by
+     * <code>BundleContext.getServiceReference</code>.
+     * 
+     * @return <code>ServiceReference</code> object or <code>null</code> if
+     *         no service is being tracked.
+     * @since 1.1
+     */
+    public ServiceReference getServiceReference() {
+        ServiceReference reference = cachedReference;
+        if (reference != null) {
+            if (DEBUG) {
+                System.out
+                        .println("ServiceTracker.getServiceReference[cached]: " + filter); //$NON-NLS-1$
+            }
+            return reference;
+        }
+        if (DEBUG) {
+            System.out.println("ServiceTracker.getServiceReference: " + filter); //$NON-NLS-1$
+        }
+        ServiceReference[] references = getServiceReferences();
+        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 cachedReference = references[index];
+    }
+
+    /**
+     * Returns the service object for the specified
+     * <code>ServiceReference</code> object if the referenced service is being
+     * tracked by this <code>ServiceTracker</code> object.
+     * 
+     * @param reference Reference to the desired service.
+     * @return Service object or <code>null</code> if the service referenced
+     *         by the specified <code>ServiceReference</code> 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;
+        }
+        synchronized (tracked) {
+            return tracked.get(reference);
+        }
+    }
+
+    /**
+     * Return an array of service objects for all services being tracked by this
+     * <code>ServiceTracker</code> object.
+     * 
+     * @return Array of service objects or <code>null</code> 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) {
+            ServiceReference[] references = getServiceReferences();
+            int length = (references == null) ? 0 : references.length;
+            if (length == 0) {
+                return null;
+            }
+            Object[] objects = new Object[length];
+            for (int i = 0; i < length; i++) {
+                objects[i] = getService(references[i]);
+            }
+            return objects;
+        }
+    }
+
+    /**
+     * Returns a service object for one of the services being tracked by this
+     * <code>ServiceTracker</code> object.
+     * 
+     * <p>
+     * If any services are being tracked, this method returns the result of
+     * calling <code>getService(getServiceReference())</code>.
+     * 
+     * @return Service object or <code>null</code> if no service is being
+     *         tracked.
+     */
+    public Object getService() {
+        Object service = cachedService;
+        if (service != null) {
+            if (DEBUG) {
+                System.out
+                        .println("ServiceTracker.getService[cached]: " + filter); //$NON-NLS-1$
+            }
+            return service;
+        }
+        if (DEBUG) {
+            System.out.println("ServiceTracker.getService: " + filter); //$NON-NLS-1$
+        }
+        ServiceReference reference = getServiceReference();
+        if (reference == null) {
+            return null;
+        }
+        return cachedService = getService(reference);
+    }
+
+    /**
+     * Remove a service from this <code>ServiceTracker</code> object.
+     * 
+     * The specified service will be removed from this
+     * <code>ServiceTracker</code> object. If the specified service was being
+     * tracked then the <code>ServiceTrackerCustomizer.removedService</code>
+     * 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
+     * <code>ServiceTracker</code> 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 <code>ServiceTracker</code> object.
+     * 
+     * The tracking count is initialized to 0 when this
+     * <code>ServiceTracker</code> object is opened. Every time a service is
+     * added, modified or removed from this <code>ServiceTracker</code> object
+     * the tracking count is incremented.
+     * 
+     * <p>
+     * The tracking count can be used to determine if this
+     * <code>ServiceTracker</code> object has added, modified 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, modified or removed from this
+     * <code>ServiceTracker</code> object since the previous tracking count
+     * was collected.
+     * 
+     * @since 1.2
+     * @return The tracking count for this <code>ServiceTracker</code> object
+     *         or -1 if this <code>ServiceTracker</code> object is not open.
+     */
+    public int getTrackingCount() {
+        return trackingCount;
+    }
+
+    /**
+     * Called by the Tracked object whenever the set of tracked services is
+     * modified. Increments the tracking count and clears the cache.
+     * 
+     * @GuardedBy tracked
+     */
+    /*
+     * This method must not be synchronized since it is called by Tracked while
+     * Tracked is synchronized. We don't want synchronization interactions
+     * between the ServiceListener thread and the user thread.
+     */
+    void modified() {
+        trackingCount++; /* increment modification count */
+        cachedReference = null; /* clear cached value */
+        cachedService = null; /* clear cached value */
+        if (DEBUG) {
+            System.out.println("ServiceTracker.modified: " + filter); //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * Inner class to track services. If a <code>ServiceTracker</code> object
+     * is reused (closed then reopened), then a new Tracked object is used. This
+     * class is a hashtable mapping <code>ServiceReference</code> object ->
+     * customized Object. This class is the <code>ServiceListener</code>
+     * object for the tracker. This class is used to synchronize access to the
+     * tracked services. This is not a public class. It is only for use by the
+     * implementation of the <code>ServiceTracker</code> class.
+     * 
+     * @ThreadSafe
+     */
+    class Tracked extends Hashtable implements ServiceListener {
+        static final long           serialVersionUID    = -7420065199791006079L;
+        /**
+         * List of ServiceReferences in the process of being added. This is used
+         * to deal with nesting of ServiceEvents. Since ServiceEvents are
+         * synchronously delivered, ServiceEvents can be nested. For example,
+         * when processing the adding of a service and the customizer causes the
+         * service to be unregistered, notification to the nested call to
+         * untrack that the service was unregistered can be made to the track
+         * method.
+         * 
+         * Since the ArrayList implementation is not synchronized, all access to
+         * this list must be protected by the same synchronized object for
+         * thread-safety.
+         * 
+         * @GuardedBy this
+         */
+        private final ArrayList     adding;
+
+        /**
+         * true if the tracked object is closed.
+         * 
+         * This field is volatile because it is set by one thread and read by
+         * another.
+         */
+        private volatile boolean    closed;
+
+        /**
+         * Initial list of ServiceReferences for the tracker. This is used to
+         * correctly process the initial services which could become
+         * unregistered before they are tracked. This is necessary since the
+         * initial set of tracked services are not "announced" by ServiceEvents
+         * and therefore the ServiceEvent for unregistration could be delivered
+         * before we track the service.
+         * 
+         * A service must not be in both the initial and adding lists at the
+         * same time. A service must be moved from the initial list to the
+         * adding list "atomically" before we begin tracking it.
+         * 
+         * Since the LinkedList implementation is not synchronized, all access
+         * to this list must be protected by the same synchronized object for
+         * thread-safety.
+         * 
+         * @GuardedBy this
+         */
+        private final LinkedList    initial;
+
+        /**
+         * Tracked constructor.
+         */
+        protected Tracked() {
+            super();
+            closed = false;
+            adding = new ArrayList(6);
+            initial = new LinkedList();
+        }
+
+        /**
+         * Set initial list of services into tracker before ServiceEvents begin
+         * to be received.
+         * 
+         * This method must be called from ServiceTracker.open while
+         * synchronized on this object in the same synchronized block as the
+         * addServiceListener call.
+         * 
+         * @param references The initial list of services to be tracked.
+         * @GuardedBy this
+         */
+        protected void setInitialServices(ServiceReference[] references) {
+            if (references == null) {
+                return;
+            }
+            int size = references.length;
+            for (int i = 0; i < size; i++) {
+                if (DEBUG) {
+                    System.out
+                            .println("ServiceTracker.Tracked.setInitialServices: " + references[i]); //$NON-NLS-1$
+                }
+                initial.add(references[i]);
+            }
+        }
+
+        /**
+         * Track the initial list of services. This is called after
+         * ServiceEvents can begin to be received.
+         * 
+         * This method must be called from ServiceTracker.open while not
+         * synchronized on this object after the addServiceListener call.
+         * 
+         */
+        protected void trackInitialServices() {
+            while (true) {
+                ServiceReference reference;
+                synchronized (this) {
+                    if (initial.size() == 0) {
+                        /*
+                         * if there are no more inital services
+                         */
+                        return; /* we are done */
+                    }
+                    /*
+                     * move the first service from the initial list to the
+                     * adding list within this synchronized block.
+                     */
+                    reference = (ServiceReference) initial.removeFirst();
+                    if (this.get(reference) != null) {
+                        /* if we are already tracking this service */
+                        if (DEBUG) {
+                            System.out
+                                    .println("ServiceTracker.Tracked.trackInitialServices[already tracked]: " + reference); //$NON-NLS-1$
+                        }
+                        continue; /* skip this service */
+                    }
+                    if (adding.contains(reference)) {
+                        /*
+                         * if this service is already in the process of being
+                         * added.
+                         */
+                        if (DEBUG) {
+                            System.out
+                                    .println("ServiceTracker.Tracked.trackInitialServices[already adding]: " + reference); //$NON-NLS-1$
+                        }
+                        continue; /* skip this service */
+                    }
+                    adding.add(reference);
+                }
+                if (DEBUG) {
+                    System.out
+                            .println("ServiceTracker.Tracked.trackInitialServices: " + reference); //$NON-NLS-1$
+                }
+                trackAdding(reference); /*
+                                         * Begin tracking it. We call
+                                         * trackAdding since we have already put
+                                         * the reference in the adding list.
+                                         */
+            }
+        }
+
+        /**
+         * Called by the owning <code>ServiceTracker</code> object when it is
+         * closed.
+         */
+        protected void close() {
+            closed = true;
+        }
+
+        /**
+         * <code>ServiceListener</code> method for the
+         * <code>ServiceTracker</code> class. This method must NOT be
+         * synchronized to avoid deadlock potential.
+         * 
+         * @param event <code>ServiceEvent</code> 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();
+            if (DEBUG) {
+                System.out
+                        .println("ServiceTracker.Tracked.serviceChanged[" + event.getType() + "]: " + reference); //$NON-NLS-1$ //$NON-NLS-2$
+            }
+
+            switch (event.getType()) {
+                case ServiceEvent.REGISTERED :
+                case ServiceEvent.MODIFIED :
+                    if (listenerFilter != null) { // constructor supplied
+                        // filter
+                        track(reference);
+                        /*
+                         * If the customizer throws an unchecked exception, it
+                         * is safe to let it propagate
+                         */
+                    }
+                    else { // user supplied filter
+                        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.
+         */
+        private void track(ServiceReference reference) {
+            Object object;
+            synchronized (this) {
+                object = this.get(reference);
+            }
+            if (object != null) /* we are already tracking the service */
+            {
+                if (DEBUG) {
+                    System.out
+                            .println("ServiceTracker.Tracked.track[modified]: " + reference); //$NON-NLS-1$
+                }
+                synchronized (this) {
+                    modified(); /* increment modification count */
+                }
+                /* 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.contains(reference)) { /*
+                                                     * if this service is
+                                                     * already in the process of
+                                                     * being added.
+                                                     */
+                    if (DEBUG) {
+                        System.out
+                                .println("ServiceTracker.Tracked.track[already adding]: " + reference); //$NON-NLS-1$
+                    }
+                    return;
+                }
+                adding.add(reference); /* mark this service is being added */
+            }
+
+            trackAdding(reference); /*
+                                     * call trackAdding now that we have put the
+                                     * reference in the adding list
+                                     */
+        }
+
+        /**
+         * Common logic to add a service to the tracker used by track and
+         * trackInitialServices. The specified reference must have been placed
+         * in the adding list before calling this method.
+         * 
+         * @param reference Reference to a service to be tracked.
+         */
+        private void trackAdding(ServiceReference reference) {
+            if (DEBUG) {
+                System.out
+                        .println("ServiceTracker.Tracked.trackAdding: " + reference); //$NON-NLS-1$
+            }
+            Object object = null;
+            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.remove(reference)) { /*
+                                                     * if the service was not
+                                                     * untracked during the
+                                                     * customizer callback
+                                                     */
+                        if (object != null) {
+                            this.put(reference, object);
+                            modified(); /* increment modification count */
+                            notifyAll(); /*
+                                             * notify any waiters in
+                                             * waitForService
+                                             */
+                            // 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) {
+                if (DEBUG) {
+                    System.out
+                            .println("ServiceTracker.Tracked.trackAdding[removed]: " + reference); //$NON-NLS-1$
+                }
+                /* 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 (initial.remove(reference)) { /*
+                                                     * if this service is
+                                                     * already in the list of
+                                                     * initial references to
+                                                     * process
+                                                     */
+                    if (DEBUG) {
+                        System.out
+                                .println("ServiceTracker.Tracked.untrack[removed from initial]: " + reference); //$NON-NLS-1$
+                    }
+                    return; /*
+                             * we have removed it from the list and it will not
+                             * be processed
+                             */
+                }
+
+                if (adding.remove(reference)) { /*
+                                                 * if the service is in the
+                                                 * process of being added
+                                                 */
+                    if (DEBUG) {
+                        System.out
+                                .println("ServiceTracker.Tracked.untrack[being added]: " + reference); //$NON-NLS-1$
+                    }
+                    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;
+                }
+                modified(); /* increment modification count */
+            }
+            if (DEBUG) {
+                System.out
+                        .println("ServiceTracker.Tracked.untrack[removed]: " + reference); //$NON-NLS-1$
+            }
+            /* Call customizer outside of synchronized region */
+            customizer.removedService(reference, object);
+            /*
+             * If the customizer throws an unchecked exception, it is safe to
+             * let it propagate
+             */
+        }
+    }
+
+    /**
+     * Subclass of Tracked which implements the AllServiceListener interface.
+     * This class is used by the ServiceTracker if open is called with true.
+     * 
+     * @since 1.3
+     * @ThreadSafe
+     */
+    class AllTracked extends Tracked implements AllServiceListener {
+        static final long   serialVersionUID    = 4050764875305137716L;
+
+        /**
+         * AllTracked constructor.
+         */
+        protected AllTracked() {
+            super();
+        }
+    }
+
+    public void addedService(ServiceReference ref, Object service) {
+        // do nothing
+    }
+}

Added: felix/trunk/dependencymanager/src/main/java/org/osgi/util/tracker/ServiceTrackerCustomizer.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/src/main/java/org/osgi/util/tracker/ServiceTrackerCustomizer.java?rev=743536&view=auto
==============================================================================
--- felix/trunk/dependencymanager/src/main/java/org/osgi/util/tracker/ServiceTrackerCustomizer.java (added)
+++ felix/trunk/dependencymanager/src/main/java/org/osgi/util/tracker/ServiceTrackerCustomizer.java Wed Feb 11 22:25:36 2009
@@ -0,0 +1,35 @@
+/*
+ * 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.osgi.util.tracker;
+
+import org.osgi.framework.ServiceReference;
+
+/**
+ * A 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 <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+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);
+}