resolved FELIX-3424, resolved FELIX-3425.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1308952 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyManager.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyManager.java
index befe82a..07a41a4 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyManager.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyManager.java
@@ -40,6 +40,7 @@
import org.apache.felix.dm.impl.dependencies.TemporalServiceDependencyImpl;
import org.apache.felix.dm.impl.index.AspectFilterIndex;
import org.apache.felix.dm.impl.index.MultiPropertyExactFilter;
+import org.apache.felix.dm.impl.index.AdapterFilterIndex;
import org.apache.felix.dm.impl.index.ServiceRegistryCache;
import org.apache.felix.dm.impl.metatype.PropertyMetaDataImpl;
import org.osgi.framework.Bundle;
@@ -104,6 +105,8 @@
for (int i = 0; i < props.length; i++) {
if (props[i].equals("*aspect*")) {
m_serviceRegistryCache.addFilterIndex(new AspectFilterIndex());
+ } else if (props[i].equals("*adapter*")) {
+ m_serviceRegistryCache.addFilterIndex(new AdapterFilterIndex());
}
else {
String[] propList = props[i].split(",");
@@ -264,7 +267,7 @@
* @return a service that acts as a factory for generating aspects
*/
public Component createAspectService(Class serviceInterface, String serviceFilter, int ranking, String autoConfig) {
- return new AspectServiceImpl(this, serviceInterface, serviceFilter, ranking, autoConfig, null, null, null);
+ return new AspectServiceImpl(this, serviceInterface, serviceFilter, ranking, autoConfig, null, null, null, null);
}
/**
* Creates a new aspect. The aspect will be applied to any service that
@@ -289,7 +292,7 @@
* @return a service that acts as a factory for generating aspects
*/
public Component createAspectService(Class serviceInterface, String serviceFilter, int ranking) {
- return new AspectServiceImpl(this, serviceInterface, serviceFilter, ranking, null, null, null, null);
+ return new AspectServiceImpl(this, serviceInterface, serviceFilter, ranking, null, null, null, null, null);
}
/**
* Creates a new aspect. The aspect will be applied to any service that
@@ -317,7 +320,37 @@
* @return a service that acts as a factory for generating aspects
*/
public Component createAspectService(Class serviceInterface, String serviceFilter, int ranking, String add, String change, String remove) {
- return new AspectServiceImpl(this, serviceInterface, serviceFilter, ranking, null, add, change, remove);
+ return new AspectServiceImpl(this, serviceInterface, serviceFilter, ranking, null, add, change, remove, null);
+ }
+
+ /**
+ * Creates a new aspect. The aspect will be applied to any service that
+ * matches the specified interface and filter. For each matching service
+ * an aspect will be created based on the aspect implementation class.
+ * The aspect will be registered with the same interface and properties
+ * as the original service, plus any extra properties you supply here.
+ * It will also inherit all dependencies, and if you declare the original
+ * service as a member it will be injected.
+ *
+ * <h3>Usage Example</h3>
+ *
+ * <blockquote><pre>
+ * manager.createAspectService(ExistingService.class, "(foo=bar)", 10, "add", "change", "remove")
+ * .setImplementation(ExistingServiceAspect.class)
+ * );
+ * </pre></blockquote>
+ *
+ * @param serviceInterface the service interface to apply the aspect to
+ * @param serviceFilter the filter condition to use with the service interface
+ * @param ranking the level used to organize the aspect chain ordering
+ * @param add name of the callback method to invoke on add
+ * @param change name of the callback method to invoke on change
+ * @param remove name of the callback method to invoke on remove
+ * @param swap name of the callback method to invoke on swap
+ * @return a service that acts as a factory for generating aspects
+ */
+ public Component createAspectService(Class serviceInterface, String serviceFilter, int ranking, String add, String change, String remove, String swap) {
+ return new AspectServiceImpl(this, serviceInterface, serviceFilter, ranking, null, add, change, remove, swap);
}
/**
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/AspectServiceImpl.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/AspectServiceImpl.java
index 0e28f76..504552e 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/AspectServiceImpl.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/AspectServiceImpl.java
@@ -38,10 +38,10 @@
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
public class AspectServiceImpl extends FilterService {
- public AspectServiceImpl(DependencyManager dm, Class aspectInterface, String aspectFilter, int ranking, String autoConfig, String add, String change, String remove)
+ public AspectServiceImpl(DependencyManager dm, Class aspectInterface, String aspectFilter, int ranking, String autoConfig, String add, String change, String remove, String swap)
{
super(dm.createComponent()); // This service will be filtered by our super class, allowing us to take control.
- m_component.setImplementation(new AspectImpl(aspectInterface, aspectFilter, ranking, autoConfig, add, change, remove))
+ m_component.setImplementation(new AspectImpl(aspectInterface, aspectFilter, ranking, autoConfig, add, change, remove, swap))
.add(dm.createServiceDependency()
.setService(aspectInterface, createDependencyFilterForAspect(aspectFilter))
.setAutoConfig(false)
@@ -70,8 +70,9 @@
private final String m_add;
private final String m_change;
private final String m_remove;
+ private final String m_swap;
- public AspectImpl(Class aspectInterface, String aspectFilter, int ranking, String autoConfig, String add, String change, String remove) {
+ public AspectImpl(Class aspectInterface, String aspectFilter, int ranking, String autoConfig, String add, String change, String remove, String swap) {
m_aspectInterface = aspectInterface;
m_aspectFilter = aspectFilter;
m_ranking = ranking;
@@ -79,6 +80,7 @@
m_add = add;
m_change = change;
m_remove = remove;
+ m_swap = swap;
}
public Component createService(Object[] params) {
@@ -93,8 +95,8 @@
if (m_autoConfig != null) {
dependency.setAutoConfig(m_autoConfig);
}
- if (m_add != null || m_change != null || m_remove != null) {
- dependency.setCallbacks(m_add, m_change, m_remove);
+ if (m_add != null || m_change != null || m_remove != null || m_swap != null) {
+ dependency.setCallbacks(m_add, m_change, m_remove, m_swap);
}
Component service = m_manager.createComponent()
.setInterface(serviceInterfaces, serviceProperties)
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/AdapterFilterIndex.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/AdapterFilterIndex.java
new file mode 100644
index 0000000..740e000
--- /dev/null
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/AdapterFilterIndex.java
@@ -0,0 +1,250 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.impl.index;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.FilterIndex;
+import org.apache.felix.dm.ServiceUtil;
+import org.apache.felix.dm.tracker.ServiceTracker;
+import org.apache.felix.dm.tracker.ServiceTrackerCustomizer;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class AdapterFilterIndex implements FilterIndex, ServiceTrackerCustomizer {
+ // (&(objectClass=com.beinformed.product.platform.interfaces.Resource)(|(service.id=18233)(org.apache.felix.dependencymanager.aspect=18233)))
+ private static final String FILTER_START = "(&(" + Constants.OBJECTCLASS + "=";
+ private static final String FILTER_SUBSTRING_0 = ")(|(" + Constants.SERVICE_ID + "=";
+ private static final String FILTER_SUBSTRING_1 = ")(" + DependencyManager.ASPECT + "=";
+ private static final String FILTER_END = ")))";
+ private final Object m_lock = new Object();
+ private ServiceTracker m_tracker;
+ private BundleContext m_context;
+ private final Map /* <Long, SortedSet<ServiceReference>> */ m_sidToServiceReferencesMap = new HashMap();
+ private final Map /* <String, List<ServiceListener>> */ m_sidToListenersMap = new HashMap();
+ private final Map /* <ServiceListener, String> */ m_listenerToFilterMap = new HashMap();
+
+ 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 boolean isApplicable(String clazz, String filter) {
+ return getFilterData(clazz, filter) != null;
+ }
+
+ /** Returns a value object with the relevant filter data, or <code>null</code> if this filter was not valid. */
+ private FilterData getFilterData(String clazz, String filter) {
+ // something like:
+ // (&(objectClass=com.beinformed.product.platform.interfaces.Resource)(|(service.id=18233)(org.apache.felix.dependencymanager.aspect=18233)))
+ if ((filter != null)
+ && (filter.startsWith(FILTER_START))
+ && (filter.endsWith(FILTER_END))
+ ) {
+ // service-id =
+ int i0 = filter.indexOf(FILTER_SUBSTRING_0);
+ if (i0 == -1) {
+ return null;
+ }
+ // org.apache.felix.dependencymanager.aspect =
+ int i1 = filter.indexOf(FILTER_SUBSTRING_1);
+ if (i1 == -1 || i1 <= i0) {
+ return null;
+ }
+ long sid = Long.parseLong(filter.substring(i0 + FILTER_SUBSTRING_0.length(), i1));
+ long sid2 = Long.parseLong(filter.substring(i1 + FILTER_SUBSTRING_1.length(), filter.length() - FILTER_END.length()));
+ if (sid != sid2) {
+ return null;
+ }
+ FilterData result = new FilterData();
+ result.serviceId = sid;
+ return result;
+ }
+ return null;
+ }
+
+ public List getAllServiceReferences(String clazz, String filter) {
+ List /* <ServiceReference> */ result = new ArrayList();
+ FilterData data = getFilterData(clazz, filter);
+ if (data != null) {
+ SortedSet /* <ServiceReference> */ list = null;
+ synchronized (m_sidToServiceReferencesMap) {
+ list = (SortedSet) m_sidToServiceReferencesMap.get(Long.valueOf(data.serviceId));
+ }
+ if (list != null) {
+ Iterator iterator = list.iterator();
+ while (iterator.hasNext()) {
+ result.add((ServiceReference) iterator.next());
+ }
+ }
+ }
+ return result;
+ }
+
+ public void serviceChanged(ServiceEvent event) {
+ ServiceReference reference = event.getServiceReference();
+ Long sid = ServiceUtil.getServiceIdObject(reference);
+ synchronized (m_sidToListenersMap) {
+ List /* <Integer, ServiceListener> */ list = (ArrayList) m_sidToListenersMap.get(sid);
+ 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) {
+ FilterData data = getFilterData(null, filter);
+ if (data != null) {
+ Long sidObject = Long.valueOf(data.serviceId);
+ synchronized (m_sidToListenersMap) {
+ List /* <ServiceListener> */ listeners = (List) m_sidToListenersMap.get(sidObject);
+ if (listeners == null) {
+ listeners = new ArrayList();
+ m_sidToListenersMap.put(sidObject, listeners);
+ }
+ listeners.add(listener);
+ }
+ }
+ }
+
+ public void removeServiceListener(ServiceListener listener) {
+ synchronized (m_sidToListenersMap) {
+ String filter = (String) m_listenerToFilterMap.remove(listener);
+ FilterData data = getFilterData(null, filter);
+ if (data != null) {
+ Long sidObject = Long.valueOf(data.serviceId);
+ List /* ServiceListener */ listeners = (List) m_sidToListenersMap.get(sidObject);
+ if (listeners != null) {
+ listeners.remove(listener);
+ }
+ }
+ }
+ }
+
+ 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) {
+ add(reference);
+ }
+
+ public void modifiedService(ServiceReference reference, Object service) {
+ modify(reference);
+ }
+
+ public void removedService(ServiceReference reference, Object service) {
+ remove(reference);
+ }
+
+ public void add(ServiceReference reference) {
+ Long sid = ServiceUtil.getServiceIdObject(reference);
+ synchronized (m_sidToServiceReferencesMap) {
+ Set list = (Set) m_sidToServiceReferencesMap.get(sid);
+ if (list == null) {
+ list = new TreeSet();
+ m_sidToServiceReferencesMap.put(sid, list);
+ }
+ list.add(reference);
+ }
+ }
+
+ public void modify(ServiceReference reference) {
+ remove(reference);
+ add(reference);
+ }
+
+ public void remove(ServiceReference reference) {
+ Long sid = ServiceUtil.getServiceIdObject(reference);
+ synchronized (m_sidToServiceReferencesMap) {
+ Set list = (Set) m_sidToServiceReferencesMap.get(sid);
+ if (list != null) {
+ list.remove(reference);
+ }
+ }
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("AdapterFilterIndex[");
+ sb.append("S2L: " + m_sidToListenersMap.size());
+ sb.append(", S2SR: " + m_sidToServiceReferencesMap.size());
+ sb.append(", L2F: " + m_listenerToFilterMap.size());
+ sb.append("]");
+ return sb.toString();
+ }
+
+ /** Structure to hold internal filter data. */
+ private static class FilterData {
+ public long serviceId;
+ }
+
+}