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);
+}