/* * Copyright 2007 Alin Dreghiciu. * * 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. */ package org.ops4j.pax.swissbox.tracker; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.ops4j.lang.NullArgumentException; import org.ops4j.pax.swissbox.lifecycle.AbstractLifecycle; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; import org.osgi.util.tracker.ServiceTracker; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A collection of tracked services. * * @author Alin Dreghiciu * @since October 14, 2007 */ public class ServiceCollection<T> extends AbstractLifecycle implements Iterable<T> { /** * Logger. */ private static final Logger LOG = LoggerFactory.getLogger( ServiceCollection.class ); /** * Bundle context. Constructor parameter. Cannot be null. */ private final BundleContext m_context; /** * Service class. Constructor parameter. Cannot be null. */ private final Class<T> m_serviceClass; /** * Listener for tracked services. Constructor parameter or if null a default one. Cannot be null. */ private final ServiceCollectionListener<T> m_collectionListener; /** * Service tracker used to track services. */ private Tracker m_serviceTracker; /** * Creates a new service tracker collection, using the specified service context and the default service collection * listener. * * @param context bundle context * @param serviceClass class of the services to be tracked */ public ServiceCollection( final BundleContext context, final Class<T> serviceClass ) { this( context, serviceClass, null ); } /** * Creates a new service tracker collection, using the specified service context and listener. * Listener can be null, case when the default service collection listener is used. * * @param context bundle context * @param serviceClass class of the services to be tracked * @param collectionListener service events listener */ public ServiceCollection( final BundleContext context, final Class<T> serviceClass, final ServiceCollectionListener<T> collectionListener ) { LOG.debug( "Creating service collection for [" + serviceClass + "]" ); NullArgumentException.validateNotNull( context, "Context" ); NullArgumentException.validateNotNull( serviceClass, "Service class" ); m_context = context; m_serviceClass = serviceClass; if( collectionListener == null ) { m_collectionListener = new DefaultServiceCollectionListener<T>(); } else { m_collectionListener = collectionListener; } } /** * Returns an iterator over the tracked services at the call point in time. * Note that a susequent call can produce a different list of services as the services are dynamic. * If there are no services available returns an empty iterator. * * @see Iterable#iterator() */ @SuppressWarnings( "unchecked" ) public Iterator<T> iterator() { final List<T> services = new ArrayList<T>(); if( m_serviceTracker != null ) { final Object[] trackedServices = m_serviceTracker.getServices(); if( trackedServices != null ) { for( Object trackedService : trackedServices ) { services.add( (T) trackedService ); } } } return Collections.unmodifiableCollection( services ).iterator(); } /** * Creates a service tracker and opens it. * * @see AbstractLifecycle#onStart */ @Override protected void onStart() { m_serviceTracker = new Tracker( m_context, m_serviceClass ); m_serviceTracker.open(); } /** * Closes the service tracker and releases resources. * * @see AbstractLifecycle#onStop */ @Override protected void onStop() { if( m_serviceTracker != null ) { m_serviceTracker.close(); m_serviceTracker = null; } } /** * Trackes services and handlers adding/removing. */ private class Tracker extends ServiceTracker { public Tracker( final BundleContext context, final Class<T> serviceClass ) { super( context, serviceClass.getName(), null ); } @Override @SuppressWarnings( "unchecked" ) public Object addingService( final ServiceReference serviceReference ) { LOG.debug( "Added service with reference [" + serviceReference + "]" ); T service = null; try { service = (T) super.addingService( serviceReference ); LOG.debug( "Related service [" + service + "]" ); if( service != null ) { if( !m_collectionListener.serviceAdded( serviceReference, service ) ) { super.removedService( serviceReference, service ); LOG.trace( "Service [" + service + "] dropped as requested by listener [" + m_collectionListener + "]" ); service = null; } } } catch( RuntimeException e ) { if( service != null ) { super.removedService( serviceReference, service ); LOG.debug( "Service [" + service + "] dropped due to exception [" + e.getClass() + ":" + e.getMessage() + "]" ); throw e; } } return service; } @Override @SuppressWarnings( "unchecked" ) public void removedService( final ServiceReference serviceReference, final Object service ) { LOG.debug( "Removed service [" + service + "]" ); // if one of the listenres is throwing an exception we will still remove it try { m_collectionListener.serviceRemoved( serviceReference, (T) service ); } catch( Throwable ignore ) { LOG.warn( "Ignored exception from collection listener", ignore ); } super.removedService( serviceReference, service ); } } }