Package org.apache.felix.das

Source Code of org.apache.felix.das.DeviceManager$LoggedCall

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.  See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.felix.das;


import java.util.ArrayList;
import java.util.Collections;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.apache.felix.das.util.DriverLoader;
import org.apache.felix.das.util.DriverMatcher;
import org.apache.felix.das.util.Util;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Filter;
import org.osgi.framework.FrameworkEvent;
import org.osgi.framework.FrameworkListener;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.service.device.Constants;
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;


/**
* This class represents the Apache Felix implementation of the device access specification.
* It is based on version 1.1 of the spec.
*
*
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
public class DeviceManager implements Log
{

    private final long DEFAULT_TIMEOUT_SEC = 1;

    // the logger
    private volatile LogService m_log;

    // the bundle context
    private final BundleContext m_context;

    // the driver selector
    private volatile DriverSelector m_selector;

    // the driver locators
    private List<DriverLocator> m_locators;

    // the devices
    private Map<ServiceReference, Object> m_devices;

    // the drivers
    private Map<ServiceReference, DriverAttributes> m_drivers;

    // performs all the background actions
    private ExecutorService m_worker;

    // used to add delayed actions
    private ScheduledExecutorService m_delayed;

    //the devices filter
    private Filter m_deviceImplFilter;

    //the drivers filter
    private Filter m_driverImplFilter;


    /**
     * Public constructor. Used by the Activator in this <code>Bundle</code>
     * to instantiate one instance.
     *
     * @param context the <code>BundleContext</code>
     */
    public DeviceManager( BundleContext context )
    {
        m_context = context;
    }


    public void debug( String message )
    {
        m_log.log( LogService.LOG_DEBUG, message );
    }


    public void info( String message )
    {
        m_log.log( LogService.LOG_INFO, message );
    }


    public void warning( String message )
    {
        m_log.log( LogService.LOG_WARNING, message );
    }


    public void error( String message, Throwable e )
    {
        System.err.println( message );
        if ( e != null )
        {
            e.printStackTrace();
        }
        m_log.log( LogService.LOG_ERROR, message, e );
    }


    // dependency manager methods
    @SuppressWarnings("unused")
    private void init() throws InvalidSyntaxException
    {
        m_locators = Collections.synchronizedList( new ArrayList<DriverLocator>() );
        m_worker = Executors.newSingleThreadExecutor( new NamedThreadFactory( "Apache Felix Device Manager" ) );
        m_delayed = Executors.newScheduledThreadPool( 1, new NamedThreadFactory(
            "Apache Felix Device Manager - delayed" ) );
        m_deviceImplFilter = Util.createFilter( "(%s=%s)", new Object[]
            { org.osgi.framework.Constants.OBJECTCLASS, Device.class.getName() } );
        m_driverImplFilter = Util.createFilter( "(%s=%s)", new Object[]
            { org.osgi.framework.Constants.OBJECTCLASS, Driver.class.getName() } );
    }


    @SuppressWarnings("unused")
    private void start()
    {
        m_drivers = new HashMap<ServiceReference, DriverAttributes>();
        m_devices = new HashMap<ServiceReference, Object>();
        submit( new WaitForStartFramework() );
    }


    public void stop()
    {
        // nothing to do ?
    }


    public void destroy()
    {
        m_worker.shutdownNow();
        m_delayed.shutdownNow();
    }


    // callback methods

    public void selectorAdded( DriverSelector selector )
    {
        m_selector = selector;
        debug( "driver selector appeared" );
    }


    public void selectorRemoved( DriverSelector selector )
    {
        m_selector = null;
        debug( "driver selector lost" );
    }
   
    public void locatorAdded( DriverLocator locator )
    {
        m_locators.add( locator );
        debug( "driver locator appeared" );
    }


    public void locatorRemoved( DriverLocator locator )
    {
        m_locators.remove( locator );
        debug( "driver locator lost" );
    }


    public void driverAdded( ServiceReference ref, Object obj )
    {
        final Driver driver = Driver.class.cast( obj );
        m_drivers.put( ref, new DriverAttributes( ref, driver ) );

        debug( "driver appeared: " + Util.showDriver( ref ) );
       
        //immediately check for idle devices
        submit( new CheckForIdleDevices() );
    }

    public void driverModified( ServiceReference ref, Object obj )
    {
        final Driver driver = Driver.class.cast( obj );
       
        debug( "driver modified: " + Util.showDriver( ref ) );
        m_drivers.remove( ref );
        m_drivers.put( ref , new DriverAttributes( ref, driver ) );

        // check if devices have become idle
        // after some time
        schedule( new CheckForIdleDevices() );
    }

    public void driverRemoved( ServiceReference ref )
    {
        debug( "driver lost: " + Util.showDriver( ref ) );
        m_drivers.remove( ref );

        // check if devices have become idle
        // after some time
        schedule( new CheckForIdleDevices() );

    }


    public void deviceAdded( ServiceReference ref, Object device )
    {
        m_devices.put( ref, device );
        debug( "device appeared: " + Util.showDevice( ref ) );
        submit( new DriverAttachAlgorithm( ref, device ) );
    }


    public void deviceModified( ServiceReference ref, Object device )
    {
        debug( "device modified: " + Util.showDevice( ref ) );
        // nothing further to do ?
        // DeviceAttributes da = m_devices.get(ref);
        // submit(new DriverAttachAlgorithm(da));
    }


    public void deviceRemoved( ServiceReference ref )
    {
        debug( "device removed: " + Util.showDevice( ref ) );
        m_devices.remove( ref );
        // nothing further to do ?
        // the services that use this
        // device should track it.
    }


    /**
     * perform this task as soon as possible.
     *
     * @param task
     *            the task
     */
    private void submit( Callable<Object> task )
    {
        m_worker.submit( new LoggedCall( task ) );
    }


    /**
     * perform this task after the default delay.
     *
     * @param task
     *            the task
     */
    private void schedule( Callable<Object> task )
    {
        m_delayed.schedule( new DelayedCall( task ), DEFAULT_TIMEOUT_SEC, TimeUnit.SECONDS );
    }

    // worker callables

    /**
     * Callable used to start the DeviceManager. It either waits (blocking the
     * worker thread) for the framework to start, or if it has already started,
     * returns immediately, freeing up the worker thread.
     *
     * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
     */
    private class WaitForStartFramework implements Callable<Object>, FrameworkListener
    {

        private final CountDownLatch m_latch = new CountDownLatch( 1 );


        public Object call() throws Exception
        {
            boolean addedAsListener = false;
            if ( m_context.getBundle( 0 ).getState() == Bundle.ACTIVE )
            {
                m_latch.countDown();
                debug( "Starting Device Manager immediately" );
            }
            else
            {
                m_context.addFrameworkListener( this );
                addedAsListener = true;
                debug( "Waiting for framework to start" );
            }

            m_latch.await();
            for ( Map.Entry<ServiceReference, Object> entry : m_devices.entrySet() )
            {
                submit( new DriverAttachAlgorithm( entry.getKey(), entry.getValue() ) );
            }
            // cleanup
            if ( addedAsListener )
            {
                m_context.removeFrameworkListener( this );
            }
            return null;
        }


        // FrameworkListener method
        public void frameworkEvent( FrameworkEvent event )
        {
            switch ( event.getType() )
            {
                case FrameworkEvent.STARTED:
                    debug( "Framework has started" );
                    m_latch.countDown();
                    break;
            }
        }


        @Override
        public String toString()
        {
            return getClass().getSimpleName();
        }
    }

    private class LoggedCall implements Callable<Object>
    {

        private final Callable<Object> m_call;


        public LoggedCall( Callable<Object> call )
        {
            m_call = call;
        }


        private String getName()
        {
            return m_call.getClass().getSimpleName();
        }


        public Object call() throws Exception
        {

            try
            {
                return m_call.call();
            }
            catch ( Exception e )
            {
                error( "call failed: " + getName(), e );
                throw e;
            }
            catch ( Throwable e )
            {
                error( "call failed: " + getName(), e );
                throw new RuntimeException( e );
            }
        }

    }

    private class DelayedCall implements Callable<Object>
    {

        private final Callable<Object> m_call;


        public DelayedCall( Callable<Object> call )
        {
            m_call = call;
        }


        private String getName()
        {
            return m_call.getClass().getSimpleName();
        }


        public Object call() throws Exception
        {
            info( "Delayed call: " + getName() );
            return m_worker.submit( m_call );
        }
    }

    /**
     * Checks for Idle devices, and attaches them
     *
     * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
     */
    private class CheckForIdleDevices implements Callable<Object>
    {

        public Object call() throws Exception
        {
            debug( "START - check for idle devices" );
            for ( ServiceReference ref : getIdleDevices() )
            {
                info( "IDLE: " + ref.getBundle().getSymbolicName() );
                submit( new DriverAttachAlgorithm( ref, m_devices.get( ref ) ) );
            }

            submit( new IdleDriverUninstallAlgorithm() );
            debug( "STOP - check for idle devices" );
            return null;
        }


        /**
         * get a list of all idle devices.
         *
         * @return
         */
        private List<ServiceReference> getIdleDevices()
        {
            List<ServiceReference> list = new ArrayList<ServiceReference>();

            for ( ServiceReference ref : m_devices.keySet() )
            {
                info( "checking if idle: " + ref.getBundle().getSymbolicName() );

                final Bundle[] usingBundles = ref.getUsingBundles();
                for ( Bundle bundle : usingBundles )
                {
                    if ( isDriverBundle( bundle ) )
                    {
                        info( "used by driver: " + bundle.getSymbolicName() );
                        debug( "not idle: " + ref.getBundle().getSymbolicName() );
                        break;
                    }
                   
                    list.add( ref );

                }
            }
            return list;
        }
    }


    private boolean isDriverBundle( Bundle bundle )
    {
        ServiceReference[] refs = bundle.getRegisteredServices();
       
        if (refs == null) {
            return false;
        }
       
        for ( ServiceReference ref : refs )
        {
            if ( m_driverImplFilter.match( ref ) )
            {
                return true;
            }
        }
        return false;
    }

    /**
     *
     * Used to uninstall unused drivers
     *
     * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
     */
    private class IdleDriverUninstallAlgorithm implements Callable<Object>
    {

        public Object call() throws Exception
        {

            info( "cleaning driver cache" );
            for ( DriverAttributes da : m_drivers.values() )
            {
                // just call the tryUninstall; the da itself
                // will know if it should really uninstall the driver.
                try
                {
                  da.tryUninstall();
                }
                catch (Exception e)
                {
                  debug(da.getDriverId() + " uninstall failed");
                }
            }

            return null;
        }
    }

    private class DriverAttachAlgorithm implements Callable<Object>
    {

        private final ServiceReference m_ref;

        private final Device m_device;

        private List<DriverAttributes> m_included;

        private List<DriverAttributes> m_excluded;

        private final DriverLoader m_driverLoader;

        private DriverAttributes m_finalDriver;


        public DriverAttachAlgorithm( ServiceReference ref, Object obj )
        {
            m_ref = ref;
            if ( m_deviceImplFilter.match( ref ) )
            {
                m_device = Device.class.cast( obj );
            }
            else
            {
                m_device = null;
            }

            m_driverLoader = new DriverLoader( DeviceManager.this, m_context );
        }


        @SuppressWarnings("all")
        private Dictionary createDictionary( ServiceReference ref )
        {
            final Properties p = new Properties();

            for ( String key : ref.getPropertyKeys() )
            {
                p.put( key, ref.getProperty( key ) );
            }
            return p;
        }


        @SuppressWarnings("all")
        public Object call() throws Exception
        {
            info( "finding suitable driver for: " + Util.showDevice( m_ref ) );

            final Dictionary dict = createDictionary( m_ref );

            // first create a copy of all the drivers that are already there.
            // during the process, drivers will be added, but also excluded.
            m_included = new ArrayList<DriverAttributes>( m_drivers.values() );
            m_excluded = new ArrayList<DriverAttributes>();

            // first find matching driver bundles
            // if there are no driver locators
            // we'll have to do with the drivers that were
            // added 'manually'
            Set<String> driverIds = m_driverLoader.findDrivers( m_locators, dict );

            // remove the driverIds that are already available
            for ( DriverAttributes da : m_drivers.values() )
            {
                driverIds.remove( da.getDriverId() );
            }
            driverIds.removeAll( m_drivers.keySet() );
            try
            {
              debug("entering attach phase for " + Util.showDevice( m_ref ) );
                return driverAttachment( dict, driverIds.toArray( new String[0] ) );
            }
            finally
            {
                // unload loaded drivers
                // that were unnecessarily loaded
                m_driverLoader.unload( m_finalDriver );
            }
        }


        @SuppressWarnings("all")
        private Object driverAttachment( Dictionary dict, String[] driverIds ) throws Exception
        {
            m_finalDriver = null;

            // remove the excluded drivers
            m_included.removeAll( m_excluded );

            // now load the drivers
            List<ServiceReference> driverRefs = m_driverLoader.loadDrivers( m_locators, driverIds );
            // these are the possible driver references that have been added
            // add them to the list of included drivers
            for ( ServiceReference serviceReference : driverRefs )
            {
                DriverAttributes da = m_drivers.get( serviceReference );
                if ( da != null )
                {
                    m_included.add( da );
                }
            }

            // now start matching all drivers
            final DriverMatcher mi = new DriverMatcher( DeviceManager.this );

            for ( DriverAttributes driver : m_included )
            {
                try
                {
                    int match = driver.match( m_ref );
                    if ( match <= Device.MATCH_NONE )
                    {
                        continue;
                    }
                    mi.add( match, driver );
                }
                catch ( Throwable t )
                {
                    error( "match threw an exception", new Exception( t ) );
                }
            }

            // get the best match
            Match bestMatch = null;

            // local copy
            final DriverSelector selector = m_selector;
           
            if ( selector != null )
            {
                bestMatch = mi.selectBestMatch( m_ref, selector );
                if (bestMatch != null) {
                  debug(String.format("DriverSelector (%s) found best match: %s", selector.getClass().getName(), Util.showDriver(bestMatch.getDriver())));
                }
            }
           
            if (bestMatch == null)
            {
                bestMatch = mi.getBestMatch();
            }

            if ( bestMatch == null )
            {
                noDriverFound();
                // really return
                return null;
            }

            String driverId = String.class.cast( bestMatch.getDriver().getProperty( Constants.DRIVER_ID ) );

            debug( "best match: " + driverId );
            m_finalDriver = m_drivers.get( bestMatch.getDriver() );

            if ( m_finalDriver == null )
            {
                error( "we found a driverId, but not the corresponding driver: " + driverId, null );
                noDriverFound();
                return null;
            }

            // here we get serious...
            try
            {
                debug( "attaching to: " + driverId );
                String newDriverId = m_finalDriver.attach( m_ref );
                if ( newDriverId == null )
                {
                    // successful attach
                    return null;
                }
                // its a referral
                info( "attach led to a referral to: " + newDriverId );
                m_excluded.add( m_finalDriver );
                return driverAttachment( dict, new String[]{ newDriverId } );
            }
            catch ( Throwable t )
            {
                error( "attach failed due to an exception", t );
            }
            m_excluded.add( m_finalDriver );
            return driverAttachment( dict, driverIds );
        }


        private void noDriverFound()
        {
            debug( "no suitable driver found for: " + Util.showDevice( m_ref ) );
            if ( m_device != null )
            {
                m_device.noDriverFound();
            }
        }


        @Override
        public String toString()
        {
            return getClass().getSimpleName();// + ": " +
            // Util.showDevice(m_ref);
        }

    }
}
TOP

Related Classes of org.apache.felix.das.DeviceManager$LoggedCall

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.