blob: f5ff9296b8a103e2e080c9ff0383e483979754c6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2003, 2005 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.osgi.framework.internal.core;
import java.security.AccessController;
import java.security.PrivilegedAction;
import org.eclipse.osgi.framework.debug.Debug;
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 the calls to ServiceFactory
* and the bundle's use count.
*/
public class ServiceUse {
/** ServiceFactory object if the service instance represents a factory,
null otherwise */
protected ServiceFactory factory;
/** Service object either registered or that returned by
ServiceFactory.getService() */
protected Object service;
/** BundleContext associated with this service use */
protected BundleContextImpl context;
/** ServiceDescription of the registered service */
protected ServiceRegistrationImpl registration;
/** bundle's use count for this service */
protected int useCount;
/** Internal framework object. */
/**
* Constructs a service use encapsulating the service object.
* Objects of this class should be constrcuted while holding the
* registrations lock.
*
* @param context bundle getting the service
* @param registration ServiceRegistration of the service
*/
protected ServiceUse(BundleContextImpl context, ServiceRegistrationImpl registration) {
this.context = context;
this.registration = registration;
this.useCount = 0;
Object service = registration.service;
if (service instanceof ServiceFactory) {
factory = (ServiceFactory) service;
this.service = null;
} else {
this.factory = null;
this.service = service;
}
}
/**
* Get a service's service object.
* Retrieves the service object for a service.
* A bundle's use of a service is tracked by a
* use count. Each time a service's service object is returned by
* {@link #getService}, the context bundle's use count for the service
* is incremented by one. Each time the service is release by
* {@link #ungetService}, the context bundle's use count
* for the service is decremented by one.
* When a bundle's use count for a service
* drops to zero, the bundle should no longer use the service.
*
* <p>The following steps are followed to get the service object:
* <ol>
* <li>The context bundle's use count for this service is incremented by one.
* <li>If the context bundle's use count for the service is now one and
* the service was registered with a {@link ServiceFactory},
* the {@link ServiceFactory#getService ServiceFactory.getService} method
* is called to create a service object for the context bundle.
* This service object is cached by the framework.
* While the context bundle's use count for the service is greater than zero,
* subsequent calls to get the services's service object for the context bundle
* 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 for the service is returned.
* </ol>
*
* @return A service object for the service associated with this
* reference.
*/
protected Object getService() {
if ((useCount == 0) && (factory != null)) {
AbstractBundle factorybundle = registration.context.bundle;
Object service;
try {
service = AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
return factory.getService(context.bundle, registration);
}
});
} catch (Throwable t) {
if (Debug.DEBUG && 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.framework.adaptor.handleRuntimeError(t);
BundleException be = new BundleException(NLS.bind(Msg.SERVICE_FACTORY_EXCEPTION, factory.getClass().getName(), "getService"), t); //$NON-NLS-1$
context.framework.publishFrameworkEvent(FrameworkEvent.ERROR, factorybundle, be);
return (null);
}
if (service == null) {
if (Debug.DEBUG && Debug.DEBUG_SERVICES) {
Debug.println(factory + ".getService() returned null."); //$NON-NLS-1$
}
BundleException be = new BundleException(NLS.bind(Msg.SERVICE_OBJECT_NULL_EXCEPTION, factory.getClass().getName()));
context.framework.publishFrameworkEvent(FrameworkEvent.ERROR, factorybundle, be);
return (null);
}
String[] clazzes = registration.clazzes;
String invalidService = BundleContextImpl.checkServiceClass(clazzes, service);
if (invalidService != null) {
if (Debug.DEBUG && Debug.DEBUG_SERVICES) {
Debug.println("Service object is not an instanceof " + invalidService); //$NON-NLS-1$
}
throw new IllegalArgumentException(NLS.bind(Msg.SERVICE_NOT_INSTANCEOF_CLASS_EXCEPTION, invalidService));
}
this.service = service;
}
useCount++;
return (this.service);
}
/**
* Unget a service's service object.
* Releases the service object for a service.
* If the context bundle's use count for the service is zero, this method
* returns <code>false</code>. Otherwise, the context bundle's use count for the
* service is decremented by one.
*
* <p>The service's service object
* should no longer be used and all references to it should be destroyed
* when a bundle's use count for the service
* drops to zero.
*
* <p>The following steps are followed to unget the service object:
* <ol>
* <li>If the context bundle's use count for the service is zero or
* the service has been unregistered,
* <code>false</code> is returned.
* <li>The context bundle's use count for this service is decremented by one.
* <li>If the context bundle's use count for the service is now zero and
* the service was registered with a {@link ServiceFactory},
* the {@link ServiceFactory#ungetService ServiceFactory.ungetService} method
* is called to release the service object for the context bundle.
* <li><code>true</code> is returned.
* </ol>
*
* @return <code>true</code> if the context bundle's use count for the service
* is zero otherwise <code>false</code>.
*/
protected boolean ungetService() {
if (useCount == 0) {
return (true);
}
useCount--;
if (useCount == 0) {
if (factory != null) {
try {
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
factory.ungetService(context.bundle, registration, service);
return null;
}
});
} catch (Throwable t) {
if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
Debug.println(factory + ".ungetService() exception"); //$NON-NLS-1$
Debug.printStackTrace(t);
}
AbstractBundle factorybundle = registration.context.bundle;
BundleException be = new BundleException(NLS.bind(Msg.SERVICE_FACTORY_EXCEPTION, factory.getClass().getName(), "ungetService"), t); //$NON-NLS-1$
context.framework.publishFrameworkEvent(FrameworkEvent.ERROR, factorybundle, be);
}
service = null;
}
return (true);
}
return (false);
}
/**
* Release a service's service object.
* <ol>
* <li>The bundle's use count for this service is set to zero.
* <li>If the service was registered with a {@link ServiceFactory},
* the {@link ServiceFactory#ungetService ServiceFactory.ungetService} method
* is called to release the service object for the bundle.
* </ol>
*/
protected void releaseService() {
if ((useCount > 0) && (factory != null)) {
try {
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
factory.ungetService(context.bundle, registration, service);
return null;
}
});
} catch (Throwable t) {
if (Debug.DEBUG && Debug.DEBUG_SERVICES) {
Debug.println(factory + ".ungetService() exception"); //$NON-NLS-1$
Debug.printStackTrace(t);
}
AbstractBundle factorybundle = registration.context.bundle;
BundleException be = new BundleException(NLS.bind(Msg.SERVICE_FACTORY_EXCEPTION, factory.getClass().getName(), "ungetService"), t); //$NON-NLS-1$
context.framework.publishFrameworkEvent(FrameworkEvent.ERROR, factorybundle, be);
}
service = null;
}
useCount = 0;
}
}