Improved support for aspects and tracking them using whiteboard style listeners. Extended our internal ServiceTracker to make it aware of aspects.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@943054 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/AspectImpl.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/AspectImpl.java
index 31c7a20..230886d 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/AspectImpl.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/AspectImpl.java
@@ -63,7 +63,7 @@
Object service = properties[1];
Properties props = new Properties();
// first add our aspect property
- props.put(DependencyManager.ASPECT, "true");
+ props.put(DependencyManager.ASPECT, ref.getProperty(Constants.SERVICE_ID));
// and the ranking
props.put(Constants.SERVICE_RANKING, Integer.valueOf(m_ranking));
String[] keys = ref.getPropertyKeys();
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/tracker/ServiceTracker.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/tracker/ServiceTracker.java
index eb0f147..09a42cf 100644
--- a/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/tracker/ServiceTracker.java
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/tracker/ServiceTracker.java
@@ -17,7 +17,11 @@
import java.security.AccessController;
import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.util.ServiceUtil;
import org.osgi.framework.AllServiceListener;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
@@ -806,6 +810,86 @@
* @ThreadSafe
*/
class Tracked extends AbstractTracked implements ServiceListener {
+ /** A list of services that are currently hidden because there is an aspect available with a higher ranking. */
+ private final List m_hidden = new ArrayList();
+
+ /**
+ * Returns the highest hidden aspect for the specified service ID.
+ *
+ * @param serviceId the service ID
+ * @return a service reference, or <code>null</code> if there was no such service
+ */
+ private ServiceReference highestHidden(long serviceId) {
+ ServiceReference result = null;
+ int max = Integer.MIN_VALUE;
+ for (int i = 0; i < m_hidden.size(); i++) {
+ ServiceReference ref = (ServiceReference) m_hidden.get(i);
+ Long sid = (Long) ref.getProperty(Constants.SERVICE_ID);
+ Long aid = (Long) ref.getProperty(DependencyManager.ASPECT);
+ if ((aid != null && aid.longValue() == serviceId)
+ || (aid == null && sid != null && sid.longValue() == serviceId)) {
+ Integer ranking = (Integer) ref.getProperty(Constants.SERVICE_RANKING);
+ int r = 0;
+ if (ranking != null) {
+ r = ranking.intValue();
+ }
+ if (r > max) {
+ max = r;
+ result = ref;
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns the highest tracked service for the specified service ID.
+ *
+ * @param serviceId the service ID
+ * @return a service reference, or <code>null</code> if there was no such service
+ */
+ private ServiceReference highestTracked(long serviceId) {
+ ServiceReference result = null;
+ int max = Integer.MIN_VALUE;
+ Object[] trackedServices = getTracked(new Object[] {});
+ for (int i = 0; i < trackedServices.length; i++) {
+ ServiceReference ref = (ServiceReference) trackedServices[i];
+ Long sid = (Long) ref.getProperty(Constants.SERVICE_ID);
+ Long aid = (Long) ref.getProperty(DependencyManager.ASPECT);
+ if ((aid != null && aid.longValue() == serviceId)
+ || (aid == null && sid != null && sid.longValue() == serviceId)) {
+ Integer ranking = (Integer) ref.getProperty(Constants.SERVICE_RANKING);
+ int r = 0;
+ if (ranking != null) {
+ r = ranking.intValue();
+ }
+ if (r > max) {
+ max = r;
+ result = ref;
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Hide a service reference, placing it in the list of hidden services.
+ *
+ * @param ref the service reference to add to the hidden list
+ */
+ private void hide(ServiceReference ref) {
+ m_hidden.add(ref);
+ }
+
+ /**
+ * Unhide a service reference, removing it from the list of hidden services.
+ *
+ * @param ref the service reference to remove from the hidden list
+ */
+ private void unhide(ServiceReference ref) {
+ m_hidden.remove(ref);
+ }
+
/**
* Tracked constructor.
*/
@@ -838,9 +922,51 @@
switch (event.getType()) {
case ServiceEvent.REGISTERED :
case ServiceEvent.MODIFIED :
- if (listenerFilter != null) { // service listener added with
- // filter
- track(reference, event);
+ ServiceReference higher = null;
+ ServiceReference lower = null;
+ boolean isAspect = ServiceUtil.isAspect(reference);
+ if (isAspect) {
+ long sid = ServiceUtil.getServiceId(reference);
+ ServiceReference sr = highestTracked(sid);
+ if (sr != null) {
+ int ranking = ServiceUtil.getRanking(reference);
+ int trackedRanking = ServiceUtil.getRanking(sr);
+ if (ranking > trackedRanking) {
+ // found a higher ranked one!
+ if (DEBUG) {
+ System.out.println("ServiceTracker.Tracked.serviceChanged[" + event.getType() + "]: Found a higher ranked aspect: " + ServiceUtil.toString(reference) + " vs " + ServiceUtil.toString(sr));
+ }
+ higher = sr;
+ }
+ else {
+ // found lower ranked one!
+ if (DEBUG) {
+ System.out.println("ServiceTracker.Tracked.serviceChanged[" + event.getType() + "]: Found a lower ranked aspect: " + ServiceUtil.toString(reference) + " vs " + ServiceUtil.toString(sr));
+ }
+ lower = sr;
+ }
+ }
+ }
+
+ if (listenerFilter != null) { // service listener added with filter
+ if (lower != null) {
+ hide(reference);
+ }
+ else {
+ try {
+ track(reference, event);
+ }
+ finally {
+ if (higher != null) {
+ try {
+ untrack(higher, null);
+ }
+ finally {
+ hide(higher);
+ }
+ }
+ }
+ }
/*
* If the customizer throws an unchecked exception, it
* is safe to let it propagate
@@ -848,14 +974,51 @@
}
else { // service listener added without filter
if (filter.match(reference)) {
- track(reference, event);
+ if (lower != null) {
+ hide(reference);
+ }
+ else {
+ try {
+ track(reference, event);
+ }
+ finally {
+ if (higher != null) {
+ try {
+ untrack(higher, null);
+ }
+ finally {
+ hide(higher);
+ }
+ }
+ }
+ }
/*
* If the customizer throws an unchecked exception,
* it is safe to let it propagate
*/
}
else {
- untrack(reference, event);
+ higher = null;
+ isAspect = ServiceUtil.isAspect(reference);
+ if (isAspect) {
+ long sid = ServiceUtil.getServiceId(reference);
+ ServiceReference sr = highestHidden(sid);
+ if (sr != null) {
+ if (DEBUG) {
+ System.out.println("ServiceTracker.Tracked.serviceChanged[" + event.getType() + "]: Found a hidden aspect: " + ServiceUtil.toString(reference));
+ }
+ higher = sr;
+ }
+ }
+ try {
+ if (higher != null) {
+ unhide(higher);
+ track(higher, null);
+ }
+ }
+ finally {
+ untrack(reference, event);
+ }
/*
* If the customizer throws an unchecked exception,
* it is safe to let it propagate
@@ -865,7 +1028,27 @@
break;
case 8 /* ServiceEvent.MODIFIED_ENDMATCH */ :
case ServiceEvent.UNREGISTERING :
- untrack(reference, event);
+ higher = null;
+ isAspect = ServiceUtil.isAspect(reference);
+ if (isAspect) {
+ long sid = ServiceUtil.getServiceId(reference);
+ ServiceReference sr = highestHidden(sid);
+ if (sr != null) {
+ if (DEBUG) {
+ System.out.println("ServiceTracker.Tracked.serviceChanged[" + event.getType() + "]: Found a hidden aspect: " + ServiceUtil.toString(reference));
+ }
+ higher = sr;
+ }
+ }
+ try {
+ if (higher != null) {
+ unhide(higher);
+ track(higher, null);
+ }
+ }
+ finally {
+ untrack(reference, event);
+ }
/*
* If the customizer throws an unchecked exception, it is
* safe to let it propagate
diff --git a/dependencymanager/core/src/main/java/org/apache/felix/dm/util/ServiceUtil.java b/dependencymanager/core/src/main/java/org/apache/felix/dm/util/ServiceUtil.java
new file mode 100644
index 0000000..d6d8d36
--- /dev/null
+++ b/dependencymanager/core/src/main/java/org/apache/felix/dm/util/ServiceUtil.java
@@ -0,0 +1,103 @@
+/*
+ * 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.util;
+
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * OSGi service utilities.
+ */
+public class ServiceUtil {
+ /**
+ * Returns the service ranking of a service, based on its service reference. If
+ * the service has a property specifying its ranking, that will be returned. If
+ * not, the default ranking of zero will be returned.
+ *
+ * @param ref the service reference to determine the ranking for
+ * @return the ranking
+ */
+ public static int getRanking(ServiceReference ref) {
+ Integer rank = (Integer) ref.getProperty(Constants.SERVICE_RANKING);
+ if (rank != null) {
+ return rank.intValue();
+ }
+ return 0;
+ }
+
+ /**
+ * Returns the service ID of a service, based on its service reference. This
+ * method is aware of service aspects as defined by the dependency manager and
+ * will return the ID of the orginal service if you give it an aspect.
+ *
+ * @param ref the service reference to determine the service ID of
+ * @return the service ID
+ */
+ public static long getServiceId(ServiceReference ref) {
+ Long sid = (Long) ref.getProperty(Constants.SERVICE_ID);
+ Long aid = (Long) ref.getProperty(DependencyManager.ASPECT);
+ if (aid != null) {
+ return aid.longValue();
+ }
+ if (sid != null) {
+ return sid.longValue();
+ }
+ throw new IllegalArgumentException("Invalid service reference, no service ID found");
+ }
+
+ /**
+ * Determines if the service is an aspect as defined by the dependency manager.
+ * Aspects are defined by a property and this method will check for its presence.
+ *
+ * @param ref the service reference
+ * @return <code>true</code> if it's an aspect, <code>false</code> otherwise
+ */
+ public static boolean isAspect(ServiceReference ref) {
+ Long aid = (Long) ref.getProperty(DependencyManager.ASPECT);
+ return (aid != null);
+ }
+
+ /**
+ * Converts a service reference to a string, listing both the bundle it was
+ * registered from and all properties.
+ *
+ * @param ref the service reference
+ * @return a string representation of the service
+ */
+ public static String toString(ServiceReference ref) {
+ if (ref == null) {
+ throw new IllegalArgumentException("Service reference cannot be null.");
+ }
+ StringBuffer buf = new StringBuffer();
+
+ buf.append("ServiceReference[" + ref.getBundle().getBundleId() + "]{");
+ String[] keys = ref.getPropertyKeys();
+ for (int i = 0; i < keys.length; i++) {
+ if (i > 0) {
+ buf.append(',');
+ }
+ buf.append(keys[i]);
+ buf.append('=');
+ buf.append(ref.getProperty(keys[i]));
+ }
+ buf.append("}");
+ return buf.toString();
+ }
+}