blob: 32b77d43e6c1306ddcd6f4a23f0b60fb352b2132 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2007 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.equinox.device;
import java.util.*;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.*;
import org.osgi.service.device.Device;
import org.osgi.service.log.LogService;
import org.osgi.util.tracker.ServiceTracker;
/**
* DeviceTracker class. This class has the logic for refining a
* Device service.
*
*/
public class DeviceTracker extends ServiceTracker {
/** OSGi Device class name */
protected final static String clazz = "org.osgi.service.device.Device"; //$NON-NLS-1$
/** DeviceManager object. */
protected Activator manager;
/** reference to Device service we are attempting to refine */
protected ServiceReference device;
/** LogService object */
protected LogService log;
/** Device services properties */
protected Dictionary properties;
/** if false the algorithm must terminate */
protected volatile boolean running;
/**
* Create a DeviceTracker from a ServiceReference.
*
* @param manager DeviceManager object
* @param device ServiceReference to the Device service.
* @param id ID of DeviceTracker object
*/
public DeviceTracker(Activator manager, ServiceReference device) {
super(manager.context, device, null);
this.manager = manager;
log = manager.log;
if (Activator.DEBUG) {
log.log(device, LogService.LOG_DEBUG, this + " constructor"); //$NON-NLS-1$
}
open();
}
/**
* Close the Device.
*/
public void close() {
if (device != null) {
if (Activator.DEBUG) {
log.log(device, LogService.LOG_DEBUG, this + " closing"); //$NON-NLS-1$
}
running = false; /* request thread to stop */
super.close();
device = null;
}
}
/**
* A service is being added to the ServiceTracker.
*
* <p>This method is called before a service which matched
* the search parameters of the ServiceTracker is
* added to the ServiceTracker. This method should return the
* service object to be tracked for this ServiceReference.
* The returned service object is stored in the ServiceTracker
* and is available from the getService and getServices
* methods.
*
* @param reference Reference to service being added to the ServiceTracker.
* @return The service object to be tracked for the
* ServiceReference or <tt>null</tt> if the ServiceReference should not
* be tracked.
*/
public Object addingService(ServiceReference reference) {
if (Activator.DEBUG) {
log.log(reference, LogService.LOG_DEBUG, this + " adding Device service"); //$NON-NLS-1$
}
device = reference;
running = true;
properties = new Properties(reference);
return (reference);
}
/**
* A service tracked by the ServiceTracker has been modified.
*
* <p>This method is called when a service being tracked
* by the ServiceTracker has had it properties modified.
*
* @param reference Reference to service that has been modified.
* @param service The service object for the modified service.
*/
public void modifiedService(ServiceReference reference, Object service) {
properties = new Properties(reference);
}
/**
* A service tracked by the ServiceTracker is being removed.
*
* <p>This method is called after a service is no longer being tracked
* by the ServiceTracker.
*
* @param reference Reference to service that has been removed.
* @param service The service object for the removed service.
*/
public void removedService(ServiceReference reference, Object service) {
if (running) {
log.log(reference, LogService.LOG_WARNING, DeviceMsg.Device_service_unregistered);
running = false; /* request algorithm to stop */
} else {
if (Activator.DEBUG) {
log.log(reference, LogService.LOG_DEBUG, this + " removing Device service"); //$NON-NLS-1$
}
}
super.removedService(reference, service);
}
/**
* Attempt to refine this Device service.
*
*/
public void refine() {
if (Activator.DEBUG) {
log.log(device, LogService.LOG_DEBUG, this + " refining " + device); //$NON-NLS-1$
}
if (running && isIdle()) {
/* List of excluded drivers from this algorithm run */
DriverTracker drivers = manager.drivers;
manager.locators.loadDrivers(properties, drivers);
Vector exclude = new Vector(drivers.size());
while (running) {
ServiceReference driver = drivers.match(device, exclude);
if (driver == null) {
noDriverFound();
break;
}
if (drivers.attach(driver, device, exclude)) {
break;
}
}
}
close();
}
/**
* Determine if the device service tracked by this object is idle.
*
* OSGi SP R2 Section 8.2.2 defines in idle device service as:
* "A Device service is not used by any other bundle according to the Framework;
* it is called an idle Device service."
*
* This method defines it as:
* A Device service is not used by any DRIVER bundle according to the Framework;
* it is called an idle Device service.
*
* Thus if a non-driver bundle uses a device service, it is still considered
* idle by this method.
*
* @return true if the device service is idle.
*/
public boolean isIdle() {
if (Activator.DEBUG) {
log.log(device, LogService.LOG_DEBUG, "Check device service idle: " + device); //$NON-NLS-1$
}
Filter filter_ = manager.driverFilter;
Bundle[] users = device.getUsingBundles();
int userCount = (users == null) ? 0 : users.length;
for (int i = 0; i < userCount; i++) {
ServiceReference[] services = users[i].getRegisteredServices();
int servicesCount = (services == null) ? 0 : services.length;
for (int j = 0; j < servicesCount; j++) {
if (filter_.match(services[j])) {
if (Activator.DEBUG) {
log.log(LogService.LOG_DEBUG, "Device " + device + " already in use by bundle " + users[i]); //$NON-NLS-1$ //$NON-NLS-2$
}
return (false);
}
}
}
return (true);
}
/**
* Called by the device manager after it has failed to attach
* any driver to the device.
* <p>
* If the device can be configured in alternate ways, the driver
* may respond by unregistering the device service and registering
* a different device service instead.</p>
*/
public void noDriverFound() {
BundleContext contxt = manager.context;
Object service = contxt.getService(device);
try {
//It is possible that this is a Free Format Device that does not
//implement Device
if (service instanceof Device) {
log.log(device, LogService.LOG_INFO, DeviceMsg.Device_noDriverFound_called);
try {
((Device) service).noDriverFound();
} catch (Throwable t) {
log.log(device, LogService.LOG_ERROR, NLS.bind(DeviceMsg.Device_noDriverFound_error, t));
}
}
} finally {
contxt.ungetService(device);
}
}
public String toString() {
return "DeviceTracker"; //$NON-NLS-1$
}
/**
* Readonly Dictionary for device properties.
*
*/
static class Properties extends Hashtable {
private static final long serialVersionUID = -8489170394007899809L;
/**
* keys in original case.
*/
protected Vector keys;
/**
* Create a properties object for the service.
*
* @param device The service to get the properties of.
*/
protected Properties(ServiceReference device) {
super();
String[] props = device.getPropertyKeys();
if (props != null) {
int size = props.length;
keys = new Vector(size);
for (int i = 0; i < size; i++) {
String key = props[i];
Object value = device.getProperty(key);
if (value != null) {
keys.addElement(key);
super.put(key.toLowerCase(), value);
}
}
} else {
keys = new Vector(0);
}
}
/**
* Override keys to support case-preserving of keys.
*/
public Enumeration keys() {
return (keys.elements());
}
/**
* Override get to support case-insensitivity.
*
* @param key header name.
*/
public Object get(Object key) {
if (key instanceof String) {
return (super.get(((String) key).toLowerCase()));
}
return (null);
}
/**
* Override put to disable it. This Dictionary is readonly once built.
*
* @param key header name.
* @param value header value.
* @throws UnsupportedOperationException
*/
public Object put(Object key, Object value) {
throw new UnsupportedOperationException();
}
/**
* Override remove to disable it. This Dictionary is readonly once built.
*
* @param key header name.
* @throws UnsupportedOperationException
*/
public Object remove(Object key) {
throw new UnsupportedOperationException();
}
}
}