blob: 9cc1736c4cf918acf793ad4e528b4f2ddd94e3b3 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2009 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.equinox.device;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.*;
import org.osgi.service.log.LogService;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
/**
* DeviceManager bundle. This bundle implements the OSGi Device Access 1.1
* specification.
*
* This implementation does not include the optimizations in section
* 8.7.4 of the OSGi SP R2 spec.
*
*/
public class Activator implements BundleActivator, ServiceTrackerCustomizer, FrameworkListener, Runnable {
protected final static boolean DEBUG = false;
/** DeviceManager BundleContext */
protected BundleContext context;
/** LogTracker object */
protected LogTracker log;
/** if false the thread must terminate */
protected volatile boolean running;
/** DeviceManager thread */
protected Thread thread;
/** DriverTracker for Driver services. */
protected DriverTracker drivers;
/** Tracker for DriverLocator services */
protected DriverLocatorTracker locators;
/** Tracker for DriverSelector services */
protected DriverSelectorTracker selectors;
/** ServiceTracker object for device services */
protected ServiceTracker devices;
/** filter for Device services */
protected Filter deviceFilter;
/** filter for Driver services */
protected Filter driverFilter;
/**
* Linked List item
*/
static class DeviceService {
/** object for this item */
final DeviceTracker device;
/** next item in event queue */
DeviceService next;
/**
* Constructor for work queue item
*
* @param o Object for this event
*/
DeviceService(DeviceTracker device) {
this.device = device;
next = null;
}
}
/** item at the head of the event queue */
private DeviceService head;
/** item at the tail of the event queue */
private DeviceService tail;
/** number of milliseconds to wait before refining idle Device services */
protected long updatewait;
/** set to true by DriverTracker when a Driver Service is registered */
protected volatile boolean driverServiceRegistered;
/**
* Create a DeviceManager object.
*
*/
public Activator() {
super();
}
/**
* Start the Device Manager.
*
* @param contxt The device manager's bundle context
*/
public void start(BundleContext contxt) throws Exception {
this.context = contxt;
running = false;
log = new LogTracker(context, System.err);
try {
deviceFilter = context.createFilter("(|(" + org.osgi.framework.Constants.OBJECTCLASS + "=" + DeviceTracker.clazz + ////-1$ ////-2$ //$NON-NLS-1$ //$NON-NLS-2$
")(" + org.osgi.service.device.Constants.DEVICE_CATEGORY + "=*))"); //$NON-NLS-1$ //$NON-NLS-2$
driverFilter = context.createFilter("(" + org.osgi.framework.Constants.OBJECTCLASS + "=" + DriverTracker.clazz + ")"); ////-1$ ////-2$ ////-3$ //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
} catch (InvalidSyntaxException e) {
log.log(LogService.LOG_ERROR, NLS.bind(DeviceMsg.Unable_to_create_Filter_for_DeviceManager, e)); ////-1$
throw e;
}
updatewait = 5 * 1000L;
String prop = context.getProperty("org.eclipse.equinox.device.updatewait"); //$NON-NLS-1$
if (prop != null) {
try {
updatewait = Long.parseLong(prop) * 1000L;
} catch (NumberFormatException e) {
//do nothing
}
}
Bundle systemBundle = context.getBundle(0);
if ((systemBundle != null) && ((systemBundle.getState() & Bundle.STARTING) != 0)) { /* if the system bundle is starting */
context.addFrameworkListener(this);
} else {
startDeviceManager();
}
log.log(LogService.LOG_INFO, DeviceMsg.DeviceManager_started);
}
/**
* Receive notification of a general framework event.
*
* @param event The FrameworkEvent.
*/
public void frameworkEvent(FrameworkEvent event) {
switch (event.getType()) {
case FrameworkEvent.STARTED : {
context.removeFrameworkListener(this);
try {
startDeviceManager();
} catch (Throwable t) {
log.log(LogService.LOG_ERROR, NLS.bind(DeviceMsg.DeviceManager_has_thrown_an_error, t)); ////-1$
}
break;
}
}
}
/**
* Start the DeviceManager thread.
*
*/
public void startDeviceManager() {
if (!running) {
head = null;
tail = null;
locators = new DriverLocatorTracker(this);
selectors = new DriverSelectorTracker(this);
drivers = new DriverTracker(this);
devices = new ServiceTracker(context, deviceFilter, this);
devices.open();
running = true;
driverServiceRegistered = false;
thread = (new SecureAction()).createThread(this, "DeviceManager"); //$NON-NLS-1$
thread.start(); /* Start DeviceManager thread */
}
}
/**
* Stop the Device Manager bundle.
*
* @param contxt The device manager's bundle context
*/
public void stop(BundleContext contxt) throws Exception {
context.removeFrameworkListener(this);
if (running) {
Thread t = thread;
running = false; /* request thread to stop */
if (t != null) {
t.interrupt();
synchronized (t) {
while (t.isAlive()) /* wait for thread to complete */
{
try {
t.wait(0);
} catch (InterruptedException e) {
// do nothing
}
}
}
}
}
if (drivers != null) {
drivers.close();
drivers = null;
}
if (devices != null) {
devices.close();
devices = null;
}
if (locators != null) {
locators.close();
locators = null;
}
if (selectors != null) {
selectors.close();
selectors = null;
}
if (log != null) {
log.close();
log = null;
}
this.context = 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) {
this.log.log(reference, LogService.LOG_DEBUG, "DeviceManager device service registered"); //$NON-NLS-1$
}
enqueue(new DeviceTracker(this, 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) {
// do nothing
}
/**
* 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 object The service object for the removed service.
*/
public void removedService(ServiceReference reference, Object object) {
if (Activator.DEBUG) {
log.log(reference, LogService.LOG_DEBUG, "DeviceManager device service unregistered"); //$NON-NLS-1$
}
/* We do not implement optional driver reclamation.
* Thus we take no specific action upon Device service unregistration .
*/
}
public void refineIdleDevices() {
if (Activator.DEBUG) {
log.log(LogService.LOG_DEBUG, "DeviceManager refining idle device services"); //$NON-NLS-1$
}
ServiceReference[] references = devices.getServiceReferences();
if (references != null) {
int size = references.length;
for (int i = 0; i < size; i++) {
ServiceReference device = references[i];
enqueue(new DeviceTracker(this, device));
}
}
}
/**
* Main thread for DeviceManager.
*
* Attempt to refine all Device services that are not in use
* by a driver bundle.
*/
public void run() {
while (running) {
DeviceTracker device;
try {
device = dequeue();
} catch (InterruptedException e) {
continue;
}
try {
device.refine();
} catch (Throwable t) {
log.log(LogService.LOG_ERROR, NLS.bind(DeviceMsg.DeviceManager_has_thrown_an_error, t)); ////-1$
}
}
}
/**
* Queue the object to be processed on the work thread.
* The thread is notified.
*
* @param device Work item.
*/
public synchronized void enqueue(DeviceTracker device) {
if (device != null) {
if (Activator.DEBUG) {
log.log(LogService.LOG_DEBUG, "DeviceManager queuing DeviceTracker"); //$NON-NLS-1$
}
DeviceService item = new DeviceService(device);
if (head == null) /* if the queue was empty */
{
head = item;
tail = item;
} else /* else add to end of queue */
{
tail.next = item;
tail = item;
}
}
notify();
}
/**
* Dequeue an object from the work thread.
* If the queue is empty, this method blocks.
*
* @return Dequeue object from the work thread.
* @throws InterruptedException If the queue has been stopped.
*/
private synchronized DeviceTracker dequeue() throws InterruptedException {
while (running && (head == null)) {
// TODO need to determine if this code is needed (bug 261197)
/* This should be included per Section 8.7.7 of the OSGi SP R2
* spec, but it causes the OSGi SP R2 Test Suite to fail.
* We should turn this on for R3.
if (driverServiceRegistered)
*/
// if (false) {
// driverServiceRegistered = false;
//
// refineIdleDevices();
// } else {
locators.uninstallDriverBundles();
try {
if (Activator.DEBUG) {
log.log(LogService.LOG_DEBUG, "DeviceManager waiting on queue"); //$NON-NLS-1$
}
wait();
} catch (InterruptedException e) {
// do nothing
}
// }
}
if (!running) /* if we are stopping */
{
throw new InterruptedException(); /* throw an exception */
}
DeviceService item = head;
head = item.next;
if (head == null) {
tail = null;
}
return (item.device);
}
}