/******************************************************************************
* Copyright (c) 2006, 2010 VMware Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html and the Apache License v2.0
* is available at http://www.opensource.org/licenses/apache2.0.php.
* You may elect to redistribute this code under either of these licenses.
*
* Contributors:
* VMware Inc.
*****************************************************************************/
package org.eclipse.gemini.blueprint.util;
import java.util.Collections;
import java.util.Dictionary;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.gemini.blueprint.util.internal.MapBasedDictionary;
import org.eclipse.gemini.blueprint.util.internal.ServiceReferenceBasedMap;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* Utility class for retrieving OSGi service references. This class offers a unified filter-based access for OSGi
* services as well as translation of checked exceptions {@link InvalidSyntaxException} into unchecked ones.
*
* <p/>
*
* This classes uses {@link OsgiFilterUtils} underneath to allow multiple class names to be used for service reference
* lookup.
*
* @see OsgiFilterUtils
* @author Costin Leau
*
*/
public abstract class OsgiServiceReferenceUtils {
private static final Log log = LogFactory.getLog(OsgiServiceReferenceUtils.class);
/**
* Returns a reference to the <em>best matching</em> service for the given class names.
*
* @param bundleContext OSGi bundle context
* @param classes array of fully qualified class names
* @return reference to the <em>best matching</em> service
*/
public static ServiceReference getServiceReference(BundleContext bundleContext, String[] classes) {
return getServiceReference(bundleContext, classes, null);
}
/**
* Returns a reference to the <em>best matching</em> service for the given class and OSGi filter.
*
* @param bundleContext OSGi bundle context
* @param clazz fully qualified class name (can be <code>null</code>)
* @param filter valid OSGi filter (can be <code>null</code>)
* @return reference to the <em>best matching</em> service
*/
public static ServiceReference getServiceReference(BundleContext bundleContext, String clazz, String filter) {
ServiceReference[] refs = getServiceReferences(bundleContext, clazz, filter);
// pick the best service
return getServiceReference(refs);
}
public static ServiceReference getServiceReference(ServiceReference... references) {
if (ObjectUtils.isEmpty(references)) {
return null;
}
ServiceReference winningReference = references[0];
if (references.length > 1) {
long winningId = getServiceId(winningReference);
int winningRanking = getServiceRanking(winningReference);
// start iterating in order to find the best match
for (int i = 1; i < references.length; i++) {
ServiceReference reference = references[i];
int serviceRanking = getServiceRanking(reference);
long serviceId = getServiceId(reference);
if ((serviceRanking > winningRanking) || (serviceRanking == winningRanking && winningId > serviceId)) {
winningReference = reference;
winningId = serviceId;
winningRanking = serviceRanking;
}
}
}
return winningReference;
}
/**
* Returns a reference to the <em>best matching</em> service for the given classes and OSGi filter.
*
* @param bundleContext OSGi bundle context
* @param classes array of fully qualified class names
* @param filter valid OSGi filter (can be <code>null</code>)
* @return reference to the <em>best matching</em> service
*/
public static ServiceReference getServiceReference(BundleContext bundleContext, String[] classes, String filter) {
// use #getServiceReference(BundleContext, String, String) method to
// speed the service lookup process by
// giving one class as a hint to the OSGi implementation
String clazz = (ObjectUtils.isEmpty(classes) ? null : classes[0]);
return getServiceReference(bundleContext, clazz, OsgiFilterUtils.unifyFilter(classes, filter));
}
/**
* Returns a reference to the <em>best matching</em> service for the given OSGi filter.
*
* @param bundleContext OSGi bundle context
* @param filter valid OSGi filter (can be <code>null</code>)
* @return reference to the <em>best matching</em> service
*/
public static ServiceReference getServiceReference(BundleContext bundleContext, String filter) {
return getServiceReference(bundleContext, (String) null, filter);
}
/**
* Returns references to <em>all</em> services matching the given class names.
*
* @param bundleContext OSGi bundle context
* @param classes array of fully qualified class names
* @return non-<code>null</code> array of references to matching services
*/
public static ServiceReference[] getServiceReferences(BundleContext bundleContext, String[] classes) {
return getServiceReferences(bundleContext, classes, null);
}
/**
* Returns references to <em>all</em> services matching the given class name and OSGi filter.
*
* @param bundleContext OSGi bundle context
* @param clazz fully qualified class name (can be <code>null</code>)
* @param filter valid OSGi filter (can be <code>null</code>)
* @return non-<code>null</code> array of references to matching services
*/
public static ServiceReference[] getServiceReferences(BundleContext bundleContext, String clazz, String filter) {
Assert.notNull(bundleContext, "bundleContext should be not null");
try {
ServiceReference[] refs = bundleContext.getServiceReferences(clazz, filter);
return (refs == null ? new ServiceReference[0] : refs);
} catch (InvalidSyntaxException ise) {
throw (RuntimeException) new IllegalArgumentException("invalid filter: " + ise.getFilter()).initCause(ise);
}
}
/**
* Returns references to <em>all</em> services matching the given class names and OSGi filter.
*
* @param bundleContext OSGi bundle context
* @param classes array of fully qualified class names
* @param filter valid OSGi filter (can be <code>null</code>)
* @return non-<code>null</code> array of references to matching services
*/
public static ServiceReference[] getServiceReferences(BundleContext bundleContext, String[] classes, String filter) {
// use #getServiceReferences(BundleContext, String, String) method to
// speed the service lookup process by
// giving one class as a hint to the OSGi implementation
// additionally this allows type filtering
String clazz = (ObjectUtils.isEmpty(classes) ? null : classes[0]);
return getServiceReferences(bundleContext, clazz, OsgiFilterUtils.unifyFilter(classes, filter));
}
/**
* Returns references to <em>all</em> services matching the OSGi filter.
*
* @param bundleContext OSGi bundle context
* @param filter valid OSGi filter (can be <code>null</code>)
* @return non-<code>null</code> array of references to matching services
*/
public static ServiceReference[] getServiceReferences(BundleContext bundleContext, String filter) {
return getServiceReferences(bundleContext, (String) null, filter);
}
/**
* Returns the service id ({@link Constants#SERVICE_ID}) of the given service reference.
*
* @param reference OSGi service reference
* @return service id
*/
public static long getServiceId(ServiceReference reference) {
Assert.notNull(reference);
return ((Long) reference.getProperty(Constants.SERVICE_ID)).longValue();
}
/**
* Returns the service ranking ({@link Constants#SERVICE_RANKING}) of the given service reference.
*
* @param reference OSGi service reference
* @return service ranking
*/
public static int getServiceRanking(ServiceReference reference) {
Assert.notNull(reference);
Object ranking = reference.getProperty(Constants.SERVICE_RANKING);
// if the property is not supplied or of incorrect type, use a
// default
return ((ranking != null && ranking instanceof Integer) ? ((Integer) ranking).intValue() : 0);
}
/**
* Returns the advertised class names ({@link Constants#OBJECTCLASS}) of the given service reference.
*
* @param reference OSGi service reference
* @return service advertised class names
*/
public static String[] getServiceObjectClasses(ServiceReference reference) {
Assert.notNull(reference);
return (String[]) reference.getProperty(Constants.OBJECTCLASS);
}
/**
* Returns a {@link Map} containing the properties available for the given service reference. This method takes a
* snapshot of the properties; future changes to the service properties will not be reflected in the returned
* dictionary.
*
* <p/> Note that the returned type implements the {@link java.util.Map} interface also.
*
* @param reference OSGi service reference
* @return a <code>Dictionary</code> containing the service reference properties taken as a snapshot
*/
public static Dictionary getServicePropertiesSnapshot(ServiceReference reference) {
return new MapBasedDictionary(getServicePropertiesSnapshotAsMap(reference));
}
/**
* Returns a {@link Map} containing the properties available for the given service reference. This method takes a
* snapshot of the properties; future changes to the service properties will not be reflected in the returned
* dictionary.
*
* @param reference OSGi service reference
* @return a <code>Map</code> containing the service reference properties taken as a snapshot
*/
public static Map getServicePropertiesSnapshotAsMap(ServiceReference reference) {
Assert.notNull(reference);
String[] keys = reference.getPropertyKeys();
Map map = new LinkedHashMap(keys.length);
for (int i = 0; i < keys.length; i++) {
map.put(keys[i], reference.getProperty(keys[i]));
}
// mark it as read-only
map = Collections.unmodifiableMap(map);
return map;
}
/**
* Returns a {@link Dictionary} containing the properties available for the given service reference. The returned
* object will reflect any updates made to to the <code>ServiceReference</code> through the owning
* <code>ServiceRegistration</code>.
*
*
* <p/> Note that the returned type implements the {@link java.util.Map} interface also.
*
* @param reference OSGi service reference
* @return a <code>Dictionary</code> containing the latest service reference properties
*/
public static Dictionary getServiceProperties(ServiceReference reference) {
return new MapBasedDictionary(getServicePropertiesAsMap(reference));
}
/**
* Returns a {@link Map} containing the properties available for the given service reference. The returned object
* will reflect any updates made to to the <code>ServiceReference</code> through the owning
* <code>ServiceRegistration</code>. Consider using {@link #getServiceProperties(ServiceReference)} which returns an
* object that extends {@link Dictionary} as well as implements the {@link Map} interface.
*
* @param reference OSGi service reference
* @return a <code>Map</code> containing the latest service reference properties
* @see #getServiceProperties(ServiceReference)
*/
public static Map getServicePropertiesAsMap(ServiceReference reference) {
Assert.notNull(reference);
return new ServiceReferenceBasedMap(reference);
}
/**
* Checks if the given filter matches at least one OSGi service or not.
*
* @param bundleContext OSGi bundle context
* @param filter valid OSGi filter (can be <code>null</code>)
* @return true if the filter matches at least one OSGi service, false otherwise
*/
public static boolean isServicePresent(BundleContext bundleContext, String filter) {
return !ObjectUtils.isEmpty(getServiceReferences(bundleContext, filter));
}
}