/* * 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.Iterator; import org.ops4j.lang.NullArgumentException; import org.ops4j.pax.swissbox.lifecycle.AbstractLifecycle; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ReplaceableService<T> extends AbstractLifecycle { /** * Logger. */ private static final Logger LOG = LoggerFactory.getLogger( ReplaceableService.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 backing service related events. Constructor paramater. Can be null. */ private final ReplaceableServiceListener<T> m_serviceListener; /** * Collection of tracked services. */ private ServiceCollection<T> m_serviceCollection; /** * Current service. Null if there is no service available or replaceable service is not started. */ private T m_service; /** * Creates a new replaceable service without a listener. * * @param context bundle context * @param serviceClass class of the replaceable service */ public ReplaceableService( final BundleContext context, final Class<T> serviceClass ) { this( context, serviceClass, null ); } /** * Creates a new replaceable service. * * @param context bundle context * @param serviceClass class of the replaceable service * @param listener a listener */ public ReplaceableService( final BundleContext context, final Class<T> serviceClass, final ReplaceableServiceListener<T> listener ) { LOG.debug( "Creating replaceable service for [" + serviceClass + "]" ); NullArgumentException.validateNotNull( context, "Context" ); NullArgumentException.validateNotNull( serviceClass, "Service class" ); m_context = context; m_serviceClass = serviceClass; m_serviceListener = listener; } /** * Returne the current service. * * @return the current service. */ public synchronized T getService() { return m_service; } /** * Sets the new service and notifies the listener that the service was changed. * * @param newService the new service */ private synchronized void setService( final T newService ) { if( m_service != newService ) { LOG.debug( "Service changed [" + m_service + "] -> [" + newService + "]" ); final T oldService = m_service; m_service = newService; if( m_serviceListener != null ) { m_serviceListener.serviceChanged( oldService, m_service ); } } } /** * Resolves a new service by serching the services collection for first available service. */ private synchronized void resolveService() { T newService = null; final Iterator<T> it = m_serviceCollection.iterator(); while( newService == null && it.hasNext() ) { final T candidateService = it.next(); if( !candidateService.equals( getService() ) ) { newService = candidateService; } } setService( newService ); } /** * Creates a service collection and starts it. * * @see AbstractLifecycle#onStart */ @Override protected void onStart() { m_serviceCollection = new ServiceCollection<T>( m_context, m_serviceClass, new CollectionListener() ); m_serviceCollection.start(); } /** * Stops the service collection and releases resources. * * @see AbstractLifecycle#onStop */ @Override protected void onStop() { if( m_serviceCollection != null ) { m_serviceCollection.stop(); m_serviceCollection = null; } setService( null ); } /** * Service collection listener that will allow services to get registered and select always the first available. * If a service gets unregister will get next available from the collection. */ private class CollectionListener extends DefaultServiceCollectionListener<T> { /** * Uses the new service if there is no current service * * @see ServiceCollectionListener#serviceAdded(org.osgi.framework.ServiceReference,Object) */ @Override public boolean serviceAdded( final ServiceReference serviceReference, final T service ) { if( getService() == null ) { setService( service ); } return true; } /** * If the service that is removed is the current service searches for a new one. Otherwise does nothing. * * @see ServiceCollectionListener#serviceRemoved(org.osgi.framework.ServiceReference,Object) */ @Override public void serviceRemoved( final ServiceReference serviceReference, final T service ) { if( service != null && service.equals( getService() ) ) { resolveService(); } } } }