You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by pd...@apache.org on 2014/02/26 00:28:11 UTC

svn commit: r1571864 [3/5] - in /felix/sandbox/pderop/dependencymanager-prototype: ./ cnf/ cnf/bin/ cnf/buildrepo/ cnf/buildrepo/biz.aQute.junit/ cnf/buildrepo/biz.aQute.launcher/ cnf/buildrepo/ee.foundation/ cnf/buildrepo/ee.minimum/ cnf/buildrepo/jun...

Added: felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/BundleContextInterceptor.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/BundleContextInterceptor.java?rev=1571864&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/BundleContextInterceptor.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/BundleContextInterceptor.java Tue Feb 25 23:28:08 2014
@@ -0,0 +1,165 @@
+/*
+ * 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 dm.impl.index;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+
+import dm.FilterIndex;
+import dm.impl.Logger;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+@SuppressWarnings("unchecked")
+public class BundleContextInterceptor extends BundleContextInterceptorBase {
+	protected static final String INDEX_LOG_TRESHOLD = "org.apache.felix.dm.index.log.treshold";
+    private final ServiceRegistryCache m_cache;
+    private final boolean m_perfmon = System.getProperty(INDEX_LOG_TRESHOLD) != null;
+	private Logger m_logger;
+	private long m_treshold;
+
+    public BundleContextInterceptor(ServiceRegistryCache cache, BundleContext context) {
+        super(context);
+        m_cache = cache;
+		if (m_perfmon) {
+			m_treshold = Long.parseLong(System.getProperty(INDEX_LOG_TRESHOLD));
+			m_logger = new Logger(context);
+		}
+    }
+
+    public void addServiceListener(ServiceListener listener, String filter) throws InvalidSyntaxException {
+        FilterIndex filterIndex = m_cache.hasFilterIndexFor(null, filter);
+        if (filterIndex != null) {
+            filterIndex.addServiceListener(listener, filter);
+        }
+        else {
+//            System.out.println("BCI:Listener " + listener.getClass().getName() + " filter " + filter);
+            m_context.addServiceListener(listener, filter);
+        }
+    }
+
+    public void addServiceListener(ServiceListener listener) {
+        FilterIndex filterIndex = m_cache.hasFilterIndexFor(null, null);
+        if (filterIndex != null) {
+            filterIndex.addServiceListener(listener, null);
+        }
+        else {
+//            System.out.println("BCI:Listener " + listener.getClass().getName() + " without filter");
+            m_context.addServiceListener(listener);
+        }
+    }
+
+    public void removeServiceListener(ServiceListener listener) {
+    	// remove servicelistener. although it would be prettier to find the correct filterindex first it's
+    	// probaby faster to do a brute force removal.
+    	Iterator filterIndexIterator = m_cache.getFilterIndices().iterator();
+    	while (filterIndexIterator.hasNext()) {
+    		((FilterIndex) filterIndexIterator.next()).removeServiceListener(listener);
+    	}
+    	m_context.removeServiceListener(listener);
+    }
+
+    public ServiceReference[] getServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
+    	long start = 0L;
+    	if (m_perfmon) {
+    		start = System.currentTimeMillis();
+    	}
+        // first we ask the cache if there is an index for our request (class and filter combination)
+        FilterIndex filterIndex = m_cache.hasFilterIndexFor(clazz, filter);
+        if (filterIndex != null) {
+            List /* <ServiceReference> */ result = filterIndex.getAllServiceReferences(clazz, filter);
+            Iterator iterator = result.iterator();
+            while (iterator.hasNext()) {
+                ServiceReference reference = (ServiceReference) iterator.next();
+                String[] list = (String[]) reference.getProperty(Constants.OBJECTCLASS);
+                for (int i = 0; i < list.length; i++) {
+                    if (!reference.isAssignableTo(m_context.getBundle(), list[i])) {
+                        iterator.remove();
+                        break;
+                    }
+                }
+            }
+            if (m_perfmon) {
+	        	long duration = System.currentTimeMillis() - start;
+	        	if (duration > m_treshold) {
+	        		m_logger.log(Logger.LOG_DEBUG, "Indexed filter exceeds lookup time treshold (" + duration + "ms.): " + clazz + " " + filter);
+	        	}
+            }
+            if (result == null || result.size() == 0) {
+                return null;
+            }
+            return (ServiceReference[]) result.toArray(new ServiceReference[result.size()]);
+        }
+        else {
+            // if they don't know, we ask the real bundle context instead
+            ServiceReference[] serviceReferences = m_context.getServiceReferences(clazz, filter);
+            if (m_perfmon) {
+	        	long duration = System.currentTimeMillis() - start;
+	        	if (duration > m_treshold) {
+	        		m_logger.log(Logger.LOG_DEBUG, "Unindexed filter exceeds lookup time treshold (" + duration + "ms.): " + clazz + " " + filter);
+	        	}
+            }
+        	return serviceReferences;
+        }
+    }
+
+    public ServiceReference[] getAllServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
+        // first we ask the cache if there is an index for our request (class and filter combination)
+        FilterIndex filterIndex = m_cache.hasFilterIndexFor(clazz, filter);
+        if (filterIndex != null) {
+            List /* <ServiceReference> */ result = filterIndex.getAllServiceReferences(clazz, filter);
+            if (result == null || result.size() == 0) {
+                return null;
+            }
+            return (ServiceReference[]) result.toArray(new ServiceReference[result.size()]);
+        }
+        else {
+            // if they don't know, we ask the real bundle context instead
+            return m_context.getAllServiceReferences(clazz, filter);
+        }
+    }
+
+    public ServiceReference getServiceReference(String clazz) {
+        ServiceReference[] references;
+        try {
+            references = getServiceReferences(clazz, null);
+            if (references == null || references.length == 0) {
+                return null;
+            }
+            Arrays.sort(references);
+            return references[references.length - 1];
+        }
+        catch (InvalidSyntaxException e) {
+            throw new Error("Invalid filter syntax thrown for null filter.", e);
+        }
+    }
+
+    public void serviceChanged(ServiceEvent event) {
+        m_cache.serviceChangedForFilterIndices(event);
+    }
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/BundleContextInterceptorBase.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/BundleContextInterceptorBase.java?rev=1571864&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/BundleContextInterceptorBase.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/BundleContextInterceptorBase.java Tue Feb 25 23:28:08 2014
@@ -0,0 +1,163 @@
+/*
+ * 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 dm.impl.index;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.BundleListener;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Base class for bundle context interceptors that keep track of service listeners and delegate incoming changes to them.
+ * 
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public abstract class BundleContextInterceptorBase implements BundleContext, ServiceListener {
+    protected final BundleContext m_context;
+    /** Keeps track of all service listeners and their optional filters. */
+    private final Map /* <ServiceListener, String> */m_serviceListenerFilterMap = new HashMap();
+    private long m_currentVersion = 0;
+    private long m_entryVersion = -1;
+    private Entry[] m_serviceListenerFilterMapEntries;
+
+    public BundleContextInterceptorBase(BundleContext context) {
+        m_context = context;
+    }
+
+    public String getProperty(String key) {
+        return m_context.getProperty(key);
+    }
+
+    public Bundle getBundle() {
+        return m_context.getBundle();
+    }
+
+    public Bundle installBundle(String location) throws BundleException {
+        return m_context.installBundle(location);
+    }
+
+    public Bundle installBundle(String location, InputStream input) throws BundleException {
+        return m_context.installBundle(location, input);
+    }
+
+    public Bundle getBundle(long id) {
+        return m_context.getBundle(id);
+    }
+
+    public Bundle[] getBundles() {
+        return m_context.getBundles();
+    }
+
+    public void addServiceListener(ServiceListener listener, String filter) throws InvalidSyntaxException {
+        synchronized (m_serviceListenerFilterMap) {
+            m_serviceListenerFilterMap.put(listener, filter);
+            m_currentVersion++;
+        }
+    }
+
+    public void addServiceListener(ServiceListener listener) {
+        synchronized (m_serviceListenerFilterMap) {
+            m_serviceListenerFilterMap.put(listener, null);
+            m_currentVersion++;
+        }
+    }
+
+    public void removeServiceListener(ServiceListener listener) {
+        synchronized (m_serviceListenerFilterMap) {
+            m_serviceListenerFilterMap.remove(listener);
+            m_currentVersion++;
+        }
+    }
+
+    public void addBundleListener(BundleListener listener) {
+        m_context.addBundleListener(listener);
+    }
+
+    public void removeBundleListener(BundleListener listener) {
+        m_context.removeBundleListener(listener);
+    }
+
+    public void addFrameworkListener(FrameworkListener listener) {
+        m_context.addFrameworkListener(listener);
+    }
+
+    public void removeFrameworkListener(FrameworkListener listener) {
+        m_context.removeFrameworkListener(listener);
+    }
+
+    public ServiceRegistration registerService(String[] clazzes, Object service, Dictionary properties) {
+        return m_context.registerService(clazzes, service, properties);
+    }
+
+    public ServiceRegistration registerService(String clazz, Object service, Dictionary properties) {
+        return m_context.registerService(clazz, service, properties);
+    }
+
+    public ServiceReference[] getServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
+        return m_context.getServiceReferences(clazz, filter);
+    }
+
+    public ServiceReference[] getAllServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
+        return m_context.getAllServiceReferences(clazz, filter);
+    }
+
+    public ServiceReference getServiceReference(String clazz) {
+        return m_context.getServiceReference(clazz);
+    }
+
+    public Object getService(ServiceReference reference) {
+        return m_context.getService(reference);
+    }
+
+    public boolean ungetService(ServiceReference reference) {
+        return m_context.ungetService(reference);
+    }
+
+    public File getDataFile(String filename) {
+        return m_context.getDataFile(filename);
+    }
+
+    public Filter createFilter(String filter) throws InvalidSyntaxException {
+        return m_context.createFilter(filter);
+    }
+
+    protected Entry[] synchronizeCollection() {
+        // lazy copy on write: we make a new copy only if writes have changed the collection
+        synchronized (m_serviceListenerFilterMap) {
+            if (m_currentVersion != m_entryVersion) {
+                m_serviceListenerFilterMapEntries = (Entry[]) m_serviceListenerFilterMap.entrySet().toArray(new Entry[m_serviceListenerFilterMap.size()]);
+                m_entryVersion = m_currentVersion;
+            }
+        }
+        return m_serviceListenerFilterMapEntries;
+    }
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/FilterIndexBundleContext.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/FilterIndexBundleContext.java?rev=1571864&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/FilterIndexBundleContext.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/FilterIndexBundleContext.java Tue Feb 25 23:28:08 2014
@@ -0,0 +1,64 @@
+/*
+ * 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 dm.impl.index;
+
+import java.util.Map.Entry;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class FilterIndexBundleContext extends BundleContextInterceptorBase {
+    public FilterIndexBundleContext(BundleContext context) {
+        super(context);
+    }
+
+    public void serviceChanged(ServiceEvent event) {
+        Entry[] entries = synchronizeCollection();
+        for (int i = 0; i < entries.length; i++) {
+            Entry serviceListenerFilterEntry = entries[i];
+            ServiceListener serviceListener = (ServiceListener) serviceListenerFilterEntry.getKey();
+            String filter = (String) serviceListenerFilterEntry.getValue();
+            if (filter == null) {
+                serviceListener.serviceChanged(event);
+            }
+            else {
+                // call service changed on the listener if the filter matches the event
+                // TODO review if we can be smarter here
+                try {
+                    if ("(objectClass=*)".equals(filter)) {
+                        serviceListener.serviceChanged(event);
+                    }
+                    else {
+                        if (m_context.createFilter(filter).match(event.getServiceReference())) {
+                            serviceListener.serviceChanged(event);
+                        }
+                    }
+                }
+                catch (InvalidSyntaxException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/ServiceRegistryCache.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/ServiceRegistryCache.java?rev=1571864&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/ServiceRegistryCache.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/ServiceRegistryCache.java Tue Feb 25 23:28:08 2014
@@ -0,0 +1,177 @@
+/*
+ * 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 dm.impl.index;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceRegistration;
+
+import dm.FilterIndex;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ServiceRegistryCache implements ServiceListener/*, CommandProvider*/ {
+	private final List /* <FilterIndex> */ m_filterIndexList = new CopyOnWriteArrayList();
+    private final BundleContext m_context;
+    private final FilterIndexBundleContext m_filterIndexBundleContext;
+    private final Map /* <BundleContext, BundleContextInterceptor> */ m_bundleContextInterceptorMap = new HashMap();
+    private long m_currentVersion = 0;
+    private long m_arrayVersion = -1;
+    private BundleContextInterceptor[] m_interceptors = null;
+    private ServiceRegistration m_registration;
+    
+    public ServiceRegistryCache(BundleContext context) {
+        m_context = context;
+        m_filterIndexBundleContext = new FilterIndexBundleContext(m_context);
+    }
+    
+    public void open() {
+        m_context.addServiceListener(this);
+//        m_registration = m_context.registerService(CommandProvider.class.getName(), this, null);
+    }
+    
+    public void close() {
+//        m_registration.unregister();
+        m_context.removeServiceListener(this);
+    }
+    
+    public void addFilterIndex(FilterIndex index) {
+        m_filterIndexList.add(index);
+        index.open(m_filterIndexBundleContext);
+    }
+    
+    public void removeFilterIndex(FilterIndex index) {
+        index.close();
+        m_filterIndexList.remove(index);
+    }
+
+    public void serviceChanged(ServiceEvent event) {
+        // any incoming event is first dispatched to the list of filter indices
+        m_filterIndexBundleContext.serviceChanged(event);
+        // and then all the other listeners can access it
+        synchronized (m_bundleContextInterceptorMap) {
+            if (m_currentVersion != m_arrayVersion) {
+                // if our copy is out of date, we make a new one
+                m_interceptors = (BundleContextInterceptor[]) m_bundleContextInterceptorMap.values().toArray(new BundleContextInterceptor[m_bundleContextInterceptorMap.size()]);
+                m_arrayVersion = m_currentVersion;
+            }
+        }
+        
+        serviceChangedForFilterIndices(event);
+    }
+    
+    /** Creates an interceptor for a bundle context that uses our cache. */
+    public BundleContext createBundleContextInterceptor(BundleContext context) {
+        synchronized (m_bundleContextInterceptorMap) {
+            BundleContextInterceptor bundleContextInterceptor = (BundleContextInterceptor) m_bundleContextInterceptorMap.get(context);
+            if (bundleContextInterceptor == null) {
+                bundleContextInterceptor = new BundleContextInterceptor(this, context);
+                m_bundleContextInterceptorMap.put(context, bundleContextInterceptor);
+                m_currentVersion++;
+                // TODO figure out a good way to clean up bundle contexts that are no longer valid so they can be garbage collected
+            }
+            return bundleContextInterceptor;
+        }
+    }
+
+    public FilterIndex hasFilterIndexFor(String clazz, String filter) {
+        Iterator iterator = m_filterIndexList.iterator();
+        while (iterator.hasNext()) {
+            FilterIndex filterIndex = (FilterIndex) iterator.next();
+            if (filterIndex.isApplicable(clazz, filter)) {
+                return filterIndex;
+            }
+        }
+        return null;
+    }
+
+    public void serviceChangedForFilterIndices(ServiceEvent event) {
+        Iterator iterator = m_filterIndexList.iterator();
+        while (iterator.hasNext()) {
+            FilterIndex filterIndex = (FilterIndex) iterator.next();
+            filterIndex.serviceChanged(event);
+        }
+    }
+
+//    public void _sc(CommandInterpreter ci) {
+//        ci.println(toString());
+//    }
+//    
+//    public void _fi(CommandInterpreter ci) {
+//        String arg = ci.nextArgument();
+//        if (arg != null) {
+//            int x = Integer.parseInt(arg);
+//            FilterIndex filterIndex = (FilterIndex) m_filterIndexList.get(x);
+//            String a1 = ci.nextArgument();
+//            String a2 = null;
+//            if (a1 != null) {
+//                if ("-".equals(a1)) {
+//                    a1 = null;
+//                }
+//                a2 = ci.nextArgument();
+//            }
+//            if (filterIndex.isApplicable(a1, a2)) {
+//                List /* <ServiceReference> */ references = filterIndex.getAllServiceReferences(a1, a2);
+//                ci.println("Found " + references.size() + " references:");
+//                for (int i = 0; i < references.size(); i++) {
+//                    ci.println("" + i + " - " + references.get(i));
+//                }
+//            }
+//            else {
+//                ci.println("Filter not applicable.");
+//            }
+//        }
+//        else {
+//            ci.println("FilterIndices:");
+//            Iterator iterator = m_filterIndexList.iterator();
+//            int index = 0;
+//            while (iterator.hasNext()) {
+//                FilterIndex filterIndex = (FilterIndex) iterator.next();
+//                ci.println("" + index + " " + filterIndex);
+//                index++;
+//            }
+//        }
+//    }
+//    
+//    public String getHelp() {
+//        return "I'm not going to help you!";
+//    }
+    
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        sb.append("ServiceRegistryCache[");
+        sb.append("FilterIndices: " + m_filterIndexList.size());
+        sb.append(", BundleContexts intercepted: " + m_bundleContextInterceptorMap.size());
+        sb.append("]");
+        return sb.toString();
+    }
+
+	public List getFilterIndices() {
+		return m_filterIndexList;
+	}
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/multiproperty/Filter.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/multiproperty/Filter.java?rev=1571864&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/multiproperty/Filter.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/multiproperty/Filter.java Tue Feb 25 23:28:08 2014
@@ -0,0 +1,149 @@
+/*
+ * 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 dm.impl.index.multiproperty;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.TreeSet;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Filter {
+	
+	private boolean m_valid = true;
+	private Map /* <String, Property> */ m_properties = new HashMap();
+	private Set /* <String> */ m_propertyKeys = new TreeSet(String.CASE_INSENSITIVE_ORDER);
+	
+	private Filter() {
+		
+	}
+	
+	// Sample valid filter string (&(objectClass=OBJECTCLASS)(&(model=MODEL)(concept=CONCEPT)(role=ROLE)(!(context=*))))
+	public static Filter parse(String filterString) {
+		Filter filter = new Filter();
+		StringTokenizer tokenizer = new StringTokenizer(filterString, "(&|=)", true);
+		
+		String token = null;
+		String prevToken = null;
+		String key = null;
+		StringBuilder valueBuilder = new StringBuilder();
+		boolean negate = false;
+
+		while (tokenizer.hasMoreTokens()) {
+			prevToken = token;
+			token = tokenizer.nextToken();
+			if (token.equals("|")) {
+				// we're not into OR's
+				filter.m_valid = false;
+				break;
+			}
+			if (token.equals("!")) {
+				negate = true;
+			} else if (token.equals("=")) {
+				key = prevToken.toLowerCase();
+			} else if (key != null) {
+				if (!token.equals(")")) {
+					valueBuilder.append(token); // might be superseded by a &
+				}
+				if (token.equals(")")) {
+					// set complete
+					if (filter.m_properties.containsKey(key)) {
+						// set current property to multivalue
+						Property property = (Property) filter.m_properties.get(key);
+						property.addValue(valueBuilder.toString(), negate);
+					} else {
+						Property property = new Property(negate, key, valueBuilder.toString());
+						filter.m_properties.put(key, property);
+						filter.m_propertyKeys.add(key);
+					}
+					negate = false;
+					key = null;
+					valueBuilder = new StringBuilder();
+				}
+			} 
+		}
+		return filter;
+	}
+	
+	public boolean containsProperty(String propertyKey) {
+		return m_properties.containsKey(propertyKey);
+	}
+	
+	public Set /* <String> */ getPropertyKeys() {
+		return m_properties.keySet();
+	}
+	
+	public Property getProperty(String key) {
+		return (Property) m_properties.get(key);
+	}
+	
+	public boolean isValid() {
+		if (!m_valid) {
+			return m_valid;
+		} else {
+			// also check the properties
+			Iterator propertiesIterator = m_properties.values().iterator();
+			while (propertiesIterator.hasNext()) {
+				Property property = (Property) propertiesIterator.next();
+				if (!property.isValid()) {
+					return false;
+				}
+			}
+		}
+		return true;
+	}
+	
+	public static void main(String args[]) {
+		Filter parser = Filter.parse("(&(objectClass=OBJECTCLASS)(&(a=x)(a=n)(a=y)(b=y)(c=z)))");
+		System.out.println("key: " + parser.createKey());
+	}
+
+	protected String createKey() {
+		StringBuilder builder = new StringBuilder();
+		Iterator keys = m_propertyKeys.iterator();
+		
+		while (keys.hasNext()) {
+			String key = (String) keys.next();
+			Property prop = (Property) m_properties.get(key);
+			if (!prop.isWildcard()) {
+				Iterator values = prop.getValues().iterator();
+				while (values.hasNext()) {
+					String value = (String) values.next();
+					builder.append(key);
+					builder.append("=");
+					builder.append(value);
+					if (keys.hasNext() || values.hasNext()) {
+						builder.append(";");
+					}
+				}
+			}
+		}
+		// strip the final ';' in case the last key was a wildcard property
+		if (builder.charAt(builder.length() - 1) == ';') {
+			return builder.toString().substring(0, builder.length() - 1);
+		} else {
+			return builder.toString();
+		}
+	}
+	
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/multiproperty/MultiPropertyFilterIndex.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/multiproperty/MultiPropertyFilterIndex.java?rev=1571864&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/multiproperty/MultiPropertyFilterIndex.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/multiproperty/MultiPropertyFilterIndex.java Tue Feb 25 23:28:08 2014
@@ -0,0 +1,487 @@
+/*
+ * 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 dm.impl.index.multiproperty;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+
+import tracker.ServiceTracker;
+import tracker.ServiceTrackerCustomizer;
+import dm.FilterIndex;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class MultiPropertyFilterIndex implements FilterIndex, ServiceTrackerCustomizer {
+
+    private final Object m_lock = new Object();
+    private ServiceTracker m_tracker;
+    private BundleContext m_context;
+	private Map /* <String, Property> */ m_configProperties = new LinkedHashMap();
+	private List /* <String> */ m_negatePropertyKeys = new ArrayList();
+    private final Map /* <String, List<ServiceReference>> */ m_keyToServiceReferencesMap = new HashMap();
+    private final Map /* <String, List<ServiceListener>> */ m_keyToListenersMap = new HashMap();
+    private final Map /* <ServiceListener, String> */ m_listenerToFilterMap = new HashMap();
+
+	public MultiPropertyFilterIndex(String configString) {
+		parseConfig(configString);
+	}
+	
+	public boolean isApplicable(String clazz, String filterString) {
+		Filter filter = createFilter(clazz, filterString);
+		
+		if (!filter.isValid()) {
+			return false;
+		}
+		// compare property keys to the ones in the configuration
+		Set /* <String> */ filterPropertyKeys = filter.getPropertyKeys();
+		if (m_configProperties.size() != filterPropertyKeys.size()) {
+			return false;
+		}
+		Iterator filterPropertyKeysIterator = filterPropertyKeys.iterator();
+		while (filterPropertyKeysIterator.hasNext()) {
+			String filterPropertyKey = (String) filterPropertyKeysIterator.next();
+			if (!m_configProperties.containsKey(filterPropertyKey)) {
+				return false;
+			} else if (((Property)m_configProperties.get(filterPropertyKey)).isNegate() != filter.getProperty(filterPropertyKey).isNegate()) {
+				// negation should be equal
+				return false;
+			} else if (!filter.getProperty(filterPropertyKey).isNegate() && filter.getProperty(filterPropertyKey).getValue().equals("*")) {
+				// no wildcards without negation allowed
+				return false;
+			} 
+		}
+		// our properties match so we're applicable
+		return true;
+	}
+	
+    public boolean isApplicable(ServiceReference ref) {
+    	String[] propertyKeys = ref.getPropertyKeys();
+        TreeSet referenceProperties = new TreeSet(String.CASE_INSENSITIVE_ORDER);
+        for (int i = 0; i < propertyKeys.length; i++) {
+            referenceProperties.add(propertyKeys[i]);
+        }
+        Iterator iterator = m_configProperties.keySet().iterator();
+        while (iterator.hasNext()) {
+            String item = (String) iterator.next();
+            Property configProperty = (Property) m_configProperties.get(item);
+            if (!configProperty.isNegate() && !(referenceProperties.contains(item))) {
+                return false;
+            } else if (configProperty.isNegate() && referenceProperties.contains(item)) {
+            	return false;
+            }
+        }
+        return true;
+    }
+	
+	private void parseConfig(String configString) {
+		String[] propertyConfigs = configString.split(",");
+		for (int i = 0; i < propertyConfigs.length; i++) {
+			String propertyConfig = propertyConfigs[i];
+			Property property = new Property();
+			String key;
+			String value = null;
+			if (propertyConfig.startsWith("!")) {
+				property.setNegate(true);
+				key = propertyConfig.substring(1);
+			} else {
+				key = propertyConfig;
+			}
+			if (key.endsWith("*")) {
+				key = key.substring(0, key.indexOf("*"));
+				value = "*";
+			}
+			property.setKey(key.toLowerCase());
+			property.addValue(value, property.isNegate());
+			m_configProperties.put(key.toLowerCase(), property);
+			if (property.isNegate()) {
+				m_negatePropertyKeys.add(key);
+			}
+		}
+	}
+	
+	protected Collection /* <Property> */ getProperties() {
+		return m_configProperties.values();
+	}
+	
+    protected String createKeyFromFilter(String clazz, String filterString) {
+    	return createFilter(clazz, filterString).createKey();
+    }
+    
+    private Filter createFilter(String clazz, String filterString) {
+		String filterStringWithObjectClass = filterString;
+		if (clazz != null) {
+			if (filterString != null) {
+				if (!filterStringWithObjectClass.startsWith("(&(objectClass=")) {
+					filterStringWithObjectClass = "(&(objectClass=" + clazz + ")" + filterString + ")";
+				}
+			} else {
+				filterStringWithObjectClass = "(objectClass=" + clazz + ")";
+			}
+		}
+		Filter filter = Filter.parse(filterStringWithObjectClass);
+		return filter;
+    }
+    
+    protected List createKeys(ServiceReference reference) {
+    	List /* <String> */ results = new ArrayList();
+    	List sets = new ArrayList();   	
+    	String[] keys = reference.getPropertyKeys();
+    	Arrays.sort(keys, String.CASE_INSENSITIVE_ORDER);
+    	for (int i = 0; i < keys.length; i++) {
+    		List set = new ArrayList();
+    		String key = keys[i].toLowerCase();
+    		if (m_configProperties.containsKey(key)) {
+	    		Object valueObject = reference.getProperty(key);
+	    		if (valueObject instanceof String[]) {
+	    			set.addAll(getPermutations(key, (String[]) valueObject));
+	    		} else {
+	    			set.add(toKey(key, valueObject));
+	    		}
+	    		sets.add(set);
+    		}
+    	}
+    	
+    	List reversedSets = new ArrayList();
+    	int size = sets.size();
+    	for (int i = size - 1; i > -1; i--) {
+    		reversedSets.add(sets.get(i));
+    	}
+    	List products = carthesianProduct(0, reversedSets);
+    	// convert sets into strings
+    	for (int i = 0; i < products.size(); i++) {
+    		List set = (List) products.get(i);
+    		StringBuilder b = new StringBuilder();
+    		for (int j = 0; j < set.size(); j++) {
+    			String item = (String) set.get(j);
+    			b.append(item);
+    			if (j < set.size() - 1) {
+    				b.append(";");
+    			}
+    		}
+    		results.add(b.toString());
+    	}
+    	
+    	return results;
+    }
+    
+    /**
+     * Note that we calculate the carthesian product for multi value properties. Use filters on these sparingly since memory
+     * consumption can get really high when multiple properties have a lot of values.
+     * 
+     * @param index
+     * @param sets
+     * @return
+     */
+    private List carthesianProduct(int index, List sets) {
+    	List result = new ArrayList();
+    	if (index == sets.size()) {
+    		result.add(new ArrayList());
+    	} else {
+			List set = (List) sets.get(index);
+			for (int i = 0; i < set.size(); i++) {
+				Object object = set.get(i);
+    			List pSets = carthesianProduct(index + 1, sets);
+    			for (int j = 0; j < pSets.size(); j++) {
+    				List pSet = (List) pSets.get(j);
+    				pSet.add(object);
+    				result.add(pSet);
+    			}
+    		}
+    	}
+    	return result;
+    }
+    
+    List getPermutations(String key, String[] values) {
+    	List results = new ArrayList();
+		Arrays.sort(values, String.CASE_INSENSITIVE_ORDER);
+		for (int v = 0; v < values.length; v++) {
+			String processValue = values[v];
+			List /* <String> */ items = new ArrayList();
+			items.add(processValue);
+			// per value get combinations
+			List /* <String> */ subItems = new ArrayList(items);
+			for (int w = v; w < values.length; w++) {
+				// make a copy of the current list
+				subItems = new ArrayList(subItems);
+				if (w != v) {
+					String value = values[w];
+					subItems.add(value);
+				}
+				results.add(toKey(key, subItems));
+			}
+		}
+		return results;
+    }
+    
+    protected String toKey(String key, List values) {
+    	StringBuilder builder = new StringBuilder();
+    	for (int i = 0; i < values.size(); i++) {
+    		builder.append(toKey(key, (String) values.get(i)));
+    		if (i < values.size() - 1) {
+    			builder.append(";");
+    		}
+    	}
+    	return builder.toString();
+    }
+    
+    protected String toKey(String key, Object value) {
+    	StringBuilder builder = new StringBuilder();
+    	builder.append(key);
+		builder.append("=");
+		builder.append(value.toString());
+		return builder.toString();
+    }
+    
+    public Object addingService(ServiceReference reference) {
+        BundleContext context;
+        synchronized (m_lock) {
+            context = m_context;
+        }
+        if (context != null) {
+            return context.getService(reference);
+        }
+        else {
+            throw new IllegalStateException("No valid bundle context.");
+        }
+    }
+
+    public void addedService(ServiceReference reference, Object service) {
+        if (isApplicable(reference) && shouldBeIndexed(reference)) {
+            handleServiceAdd(reference);
+        }
+    }
+
+    public void modifiedService(ServiceReference reference, Object service) {
+        if (isApplicable(reference)) {
+            handleServicePropertiesChange(reference);
+        }
+    }
+
+    public void removedService(ServiceReference reference, Object service) {
+        if (isApplicable(reference) && shouldBeIndexed(reference)) {
+            handleServiceRemove(reference);
+        }
+    }
+    
+    protected void handleServiceAdd(ServiceReference reference) {
+        List /* <String> */ keys = createKeys(reference);
+        synchronized (m_keyToServiceReferencesMap) {
+            for (int i = 0; i < keys.size(); i++) {
+                List /* <ServiceReference> */ references = (List) m_keyToServiceReferencesMap.get(keys.get(i));
+                if (references == null) {
+                    references = new ArrayList();
+                    m_keyToServiceReferencesMap.put(keys.get(i), references);
+                }
+                references.add(reference);
+            }
+        }
+    }
+
+    protected void handleServicePropertiesChange(ServiceReference reference) {
+        
+        synchronized (m_keyToServiceReferencesMap) {
+            // TODO this is a quite expensive linear scan over the existing collection
+            // because we first need to remove any existing references and they can be
+            // all over the place :)
+            Iterator iterator = m_keyToServiceReferencesMap.values().iterator();
+            while (iterator.hasNext()) {
+                List /* <ServiceReference> */ list = (List) iterator.next();
+                if (list != null) {
+                    Iterator i2 = list.iterator();
+                    while (i2.hasNext()) {
+                        ServiceReference ref = (ServiceReference) i2.next();
+                        if (ref.equals(reference)) {
+                            i2.remove();
+                        }
+                    }
+                }
+            }
+            // only re-add the reference when it is still applicable for this filter index
+            if (shouldBeIndexed(reference)) {
+            	List /* <String> */ keys = createKeys(reference);
+	            for (int i = 0; i < keys.size(); i++) {
+	                List /* <ServiceReference> */ references = (List) m_keyToServiceReferencesMap.get(keys.get(i));
+	                if (references == null) {
+	                    references = new ArrayList();
+	                    m_keyToServiceReferencesMap.put(keys.get(i), references);
+	                }
+	                references.add(reference);
+	            }
+            }
+        }
+    }
+
+    protected void handleServiceRemove(ServiceReference reference) {
+        List /* <String> */ keys = createKeys(reference);
+        synchronized (m_keyToServiceReferencesMap) {
+            for (int i = 0; i < keys.size(); i++) {
+                List /* <ServiceReference> */ references = (List) m_keyToServiceReferencesMap.get(keys.get(i));
+                if (references != null) {
+                    references.remove(reference);
+                    if (references.isEmpty()) {
+                    	m_keyToServiceReferencesMap.remove(keys.get(i));
+                    }
+                }
+            }
+        }
+    }
+    
+    protected boolean shouldBeIndexed(ServiceReference reference) {
+    	// is already applicable, so we should only check whether there's a negate field in the filter which has a value in the reference
+    	Iterator negatePropertyKeyIterator = m_negatePropertyKeys.iterator();
+    	while (negatePropertyKeyIterator.hasNext()) {
+    		String negatePropertyKey = (String) negatePropertyKeyIterator.next();
+    		if (reference.getProperty(negatePropertyKey) != null) {
+    			return false;
+    		}
+    	}
+    	return true;
+    }
+
+    public void open(BundleContext context) {
+        synchronized (m_lock) {
+            if (m_context != null) {
+                throw new IllegalStateException("Filter already open.");
+            }
+            try {
+                m_tracker = new ServiceTracker(context, context.createFilter("(" + Constants.OBJECTCLASS + "=*)"), this);
+            }
+            catch (InvalidSyntaxException e) {
+                throw new Error();
+            }
+            m_context = context;
+        }
+        m_tracker.open(true, true);
+    }
+
+	public void close() {
+        ServiceTracker tracker;
+        synchronized (m_lock) {
+            if (m_context == null) {
+                throw new IllegalStateException("Filter already closed.");
+            }
+            tracker = m_tracker;
+            m_tracker = null;
+            m_context = null;
+        }
+        tracker.close();
+	}
+
+    public List /* <ServiceReference> */ getAllServiceReferences(String clazz, String filter) {
+        List /* <ServiceReference> */ result = new ArrayList();
+        String key = createKeyFromFilter(clazz, filter);
+        ServiceReference reference;
+        synchronized (m_keyToServiceReferencesMap) {
+            List references = (List) m_keyToServiceReferencesMap.get(key);
+            if (references != null) {
+                result.addAll(references);
+            }
+        }
+        return result;
+    }
+
+    public void serviceChanged(ServiceEvent event) {
+        if (isApplicable(event.getServiceReference())) {
+            List /* <String> */ keys = createKeys(event.getServiceReference());
+            List list = new ArrayList();
+            synchronized (m_keyToListenersMap) {
+                for (int i = 0; i < keys.size(); i++) {
+                    String key = (String) keys.get(i);
+                    List listeners = (List) m_keyToListenersMap.get(key);
+                    if (listeners != null) {
+                        list.addAll(listeners);
+                    }
+                }
+            }
+            if (list != null) {
+                Iterator iterator = list.iterator();
+                while (iterator.hasNext()) {
+                    ServiceListener listener = (ServiceListener) iterator.next();
+                    listener.serviceChanged(event);
+                }
+            }
+        }
+    }
+
+    public void addServiceListener(ServiceListener listener, String filter) {
+        String key = createKeyFromFilter(null, filter);
+        synchronized (m_keyToListenersMap) {
+            List /* <ServiceListener> */ listeners = (List) m_keyToListenersMap.get(key);
+            if (listeners == null) {
+                listeners = new CopyOnWriteArrayList();
+                m_keyToListenersMap.put(key, listeners);
+            }
+            listeners.add(listener);
+            m_listenerToFilterMap.put(listener, filter);
+        }
+    }
+
+    public void removeServiceListener(ServiceListener listener) {
+        synchronized (m_keyToListenersMap) {
+            String filter = (String) m_listenerToFilterMap.remove(listener);
+            if (filter != null) {
+            	// the listener does exist
+        		String key = createKeyFromFilter(null, filter);
+        		
+        		boolean result = filter != null;
+        		if (result) {
+        			List /* <ServiceListener> */ listeners = (List) m_keyToListenersMap.get(key);
+        			if (listeners != null) {
+        				listeners.remove(listener);
+        				if (listeners.isEmpty()) {
+        					m_keyToListenersMap.remove(key);
+        				}
+        			}
+        			// TODO actually, if listeners == null that would be strange....
+        		}
+            }
+        }
+    }
+    
+    protected Collection getServiceListeners() {
+    	return m_listenerToFilterMap.keySet();
+    }
+    
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        sb.append(" dMultiPropertyExactFilter[");
+        sb.append("K2L: " + m_keyToListenersMap.size());
+        sb.append(", K2SR: " + m_keyToServiceReferencesMap.size());
+        sb.append(", L2F: " + m_listenerToFilterMap.size());
+        sb.append("]");
+        return sb.toString();
+    }
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/multiproperty/Property.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/multiproperty/Property.java?rev=1571864&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/multiproperty/Property.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/impl/index/multiproperty/Property.java Tue Feb 25 23:28:08 2014
@@ -0,0 +1,102 @@
+/*
+ * 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 dm.impl.index.multiproperty;
+
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+import java.util.Set;
+import java.util.TreeSet;
+
+public class Property {
+	boolean m_negate;
+	boolean m_valid = true;
+	String m_key;
+	String m_value;
+	Set m_values = new TreeSet(String.CASE_INSENSITIVE_ORDER);
+	
+	public Property() {
+	}
+	
+	public Property(boolean negate, String key, String value) {
+		super();
+		this.m_negate = negate;
+		this.m_key = key.toLowerCase();
+		this.m_values.add(value);
+		this.m_value = value;
+	}
+
+	public void setNegate(boolean negate) {
+		this.m_negate = negate;
+	}
+	
+	public void setKey(String key) {
+		this.m_key = key.toLowerCase();
+	}
+	
+	public void addValue(String value, boolean negate) {
+		if (this.m_negate != negate) {
+			// multiproperty with different negations, causes invalid configuration.
+			m_valid = false;
+		}
+		if (this.m_value == null) {
+			// value has not bee set yet
+			this.m_value = value;
+		}
+		if (value != null) {
+			m_values.add(value);
+		}
+	}
+	
+	public boolean isNegate() {
+		return m_negate;
+	}
+	
+	public String getKey() {
+		return m_key;
+	}
+	
+	public String getValue() {
+		return m_value;
+	}
+	
+	public Set getValues() {
+		return m_values;
+	}
+	
+	public boolean isWildcard() {
+		return "*".equals(m_value);
+	}
+	
+	public boolean isMultiValue() {
+		return m_values.size() > 1;
+	}
+
+	public String toString() {
+		return "Property [negate=" + m_negate + ", key=" + m_key + ", values="
+				+ m_values + "]";
+	}
+	
+	public boolean isValid() {
+		return m_valid;
+	}
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/packageinfo
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/packageinfo?rev=1571864&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/packageinfo (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/dm/packageinfo Tue Feb 25 23:28:08 2014
@@ -0,0 +1 @@
+version 1.0
\ No newline at end of file

Added: felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/AbstractTracked.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/AbstractTracked.java?rev=1571864&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/AbstractTracked.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/AbstractTracked.java Tue Feb 25 23:28:08 2014
@@ -0,0 +1,460 @@
+package tracker;
+/*
+ * Copyright (c) OSGi Alliance (2007, 2008). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Abstract class to track items. If a Tracker is reused (closed then reopened),
+ * then a new AbstractTracked object is used. This class acts a map of tracked
+ * item -> customized object. Subclasses of this class will act as the listener
+ * object for the tracker. This class is used to synchronize access to the
+ * tracked items. This is not a public class. It is only for use by the
+ * implementation of the Tracker class.
+ * 
+ * @ThreadSafe
+ * @version $Revision: 5871 $
+ * @since 1.4
+ */
+abstract class AbstractTracked {
+	/* set this to true to compile in debug messages */
+	private static final boolean		DEBUG	= false;
+
+	/**
+	 * Map of tracked items to customized objects.
+	 * 
+	 * @GuardedBy this
+	 */
+	private Map			tracked;
+
+	/**
+	 * Modification count. This field is initialized to zero and incremented by
+	 * modified.
+	 * 
+	 * @GuardedBy this
+	 */
+	private int					trackingCount;
+
+	/**
+	 * List of items in the process of being added. This is used to deal with
+	 * nesting of events. Since events may be synchronously delivered, events
+	 * 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 List			adding;
+
+	/**
+	 * true if the tracked object is closed.
+	 * 
+	 * This field is volatile because it is set by one thread and read by
+	 * another.
+	 */
+	volatile boolean			closed;
+
+	/**
+	 * Initial list of items for the tracker. This is used to correctly process
+	 * the initial items which could be modified before they are tracked. This
+	 * is necessary since the initial set of tracked items are not "announced"
+	 * by events and therefore the event which makes the item untracked could be
+	 * delivered before we track the item.
+	 * 
+	 * An item must not be in both the initial and adding lists at the same
+	 * time. An item 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;
+
+	/**
+	 * AbstractTracked constructor.
+	 */
+	AbstractTracked() {
+	    this.tracked = new HashMap();
+	    trackingCount = 0;
+	    adding = new ArrayList(6);
+	    initial = new LinkedList();
+	    closed = false;
+	}
+	
+	void setTracked(HashMap map) {
+	    this.tracked = map;
+	}
+
+	/**
+	 * Set initial list of items into tracker before events begin to be
+	 * received.
+	 * 
+	 * This method must be called from Tracker's open method while synchronized
+	 * on this object in the same synchronized block as the add listener call.
+	 * 
+	 * @param list The initial list of items to be tracked. <code>null</code>
+	 *        entries in the list are ignored.
+	 * @GuardedBy this
+	 */
+	void setInitial(Object[] list) {
+		if (list == null) {
+			return;
+		}
+		int size = list.length;
+		for (int i = 0; i < size; i++) {
+			Object item = list[i];
+			if (item == null) {
+				continue;
+			}
+			if (DEBUG) {
+				System.out.println("AbstractTracked.setInitial: " + item); //$NON-NLS-1$
+			}
+			initial.add(item);
+		}
+	}
+
+	/**
+	 * Track the initial list of items. This is called after events can begin to
+	 * be received.
+	 * 
+	 * This method must be called from Tracker's open method while not
+	 * synchronized on this object after the add listener call.
+	 * 
+	 */
+	void trackInitial() {
+		while (true) {
+			Object item;
+			synchronized (this) {
+				if (closed || (initial.size() == 0)) {
+					/*
+					 * if there are no more initial items
+					 */
+					return; /* we are done */
+				}
+				/*
+				 * move the first item from the initial list to the adding list
+				 * within this synchronized block.
+				 */
+				item = initial.removeFirst();
+				if (tracked.get(item) != null) {
+					/* if we are already tracking this item */
+					if (DEBUG) {
+						System.out
+								.println("AbstractTracked.trackInitial[already tracked]: " + item); //$NON-NLS-1$
+					}
+					continue; /* skip this item */
+				}
+				if (adding.contains(item)) {
+					/*
+					 * if this item is already in the process of being added.
+					 */
+					if (DEBUG) {
+						System.out
+								.println("AbstractTracked.trackInitial[already adding]: " + item); //$NON-NLS-1$
+					}
+					continue; /* skip this item */
+				}
+				adding.add(item);
+			}
+			if (DEBUG) {
+				System.out.println("AbstractTracked.trackInitial: " + item); //$NON-NLS-1$
+			}
+			trackAdding(item, null); /*
+									 * Begin tracking it. We call trackAdding
+									 * since we have already put the item in the
+									 * adding list.
+									 */
+		}
+	}
+
+	/**
+	 * Called by the owning Tracker object when it is closed.
+	 */
+	void close() {
+		closed = true;
+	}
+
+	/**
+	 * Begin to track an item.
+	 * 
+	 * @param item Item to be tracked.
+	 * @param related Action related object.
+	 */
+	void track(final Object item, final Object related) {
+		final Object object;
+		synchronized (this) {
+			if (closed) {
+				return;
+			}
+			object = tracked.get(item);
+			if (object == null) { /* we are not tracking the item */
+				if (adding.contains(item)) {
+					/* if this item is already in the process of being added. */
+					if (DEBUG) {
+						System.out
+								.println("AbstractTracked.track[already adding]: " + item); //$NON-NLS-1$
+					}
+					return;
+				}
+				adding.add(item); /* mark this item is being added */
+			}
+			else { /* we are currently tracking this item */
+				if (DEBUG) {
+					System.out
+							.println("AbstractTracked.track[modified]: " + item); //$NON-NLS-1$
+				}
+				modified(); /* increment modification count */
+			}
+		}
+
+		if (object == null) { /* we are not tracking the item */
+			trackAdding(item, related);
+		}
+		else {
+			/* Call customizer outside of synchronized region */
+			customizerModified(item, related, object);
+			/*
+			 * If the customizer throws an unchecked exception, it is safe to
+			 * let it propagate
+			 */
+		}
+	}
+
+	/**
+	 * Common logic to add an item to the tracker used by track and
+	 * trackInitial. The specified item must have been placed in the adding list
+	 * before calling this method.
+	 * 
+	 * @param item Item to be tracked.
+	 * @param related Action related object.
+	 */
+	private void trackAdding(final Object item, final Object related) {
+		if (DEBUG) {
+			System.out.println("AbstractTracked.trackAdding: " + item); //$NON-NLS-1$
+		}
+		Object object = null;
+		boolean becameUntracked = false;
+		/* Call customizer outside of synchronized region */
+		try {
+			object = customizerAdding(item, related);
+			/*
+			 * If the customizer throws an unchecked exception, it will
+			 * propagate after the finally
+			 */
+		}
+		finally {
+			boolean needToCallback = false;
+			synchronized (this) {
+				if (adding.remove(item) && !closed) {
+					/*
+					 * if the item was not untracked during the customizer
+					 * callback
+					 */
+					if (object != null) {
+						tracked.put(item, object);
+						modified(); /* increment modification count */
+						notifyAll(); /* notify any waiters */
+						needToCallback = true; /* marrs: invoke added callback */
+					}
+				}
+				else {
+					becameUntracked = true;
+				}
+			}
+			if (needToCallback) {
+				customizerAdded(item, related, object);
+			}
+		}
+		/*
+		 * The item became untracked during the customizer callback.
+		 */
+		if (becameUntracked && (object != null)) {
+			if (DEBUG) {
+				System.out
+						.println("AbstractTracked.trackAdding[removed]: " + item); //$NON-NLS-1$
+			}
+			/* Call customizer outside of synchronized region */
+			customizerRemoved(item, related, object);
+			/*
+			 * If the customizer throws an unchecked exception, it is safe to
+			 * let it propagate
+			 */
+		}
+	}
+
+	/**
+	 * Discontinue tracking the item.
+	 * 
+	 * @param item Item to be untracked.
+	 * @param related Action related object.
+	 */
+	void untrack(final Object item, final Object related) {
+		final Object object;
+		synchronized (this) {
+			if (initial.remove(item)) { /*
+										 * if this item is already in the list
+										 * of initial references to process
+										 */
+				if (DEBUG) {
+					System.out
+							.println("AbstractTracked.untrack[removed from initial]: " + item); //$NON-NLS-1$
+				}
+				return; /*
+						 * we have removed it from the list and it will not be
+						 * processed
+						 */
+			}
+
+			if (adding.remove(item)) { /*
+										 * if the item is in the process of
+										 * being added
+										 */
+				if (DEBUG) {
+					System.out
+							.println("AbstractTracked.untrack[being added]: " + item); //$NON-NLS-1$
+				}
+				return; /*
+						 * in case the item is untracked while in the process of
+						 * adding
+						 */
+			}
+			object = tracked.remove(item); /*
+											 * must remove from tracker before
+											 * calling customizer callback
+											 */
+			if (object == null) { /* are we actually tracking the item */
+				return;
+			}
+			modified(); /* increment modification count */
+		}
+		if (DEBUG) {
+			System.out.println("AbstractTracked.untrack[removed]: " + item); //$NON-NLS-1$
+		}
+		/* Call customizer outside of synchronized region */
+		customizerRemoved(item, related, object);
+		/*
+		 * If the customizer throws an unchecked exception, it is safe to let it
+		 * propagate
+		 */
+	}
+
+	/**
+	 * Returns the number of tracked items.
+	 * 
+	 * @return The number of tracked items.
+	 * 
+	 * @GuardedBy this
+	 */
+	int size() {
+		return tracked.size();
+	}
+
+	/**
+	 * Return the customized object for the specified item
+	 * 
+	 * @param item The item to lookup in the map
+	 * @return The customized object for the specified item.
+	 * 
+	 * @GuardedBy this
+	 */
+	Object getCustomizedObject(final Object item) {
+		return tracked.get(item);
+	}
+
+	/**
+	 * Return the list of tracked items.
+	 * 
+	 * @param list An array to contain the tracked items.
+	 * @return The specified list if it is large enough to hold the tracked
+	 *         items or a new array large enough to hold the tracked items.
+	 * @GuardedBy this
+	 */
+	Object[] getTracked(final Object[] list) {
+		return tracked.keySet().toArray(list);
+	}
+
+	/**
+	 * Increment the modification count. If this method is overridden, the
+	 * overriding method MUST call this method to increment the tracking count.
+	 * 
+	 * @GuardedBy this
+	 */
+	void modified() {
+		trackingCount++;
+	}
+
+	/**
+	 * Returns the tracking count for this <code>ServiceTracker</code> object.
+	 * 
+	 * The tracking count is initialized to 0 when this object is opened. Every
+	 * time an item is added, modified or removed from this object the tracking
+	 * count is incremented.
+	 * 
+	 * @GuardedBy this
+	 * @return The tracking count for this object.
+	 */
+	int getTrackingCount() {
+		return trackingCount;
+	}
+
+	/**
+	 * Call the specific customizer adding method. This method must not be
+	 * called while synchronized on this object.
+	 * 
+	 * @param item Item to be tracked.
+	 * @param related Action related object.
+	 * @return Customized object for the tracked item or <code>null</code> if
+	 *         the item is not to be tracked.
+	 */
+	abstract Object customizerAdding(final Object item, final Object related);
+
+	/** marrs: Call the specific customizer added method. */
+	abstract void customizerAdded(final Object item, final Object related, final Object object);
+	
+	/**
+	 * Call the specific customizer modified method. This method must not be
+	 * called while synchronized on this object.
+	 * 
+	 * @param item Tracked item.
+	 * @param related Action related object.
+	 * @param object Customized object for the tracked item.
+	 */
+	abstract void customizerModified(final Object item, final Object related,
+			final Object object);
+
+	/**
+	 * Call the specific customizer removed method. This method must not be
+	 * called while synchronized on this object.
+	 * 
+	 * @param item Tracked item.
+	 * @param related Action related object.
+	 * @param object Customized object for the tracked item.
+	 */
+	abstract void customizerRemoved(final Object item, final Object related,
+			final Object object);
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/BundleTracker.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/BundleTracker.java?rev=1571864&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/BundleTracker.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/BundleTracker.java Tue Feb 25 23:28:08 2014
@@ -0,0 +1,478 @@
+package tracker;
+/*
+ * Copyright (c) OSGi Alliance (2007, 2008). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.SynchronousBundleListener;
+
+/**
+ * The <code>BundleTracker</code> class simplifies tracking bundles much like
+ * the <code>ServiceTracker</code> simplifies tracking services.
+ * <p>
+ * A <code>BundleTracker</code> is constructed with state criteria and a
+ * <code>BundleTrackerCustomizer</code> object. A <code>BundleTracker</code> can
+ * use the <code>BundleTrackerCustomizer</code> to select which bundles are
+ * tracked and to create a customized object to be tracked with the bundle. The
+ * <code>BundleTracker</code> can then be opened to begin tracking all bundles
+ * whose state matches the specified state criteria.
+ * <p>
+ * The <code>getBundles</code> method can be called to get the
+ * <code>Bundle</code> objects of the bundles being tracked. The
+ * <code>getObject</code> method can be called to get the customized object for
+ * a tracked bundle.
+ * <p>
+ * The <code>BundleTracker</code> class is thread-safe. It does not call a
+ * <code>BundleTrackerCustomizer</code> while holding any locks.
+ * <code>BundleTrackerCustomizer</code> implementations must also be
+ * thread-safe.
+ * 
+ * @ThreadSafe
+ * @version $Revision: 5894 $
+ * @since 1.4
+ */
+public class BundleTracker implements BundleTrackerCustomizer {
+	/* set this to true to compile in debug messages */
+	static final boolean			DEBUG	= false;
+
+	/**
+	 * The Bundle Context used by this <code>BundleTracker</code>.
+	 */
+	protected final BundleContext	context;
+
+	/**
+	 * The <code>BundleTrackerCustomizer</code> object for this tracker.
+	 */
+	final BundleTrackerCustomizer	customizer;
+
+	/**
+	 * Tracked bundles: <code>Bundle</code> object -> customized Object and
+	 * <code>BundleListener</code> object
+	 */
+	private volatile Tracked		tracked;
+
+	/**
+	 * Accessor method for the current Tracked object. This method is only
+	 * intended to be used by the unsynchronized methods which do not modify the
+	 * tracked field.
+	 * 
+	 * @return The current Tracked object.
+	 */
+	private Tracked tracked() {
+		return tracked;
+	}
+
+	/**
+	 * State mask for bundles being tracked. This field contains the ORed values
+	 * of the bundle states being tracked.
+	 */
+	final int						mask;
+
+	/**
+	 * Create a <code>BundleTracker</code> for bundles whose state is present in
+	 * the specified state mask.
+	 * 
+	 * <p>
+	 * Bundles whose state is present on the specified state mask will be
+	 * tracked by this <code>BundleTracker</code>.
+	 * 
+	 * @param context The <code>BundleContext</code> against which the tracking
+	 *        is done.
+	 * @param stateMask The bit mask of the <code>OR</code>ing of the bundle
+	 *        states to be tracked.
+	 * @param customizer The customizer object to call when bundles are added,
+	 *        modified, or removed in this <code>BundleTracker</code>. If
+	 *        customizer is <code>null</code>, then this
+	 *        <code>BundleTracker</code> will be used as the
+	 *        <code>BundleTrackerCustomizer</code> and this
+	 *        <code>BundleTracker</code> will call the
+	 *        <code>BundleTrackerCustomizer</code> methods on itself.
+	 * @see Bundle#getState()
+	 */
+	public BundleTracker(BundleContext context, int stateMask,
+			BundleTrackerCustomizer customizer) {
+		this.context = context;
+		this.mask = stateMask;
+		this.customizer = (customizer == null) ? this : customizer;
+	}
+
+	/**
+	 * Open this <code>BundleTracker</code> and begin tracking bundles.
+	 * 
+	 * <p>
+	 * Bundle which match the state criteria specified when this
+	 * <code>BundleTracker</code> was created are now tracked by this
+	 * <code>BundleTracker</code>.
+	 * 
+	 * @throws java.lang.IllegalStateException If the <code>BundleContext</code>
+	 *         with which this <code>BundleTracker</code> was created is no
+	 *         longer valid.
+	 * @throws java.lang.SecurityException If the caller and this class do not
+	 *         have the appropriate
+	 *         <code>AdminPermission[context bundle,LISTENER]</code>, and the
+	 *         Java Runtime Environment supports permissions.
+	 */
+	public void open() {
+		final Tracked t;
+		synchronized (this) {
+			if (tracked != null) {
+				return;
+			}
+			if (DEBUG) {
+				System.out.println("BundleTracker.open"); //$NON-NLS-1$
+			}
+			t = new Tracked();
+			synchronized (t) {
+				context.addBundleListener(t);
+				Bundle[] bundles = context.getBundles();
+				if (bundles != null) {
+					int length = bundles.length;
+					for (int i = 0; i < length; i++) {
+						int state = bundles[i].getState();
+						if ((state & mask) == 0) {
+							/* null out bundles whose states are not interesting */
+							bundles[i] = null;
+						}
+					}
+					/* set tracked with the initial bundles */
+					t.setInitial(bundles); 
+				}
+			}
+			tracked = t;
+		}
+		/* Call tracked outside of synchronized region */
+		t.trackInitial(); /* process the initial references */
+	}
+
+	/**
+	 * Close this <code>BundleTracker</code>.
+	 * 
+	 * <p>
+	 * This method should be called when this <code>BundleTracker</code> should
+	 * end the tracking of bundles.
+	 * 
+	 * <p>
+	 * This implementation calls {@link #getBundles()} to get the list of
+	 * tracked bundles to remove.
+	 */
+	public void close() {
+		final Bundle[] bundles;
+		final Tracked outgoing;
+		synchronized (this) {
+			outgoing = tracked;
+			if (outgoing == null) {
+				return;
+			}
+			if (DEBUG) {
+				System.out.println("BundleTracker.close"); //$NON-NLS-1$
+			}
+			outgoing.close();
+			bundles = getBundles();
+			tracked = null;
+			try {
+				context.removeBundleListener(outgoing);
+			}
+			catch (IllegalStateException e) {
+				/* In case the context was stopped. */
+			}
+		}
+		if (bundles != null) {
+			for (int i = 0; i < bundles.length; i++) {
+				outgoing.untrack(bundles[i], null);
+			}
+		}
+	}
+
+	/**
+	 * Default implementation of the
+	 * <code>BundleTrackerCustomizer.addingBundle</code> method.
+	 * 
+	 * <p>
+	 * This method is only called when this <code>BundleTracker</code> has been
+	 * constructed with a <code>null BundleTrackerCustomizer</code> argument.
+	 * 
+	 * <p>
+	 * This implementation simply returns the specified <code>Bundle</code>.
+	 * 
+	 * <p>
+	 * This method can be overridden in a subclass to customize the object to be
+	 * tracked for the bundle being added.
+	 * 
+	 * @param bundle The <code>Bundle</code> being added to this
+	 *        <code>BundleTracker</code> object.
+	 * @param event The bundle event which caused this customizer method to be
+	 *        called or <code>null</code> if there is no bundle event associated
+	 *        with the call to this method.
+	 * @return The specified bundle.
+	 * @see BundleTrackerCustomizer#addingBundle(Bundle, BundleEvent)
+	 */
+	public Object addingBundle(Bundle bundle, BundleEvent event) {
+		return bundle;
+	}
+	
+	public void addedBundle(Bundle bundle, BundleEvent event, Object object) {
+		/* do nothing */
+	}
+
+	/**
+	 * Default implementation of the
+	 * <code>BundleTrackerCustomizer.modifiedBundle</code> method.
+	 * 
+	 * <p>
+	 * This method is only called when this <code>BundleTracker</code> has been
+	 * constructed with a <code>null BundleTrackerCustomizer</code> argument.
+	 * 
+	 * <p>
+	 * This implementation does nothing.
+	 * 
+	 * @param bundle The <code>Bundle</code> whose state has been modified.
+	 * @param event The bundle event which caused this customizer method to be
+	 *        called or <code>null</code> if there is no bundle event associated
+	 *        with the call to this method.
+	 * @param object The customized object for the specified Bundle.
+	 * @see BundleTrackerCustomizer#modifiedBundle(Bundle, BundleEvent, Object)
+	 */
+	public void modifiedBundle(Bundle bundle, BundleEvent event, Object object) {
+		/* do nothing */
+	}
+
+	/**
+	 * Default implementation of the
+	 * <code>BundleTrackerCustomizer.removedBundle</code> method.
+	 * 
+	 * <p>
+	 * This method is only called when this <code>BundleTracker</code> has been
+	 * constructed with a <code>null BundleTrackerCustomizer</code> argument.
+	 * 
+	 * <p>
+	 * This implementation does nothing.
+	 * 
+	 * @param bundle The <code>Bundle</code> being removed.
+	 * @param event The bundle event which caused this customizer method to be
+	 *        called or <code>null</code> if there is no bundle event associated
+	 *        with the call to this method.
+	 * @param object The customized object for the specified bundle.
+	 * @see BundleTrackerCustomizer#removedBundle(Bundle, BundleEvent, Object)
+	 */
+	public void removedBundle(Bundle bundle, BundleEvent event, Object object) {
+		/* do nothing */
+	}
+
+	/**
+	 * Return an array of <code>Bundle</code>s for all bundles being tracked by
+	 * this <code>BundleTracker</code>.
+	 * 
+	 * @return An array of <code>Bundle</code>s or <code>null</code> if no
+	 *         bundles are being tracked.
+	 */
+	public Bundle[] getBundles() {
+		final Tracked t = tracked();
+		if (t == null) { /* if BundleTracker is not open */
+			return null;
+		}
+		synchronized (t) {
+			int length = t.size();
+			if (length == 0) {
+				return null;
+			}
+			return (Bundle[]) t.getTracked(new Bundle[length]);
+		}
+	}
+
+	/**
+	 * Returns the customized object for the specified <code>Bundle</code> if
+	 * the specified bundle is being tracked by this <code>BundleTracker</code>.
+	 * 
+	 * @param bundle The <code>Bundle</code> being tracked.
+	 * @return The customized object for the specified <code>Bundle</code> or
+	 *         <code>null</code> if the specified <code>Bundle</code> is not
+	 *         being tracked.
+	 */
+	public Object getObject(Bundle bundle) {
+		final Tracked t = tracked();
+		if (t == null) { /* if BundleTracker is not open */
+			return null;
+		}
+		synchronized (t) {
+			return t.getCustomizedObject(bundle);
+		}
+	}
+
+	/**
+	 * Remove a bundle from this <code>BundleTracker</code>.
+	 * 
+	 * The specified bundle will be removed from this <code>BundleTracker</code>
+	 * . If the specified bundle was being tracked then the
+	 * <code>BundleTrackerCustomizer.removedBundle</code> method will be called
+	 * for that bundle.
+	 * 
+	 * @param bundle The <code>Bundle</code> to be removed.
+	 */
+	public void remove(Bundle bundle) {
+		final Tracked t = tracked();
+		if (t == null) { /* if BundleTracker is not open */
+			return;
+		}
+		t.untrack(bundle, null);
+	}
+
+	/**
+	 * Return the number of bundles being tracked by this
+	 * <code>BundleTracker</code>.
+	 * 
+	 * @return The number of bundles being tracked.
+	 */
+	public int size() {
+		final Tracked t = tracked();
+		if (t == null) { /* if BundleTracker is not open */
+			return 0;
+		}
+		synchronized (t) {
+			return t.size();
+		}
+	}
+
+	/**
+	 * Returns the tracking count for this <code>BundleTracker</code>.
+	 * 
+	 * The tracking count is initialized to 0 when this
+	 * <code>BundleTracker</code> is opened. Every time a bundle is added,
+	 * modified or removed from this <code>BundleTracker</code> the tracking
+	 * count is incremented.
+	 * 
+	 * <p>
+	 * The tracking count can be used to determine if this
+	 * <code>BundleTracker</code> has added, modified or removed a bundle by
+	 * comparing a tracking count value previously collected with the current
+	 * tracking count value. If the value has not changed, then no bundle has
+	 * been added, modified or removed from this <code>BundleTracker</code>
+	 * since the previous tracking count was collected.
+	 * 
+	 * @return The tracking count for this <code>BundleTracker</code> or -1 if
+	 *         this <code>BundleTracker</code> is not open.
+	 */
+	public int getTrackingCount() {
+		final Tracked t = tracked();
+		if (t == null) { /* if BundleTracker is not open */
+			return -1;
+		}
+		synchronized (t) {
+			return t.getTrackingCount();
+		}
+	}
+
+	/**
+	 * Inner class which subclasses AbstractTracked. This class is the
+	 * <code>SynchronousBundleListener</code> object for the tracker.
+	 * 
+	 * @ThreadSafe
+	 * @since 1.4
+	 */
+	class Tracked extends AbstractTracked implements SynchronousBundleListener {
+		/**
+		 * Tracked constructor.
+		 */
+		Tracked() {
+			super();
+		}
+
+		/**
+		 * <code>BundleListener</code> method for the <code>BundleTracker</code>
+		 * class. This method must NOT be synchronized to avoid deadlock
+		 * potential.
+		 * 
+		 * @param event <code>BundleEvent</code> object from the framework.
+		 */
+		public void bundleChanged(final BundleEvent event) {
+			/*
+			 * Check if we had a delayed call (which could happen when we
+			 * close).
+			 */
+			if (closed) {
+				return;
+			}
+			final Bundle bundle = event.getBundle();
+			final int state = bundle.getState();
+			if (DEBUG) {
+				System.out
+						.println("BundleTracker.Tracked.bundleChanged[" + state + "]: " + bundle); //$NON-NLS-1$ //$NON-NLS-2$
+			}
+
+			if ((state & mask) != 0) {
+				track(bundle, event);
+				/*
+				 * If the customizer throws an unchecked exception, it is safe
+				 * to let it propagate
+				 */
+			}
+			else {
+				untrack(bundle, event);
+				/*
+				 * If the customizer throws an unchecked exception, it is safe
+				 * to let it propagate
+				 */
+			}
+		}
+
+		/**
+		 * Call the specific customizer adding method. This method must not be
+		 * called while synchronized on this object.
+		 * 
+		 * @param item Item to be tracked.
+		 * @param related Action related object.
+		 * @return Customized object for the tracked item or <code>null</code>
+		 *         if the item is not to be tracked.
+		 */
+		Object customizerAdding(final Object item,
+				final Object related) {
+			return customizer
+					.addingBundle((Bundle) item, (BundleEvent) related);
+		}
+		
+		void customizerAdded(final Object item, final Object related, final Object object) {
+			customizer.addedBundle((Bundle) item, (BundleEvent) related, object);
+		}
+
+		/**
+		 * Call the specific customizer modified method. This method must not be
+		 * called while synchronized on this object.
+		 * 
+		 * @param item Tracked item.
+		 * @param related Action related object.
+		 * @param object Customized object for the tracked item.
+		 */
+		void customizerModified(final Object item,
+				final Object related, final Object object) {
+			customizer.modifiedBundle((Bundle) item, (BundleEvent) related,
+					object);
+		}
+
+		/**
+		 * Call the specific customizer removed method. This method must not be
+		 * called while synchronized on this object.
+		 * 
+		 * @param item Tracked item.
+		 * @param related Action related object.
+		 * @param object Customized object for the tracked item.
+		 */
+		void customizerRemoved(final Object item,
+				final Object related, final Object object) {
+			customizer.removedBundle((Bundle) item, (BundleEvent) related,
+					object);
+		}
+	}
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/BundleTrackerCustomizer.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/BundleTrackerCustomizer.java?rev=1571864&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/BundleTrackerCustomizer.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/src/tracker/BundleTrackerCustomizer.java Tue Feb 25 23:28:08 2014
@@ -0,0 +1,106 @@
+package tracker;
+/*
+ * Copyright (c) OSGi Alliance (2007, 2008). All Rights Reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleEvent;
+
+/**
+ * The <code>BundleTrackerCustomizer</code> interface allows a
+ * <code>BundleTracker</code> to customize the <code>Bundle</code>s that are
+ * tracked. A <code>BundleTrackerCustomizer</code> is called when a bundle is
+ * being added to a <code>BundleTracker</code>. The
+ * <code>BundleTrackerCustomizer</code> can then return an object for the
+ * tracked bundle. A <code>BundleTrackerCustomizer</code> is also called when a
+ * tracked bundle is modified or has been removed from a
+ * <code>BundleTracker</code>.
+ * 
+ * <p>
+ * The methods in this interface may be called as the result of a
+ * <code>BundleEvent</code> being received by a <code>BundleTracker</code>.
+ * Since <code>BundleEvent</code>s are received synchronously by the
+ * <code>BundleTracker</code>, it is highly recommended that implementations of
+ * these methods do not alter bundle states while being synchronized on any
+ * object.
+ * 
+ * <p>
+ * The <code>BundleTracker</code> class is thread-safe. It does not call a
+ * <code>BundleTrackerCustomizer</code> while holding any locks.
+ * <code>BundleTrackerCustomizer</code> implementations must also be
+ * thread-safe.
+ * 
+ * @ThreadSafe
+ * @version $Revision: 5874 $
+ * @since 1.4
+ */
+public interface BundleTrackerCustomizer {
+	/**
+	 * A bundle is being added to the <code>BundleTracker</code>.
+	 * 
+	 * <p>
+	 * This method is called before a bundle which matched the search parameters
+	 * of the <code>BundleTracker</code> is added to the
+	 * <code>BundleTracker</code>. This method should return the object to be
+	 * tracked for the specified <code>Bundle</code>. The returned object is
+	 * stored in the <code>BundleTracker</code> and is available from the
+	 * {@link BundleTracker#getObject(Bundle) getObject} method.
+	 * 
+	 * @param bundle The <code>Bundle</code> being added to the
+	 *        <code>BundleTracker</code>.
+	 * @param event The bundle event which caused this customizer method to be
+	 *        called or <code>null</code> if there is no bundle event associated
+	 *        with the call to this method.
+	 * @return The object to be tracked for the specified <code>Bundle</code>
+	 *         object or <code>null</code> if the specified <code>Bundle</code>
+	 *         object should not be tracked.
+	 */
+	public Object addingBundle(Bundle bundle, BundleEvent event);
+	
+	/** marrs: A bundle has been added to the BundleTracker. */
+	public void addedBundle(Bundle bundle, BundleEvent event, Object object);
+
+	/**
+	 * A bundle tracked by the <code>BundleTracker</code> has been modified.
+	 * 
+	 * <p>
+	 * This method is called when a bundle being tracked by the
+	 * <code>BundleTracker</code> has had its state modified.
+	 * 
+	 * @param bundle The <code>Bundle</code> whose state has been modified.
+	 * @param event The bundle event which caused this customizer method to be
+	 *        called or <code>null</code> if there is no bundle event associated
+	 *        with the call to this method.
+	 * @param object The tracked object for the specified bundle.
+	 */
+	public void modifiedBundle(Bundle bundle, BundleEvent event,
+			Object object);
+
+	/**
+	 * A bundle tracked by the <code>BundleTracker</code> has been removed.
+	 * 
+	 * <p>
+	 * This method is called after a bundle is no longer being tracked by the
+	 * <code>BundleTracker</code>.
+	 * 
+	 * @param bundle The <code>Bundle</code> that has been removed.
+	 * @param event The bundle event which caused this customizer method to be
+	 *        called or <code>null</code> if there is no bundle event associated
+	 *        with the call to this method.
+	 * @param object The tracked object for the specified bundle.
+	 */
+	public void removedBundle(Bundle bundle, BundleEvent event,
+			Object object);
+}