- Deactivate the service when a dependency is lost while the framework is stopping. This is indeed
useless to block unavailable service dependency method invocation when the fwk is stopping, since the
lost service won't come up again. 

- Added setRequired(boolean required), which just check if the 
"required" flag is true (the TemporalServiceDependency class does not support optional dependency).

- Synchronized method setTimeout.


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1063785 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/TemporalServiceDependencyImpl.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/TemporalServiceDependencyImpl.java
index 6aeafc3..7ae64f0 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/TemporalServiceDependencyImpl.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/TemporalServiceDependencyImpl.java
@@ -17,18 +17,27 @@
 
 import org.apache.felix.dm.DependencyActivatorBase;
 import org.apache.felix.dm.DependencyService;
+import org.apache.felix.dm.ServiceDependency;
 import org.apache.felix.dm.TemporalServiceDependency;
 import org.apache.felix.dm.impl.Logger;
+import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceReference;
 
 /**
-* Temporal Service dependency implementation.
+* Temporal Service dependency implementation, used to hide temporary service dependency "outage".
+* Only works with required dependency.
 * (see javadoc in {@link TemporalServiceDependency}).
 */
 public class TemporalServiceDependencyImpl extends ServiceDependencyImpl implements TemporalServiceDependency, InvocationHandler {
     // Max millis to wait for service availability.
     private long m_timeout = 30000;
+    
+    // Dependency service currently used (with highest rank or highest service id).
+    private volatile Object m_cachedService;
+    
+    // Framework bundle (we use it to detect if the framework is stopping)
+    private final Bundle m_frameworkBundle;
 
     /**
      * Creates a new Temporal Service Dependency.
@@ -40,6 +49,7 @@
     public TemporalServiceDependencyImpl(BundleContext context, Logger logger) {
         super(context, logger);
         super.setRequired(true);
+        m_frameworkBundle = context.getBundle(0);
     }
 
     /**
@@ -50,7 +60,7 @@
      * @throws IllegalArgumentException if the timeout is negative
      * @return this temporal dependency
      */
-    public TemporalServiceDependency setTimeout(long timeout) {
+    public synchronized TemporalServiceDependency setTimeout(long timeout) {
         if (timeout < 0) {
             throw new IllegalArgumentException("Invalid timeout value: " + timeout);
         }
@@ -59,9 +69,29 @@
     }
 
     /**
+     * Sets the required flag which determines if this service is required or not. This method
+     * just override the superclass method in order to check if the required flag is true 
+     * (optional dependency is not supported by this class).
+     * 
+     * @param required the required flag, which must be set to true
+     * @return this service dependency
+     * @throws IllegalArgumentException if the "required" parameter is not true.
+     */
+    public ServiceDependency setRequired(boolean required) {
+        if (! required) {
+            throw new IllegalArgumentException("A Temporal Service dependency can't be optional");
+        }
+        super.setRequired(required);
+        return this;
+    }
+
+    /**
      * The ServiceTracker calls us here in order to inform about a service arrival.
      */
     public synchronized void addedService(ServiceReference ref, Object service) {
+        // Update our service cache, using the tracker. We do this because the
+        // just added service might not be the service with the highest rank ...
+        m_cachedService = m_tracker.getService(); 
         boolean makeAvailable = makeAvailable();
         if (makeAvailable) {
             m_serviceInstance = Proxy.newProxyInstance(m_trackedServiceName.getClassLoader(), new Class[] { m_trackedServiceName }, this);
@@ -89,8 +119,25 @@
      * The ServiceTracker calls us here when a tracked service is lost.
      */
     public synchronized void removedService(ServiceReference ref, Object service) {
-        // Unget what we got in addingService (see ServiceTracker 701.4.1)
-        m_context.ungetService(ref);
+        // If we detect that the fwk is stopping, we behave as our superclass. That is:
+        // the lost dependency has to trigger our service deactivation, since the fwk is stopping
+        // and the lost dependency won't come up anymore.
+        if (m_frameworkBundle.getState() == Bundle.STOPPING) {
+            // Important: Notice that calling "super.removedService() might invoke our service "stop" 
+            // callback, which in turn might invoke the just removed service dependency. In this case, 
+            // our "invoke" method won't use the tracker to get the service dependency (because at this point, 
+            // the tracker has withdrawn its reference to the lost service). So, you will see that the "invoke" 
+            // method will use the "m_cachedService" instead ...
+            super.removedService(ref, service);
+        } else {
+            // Unget what we got in addingService (see ServiceTracker 701.4.1)
+            m_context.ungetService(ref);
+            // Now, ask the service tracker if there is another available service (with a lower rank).
+            // If no more service dependencies are available, the tracker will then return null;
+            // and our invoke method will block the service method invocation, until another service
+            // becomes available.
+            m_cachedService = m_tracker.getService();
+        }
     }
 
     /**
@@ -101,7 +148,7 @@
     }
 
     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
-        Object service = m_tracker.getService();
+        Object service = m_cachedService;
         if (service == null) {
             synchronized (this) {
                 long start = System.currentTimeMillis();
@@ -117,8 +164,9 @@
                         throw new IllegalStateException("Service unavailable: " + m_trackedServiceName.getName());
                     }
                     waitTime = m_timeout - (System.currentTimeMillis() - start);
-                    service = m_tracker.getService();
+                    service = m_cachedService;
                 }
+                
             }
         }
         try {