Package org.knopflerfish.bundle.device

Source Code of org.knopflerfish.bundle.device.Activator

/*
* Copyright (c) 2003, KNOPFLERFISH project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
*   notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above
*   copyright notice, this list of conditions and the following
*   disclaimer in the documentation and/or other materials
*   provided with the distribution.
*
* - Neither the name of the KNOPFLERFISH project nor the names of its
*   contributors may be used to endorse or promote products derived
*   from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package org.knopflerfish.bundle.device;

import java.io.InputStream;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleListener;
import org.osgi.framework.Filter;
import org.osgi.framework.FrameworkEvent;
import org.osgi.framework.FrameworkListener;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
import org.osgi.service.device.Device;
import org.osgi.service.device.Driver;
import org.osgi.service.device.DriverLocator;
import org.osgi.service.device.DriverSelector;
import org.osgi.service.device.Match;
import org.osgi.service.log.LogService;

public class Activator extends Thread implements BundleActivator,
        ServiceListener, BundleListener, FrameworkListener {

    // NB: These constants should be configurable

    private static final long LONG_LIFE = 15 * 60 * 1000;

    private static final long SHORT_LIFE = 5 * 60 * 1000;

    private static final long REAP_INTERVAL = 1 * 60 * 1000;

    static final String DYNAMIC_DRIVER_TAG = "__DD_";

    private static final String LOG_FILTER = "(objectClass="
            + LogService.class.getName() + ")";

    private static final String DEVICE_FILTER = "(|(objectClass="
            + Device.class.getName() + ")(DEVICE_CATEGORY=*))";

    private static final String DRIVER_FILTER = "(objectClass="
            + Driver.class.getName() + ")";

    private static final String SELECTOR_FILTER = "(objectClass="
            + DriverSelector.class.getName() + ")";

    private static final String LOCATOR_FILTER = "(objectClass="
            + DriverLocator.class.getName() + ")";

    private static final String GLOBAL_FILTER = "(|" + LOG_FILTER
            + DEVICE_FILTER + DRIVER_FILTER + SELECTOR_FILTER + LOCATOR_FILTER
            + ")";

    BundleContext bc;

    private boolean active;

    private boolean quit;

    private Filter isLog;

    private Filter isDevice;

    private Filter isDriver;

    private Filter isSelector;

    private Filter isLocator;

    private Vector /* DriverRef */drivers = new Vector();

    private ServiceReference[] locatorRefs;

    DriverLocator[] locators;

    private ServiceReference selectorRef;

    private DriverSelector selector;

    private ServiceReference logRef;

    private LogService log;

    private Hashtable /* Integer->MatchValue */cache = new Hashtable();

    private Hashtable /* ServiceReference->ServiceReference */newDevices = new Hashtable(
            20);

    private Hashtable /* Bundle->Long */tempDrivers = new Hashtable(10);

    private long reapTime;

    public Activator() {
        super("DeviceManager");
    }

    public void start(BundleContext bc) throws Exception {
        this.bc = bc;

        isLog = bc.createFilter(LOG_FILTER);
        isDevice = bc.createFilter(DEVICE_FILTER);
        isDriver = bc.createFilter(DRIVER_FILTER);
        isSelector = bc.createFilter(SELECTOR_FILTER);
        isLocator = bc.createFilter(LOCATOR_FILTER);

        startService(LOG_FILTER);

        start();

        bc.addFrameworkListener(this);
        Bundle b = bc.getBundle(0);
        if (b.getState() == Bundle.ACTIVE)
            activate();
        else
            info("Passive start");
    }

    public void stop(BundleContext bc) {
        info("Stopping");
        quit = true;
        synchronized (this) {
            notifyAll();
        }
    }

    private synchronized void activate() throws Exception {

        if (active)
            return;
        active = true;
        info("Activating");

        bc.addBundleListener(this);

        // Thanks to TID for combining the (buggy)
        // separate listeners into one
        startService(GLOBAL_FILTER);
    }

    public void frameworkEvent(FrameworkEvent e) {
        try {
            if (e.getType() == FrameworkEvent.STARTED)
                activate();
        } catch (Exception e1) {
        }
    }

    private void startService(String filter) throws Exception {
        bc.addServiceListener(this, filter);
        ServiceReference[] sra = bc.getServiceReferences(null, filter);
        if (sra != null) {
            for (int i = 0; i < sra.length; i++) {
                try {
                    serviceChanged(new ServiceEvent(ServiceEvent.REGISTERED,
                            sra[i]));
                } catch (Exception e) {
                }
            }
        }
    }

    public void serviceChanged(ServiceEvent e) {
        ServiceReference sr = e.getServiceReference();
        if (isDevice.match(sr)) {
            switch (e.getType()) {
            case ServiceEvent.REGISTERED:
                info("device found, " + showDevice(sr));
                touchDevice(sr);
                break;
            case ServiceEvent.MODIFIED:
                // We should most likely not do anything here
                // removeSRCachedMatch(sr);
                // touchDevice(sr);
                break;
            case ServiceEvent.UNREGISTERING:
                info("device lost, " + showDevice(sr));
                removeSRCachedMatch(sr);
                break;
            }
        }
        if (isDriver.match(sr)) {
            try {
                switch (e.getType()) {
                case ServiceEvent.REGISTERED:
                    info("driver found, " + showDriver(sr));
                    driverAppeared(sr);
                    touchAllDevices();
                    break;
                case ServiceEvent.MODIFIED:
                    // We should probably not do anything here
                    // driverGone(sr);
                    // driverAppeared(sr);
                    break;
                case ServiceEvent.UNREGISTERING:
                    info("driver lost, " + showDriver(sr));
                    driverGone(sr);
                    touchAllDevices();
                    break;
                }
            } catch (Exception e1) {
            }
        }
        if (isSelector.match(sr)) {
            try {
                bc.ungetService(selectorRef);
            } catch (Exception e1) {
            }
            selector = null;
            try {
                selectorRef = bc.getServiceReference(DriverSelector.class
                        .getName());
                selector = (DriverSelector) bc.getService(selectorRef);
            } catch (Exception e1) {
            }
        }
        if (isLocator.match(sr)) {
            try {
                for (int i = 0; i < locatorRefs.length; i++) {
                    bc.ungetService(locatorRefs[i]);
                }
            } catch (Exception e1) {
            }
            locatorRefs = null;
            locators = null;
            try {
                locatorRefs = bc.getServiceReferences(DriverLocator.class
                        .getName(), null);
                locators = new DriverLocator[locatorRefs.length];
                for (int i = 0; i < locatorRefs.length; i++) {
                    locators[i] = (DriverLocator) bc.getService(locatorRefs[i]);
                }
            } catch (Exception e1) {
            }
        }
        if (isLog.match(sr)) {
            try {
                bc.ungetService(logRef);
            } catch (Exception e1) {
            }
            try {
                logRef = bc.getServiceReference(LogService.class.getName());
                log = (LogService) bc.getService(logRef);
            } catch (Exception e1) {
            }
        }
    }

    private void driverAppeared(ServiceReference sr) {
        try {
            for (int i = 0; i < drivers.size(); i++) {
                DriverRef dr = (DriverRef) drivers.elementAt(i);
                if (dr.sr == sr)
                    return;
            }
            DriverRef dr = new DriverRef();
            try {
                dr.ranking = ((Integer) sr
                        .getProperty(org.osgi.framework.Constants.SERVICE_RANKING))
                        .intValue();
            } catch (Exception e) {
            }
            dr.servid = ((Long) sr
                    .getProperty(org.osgi.framework.Constants.SERVICE_ID))
                    .longValue();
            dr.id = (String) sr
                    .getProperty(org.osgi.service.device.Constants.DRIVER_ID);
            dr.sr = sr;
            if (dr.id != null)
                dr.drv = (Driver) bc.getService(sr);
            else
                error("ignoring driver without id " + showDriver(sr));
            if (dr.drv != null)
                drivers.addElement(dr);
        } catch (Exception e) {
        }
    }

    private void driverGone(ServiceReference sr) {
        try {
            for (int i = 0; i < drivers.size(); i++) {
                DriverRef dr = (DriverRef) drivers.elementAt(i);
                if (dr.sr == sr) {
                    drivers.removeElementAt(i);
                    return;
                }
            }
        } catch (Exception e) {
        }
    }

    public void bundleChanged(BundleEvent e) {
        if (e.getType() == BundleEvent.UNINSTALLED) {
            tempDrivers.remove(e.getBundle());
        }
    }

    private void info(String msg) {
        try {
            log.log(LogService.LOG_INFO, msg);
        } catch (Exception e) {
            System.err.println("[Device Manager] info: " + msg);
        }
    }

    private void error(String msg) {
        try {
            log.log(LogService.LOG_ERROR, msg);
        } catch (Exception e) {
            System.err.println("[Device Manager] ERROR: " + msg);
        }
    }

    public void run() {
        while (!quit) {
            boolean sleep = true;
            try {
                ServiceReference dev = (ServiceReference) newDevices.keys()
                        .nextElement();
                newDevices.remove(dev);
                sleep = false;
                handleDevice(dev);
            } catch (Exception e) {
            }

            if (!sleep)
                continue;

            long now = System.currentTimeMillis();
            if (now >= reapTime) {
                reapDrivers();
                reapTime = now + REAP_INTERVAL;

                // NB: Should also clean out old matches every now
                // and then based on some kind of LRU scheme
            }
            synchronized (this) {
                if (!quit)
                    try {
                        wait(reapTime - now);
                    } catch (Exception e) {
                    }
            }
        }
    }

    private void handleDevice(ServiceReference dev) {
        if (isUsed(dev))
            return;

        Dictionary props = collectProperties(dev);

        Vector /* MatchImpl */matches = new Vector();

        // Populate matches with driver locator recommendations
        DriverLocator[] dla = locators;
        if (dla != null) {
            for (int i = 0; i < dla.length; i++) {
                try {
                    DriverLocator dl = dla[i];
                    String[] dria = dl.findDrivers(props);
                    for (int j = 0; j < dria.length; j++) {
                        String dri = dria[j];
                        MatchImpl m = null;
                        for (int k = 0; k < matches.size(); k++) {
                            m = (MatchImpl) matches.elementAt(k);
                            if (m.equals(dri))
                                break;
                            m = null;
                        }
                        if (m == null) {
                            m = new MatchImpl(this, dev, dri);
                            matches.addElement(m);
                        }
                        m.addDriverLocator(dl);
                    }
                } catch (Exception e) {
                }
            }
        }

        for (;;) {
            // Add current drivers to matches
            for (int i = 0; i < drivers.size(); i++) {
                DriverRef dr = (DriverRef) drivers.elementAt(i);
                MatchImpl m = null;
                for (int k = 0; k < matches.size(); k++) {
                    m = (MatchImpl) matches.elementAt(k);
                    if (m.connect(dr))
                        break;
                    m = null;
                }
                if (m == null) {
                    m = new MatchImpl(this, dev, dr);
                    matches.addElement(m);
                }
            }

            int n = 0;
            boolean loading = false;

            // Count good matches and trigger loading
            for (int i = 0; i < matches.size(); i++) {
                MatchImpl m = (MatchImpl) matches.elementAt(i);
                int match = m.getMatchValue();
                if (match == MatchImpl.UNKNOWN)
                    loading = true;
                else if (match > Device.MATCH_NONE)
                    n++;
            }

            // If not finished loading continue
            if (loading)
                continue;

            // If nothing matches we're done
            if (n == 0) {
                tellNotFound(dev);
                return;
            }

            // Filter out good matches and select default best match
            MatchImpl best = null;
            Match[] sel = new Match[n];
            n = 0;
            for (int i = 0; i < matches.size(); i++) {
                MatchImpl m = (MatchImpl) matches.elementAt(i);
                if (m.getMatchValue() > Device.MATCH_NONE) {
                    sel[n++] = m;
                    if (best == null || best.compare(m) < 0)
                        best = m;
                }
            }

            // Maybe use driver selector instead
            DriverSelector ds = selector;
            if (ds != null) {
                int ix = ds.select(dev, sel);
                if (ix == DriverSelector.SELECT_NONE) {
                    tellNotFound(dev);
                    return;
                }
                try {
                    best = (MatchImpl) sel[ix];
                } catch (Exception e) {
                }
            }

            // If attach succeeds we're done
            String ref = null;
            try {
                ref = best.attach();
            } catch (Exception e) {
                error("failed attach " + showDriver(best.getDriver()) + " -> "
                        + showDevice(dev));
                continue;
            }

            if (best.getMatchValue() > Device.MATCH_NONE) {
                // Just loaded, go around and pick up the driver ref
                continue;
            }

            if (ref == null) {
                info("attached " + showDriver(best.getDriver()) + " -> "
                        + showDevice(dev));
                Bundle b = best.getBundle();
                if (b != null)
                    updateLife(b, LONG_LIFE);
                return;
            }

            info(showDriver(best.getDriver()) + " refers to " + ref);

            // Append the referred match
            MatchImpl m = null;
            for (int i = 0; i < matches.size(); i++) {
                m = (MatchImpl) matches.elementAt(i);
                if (m.equals(ref))
                    break;
                m = null;
            }
            if (m == null) {
                m = new MatchImpl(this, dev, ref);
                matches.addElement(m);
            }
        }
    }

    private void updateLife(Bundle b, long t) {
        tempDrivers.put(b, new Long(System.currentTimeMillis() + t));
    }

    private boolean isUsed(ServiceReference sr) {
        Bundle[] ba = sr.getUsingBundles();
        if (ba != null) {
            for (int i = 0; i < ba.length; i++) {
                Bundle b = ba[i];
                try {
                    for (int j = 0; j < drivers.size(); j++) {
                        DriverRef dr = (DriverRef) drivers.elementAt(j);
                        if (dr.sr.getBundle() == b)
                            return true;
                    }
                } catch (Exception e) {
                    return true;
                }
            }
        }
        return false;
    }

    private void reapDrivers() {
        long now = System.currentTimeMillis();
        Bundle[] ba = bc.getBundles();
        if (ba != null) {
            for (int i = 0; i < ba.length; i++) {
                try {
                    Bundle b = ba[i];
                    if (b.getLocation().startsWith(DYNAMIC_DRIVER_TAG)) {
                        Long expire = (Long) tempDrivers.get(b);
                        boolean inUse = false;

                        ServiceReference[] sra = b.getServicesInUse();
                        if (sra != null) {
                            for (int j = 0; j < sra.length; j++) {
                                if (isDevice.match(sra[j])) {
                                    inUse = true;
                                    break;
                                }
                            }
                        }

                        if (inUse) {
                            updateLife(b, LONG_LIFE);
                        } else if (expire == null) {
                            updateLife(b, SHORT_LIFE);
                        } else if (expire.longValue() < now) {
                            info("uninstalling " + b.getLocation());
                            b.uninstall();
                        }
                    }
                } catch (Exception e) {
                }
            }
        }
    }

    private void touchDevice(ServiceReference dev) {
        if (newDevices.put(dev, dev) == null) {
            synchronized (this) {
                notifyAll();
            }
        }
    }

    private void touchAllDevices() {
        boolean added = false;
        try {
            ServiceReference[] sra = bc.getServiceReferences(null,
                    DEVICE_FILTER);
            if (sra != null) {
                for (int i = 0; i < sra.length; i++) {
                    ServiceReference dev = sra[i];
                    if (newDevices.put(dev, dev) == null)
                        added = true;
                }
            }
        } catch (Exception e) {
        }
        if (added) {
            synchronized (this) {
                notifyAll();
            }
        }
    }

    private void tellNotFound(ServiceReference dev) {
        // NB: Should we avoid repeating the call to the same device?

        info("no driver for " + showDevice(dev));
        Object d = null;
        try {
            d = bc.getService(dev);
            ((Device) d).noDriverFound();
        } catch (Exception e) {
        } finally {
            try {
                bc.ungetService(dev);
            } catch (Exception e1) {
            }
        }
    }

    private Dictionary /* String->Object */collectProperties(
            ServiceReference sr) {
        Dictionary props = new Hashtable();
        String[] keys = sr.getPropertyKeys();
        if (keys != null) {
            for (int i = 0; i < keys.length; i++) {
                String key = keys[i];
                props.put(key, sr.getProperty(key));
            }
        }
        return props;
    }

    Bundle installBundle(String name, InputStream is) {
        Bundle b = null;
        try {
            info("installing " + name);
            b = bc.installBundle(name, is);
            b.start();
            updateLife(b, SHORT_LIFE);
            return b;
        } catch (Exception e) {
            error("failed to install " + name);
            try {
                b.uninstall();
            } catch (Exception e1) {
            }
            return null;
        } finally {
            try {
                is.close();
            } catch (Exception e1) {
            }
        }
    }

    void removeSRCachedMatch(ServiceReference sr) {

        // NB: Index to speed up this process?

        String pid = null;
        try {
            pid = (String) sr
                    .getProperty(org.osgi.framework.Constants.SERVICE_PID);
        } catch (Exception e) {
        }
        if (pid != null)
            return;
        for (Enumeration e = cache.keys(); e.hasMoreElements();) {
            Integer k = (Integer) e.nextElement();
            MatchValue mv0 = (MatchValue) cache.get(k);
            MatchValue mv = mv0;
            while (mv != null) {
                if (mv.dev != sr) {
                    // Keep it
                    mv0 = mv;
                    mv = mv.next;
                } else if (mv == mv0) {
                    // Update hash table entry
                    mv0 = mv = mv.next;
                    if (mv != null)
                        cache.put(k, mv);
                    else
                        cache.remove(k);
                } else {
                    // Link past this one
                    mv = mv.next;
                    mv0.next = mv;
                }
            }
        }
    }

    int getCachedMatch(String drvid, ServiceReference dev) {
        MatchValue mv = findMatch(drvid, dev, false);
        return mv != null ? mv.match : MatchImpl.UNKNOWN;
    }

    void putCachedMatch(String drvid, ServiceReference dev, int match) {
        MatchValue mv = findMatch(drvid, dev, true);
        mv.match = match;
    }

    private MatchValue findMatch(String drvid, ServiceReference dev,
            boolean create) {
        String pid = null;
        try {
            pid = (String) dev
                    .getProperty(org.osgi.framework.Constants.SERVICE_PID);
        } catch (Exception e) {
        }
        int k1 = pid != null ? pid.hashCode() : dev.hashCode();
        int k2 = drvid.hashCode();
        Integer key = new Integer(k1 + k2);
        MatchValue mv0 = (MatchValue) cache.get(key);
        MatchValue mv = mv0;
        while (mv != null) {
            if (drvid.equals(mv.drvid) && pid != null ? pid.equals(mv.pid)
                    : dev == mv.dev)
                return mv;
            mv = mv.next;
        }

        if (!create)
            return null;

        mv = new MatchValue();
        mv.next = mv0;
        mv.key = key;
        mv.drvid = drvid;
        mv.pid = pid;
        if (pid == null)
            mv.dev = dev;
        cache.put(key, mv);
        return mv;
    }

    private String showDevice(ServiceReference sr) {
        StringBuffer sb = new StringBuffer();
        Object o = sr
                .getProperty(org.osgi.service.device.Constants.DEVICE_CATEGORY);
        if (o instanceof String) {
            sb.append(o);
        } else if (o instanceof String[]) {
            String[] dca = (String[]) o;
            for (int i = 0; i < dca.length; i++) {
                if (i > 0)
                    sb.append('_');
                sb.append(dca[i]);
            }
        }
        o = sr.getProperty(org.osgi.framework.Constants.SERVICE_ID);
        if (o != null)
            sb.append(o);
        Bundle b = sr.getBundle();
        if (b != null) {
            sb.append('/');
            sb.append(b.getBundleId());
        }
        return sb.toString();
    }

    private String showDriver(ServiceReference sr) {
        StringBuffer sb = new StringBuffer();
        String s = (String) sr
                .getProperty(org.osgi.service.device.Constants.DRIVER_ID);
        sb.append(s != null ? s : "driver");
        Bundle b = sr.getBundle();
        if (b != null) {
            sb.append('/');
            sb.append(b.getBundleId());
        }
        return sb.toString();
    }
}
TOP

Related Classes of org.knopflerfish.bundle.device.Activator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.