/****************************************************************************** * 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 org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.osgi.framework.BundleContext; import org.osgi.framework.Filter; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceEvent; import org.osgi.framework.ServiceListener; import org.osgi.framework.ServiceReference; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; /** * Utility class for dealing with various OSGi listeners, mainly * {@link ServiceListener}s. This class contains common functionality such as * broadcasting events or safely registering an OSGi listener. * * @author Costin Leau */ public abstract class OsgiListenerUtils { private static final Log log = LogFactory.getLog(OsgiListenerUtils.class); /** * Adds a service listener to the given bundle context under the specified * filter. This method will deliver <em>synthetic</em> events of type * <code>REGISTERED</code> for <em>all</em> existing services (that * match the given filter) as if the services were registered after the * listener registration. * * <p/> This might cause problems if a service is registered between the * listener registration and the retrieval of existing services since the * listener will receive two events for the same service. For most listeners * implementations however, this should not be a problem * * @param context bundle context to register the listener with * @param listener service listener to be registered * @param filter OSGi filter (given as a Filter) for registering the * listener (can be <code>null</code>) * @see #addServiceListener(BundleContext, ServiceListener, String) */ public static void addServiceListener(BundleContext context, ServiceListener listener, Filter filter) { String toStringFilter = (filter == null ? null : filter.toString()); addServiceListener(context, listener, toStringFilter); } /** * Adds a service listener to the given bundle context under the specified * filter given as a String. The method will also retrieve <em>all</em> * the services registered before the listener registration (that match the * given filter) and will inform the listener through service events of type * <code>REGISTERED</code>. * * <p/> This might cause problems if a service is registered between the * listener registration and the retrieval of existing services since the * listener will receive two events for the same service. For most listeners * implementations however, this should not be a problem * * * @param context bundle context to register the listener with * @param listener service listener to be registered * @param filter OSGi filter (given as a String) for registering the * listener (can be <code>null</code>) * @see BundleContext#getServiceReference(String) * @see BundleContext#getServiceReferences(String, String) */ public static void addServiceListener(BundleContext context, ServiceListener listener, String filter) { registerListener(context, listener, filter); // now get the already registered services and call the listener // (the listener should be able to handle duplicates) dispatchServiceRegistrationEvents(OsgiServiceReferenceUtils.getServiceReferences(context, filter), listener); } private static void registerListener(BundleContext context, ServiceListener listener, String filter) { Assert.notNull(context); Assert.notNull(listener); try { // add listener context.addServiceListener(listener, filter); } catch (InvalidSyntaxException isex) { throw (RuntimeException) new IllegalArgumentException("Invalid filter").initCause(isex); } } private static void dispatchServiceRegistrationEvents(ServiceReference[] alreadyRegistered, ServiceListener listener) { if (log.isTraceEnabled()) log.trace("Calling listener for already registered services: " + ObjectUtils.nullSafeToString(alreadyRegistered)); if (alreadyRegistered != null) { for (int i = 0; i < alreadyRegistered.length; i++) { listener.serviceChanged(new ServiceEvent(ServiceEvent.REGISTERED, alreadyRegistered[i])); } } } /** * Adds a service listener to the given bundle context, under the specified * filter. This method will deliver at most one <em>synthetic</em> event * of type <code>REGISTERED</code> for the <em>best matching</em> * existing service as if the services were registered after the listener * registration. * * * <p/> This might cause problems if a service is registered between the * listener registration and the retrieval of existing services since the * listener will receive two events for the same service. For most listeners * implementations however, this should not be a problem * * * @param context bundle context to register the listener with * @param listener service listener to be registered * @param filter OSGi filter (given as a Filter) for registering the * listener (can be <code>null</code>) * @see #addSingleServiceListener(BundleContext, ServiceListener, String) */ public static void addSingleServiceListener(BundleContext context, ServiceListener listener, Filter filter) { String toStringFilter = (filter == null ? null : filter.toString()); addSingleServiceListener(context, listener, toStringFilter); } /** * Adds a service listener to the given application context, under the * specified filter given as a String. The method will also retrieve the * <em>best matching</em> service registered before the listener * registration and will inform the listener through a service event of type * <code>REGISTERED</code>. This is the only difference from * {@link #addServiceListener(BundleContext, ServiceListener, Filter)} which * considers all services, not just the best match. * * <p/> This might cause problems if a service is registered between the * listener registration and the retrieval of existing services since the * listener will receive two events for the same service. For most listeners * implementations however, this should not be a problem * * * @param context bundle context to register the listener with * @param listener service listener to be registered * @param filter OSGi filter (given as a String) for registering the * listener (can be <code>null</code>) * @see BundleContext#getServiceReference(String) * @see BundleContext#getServiceReferences(String, String) */ public static void addSingleServiceListener(BundleContext context, ServiceListener listener, String filter) { registerListener(context, listener, filter); // now get the already registered services and call the listener // (the listener should be able to handle duplicates) ServiceReference ref = OsgiServiceReferenceUtils.getServiceReference(context, filter); ServiceReference[] refs = (ref == null ? null : new ServiceReference[] { ref }); dispatchServiceRegistrationEvents(refs, listener); } /** * Removes a service listener from the given bundle context. This method * simply takes care of any exceptions that might be thrown (in case the * context is invalid). * * @param context bundle context to unregister the listener from * @param listener service listener to unregister * @return true if the listener unregistration has succeeded, false * otherwise (for example if the bundle context is invalid) */ public static boolean removeServiceListener(BundleContext context, ServiceListener listener) { if (context == null || listener == null) return false; try { context.removeServiceListener(listener); return true; } catch (IllegalStateException e) { // Bundle context is no longer valid } return false; } }