Copyright (c) 2003, 2013 IBM Corporation and others. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ SPDX-License-Identifier: EPL-2.0 Contributors: IBM Corporation - initial API and implementation
/******************************************************************************* * Copyright (c) 2003, 2013 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/
package org.eclipse.osgi.internal.serviceregistry; import java.security.AccessController; import java.security.PrivilegedAction; import org.eclipse.osgi.internal.debug.Debug; import org.eclipse.osgi.internal.framework.BundleContextImpl; import org.eclipse.osgi.internal.messages.Msg; import org.eclipse.osgi.util.NLS; import org.osgi.framework.*;
This class represents the use of a service by a bundle. One is created for each service acquired by a bundle.

This class manages a service factory.

@ThreadSafe
/** * This class represents the use of a service by a bundle. One is created for each * service acquired by a bundle. * * <p> * This class manages a service factory. * * @ThreadSafe */
public class ServiceFactoryUse<S> extends ServiceUse<S> {
BundleContext associated with this service use
/** BundleContext associated with this service use */
final BundleContextImpl context;
ServiceFactory object
/** ServiceFactory object */
final ServiceFactory<S> factory; final Debug debug;
Service object returned by ServiceFactory.getService()
/** Service object returned by ServiceFactory.getService() */
/* @GuardedBy("this") */ private S cachedService;
true if we are calling the factory getService method. Used to detect recursion.
/** true if we are calling the factory getService method. Used to detect recursion. */
/* @GuardedBy("this") */ private boolean factoryInUse;
Constructs a service use encapsulating the service factory.
Params:
  • context – bundle getting the service
  • registration – ServiceRegistration of the service
/** * Constructs a service use encapsulating the service factory. * * @param context bundle getting the service * @param registration ServiceRegistration of the service */
ServiceFactoryUse(BundleContextImpl context, ServiceRegistrationImpl<S> registration) { super(context, registration); this.debug = context.getContainer().getConfiguration().getDebug(); this.context = context; this.factoryInUse = false; this.cachedService = null; @SuppressWarnings("unchecked") ServiceFactory<S> f = (ServiceFactory<S>) registration.getServiceObject(); this.factory = f; }
Get a service's service object and increment the use count.

The following steps are followed to get the service object:

  1. The use count is incremented by one.
  2. If the use count is now one, the ServiceFactory.getService(Bundle, ServiceRegistration) method is called to create a service object for the context bundle. This service object is cached. While the use count is greater than zero, subsequent calls to get the service object will return the cached service object.
    If the service object returned by the ServiceFactory is not an instanceof all the classes named when the service was registered or the ServiceFactory throws an exception, null is returned and a FrameworkEvent of type FrameworkEvent.ERROR is broadcast.
  3. The service object is returned.
Returns:The service object.
/** * Get a service's service object and increment the use count. * * <p>The following steps are followed to get the service object: * <ol> * <li>The use count is incremented by one. * <li>If the use count is now one, * the {@link ServiceFactory#getService(Bundle, ServiceRegistration)} method * is called to create a service object for the context bundle. * This service object is cached. * While the use count is greater than zero, * subsequent calls to get the service object * will return the cached service object. * <br>If the service object returned by the {@link ServiceFactory} * is not an <code>instanceof</code> * all the classes named when the service was registered or * the {@link ServiceFactory} throws an exception, * <code>null</code> is returned and a * {@link FrameworkEvent} of type {@link FrameworkEvent#ERROR} is broadcast. * <li>The service object is returned. * </ol> * * @return The service object. */
/* @GuardedBy("this") */ @Override S getService() { assert Thread.holdsLock(this); if (inUse()) { incrementUse(); return cachedService; } if (debug.DEBUG_SERVICES) { Debug.println("getService[factory=" + registration.getBundle() + "](" + context.getBundleImpl() + "," + registration + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ } // check for recursive call on this thread if (factoryInUse) { if (debug.DEBUG_SERVICES) { Debug.println(factory + ".getService() recursively called."); //$NON-NLS-1$ } ServiceException se = new ServiceException(NLS.bind(Msg.SERVICE_FACTORY_RECURSION, factory.getClass().getName(), "getService"), ServiceException.FACTORY_RECURSION); //$NON-NLS-1$ context.getContainer().getEventPublisher().publishFrameworkEvent(FrameworkEvent.WARNING, registration.getBundle(), se); return null; } factoryInUse = true; final S service; try { service = factoryGetService(); if (service == null) { return null; } } finally { factoryInUse = false; } this.cachedService = service; incrementUse(); return service; }
Unget a service's service object.

Decrements the use count if the service was being used.

The following steps are followed to unget the service object:

  1. If the use count is zero, return.
  2. The use count is decremented by one.
  3. If the use count is non zero, return.
  4. The ServiceFactory.ungetService(Bundle, ServiceRegistration, Object) method is called to release the service object for the context bundle.
Returns:true if the service was ungotten; otherwise false.
/** * Unget a service's service object. * * <p> * Decrements the use count if the service was being used. * * <p>The following steps are followed to unget the service object: * <ol> * <li>If the use count is zero, return. * <li>The use count is decremented by one. * <li>If the use count is non zero, return. * <li>The {@link ServiceFactory#ungetService(Bundle, ServiceRegistration, Object)} method * is called to release the service object for the context bundle. * </ol> * @return true if the service was ungotten; otherwise false. */
/* @GuardedBy("this") */ @Override boolean ungetService() { assert Thread.holdsLock(this); if (!inUse()) { return false; } decrementUse(); if (inUse()) { return true; } final S service = cachedService; cachedService = null; if (debug.DEBUG_SERVICES) { Debug.println("ungetService[factory=" + registration.getBundle() + "](" + context.getBundleImpl() + "," + registration + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ } factoryUngetService(service); return true; }
Release all uses of the service and reset the use count to zero.
  1. The bundle's use count for this service is set to zero.
  2. The ServiceFactory.ungetService(Bundle, ServiceRegistration, Object) method is called to release the service object for the bundle.
/** * Release all uses of the service and reset the use count to zero. * * <ol> * <li>The bundle's use count for this service is set to zero. * <li>The {@link ServiceFactory#ungetService(Bundle, ServiceRegistration, Object)} method * is called to release the service object for the bundle. * </ol> */
/* @GuardedBy("this") */ @Override void release() { super.release(); final S service = cachedService; if (service == null) { return; } cachedService = null; if (debug.DEBUG_SERVICES) { Debug.println("releaseService[factory=" + registration.getBundle() + "](" + context.getBundleImpl() + "," + registration + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ } factoryUngetService(service); }
Return the service object for this service use.
Returns:The service object.
/** * Return the service object for this service use. * * @return The service object. */
/* @GuardedBy("this") */ @Override S getCachedService() { return cachedService; }
Call the service factory to get the service.
Returns:The service returned by the factory or null if there was an error.
/** * Call the service factory to get the service. * * @return The service returned by the factory or null if there was an error. */
/* @GuardedBy("this") */ S factoryGetService() { final S service; try { service = AccessController.doPrivileged(new PrivilegedAction<S>() { @Override public S run() { return factory.getService(context.getBundleImpl(), registration); } }); } catch (Throwable t) { if (debug.DEBUG_SERVICES) { Debug.println(factory + ".getService() exception: " + t.getMessage()); //$NON-NLS-1$ Debug.printStackTrace(t); } // allow the adaptor to handle this unexpected error context.getContainer().handleRuntimeError(t); ServiceException se = new ServiceException(NLS.bind(Msg.SERVICE_FACTORY_EXCEPTION, factory.getClass().getName(), "getService"), ServiceException.FACTORY_EXCEPTION, t); //$NON-NLS-1$ context.getContainer().getEventPublisher().publishFrameworkEvent(FrameworkEvent.ERROR, registration.getBundle(), se); return null; } if (service == null) { if (debug.DEBUG_SERVICES) { Debug.println(factory + ".getService() returned null."); //$NON-NLS-1$ } ServiceException se = new ServiceException(NLS.bind(Msg.SERVICE_OBJECT_NULL_EXCEPTION, factory.getClass().getName()), ServiceException.FACTORY_ERROR); context.getContainer().getEventPublisher().publishFrameworkEvent(FrameworkEvent.WARNING, registration.getBundle(), se); return null; } String[] clazzes = registration.getClasses(); String invalidService = ServiceRegistry.checkServiceClass(clazzes, service); if (invalidService != null) { if (debug.DEBUG_SERVICES) { Debug.println("Service object is not an instanceof " + invalidService); //$NON-NLS-1$ } ServiceException se = new ServiceException(NLS.bind(Msg.SERVICE_FACTORY_NOT_INSTANCEOF_CLASS_EXCEPTION, factory.getClass().getName(), invalidService), ServiceException.FACTORY_ERROR); context.getContainer().getEventPublisher().publishFrameworkEvent(FrameworkEvent.ERROR, registration.getBundle(), se); return null; } return service; }
Call the service factory to unget the service. @param service The service object to pass to the factory.
/** * Call the service factory to unget the service. * * @param service The service object to pass to the factory. */
/* @GuardedBy("this") */ void factoryUngetService(final S service) { try { AccessController.doPrivileged(new PrivilegedAction<Void>() { @Override public Void run() { factory.ungetService(context.getBundleImpl(), registration, service); return null; } }); } catch (Throwable t) { if (debug.DEBUG_SERVICES) { Debug.println(factory + ".ungetService() exception"); //$NON-NLS-1$ Debug.printStackTrace(t); } ServiceException se = new ServiceException(NLS.bind(Msg.SERVICE_FACTORY_EXCEPTION, factory.getClass().getName(), "ungetService"), ServiceException.FACTORY_EXCEPTION, t); //$NON-NLS-1$ context.getContainer().getEventPublisher().publishFrameworkEvent(FrameworkEvent.ERROR, registration.getBundle(), se); } } }