You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ui...@apache.org on 2012/02/24 11:47:51 UTC

svn commit: r1293173 [5/8] - in /felix/sandbox/uiterlix/dependencymanager/core: ./ .externalToolBuilders/ .settings/ 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/fel...

Added: felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/DependencyBase.java
URL: http://svn.apache.org/viewvc/felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/DependencyBase.java?rev=1293173&view=auto
==============================================================================
--- felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/DependencyBase.java (added)
+++ felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/DependencyBase.java Fri Feb 24 10:47:49 2012
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl.dependencies;
+
+import org.apache.felix.dm.ComponentDependencyDeclaration;
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyActivation;
+import org.apache.felix.dm.impl.Logger;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public abstract class DependencyBase implements Dependency, DependencyActivation {
+    private boolean m_isRequired;
+    private boolean m_isInstanceBound;
+    protected final Logger m_logger;
+    protected volatile boolean m_isStarted;
+
+    public DependencyBase(Logger logger) {
+        m_logger = logger;
+    }
+    
+    public DependencyBase(DependencyBase prototype) {
+        m_logger = prototype.m_logger;
+        m_isRequired = prototype.isRequired();
+        m_isInstanceBound = prototype.m_isInstanceBound;
+    }
+
+    public synchronized boolean isRequired() {
+        return m_isRequired;
+    }
+    
+    protected synchronized void setIsRequired(boolean isRequired) {
+        m_isRequired = isRequired;
+    }
+    
+    public final boolean isInstanceBound() {
+        return m_isInstanceBound;
+    }
+
+    public final void setIsInstanceBound(boolean isInstanceBound) {
+        m_isInstanceBound = isInstanceBound;
+    }
+    
+    public int getState() {
+        if (m_isStarted) {
+            return (isAvailable() ? 1 : 0) + (isRequired() ? 2 : 0);
+        }
+        else {
+            return isRequired() ? ComponentDependencyDeclaration.STATE_REQUIRED : ComponentDependencyDeclaration.STATE_OPTIONAL;
+        }
+    }
+
+}

Added: felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ResourceDependencyImpl.java
URL: http://svn.apache.org/viewvc/felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ResourceDependencyImpl.java?rev=1293173&view=auto
==============================================================================
--- felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ResourceDependencyImpl.java (added)
+++ felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ResourceDependencyImpl.java Fri Feb 24 10:47:49 2012
@@ -0,0 +1,531 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl.dependencies;
+
+import java.lang.reflect.InvocationTargetException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ComponentDependencyDeclaration;
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyActivation;
+import org.apache.felix.dm.DependencyService;
+import org.apache.felix.dm.InvocationUtil;
+import org.apache.felix.dm.ResourceDependency;
+import org.apache.felix.dm.ResourceHandler;
+import org.apache.felix.dm.impl.Logger;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.log.LogService;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ResourceDependencyImpl extends DependencyBase implements ResourceDependency, ResourceHandler, DependencyActivation, ComponentDependencyDeclaration {
+	private volatile BundleContext m_context;
+	private volatile ServiceRegistration m_registration;
+    private Object m_callbackInstance;
+    private String m_callbackAdded;
+    private String m_callbackChanged;
+    private String m_callbackRemoved;
+    private boolean m_autoConfig;
+    private String m_autoConfigInstance;
+    protected List m_services = new ArrayList();
+	private String m_resourceFilter;
+	private URL m_trackedResource;
+    private List m_resources = new ArrayList();
+    private List m_resourceProperties = new ArrayList();
+    private URL m_resourceInstance;
+    private Dictionary m_resourcePropertiesInstance;
+    private boolean m_propagate;
+    private Object m_propagateCallbackInstance;
+    private String m_propagateCallbackMethod;
+	
+    public ResourceDependencyImpl(BundleContext context, Logger logger) {
+        super(logger);
+    	m_context = context;
+    	m_autoConfig = true;
+    }
+    
+    public ResourceDependencyImpl(ResourceDependencyImpl prototype) {
+        super(prototype);
+        m_context = prototype.m_context;
+        m_autoConfig = prototype.m_autoConfig;
+        m_callbackInstance = prototype.m_callbackInstance;
+        m_callbackAdded = prototype.m_callbackAdded;
+        m_callbackChanged = prototype.m_callbackChanged;
+        m_callbackRemoved = prototype.m_callbackRemoved;
+        m_autoConfigInstance = prototype.m_autoConfigInstance;
+        m_resourceFilter = prototype.m_resourceFilter;
+        m_trackedResource = prototype.m_trackedResource;
+        m_propagate = prototype.m_propagate;
+    }
+    
+    public Dependency createCopy() {
+        return new ResourceDependencyImpl(this);
+    }
+    
+	public synchronized boolean isAvailable() {
+		return m_resources.size() > 0;
+	}
+
+	public void start(DependencyService service) {
+	    boolean needsStarting = false;
+	    synchronized (this) {
+	        m_services.add(service);
+	        if (!m_isStarted) {
+	            m_isStarted = true;
+	            needsStarting = true;
+	        }
+	    }
+	    if (needsStarting) {
+	        Dictionary props = null;
+	        if (m_trackedResource != null) {
+                props = new Properties();
+                props.put(ResourceHandler.URL, m_trackedResource);
+	        }
+	        else { 
+	        	if (m_resourceFilter != null) {
+		            props = new Properties();
+		            props.put(ResourceHandler.FILTER, m_resourceFilter);
+	        	}
+	        }
+	        m_registration = m_context.registerService(ResourceHandler.class.getName(), this, props);
+	    }
+	}
+
+	public void stop(DependencyService service) {
+	    boolean needsStopping = false;
+	    synchronized (this) {
+            if (m_services.size() == 1 && m_services.contains(service)) {
+                m_isStarted = false;
+                needsStopping = true;
+                m_services.remove(service);
+            }
+	    }
+	    if (needsStopping) {
+	        m_registration.unregister();
+	        m_registration = null;
+	    }
+	}
+
+	public void added(URL resource) {
+		handleResourceAdded(resource, null);
+	}
+	
+	public void added(URL resource, Dictionary resourceProperties) {
+		handleResourceAdded(resource, resourceProperties);
+	}
+	
+	private void handleResourceAdded(URL resource, Dictionary resourceProperties) {
+	    if (m_trackedResource == null || m_trackedResource.equals(resource)) {
+    		long counter;
+    		Object[] services;
+    		synchronized (this) {
+    		    m_resources.add(resource);
+    		    m_resourceProperties.add(resourceProperties);
+    			counter = m_resources.size();
+    			services = m_services.toArray();
+    		}
+            for (int i = 0; i < services.length; i++) {
+                DependencyService ds = (DependencyService) services[i];
+                if (counter == 1) {
+                    ds.dependencyAvailable(this);
+                    if (!isRequired()) {
+                        invokeAdded(ds, resource, resourceProperties);
+                    }
+                }
+                else {
+                    ds.dependencyChanged(this);
+                    invokeAdded(ds, resource, resourceProperties);
+                }
+            }
+	    }
+	}
+	
+	public void changed(URL resource) {
+		handleResourceChanged(resource, null);
+	}
+	
+	public void changed(URL resource, Dictionary resourceProperties) {
+		handleResourceChanged(resource, resourceProperties);
+	}
+
+	private void handleResourceChanged(URL resource, Dictionary resourceProperties) {
+        if (m_trackedResource == null || m_trackedResource.equals(resource)) {
+            Object[] services;
+            synchronized (this) {
+            	// change the resource properties for the resource
+            	m_resourceProperties.set(m_resources.indexOf(resource), resourceProperties);
+                services = m_services.toArray();
+            }
+            for (int i = 0; i < services.length; i++) {
+                DependencyService ds = (DependencyService) services[i];
+                invokeChanged(ds, resource, resourceProperties);
+            }
+        }
+	}
+	
+	public void removed(URL resource) {
+		handleResourceRemoved(resource, null);
+	}
+	
+	public void removed(URL resource, Dictionary resourceProperties) {
+		handleResourceRemoved(resource, resourceProperties);
+	}
+
+	public void handleResourceRemoved(URL resource, Dictionary resourceProperties) {
+        if (m_trackedResource == null || m_trackedResource.equals(resource)) {
+    		long counter;
+    		Object[] services;
+    		synchronized (this) {
+    			m_resourceProperties.remove(m_resources.indexOf(resource));
+    		    m_resources.remove(resource);
+    			counter = m_resources.size();
+    			services = m_services.toArray();
+    		}
+            for (int i = 0; i < services.length; i++) {
+                DependencyService ds = (DependencyService) services[i];
+                if (counter == 0) {
+                    ds.dependencyUnavailable(this);
+                    if (!isRequired()) {
+                        invokeRemoved(ds, resource, resourceProperties);
+                    }
+                }
+                else {
+                    ds.dependencyChanged(this);
+                    invokeRemoved(ds, resource, resourceProperties);
+                }
+            }
+        }
+	}
+	
+    public void invokeAdded(DependencyService ds, URL serviceInstance, Dictionary resourceProperties) {
+        invoke(ds, serviceInstance, resourceProperties, m_callbackAdded);
+    }
+
+    public void invokeChanged(DependencyService ds, URL serviceInstance, Dictionary resourceProperties) {
+    	invoke(ds, serviceInstance, resourceProperties, m_callbackChanged);
+    }
+
+    public void invokeRemoved(DependencyService ds, URL serviceInstance, Dictionary resourceProperties) {
+    	invoke(ds, serviceInstance, resourceProperties, m_callbackRemoved);
+    }
+    
+    private void invoke(DependencyService ds, URL serviceInstance, Dictionary resourceProperties, String name) {
+    	if (name != null) {
+	        ds.invokeCallbackMethod(getCallbackInstances(ds), name,
+	                new Class[][] {
+	        				{ Component.class, URL.class, Dictionary.class }, 
+	        				{ Component.class, URL.class },
+	        				{ Component.class },  
+	        				{ URL.class, Dictionary.class }, 
+	        				{ URL.class },
+	        				{ Object.class }, 
+	        				{}},
+	                new Object[][] {
+	        				{ ds.getServiceInterface(), serviceInstance, resourceProperties }, 
+	        				{ ds.getServiceInterface(), serviceInstance }, 
+	        				{ ds.getServiceInterface() }, 
+	        				{ serviceInstance, resourceProperties },
+	        				{ serviceInstance },
+	        				{ serviceInstance }, 
+	        				{}}
+	            );
+    	}
+    }
+    
+    /**
+     * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+     * dependency is added or removed. When you specify callbacks, the auto configuration 
+     * feature is automatically turned off, because we're assuming you don't need it in this 
+     * case.
+     * 
+     * @param added the method to call when a service was added
+     * @param removed the method to call when a service was removed
+     * @return this service dependency
+     */
+    public synchronized ResourceDependency setCallbacks(String added, String removed) {
+        return setCallbacks(null, added, null, removed);
+    }
+
+    /**
+     * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+     * dependency is added, changed or removed. When you specify callbacks, the auto 
+     * configuration feature is automatically turned off, because we're assuming you don't 
+     * need it in this case.
+     * 
+     * @param added the method to call when a service was added
+     * @param changed the method to call when a service was changed
+     * @param removed the method to call when a service was removed
+     * @return this service dependency
+     */
+    public synchronized ResourceDependency setCallbacks(String added, String changed, String removed) {
+        return setCallbacks(null, added, changed, removed);
+    }
+
+    /**
+     * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+     * dependency is added or removed. They are called on the instance you provide. When you
+     * specify callbacks, the auto configuration feature is automatically turned off, because
+     * we're assuming you don't need it in this case.
+     * 
+     * @param instance the instance to call the callbacks on
+     * @param added the method to call when a service was added
+     * @param removed the method to call when a service was removed
+     * @return this service dependency
+     */
+    public synchronized ResourceDependency setCallbacks(Object instance, String added, String removed) {
+        return setCallbacks(instance, added, null, removed);
+    }
+    
+    /**
+     * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+     * dependency is added, changed or removed. They are called on the instance you provide. When you
+     * specify callbacks, the auto configuration feature is automatically turned off, because
+     * we're assuming you don't need it in this case.
+     * 
+     * @param instance the instance to call the callbacks on
+     * @param added the method to call when a service was added
+     * @param changed the method to call when a service was changed
+     * @param removed the method to call when a service was removed
+     * @return this service dependency
+     */
+    public synchronized ResourceDependency setCallbacks(Object instance, String added, String changed, String removed) {
+        ensureNotActive();
+        // if at least one valid callback is specified, we turn off auto configuration
+        if (added != null || removed != null || changed != null) {
+            setAutoConfig(false);
+        }
+        m_callbackInstance = instance;
+        m_callbackAdded = added;
+        m_callbackChanged = changed;
+        m_callbackRemoved = removed;
+        return this;
+    }
+    
+    private void ensureNotActive() {
+        if (m_registration != null) {
+            throw new IllegalStateException("Cannot modify state while active.");
+        }
+    }
+    
+    /**
+     * Sets auto configuration for this service. Auto configuration allows the
+     * dependency to fill in any attributes in the service implementation that
+     * are of the same type as this dependency. Default is on.
+     * 
+     * @param autoConfig the value of auto config
+     * @return this service dependency
+     */
+    public synchronized ResourceDependency setAutoConfig(boolean autoConfig) {
+        ensureNotActive();
+        m_autoConfig = autoConfig;
+        return this;
+    }
+    
+    /**
+     * Sets auto configuration for this service. Auto configuration allows the
+     * dependency to fill in the attribute in the service implementation that
+     * has the same type and instance name.
+     * 
+     * @param instanceName the name of attribute to auto config
+     * @return this service dependency
+     */
+    public synchronized ResourceDependency setAutoConfig(String instanceName) {
+        ensureNotActive();
+        m_autoConfig = (instanceName != null);
+        m_autoConfigInstance = instanceName;
+        return this;
+    }
+    
+    private synchronized Object[] getCallbackInstances(DependencyService ds) {
+        if (m_callbackInstance == null) {
+            return ds.getCompositionInstances();
+        }
+        else {
+            return new Object[] { m_callbackInstance };
+        }
+    }
+
+	public ResourceDependency setResource(URL resource) {
+		m_trackedResource = resource;
+		return this;
+	}
+	
+    public synchronized ResourceDependency setRequired(boolean required) {
+        ensureNotActive();
+        setIsRequired(required);
+        return this;
+    }
+
+	public ResourceDependency setFilter(String resourceFilter) {
+        ensureNotActive();
+		m_resourceFilter = resourceFilter;
+		return this;
+	}
+	
+	public ResourceDependency setFilter(String resourceFilter, String resourcePropertiesFilter) {
+        ensureNotActive();
+        m_resourceFilter = resourceFilter;
+		return this;
+	}
+	
+	public void setResourcePropertiesConfigurationMember() {
+		
+	}
+	
+    public synchronized boolean isAutoConfig() {
+        return m_autoConfig;
+    }
+    
+    public URL getResource() {
+    	return lookupResource();
+    }
+
+    private URL lookupResource() {
+        try {
+            return (URL) m_resources.get(0);
+        }
+        catch (IndexOutOfBoundsException e) {
+            return null;
+        }
+    }
+    
+    private Dictionary lookupResourceProperties() {
+    	try {
+    		return (Dictionary) m_resourceProperties.get(0);
+    	}
+        catch (IndexOutOfBoundsException e) {
+            return null;
+        }    	
+    }
+    
+    public Object getAutoConfigInstance() {
+        return lookupResource();
+    }
+
+    public String getAutoConfigName() {
+        return m_autoConfigInstance;
+    }
+
+    public Class getAutoConfigType() {
+        return URL.class;
+    }
+
+    public void invokeAdded(DependencyService service) {
+        // we remember these for future reference, needed for required callbacks
+        m_resourceInstance = lookupResource();
+        m_resourcePropertiesInstance = lookupResourceProperties();
+        invokeAdded(service, m_resourceInstance, m_resourcePropertiesInstance);
+    }
+
+    public void invokeRemoved(DependencyService service) {
+        invokeRemoved(service, m_resourceInstance, m_resourcePropertiesInstance);
+        m_resourceInstance = null;
+        m_resourcePropertiesInstance = null;
+    }
+
+    public ResourceDependency setPropagate(boolean propagate) {
+        ensureNotActive();
+        m_propagate = propagate;
+        return this;
+    }
+    
+    public ResourceDependency setPropagate(Object instance, String method) {
+        setPropagate(instance != null && method != null);
+        m_propagateCallbackInstance = instance;
+        m_propagateCallbackMethod = method;
+        return this;
+    }
+    
+    public Dictionary getProperties() {
+        URL resource = lookupResource();
+        Dictionary resourceProperties = lookupResourceProperties();
+        if (resource != null) {
+            if (m_propagateCallbackInstance != null && m_propagateCallbackMethod != null) {
+                try {
+                    return (Dictionary) InvocationUtil.invokeCallbackMethod(m_propagateCallbackInstance, m_propagateCallbackMethod, new Class[][] {{ URL.class }}, new Object[][] {{ resource }});
+                }
+                catch (InvocationTargetException e) {
+                    m_logger.log(LogService.LOG_WARNING, "Exception while invoking callback method", e.getCause());
+                }
+                catch (Exception e) {
+                    m_logger.log(LogService.LOG_WARNING, "Exception while trying to invoke callback method", e);
+                }
+                throw new IllegalStateException("Could not invoke callback");
+            }
+            else {
+                Properties props = new Properties();
+                props.setProperty(ResourceHandler.HOST, resource.getHost());
+                props.setProperty(ResourceHandler.PATH, resource.getPath());
+                props.setProperty(ResourceHandler.PROTOCOL, resource.getProtocol());
+                props.setProperty(ResourceHandler.PORT, Integer.toString(resource.getPort()));
+                // add the custom resource properties
+                if (resourceProperties != null) {
+                	Enumeration properyKeysEnum = resourceProperties.keys(); 
+                	while (properyKeysEnum.hasMoreElements()) {
+                		String key = (String) properyKeysEnum.nextElement();
+                		if (!key.equals(ResourceHandler.HOST) &&
+                				!key.equals(ResourceHandler.PATH) &&
+                				!key.equals(ResourceHandler.PROTOCOL) &&
+                				!key.equals(ResourceHandler.PORT)) {
+                			props.setProperty(key, resourceProperties.get(key).toString());
+                		} else {
+                			m_logger.log(LogService.LOG_WARNING, "Custom resource property is overlapping with the default resource property for key: " + key);
+                		}
+                	}
+                }
+                return props;
+            }
+        }
+        else {
+            throw new IllegalStateException("cannot find resource");
+        }
+    }
+
+    public boolean isPropagated() {
+        return m_propagate;
+    }
+
+    public ResourceDependency setInstanceBound(boolean isInstanceBound) {
+        setIsInstanceBound(isInstanceBound);
+        return this;
+    }
+
+    public String getName() {
+        StringBuilder sb = new StringBuilder();
+        if (m_resourceFilter != null) {
+            sb.append(m_resourceFilter);
+        }
+        if (m_trackedResource != null) {
+            sb.append(m_trackedResource.toString());
+        }
+        return sb.toString();
+    }
+
+    public String getType() {
+        return "resource";
+    }
+}

Added: felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ServiceDependencyImpl.java
URL: http://svn.apache.org/viewvc/felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ServiceDependencyImpl.java?rev=1293173&view=auto
==============================================================================
--- felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ServiceDependencyImpl.java (added)
+++ felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/ServiceDependencyImpl.java Fri Feb 24 10:47:49 2012
@@ -0,0 +1,1136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl.dependencies;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Proxy;
+import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.ComponentDependencyDeclaration;
+import org.apache.felix.dm.Dependency;
+import org.apache.felix.dm.DependencyService;
+import org.apache.felix.dm.InvocationUtil;
+import org.apache.felix.dm.ServiceDependency;
+import org.apache.felix.dm.ServiceUtil;
+import org.apache.felix.dm.impl.DefaultNullObject;
+import org.apache.felix.dm.impl.Logger;
+import org.apache.felix.dm.tracker.ServiceTracker;
+import org.apache.felix.dm.tracker.ServiceTrackerCustomizer;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.log.LogService;
+
+/**
+ * Service dependency that can track an OSGi service.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceDependencyImpl extends DependencyBase implements ServiceDependency, ServiceTrackerCustomizer, ComponentDependencyDeclaration {
+    protected List m_services = new ArrayList();
+    protected volatile ServiceTracker m_tracker;
+    protected BundleContext m_context;
+    protected volatile Class m_trackedServiceName;
+    private Object m_nullObject;
+    private volatile String m_trackedServiceFilter;
+    private volatile String m_trackedServiceFilterUnmodified;
+    private volatile ServiceReference m_trackedServiceReference;
+    private Object m_callbackInstance;
+    private String m_callbackAdded;
+    private String m_callbackChanged;
+    private String m_callbackRemoved;
+    private String m_callbackSwapped;
+    private boolean m_autoConfig;
+    protected ServiceReference m_reference;
+    protected Object m_serviceInstance;
+    private String m_autoConfigInstance;
+    private boolean m_autoConfigInvoked;
+    private Object m_defaultImplementation;
+    private Object m_defaultImplementationInstance;
+    private boolean m_isAvailable;
+    private boolean m_propagate;
+    private Object m_propagateCallbackInstance;
+    private String m_propagateCallbackMethod;
+    private final Map m_sr = new HashMap(); /* <DependencyService, Set<Tuple<ServiceReference, Object>> */
+	private Map m_componentByRank = new HashMap(); /* <Component, Map<Long, Map<Integer, Tuple>>> */
+    
+    private static final Comparator COMPARATOR = new Comparator() {
+        public int getRank(ServiceReference ref) {
+            Object ranking = ref.getProperty(Constants.SERVICE_RANKING);
+            if (ranking != null && (ranking instanceof Integer)) {
+                return ((Integer) ranking).intValue();
+            }
+            return 0;
+        }
+
+        public int compare(Object a, Object b) {
+            ServiceReference ra = (ServiceReference) a, rb = (ServiceReference) b;
+            int ranka = getRank(ra);
+            int rankb = getRank(rb);
+            if (ranka < rankb) {
+                return -1;
+            }
+            else if (ranka > rankb) {
+                return 1;
+            }
+            return 0;
+        }
+    };
+    
+    private static final class Tuple /* <ServiceReference, Object> */ {
+        private final ServiceReference m_serviceReference;
+        private final Object m_service;
+        
+        public Tuple(ServiceReference first, Object last) {
+            m_serviceReference = first;
+            m_service = last;
+        }
+        
+        public ServiceReference getServiceReference() {
+            return m_serviceReference;
+        }
+        
+        public Object getService() {
+            return m_service;
+        }
+        
+        public boolean equals(Object obj) {
+            return ((Tuple) obj).getServiceReference().equals(getServiceReference());
+        }
+        
+        public int hashCode() {
+            return m_serviceReference.hashCode();
+        }
+    }
+    
+    /**
+     * Entry to wrap service properties behind a Map.
+     */
+    private static final class ServicePropertiesMapEntry implements Map.Entry {
+        private final String m_key;
+        private Object m_value;
+
+        public ServicePropertiesMapEntry(String key, Object value) {
+            m_key = key;
+            m_value = value;
+        }
+
+        public Object getKey() {
+            return m_key;
+        }
+
+        public Object getValue() {
+            return m_value;
+        }
+
+        public String toString() {
+            return m_key + "=" + m_value;
+        }
+
+        public Object setValue(Object value) {
+            Object oldValue = m_value;
+            m_value = value;
+            return oldValue;
+        }
+
+        public boolean equals(Object o) {
+            if (!(o instanceof Map.Entry)) {
+                return false;
+            }
+            Map.Entry e = (Map.Entry) o;
+            return eq(m_key, e.getKey()) && eq(m_value, e.getValue());
+        }
+
+        public int hashCode() {
+            return ((m_key == null) ? 0 : m_key.hashCode()) ^ ((m_value == null) ? 0 : m_value.hashCode());
+        }
+
+        private static final boolean eq(Object o1, Object o2) {
+            return (o1 == null ? o2 == null : o1.equals(o2));
+        }
+    }
+
+    /**
+     * Wraps service properties behind a Map.
+     */
+    private final static class ServicePropertiesMap extends AbstractMap {
+        private final ServiceReference m_ref;
+
+        public ServicePropertiesMap(ServiceReference ref) {
+            m_ref = ref;
+        }
+
+        public Object get(Object key) {
+            return m_ref.getProperty(key.toString());
+        }
+
+        public int size() {
+            return m_ref.getPropertyKeys().length;
+        }
+
+        public Set entrySet() {
+            Set set = new HashSet();
+            String[] keys = m_ref.getPropertyKeys();
+            for (int i = 0; i < keys.length; i++) {
+                set.add(new ServicePropertiesMapEntry(keys[i], m_ref.getProperty(keys[i])));
+            }
+            return set;
+        }
+    }
+        
+    /**
+     * Creates a new service dependency.
+     * 
+     * @param context the bundle context
+     * @param logger the logger
+     */
+    public ServiceDependencyImpl(BundleContext context, Logger logger) {
+        super(logger);
+        m_context = context;
+        m_autoConfig = true;
+    }
+    
+    /** Copying constructor that clones an existing instance. */
+    public ServiceDependencyImpl(ServiceDependencyImpl prototype) {
+        super(prototype);
+        synchronized (prototype) {
+            m_context = prototype.m_context;
+            m_autoConfig = prototype.m_autoConfig;
+            m_trackedServiceName = prototype.m_trackedServiceName;
+            m_nullObject = prototype.m_nullObject;
+            m_trackedServiceFilter = prototype.m_trackedServiceFilter;
+            m_trackedServiceFilterUnmodified = prototype.m_trackedServiceFilterUnmodified;
+            m_trackedServiceReference = prototype.m_trackedServiceReference;
+            m_callbackInstance = prototype.m_callbackInstance;
+            m_callbackAdded = prototype.m_callbackAdded;
+            m_callbackChanged = prototype.m_callbackChanged;
+            m_callbackRemoved = prototype.m_callbackRemoved;
+            m_autoConfigInstance = prototype.m_autoConfigInstance;
+            m_defaultImplementation = prototype.m_defaultImplementation;
+        }
+    }
+    
+    public Dependency createCopy() {
+        return new ServiceDependencyImpl(this);
+    }
+    
+    public synchronized boolean isAutoConfig() {
+        return m_autoConfig;
+    }
+    
+    public synchronized boolean isAvailable() {
+        return m_isAvailable;
+    }
+
+    public synchronized Object getService() {
+        Object service = null;
+        if (m_isStarted) {
+            service = m_tracker.getService();
+        }
+        if (service == null && isAutoConfig()) {
+            service = getDefaultImplementation();
+            if (service == null) {
+                service = getNullObject();
+            }
+        }
+        return service;
+    }
+
+    public Object lookupService() {
+        Object service = null;
+        if (m_isStarted) {
+            service = getService();
+        }
+        else {
+            ServiceReference[] refs = null;
+            ServiceReference ref = null;
+            if (m_trackedServiceName != null) {
+                if (m_trackedServiceFilter != null) {
+                    try {
+                        refs = m_context.getServiceReferences(m_trackedServiceName.getName(), m_trackedServiceFilter);
+                        if (refs != null) {
+                            Arrays.sort(refs, COMPARATOR);
+                            ref = refs[0];
+                        }
+                    }
+                    catch (InvalidSyntaxException e) {
+                        throw new IllegalStateException("Invalid filter definition for dependency.");
+                    }
+                }
+                else if (m_trackedServiceReference != null) {
+                    ref = m_trackedServiceReference;
+                }
+                else {
+                    ref = m_context.getServiceReference(m_trackedServiceName.getName());
+                }
+                if (ref != null) {
+                    service = m_context.getService(ref);
+                }
+            }
+            else {
+                throw new IllegalStateException("Could not lookup dependency, no service name specified.");
+            }
+        }
+        if (service == null && isAutoConfig()) {
+            service = getDefaultImplementation();
+            if (service == null) {
+                service = getNullObject();
+            }
+        }
+        return service;
+    }
+
+    // TODO lots of duplication in lookupService()
+    public ServiceReference lookupServiceReference() {
+        ServiceReference service = null;
+        if (m_isStarted) {
+            service = m_tracker.getServiceReference();
+        }
+        else {
+            ServiceReference[] refs = null;
+            ServiceReference ref = null;
+            if (m_trackedServiceName != null) {
+                if (m_trackedServiceFilter != null) {
+                    try {
+                        refs = m_context.getServiceReferences(m_trackedServiceName.getName(), m_trackedServiceFilter);
+                        if (refs != null) {
+                            Arrays.sort(refs, COMPARATOR);
+                            ref = refs[0];
+                        }
+                    }
+                    catch (InvalidSyntaxException e) {
+                        throw new IllegalStateException("Invalid filter definition for dependency.");
+                    }
+                }
+                else if (m_trackedServiceReference != null) {
+                    ref = m_trackedServiceReference;
+                }
+                else {
+                    ref = m_context.getServiceReference(m_trackedServiceName.getName());
+                }
+                if (ref != null) {
+                    service = ref;
+                }
+            }
+            else {
+                throw new IllegalStateException("Could not lookup dependency, no service name specified.");
+            }
+        }
+        return service;
+    }
+
+    private Object getNullObject() {
+        if (m_nullObject == null) {
+            Class trackedServiceName;
+            synchronized (this) {
+                trackedServiceName = m_trackedServiceName;
+            }
+            try {
+                m_nullObject = Proxy.newProxyInstance(trackedServiceName.getClassLoader(), new Class[] {trackedServiceName}, new DefaultNullObject()); 
+            }
+            catch (Exception e) {
+                m_logger.log(Logger.LOG_ERROR, "Could not create null object for " + trackedServiceName + ".", e);
+            }
+        }
+        return m_nullObject;
+    }
+    
+    private Object getDefaultImplementation() {
+        if (m_defaultImplementation != null) {
+            if (m_defaultImplementation instanceof Class) {
+                try {
+                    m_defaultImplementationInstance = ((Class) m_defaultImplementation).newInstance();
+                }
+                catch (Exception e) {
+                    m_logger.log(Logger.LOG_ERROR, "Could not create default implementation instance of class " + m_defaultImplementation + ".", e);
+                }
+            }
+            else {
+                m_defaultImplementationInstance = m_defaultImplementation;
+            }
+        }
+        return m_defaultImplementationInstance;
+    }
+    
+    public synchronized Class getInterface() {
+        return m_trackedServiceName;
+    }
+
+    public void start(DependencyService service) {
+        boolean needsStarting = false;
+        synchronized (this) {
+            m_services.add(service);
+            if (!m_isStarted) {
+                if (m_trackedServiceName != null) {
+                    if (m_trackedServiceFilter != null) {
+                        try {
+                            m_tracker = new ServiceTracker(m_context, m_context.createFilter(m_trackedServiceFilter), this);
+                        }
+                        catch (InvalidSyntaxException e) {
+                            throw new IllegalStateException("Invalid filter definition for dependency: " + m_trackedServiceFilter);
+                        }
+                    }
+                    else if (m_trackedServiceReference != null) {
+                        m_tracker = new ServiceTracker(m_context, m_trackedServiceReference, this);
+                    }
+                    else {
+                        m_tracker = new ServiceTracker(m_context, m_trackedServiceName.getName(), this);
+                    }
+                }
+                else {
+                    throw new IllegalStateException("Could not create tracker for dependency, no service name specified.");
+                }
+                m_isStarted = true;
+                needsStarting = true;
+            }
+        }
+        if (needsStarting) {
+            m_tracker.open();
+        }
+    }
+
+    public void stop(DependencyService service) {
+        boolean needsStopping = false;
+        synchronized (this) {
+            if (m_services.size() == 1 && m_services.contains(service)) {
+                m_isStarted = false;
+                needsStopping = true;
+            }
+        }
+        if (needsStopping) {
+            m_tracker.close();
+            m_tracker = null;
+        }
+        //moved this down
+        synchronized (this) {
+            m_services.remove(service);
+        }
+    }
+
+    public Object addingService(ServiceReference ref) {
+        Object service = m_context.getService(ref);
+        // first check to make sure the service is actually an instance of our service
+        if (!m_trackedServiceName.isInstance(service)) {
+            return null;
+        }
+        return service;
+    }
+
+    public void addedService(ServiceReference ref, Object service) {
+        boolean makeAvailable = makeAvailable();
+        
+        Object[] services;
+        synchronized (this) {
+            services = m_services.toArray();
+        }
+        for (int i = 0; i < services.length; i++) {
+            DependencyService ds = (DependencyService) services[i];
+            if (makeAvailable) {
+                if (ds.isInstantiated() && isInstanceBound() && isRequired()) {
+                    invokeAdded(ds, ref, service);
+                }
+                // The dependency callback will be defered until all required dependency are available.
+                ds.dependencyAvailable(this);
+                if (!isRequired()) {
+                    // For optional dependency, we always invoke callback, because at this point, we know
+                    // that the service has been started, and the service start method has been called.
+                    // (See the ServiceImpl.bindService method, which will activate optional dependencies using 
+                    // startTrackingOptional() method). 
+                    invokeAdded(ds, ref, service);
+                }
+            }
+            else {
+                ds.dependencyChanged(this);
+                // At this point, either the dependency is optional (meaning that the service has been started,
+                // because if not, then our dependency would not be active); or the dependency is required,
+                // meaning that either the service is not yet started, or already started.
+                // In all cases, we have to inject the required dependency.
+                
+                // we only try to invoke the method here if we are really already instantiated
+                if (ds.isInstantiated() && ds.getCompositionInstances().length > 0) {
+                    invokeAdded(ds, ref, service);
+                }
+            }
+        }
+    }
+
+    public void modifiedService(ServiceReference ref, Object service) {
+        Object[] services;
+        synchronized (this) {
+            services = m_services.toArray();
+        }
+        for (int i = 0; i < services.length; i++) {
+            DependencyService ds = (DependencyService) services[i];
+            ds.dependencyChanged(this);
+            if (ds.isRegistered()) {
+                invokeChanged(ds, ref, service);
+            }
+        }
+    }
+
+    public void removedService(ServiceReference ref, Object service) {
+        boolean makeUnavailable = makeUnavailable();
+        
+        Object[] services;
+        synchronized (this) {
+            services = m_services.toArray();
+        }
+
+        for (int i = 0; i < services.length; i++) {
+            DependencyService ds = (DependencyService) services[i];
+            if (makeUnavailable) {
+                ds.dependencyUnavailable(this);
+                if (!isRequired() || (ds.isInstantiated() && isInstanceBound())) {
+                    invokeRemoved(ds, ref, service);
+                }
+            }
+            else {
+                ds.dependencyChanged(this);
+                invokeRemoved(ds, ref, service);
+            }
+        }
+        // unget what we got in addingService (see ServiceTracker 701.4.1)
+        m_context.ungetService(ref);
+
+    }
+    
+    public void invokeAdded(DependencyService dependencyService, ServiceReference reference, Object service) {
+        boolean added = false;
+        synchronized (m_sr) {
+            Set set = (Set) m_sr.get(dependencyService);
+            if (set == null) {
+                set = new HashSet();
+                m_sr.put(dependencyService, set);
+            }
+            added = set.add(new Tuple(reference, service));
+        }
+        if (added) { 
+        	// when a changed callback is specified we might not call the added callback just yet
+        	if (m_callbackSwapped != null) {
+        		handleAspectAwareAdded(dependencyService, reference, service);
+        	}
+        	else {
+        		invoke(dependencyService, reference, service, m_callbackAdded);
+        	}
+        }
+    }
+    
+    private void handleAspectAwareAdded(DependencyService dependencyService, ServiceReference reference, Object service) {
+		if (componentIsDependencyManagerFactory(dependencyService)) {
+			// component is either aspect or adapter factory instance, these must be ignored.
+			return;
+		}
+		boolean invokeAdded = false;
+		Integer ranking = ServiceUtil.getRankingAsInteger(reference);
+		Tuple highestRankedService = null;
+		synchronized (m_componentByRank) {
+			Long originalServiceId = ServiceUtil.getServiceIdAsLong(reference);
+			Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
+			if (componentMap == null) {
+				// create new componentMap
+				componentMap = new HashMap(); /* <Long, Map<Integer, Tuple>> */
+				m_componentByRank.put(dependencyService, componentMap);
+			}
+			Map rankings = (Map) componentMap.get(originalServiceId); /* <Integer, Tuple> */
+			if (rankings == null) {
+				// new component added
+				rankings = new HashMap(); /* <Integer, Tuple> */
+				componentMap.put(originalServiceId, rankings);
+				rankings.put(ranking, new Tuple(reference, service));
+				invokeAdded = true;
+			} 
+			
+			if (!invokeAdded) {
+				highestRankedService = swapHighestRankedService(dependencyService, originalServiceId, reference, service, ranking);
+			}
+		}
+		if (invokeAdded) {
+			invoke(dependencyService, reference, service, m_callbackAdded);
+		} else {
+			invokeSwappedCallback(dependencyService, highestRankedService.getServiceReference(), highestRankedService.getService(), reference, service);
+		}    	
+    }
+    
+    private boolean componentIsDependencyManagerFactory(DependencyService dependencyService) {
+        Object component = dependencyService.getService();
+        if (component != null) {
+            String className = component.getClass().getName();
+            return className.startsWith("org.apache.felix.dm")
+                && !className.startsWith("org.apache.felix.dm.impl.AdapterServiceImpl$AdapterImpl")
+                && !className.startsWith("org.apache.felix.dm.test");
+        }
+        return false;
+    }
+    
+	private Tuple swapHighestRankedService(DependencyService dependencyService, Long serviceId, ServiceReference newReference, Object newService, Integer newRanking) {
+		// does a component with a higher ranking exists
+		synchronized (m_componentByRank) {
+			Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
+			Map rankings = (Map) componentMap.get(serviceId); /* <Integer, Tuple> */
+			Entry highestEntry = getHighestRankedService(dependencyService, serviceId); /* <Integer, Tuple> */
+			rankings.remove(highestEntry.getKey());
+			rankings.put(newRanking, new Tuple(newReference, newService));
+			return (Tuple) highestEntry.getValue();
+		}
+	}
+	
+	private Entry getHighestRankedService(DependencyService dependencyService, Long serviceId) { /* <Integer, Tuple> */
+		Entry highestEntry = null; /* <Integer, Tuple> */
+		Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
+		Map rankings = (Map) componentMap.get(serviceId); /* <Integer, Tuple> */
+		if (rankings != null) {
+			for (Iterator entryIterator = rankings.entrySet().iterator(); entryIterator.hasNext(); ) { /* <Integer, Tuple> */
+				Entry mapEntry = (Entry) entryIterator.next();
+				if (highestEntry == null) {
+					highestEntry = mapEntry;
+				} else {
+					if (((Integer)mapEntry.getKey()).intValue() > ((Integer)highestEntry.getKey()).intValue()) {
+						highestEntry = mapEntry;
+					}
+				}
+			}
+		}
+		return highestEntry;
+	}
+
+
+
+	private boolean isLastService(DependencyService dependencyService, ServiceReference reference, Object object, Long serviceId) {
+		// get the collection of rankings
+		Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
+		
+		Map rankings = null; /* <Integer, Tuple> */
+		if (componentMap != null) {
+			rankings = (Map) componentMap.get(serviceId);
+		}
+		// if there is only one element left in the collection of rankings
+		// and this last element has the same ranking as the supplied service (in other words, it is the same)
+		// then this is the last service
+		// NOTE: it is possible that there is only one element, but that it's not equal to the supplied service,
+		// because an aspect on top of the original service is being removed (but the original service is still
+		// there). That in turn triggers:
+		// 1) a call to added(original-service)
+		// 2) that causes a swap
+		// 3) a call to removed(aspect-service) <-- that's what we're talking about
+		return (componentMap != null && rankings != null && rankings.size() == 1 && ((Entry)rankings.entrySet().iterator().next()).getKey()
+				.equals(ServiceUtil.getRankingAsInteger(reference)));
+	}
+	
+	
+    public void invokeChanged(DependencyService dependencyService, ServiceReference reference, Object service) {
+        invoke(dependencyService, reference, service, m_callbackChanged);
+    }
+
+    public void invokeRemoved(DependencyService dependencyService, ServiceReference reference, Object service) {
+        boolean removed = false;
+        synchronized (m_sr) {
+            Set set = (Set) m_sr.get(dependencyService);
+            removed = (set != null && set.remove(new Tuple(reference, service)));
+        }
+        if (removed) {
+        	if (m_callbackSwapped != null) {
+        		handleAspectAwareRemoved(dependencyService, reference, service);
+        	}
+        	else {
+        		invoke(dependencyService, reference, service, m_callbackRemoved);
+        	}
+        }
+    }
+    
+	private void handleAspectAwareRemoved(DependencyService dependencyService, ServiceReference reference, Object service) {
+		if (componentIsDependencyManagerFactory(dependencyService)) {
+			// component is either aspect or adapter factory instance, these must be ignored.
+			return;
+		}
+		Long serviceId = ServiceUtil.getServiceIdAsLong(reference);
+			synchronized (m_componentByRank) {
+				if (isLastService(dependencyService, reference, service, serviceId)) {
+					invoke(dependencyService, reference, service, m_callbackRemoved);
+				}
+				Long originalServiceId = ServiceUtil.getServiceIdAsLong(reference);
+				Map componentMap = (Map) m_componentByRank.get(dependencyService); /* <Long, Map<Integer, Tuple>> */
+				if (componentMap != null) {
+					Map rankings = (Map) componentMap.get(originalServiceId); /* <Integer, Tuple> */
+					for (Iterator entryIterator = rankings.entrySet().iterator(); entryIterator.hasNext(); ) {
+						Entry mapEntry = (Entry) entryIterator.next();
+						if (((Tuple)mapEntry.getValue()).getServiceReference().equals(reference)) {
+							// remove the reference
+							rankings.remove(mapEntry.getKey());
+						}
+					}
+					if (rankings.size() == 0) {
+						componentMap.remove(originalServiceId);
+					}
+					if (componentMap.size() == 0) {
+						m_componentByRank.remove(dependencyService);
+					}
+				}
+			}
+	}    
+
+    public void invoke(DependencyService dependencyService, ServiceReference reference, Object service, String name) {
+        if (name != null) {
+            dependencyService.invokeCallbackMethod(getCallbackInstances(dependencyService), name, 
+                new Class[][] {
+                    {Component.class, ServiceReference.class, m_trackedServiceName}, {Component.class, ServiceReference.class, Object.class}, {Component.class, ServiceReference.class}, {Component.class, m_trackedServiceName}, {Component.class, Object.class}, {Component.class}, {Component.class, Map.class, m_trackedServiceName},
+                    {ServiceReference.class, m_trackedServiceName}, {ServiceReference.class, Object.class}, {ServiceReference.class}, {m_trackedServiceName}, {Object.class}, {}, {Map.class, m_trackedServiceName}
+                },
+                new Object[][] {
+                    {dependencyService, reference, service}, {dependencyService, reference, service}, {dependencyService, reference}, {dependencyService, service}, {dependencyService, service}, {dependencyService}, {dependencyService, new ServicePropertiesMap(reference), service},
+                    {reference, service}, {reference, service}, {reference}, {service}, {service}, {}, {new ServicePropertiesMap(reference), service}
+                }    
+            );
+        }
+    }
+    
+	private void invokeSwappedCallback(DependencyService component, ServiceReference previousReference, Object previous, ServiceReference currentServiceReference,
+			Object current) {
+		component.invokeCallbackMethod(getCallbackInstances(component), m_callbackSwapped, new Class[][] { { m_trackedServiceName, m_trackedServiceName },
+				{ Object.class, Object.class }, { ServiceReference.class, m_trackedServiceName, ServiceReference.class, m_trackedServiceName },
+				{ ServiceReference.class, Object.class, ServiceReference.class, Object.class } }, new Object[][] { { previous, current },
+				{ previous, current }, { previousReference, previous, currentServiceReference, current },
+				{ previousReference, previous, currentServiceReference, current } });
+	}    
+
+    protected synchronized boolean makeAvailable() {
+        if (!isAvailable()) {
+            m_isAvailable = true;
+            return true;
+        }
+        return false;
+    }
+    
+    private synchronized boolean makeUnavailable() {
+        if ((isAvailable()) && (m_tracker.getServiceReference() == null)) {
+            m_isAvailable = false;
+            return true;
+        }
+        return false;
+    }
+    
+    private synchronized Object[] getCallbackInstances(DependencyService dependencyService) {
+        if (m_callbackInstance == null) {
+            return dependencyService.getCompositionInstances();
+        }
+        else {
+            return new Object[] { m_callbackInstance };
+        }
+    }
+    
+    // ----- CREATION
+
+    /**
+     * Sets the name of the service that should be tracked. 
+     * 
+     * @param serviceName the name of the service
+     * @return this service dependency
+     */
+    public synchronized ServiceDependency setService(Class serviceName) {
+        setService(serviceName, null, null);
+        return this;
+    }
+    
+    /**
+     * Sets the name of the service that should be tracked. You can either specify
+     * only the name, only the filter, or the name and a filter.
+     * <p>
+     * If you specify name and filter, the filter is used
+     * to track the service and should only return services of the type that was specified
+     * in the name. To make sure of this, the filter is actually extended internally to
+     * filter on the correct name.
+     * <p>
+     * If you specify only the filter, the name is assumed to be a service of type
+     * <code>Object</code> which means that, when auto configuration is on, instances
+     * of that service will be injected in any field of type <code>Object</code>.
+     * 
+     * @param serviceName the name of the service
+     * @param serviceFilter the filter condition
+     * @return this service dependency
+     */
+    public synchronized ServiceDependency setService(Class serviceName, String serviceFilter) {
+        setService(serviceName, null, serviceFilter);
+        return this;
+    }
+    
+    /**
+     * Sets the name of the service that should be tracked. The name is assumed to be 
+     * a service of type <code>Object</code> which means that, when auto configuration 
+     * is on, instances of that service will be injected in any field of type 
+     * <code>Object</code>.
+     * 
+     * @param serviceFilter the filter condition
+     * @return this service dependency
+     */
+    public synchronized ServiceDependency setService(String serviceFilter) {
+        if (serviceFilter == null) {
+            throw new IllegalArgumentException("Service filter cannot be null.");
+        }
+        setService(null, null, serviceFilter);
+        return this;
+    }
+
+    /**
+     * Sets the name of the service that should be tracked. By specifying the service
+     * reference of the service you want to track, you can directly track a single
+     * service. The name you use must match the type of service referred to by the
+     * service reference and it is up to you to make sure that is the case.
+     * 
+     * @param serviceName the name of the service
+     * @param serviceReference the service reference to track
+     * @return this service dependency
+     */
+    public synchronized ServiceDependency setService(Class serviceName, ServiceReference serviceReference) {
+        setService(serviceName, serviceReference, null);
+        return this;
+    }
+
+    /** Internal method to set the name, service reference and/or filter. */
+    private void setService(Class serviceName, ServiceReference serviceReference, String serviceFilter) {
+        ensureNotActive();
+        if (serviceName == null) {
+            m_trackedServiceName = Object.class;
+        }
+        else {
+            m_trackedServiceName = serviceName;
+        }
+        if (serviceFilter != null) {
+            m_trackedServiceFilterUnmodified = serviceFilter;
+            if (serviceName == null) {
+                m_trackedServiceFilter = serviceFilter;
+            }
+            else {
+                m_trackedServiceFilter ="(&(" + Constants.OBJECTCLASS + "=" + serviceName.getName() + ")" + serviceFilter + ")";
+            }
+        }
+        else {
+            m_trackedServiceFilterUnmodified = null;
+            m_trackedServiceFilter = null;
+        }
+        if (serviceReference != null) {
+            m_trackedServiceReference = serviceReference;
+            if (serviceFilter != null) {
+                throw new IllegalArgumentException("Cannot specify both a filter and a service reference.");
+            }
+        }
+        else {
+            m_trackedServiceReference = null;
+        }
+    }
+    
+    /**
+     * Sets the default implementation for this service dependency. You can use this to supply
+     * your own implementation that will be used instead of a Null Object when the dependency is
+     * not available. This is also convenient if the service dependency is not an interface
+     * (which would cause the Null Object creation to fail) but a class.
+     * 
+     * @param implementation the instance to use or the class to instantiate if you want to lazily
+     *     instantiate this implementation
+     * @return this service dependency
+     */
+    public synchronized ServiceDependency setDefaultImplementation(Object implementation) {
+        ensureNotActive();
+        m_defaultImplementation = implementation;
+        return this;
+    }
+
+    /**
+     * Sets the required flag which determines if this service is required or not.
+     * 
+     * @param required the required flag
+     * @return this service dependency
+     */
+    public synchronized ServiceDependency setRequired(boolean required) {
+        ensureNotActive();
+        setIsRequired(required);
+        return this;
+    }
+    
+    public ServiceDependency setInstanceBound(boolean isInstanceBound) {
+        setIsInstanceBound(isInstanceBound);
+        return this;
+    }
+
+    /**
+     * Sets auto configuration for this service. Auto configuration allows the
+     * dependency to fill in any attributes in the service implementation that
+     * are of the same type as this dependency. Default is on.
+     * 
+     * @param autoConfig the value of auto config
+     * @return this service dependency
+     */
+    public synchronized ServiceDependency setAutoConfig(boolean autoConfig) {
+        ensureNotActive();
+        m_autoConfig = autoConfig;
+        m_autoConfigInvoked = true;
+        return this;
+    }
+    
+    /**
+     * Sets auto configuration for this service. Auto configuration allows the
+     * dependency to fill in the attribute in the service implementation that
+     * has the same type and instance name.
+     * 
+     * @param instanceName the name of attribute to auto config
+     * @return this service dependency
+     */
+    public synchronized ServiceDependency setAutoConfig(String instanceName) {
+        ensureNotActive();
+        m_autoConfig = (instanceName != null);
+        m_autoConfigInstance = instanceName;
+        m_autoConfigInvoked = true;
+        return this;
+    }
+    
+    /**
+     * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+     * dependency is added or removed. When you specify callbacks, the auto configuration 
+     * feature is automatically turned off, because we're assuming you don't need it in this 
+     * case.
+     * 
+     * @param added the method to call when a service was added
+     * @param removed the method to call when a service was removed
+     * @return this service dependency
+     */
+    public synchronized ServiceDependency setCallbacks(String added, String removed) {
+        return setCallbacks((Object) null, added, null, removed);
+    }
+
+    /**
+     * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+     * dependency is added, changed or removed. When you specify callbacks, the auto 
+     * configuration feature is automatically turned off, because we're assuming you don't 
+     * need it in this case.
+     * 
+     * @param added the method to call when a service was added
+     * @param changed the method to call when a service was changed
+     * @param removed the method to call when a service was removed
+     * @return this service dependency
+     */
+    public synchronized ServiceDependency setCallbacks(String added, String changed, String removed) {
+        return setCallbacks((Object) null, added, changed, removed);
+    }
+    
+    /**
+     * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+     * dependency is added, changed or removed. When you specify callbacks, the auto 
+     * configuration feature is automatically turned off, because we're assuming you don't 
+     * need it in this case.
+     * @param added the method to call when a service was added
+     * @param changed the method to call when a service was changed
+     * @param removed the method to call when a service was removed
+     * @param swapped the method to call when the service was swapped due to addition or 
+     * removal of an aspect
+     * @return this service dependency
+     */
+    public synchronized ServiceDependency setCallbacks(String added, String changed, String removed, String swapped) {
+    	return setCallbacks((Object) null, added, changed, removed, swapped);
+    }
+
+    /**
+     * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+     * dependency is added or removed. They are called on the instance you provide. When you
+     * specify callbacks, the auto configuration feature is automatically turned off, because
+     * we're assuming you don't need it in this case.
+     * 
+     * @param instance the instance to call the callbacks on
+     * @param added the method to call when a service was added
+     * @param removed the method to call when a service was removed
+     * @return this service dependency
+     */
+    public synchronized ServiceDependency setCallbacks(Object instance, String added, String removed) {
+        return setCallbacks(instance, added, (String) null, removed);
+    }
+    
+    /**
+     * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+     * dependency is added, changed or removed. They are called on the instance you provide. When you
+     * specify callbacks, the auto configuration feature is automatically turned off, because
+     * we're assuming you don't need it in this case.
+     * 
+     * @param instance the instance to call the callbacks on
+     * @param added the method to call when a service was added
+     * @param changed the method to call when a service was changed
+     * @param removed the method to call when a service was removed
+     * @return this service dependency
+     */
+    public synchronized ServiceDependency setCallbacks(Object instance, String added, String changed, String removed) {
+    	return setCallbacks(instance, added, changed, removed, null);
+    }
+    
+    /**
+     * Sets the callbacks for this service. These callbacks can be used as hooks whenever a
+     * dependency is added, changed or removed. When you specify callbacks, the auto 
+     * configuration feature is automatically turned off, because we're assuming you don't 
+     * need it in this case.
+     * @param instance the instance to call the callbacks on
+     * @param added the method to call when a service was added
+     * @param changed the method to call when a service was changed
+     * @param removed the method to call when a service was removed
+     * @param swapped the method to call when the service was swapped due to addition or 
+     * removal of an aspect
+     * @return this service dependency
+     */    
+    public synchronized ServiceDependency setCallbacks(Object instance, String added, String changed, String removed, String swapped) {
+        ensureNotActive();
+        // if at least one valid callback is specified, we turn off auto configuration, unless
+        // someone already explicitly invoked autoConfig
+        if ((added != null || removed != null || changed != null || swapped != null) && ! m_autoConfigInvoked) {
+            setAutoConfig(false);
+        }
+    	m_callbackInstance = instance;
+        m_callbackAdded = added;
+        m_callbackChanged = changed;
+        m_callbackRemoved = removed;    
+        m_callbackSwapped = swapped;
+    	return this;
+    }
+    
+    private void ensureNotActive() {
+        if (m_tracker != null) {
+            throw new IllegalStateException("Cannot modify state while active.");
+        }
+    }
+    
+    public synchronized String toString() {
+        return "ServiceDependency[" + m_trackedServiceName + " " + m_trackedServiceFilterUnmodified + "]";
+    }
+
+    public String getAutoConfigName() {
+        return m_autoConfigInstance;
+    }
+    
+    public Object getAutoConfigInstance() {
+        return lookupService();
+    }
+    
+    public Class getAutoConfigType() {
+        return getInterface();
+    }
+
+    public String getName() {
+        StringBuilder sb = new StringBuilder();
+        if (m_trackedServiceName != null) {
+            sb.append(m_trackedServiceName.getName());
+            if (m_trackedServiceFilterUnmodified != null) {
+                sb.append(' ');
+                sb.append(m_trackedServiceFilterUnmodified);
+            }
+        }
+        if (m_trackedServiceReference != null) {
+            sb.append("{service.id=" + m_trackedServiceReference.getProperty(Constants.SERVICE_ID)+"}");
+        }
+        return sb.toString();
+    }
+
+    public String getType() {
+        return "service";
+    }
+
+    public void invokeAdded(DependencyService service) {
+        ServiceReference[] refs = m_tracker.getServiceReferences();
+        if (refs != null) {
+            for (int i = 0; i < refs.length; i++) {
+                ServiceReference sr = refs[i];
+                Object svc = m_context.getService(sr);
+                invokeAdded(service, sr, svc);
+            }
+        }
+    }
+    
+    public void invokeRemoved(DependencyService service) {
+        Set references = null;
+        synchronized (m_sr) {
+            references = (Set) m_sr.get(service);
+        }
+        Tuple[] refs = (Tuple[]) (references != null ? references.toArray(new Tuple[references.size()]) : new Tuple[0]);
+    
+        for (int i = 0; i < refs.length; i++) {
+            ServiceReference sr = refs[i].getServiceReference();
+            Object svc = refs[i].getService();
+            invokeRemoved(service, sr, svc);
+        }
+        if (references != null) {
+            references.clear();
+        }
+    }
+
+    public Dictionary getProperties() {
+        ServiceReference reference = lookupServiceReference();
+        Object service = lookupService();
+        if (reference != null) {
+            if (m_propagateCallbackInstance != null && m_propagateCallbackMethod != null) {
+                try {
+                    return (Dictionary) InvocationUtil.invokeCallbackMethod(m_propagateCallbackInstance, m_propagateCallbackMethod, new Class[][] {{ ServiceReference.class, Object.class }, { ServiceReference.class }}, new Object[][] {{ reference, service }, { reference }});
+                }
+                catch (InvocationTargetException e) {
+                    m_logger.log(LogService.LOG_WARNING, "Exception while invoking callback method", e.getCause());
+                }
+                catch (Exception e) {
+                    m_logger.log(LogService.LOG_WARNING, "Exception while trying to invoke callback method", e);
+                }
+                throw new IllegalStateException("Could not invoke callback");
+            }
+            else {
+                Properties props = new Properties();
+                String[] keys = reference.getPropertyKeys();
+                for (int i = 0; i < keys.length; i++) {
+                    if (!(keys[i].equals(Constants.SERVICE_ID) || keys[i].equals(Constants.SERVICE_PID))) {
+                        props.put(keys[i], reference.getProperty(keys[i]));
+                    }
+                }
+                return props;
+            }
+        }
+        else {
+            throw new IllegalStateException("cannot find service reference");
+        }
+    }
+
+    public boolean isPropagated() {
+        return m_propagate;
+    }
+    
+    public ServiceDependency setPropagate(boolean propagate) {
+        ensureNotActive();
+        m_propagate = propagate;
+        return this;
+    }
+    
+    public ServiceDependency setPropagate(Object instance, String method) {
+        setPropagate(instance != null && method != null);
+        m_propagateCallbackInstance = instance;
+        m_propagateCallbackMethod = method;
+        return this;
+    }
+   
+}

Added: felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/TemporalServiceDependencyImpl.java
URL: http://svn.apache.org/viewvc/felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/TemporalServiceDependencyImpl.java?rev=1293173&view=auto
==============================================================================
--- felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/TemporalServiceDependencyImpl.java (added)
+++ felix/sandbox/uiterlix/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/dependencies/TemporalServiceDependencyImpl.java Fri Feb 24 10:47:49 2012
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless
+ * required by applicable law or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl.dependencies;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+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, used to hide temporary service dependency "outage".
+ * Only works with a required dependency.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+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.
+     * 
+     * @param context The bundle context of the bundle which is instantiating this dependency object
+     * @param logger the logger our Internal logger for logging events.
+     * @see DependencyActivatorBase#createTemporalServiceDependency()
+     */
+    public TemporalServiceDependencyImpl(BundleContext context, Logger logger) {
+        super(context, logger);
+        super.setRequired(true);
+        m_frameworkBundle = context.getBundle(0);
+    }
+
+    /**
+     * Sets the timeout for this temporal dependency. Specifying a timeout value of zero means that there is no timeout period,
+     * and an invocation on a missing service will fail immediately.
+     * 
+     * @param timeout the dependency timeout value greater or equals to 0
+     * @throws IllegalArgumentException if the timeout is negative
+     * @return this temporal dependency
+     */
+    public synchronized TemporalServiceDependency setTimeout(long timeout) {
+        if (timeout < 0) {
+            throw new IllegalArgumentException("Invalid timeout value: " + timeout);
+        }
+        m_timeout = timeout;
+        return this;
+    }
+
+    /**
+     * 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);
+        }
+        Object[] services = m_services.toArray();
+        for (int i = 0; i < services.length; i++) {
+            DependencyService ds = (DependencyService) services[i];
+            if (makeAvailable) {
+                ds.dependencyAvailable(this);
+            }
+        }
+        if (!makeAvailable) {
+            notifyAll();
+        }
+    }
+
+    /**
+     * The ServiceTracker calls us here when a tracked service properties are modified.
+     */
+    public void modifiedService(ServiceReference ref, Object service) {
+        // We don't care.
+    }
+
+    /**
+     * The ServiceTracker calls us here when a tracked service is lost.
+     */
+    public synchronized void removedService(ServiceReference ref, Object service) {
+        // 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();
+        }
+    }
+
+    /**
+     * @returns our dependency instance. Unlike in ServiceDependency, we always returns our proxy.
+     */
+    public synchronized Object getService() {
+        return m_serviceInstance;
+    }
+
+    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+        Object service = m_cachedService;
+        if (service == null) {
+            synchronized (this) {
+                long start = System.currentTimeMillis();
+                long waitTime = m_timeout;
+                while (service == null) {
+                    if (waitTime <= 0) {
+                        throw new IllegalStateException("Service unavailable: " + m_trackedServiceName.getName());
+                    }
+                    try {
+                        wait(waitTime);
+                    }
+                    catch (InterruptedException e) {
+                        throw new IllegalStateException("Service unavailable: " + m_trackedServiceName.getName());
+                    }
+                    waitTime = m_timeout - (System.currentTimeMillis() - start);
+                    service = m_cachedService;
+                }
+                
+            }
+        }
+        try {
+            return method.invoke(service, args);
+        }
+        catch (IllegalAccessException iae) {
+            method.setAccessible(true);
+            return method.invoke(service, args);
+        }
+    }
+}