blob: d2dcf354ff6c07c04bdec2c8a156e008ffb8bdc4 [file] [log] [blame]
Marcel Offermans613a6202009-03-11 22:31:44 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19package org.apache.felix.das;
20
21
22import java.util.ArrayList;
23import java.util.Collections;
24import java.util.Dictionary;
25import java.util.HashMap;
26import java.util.List;
27import java.util.Map;
28import java.util.Properties;
29import java.util.concurrent.Callable;
30import java.util.concurrent.CountDownLatch;
31import java.util.concurrent.ExecutorService;
32import java.util.concurrent.Executors;
33import java.util.concurrent.ScheduledExecutorService;
34import java.util.concurrent.TimeUnit;
35
36import org.apache.felix.das.util.DriverLoader;
37import org.apache.felix.das.util.DriverMatcher;
38import org.apache.felix.das.util.Util;
39import org.osgi.framework.Bundle;
40import org.osgi.framework.BundleContext;
41import org.osgi.framework.Filter;
42import org.osgi.framework.FrameworkEvent;
43import org.osgi.framework.FrameworkListener;
44import org.osgi.framework.InvalidSyntaxException;
45import org.osgi.framework.ServiceReference;
46import org.osgi.service.device.Constants;
47import org.osgi.service.device.Device;
48import org.osgi.service.device.Driver;
49import org.osgi.service.device.DriverLocator;
50import org.osgi.service.device.DriverSelector;
51import org.osgi.service.device.Match;
52import org.osgi.service.log.LogService;
53
54
55/**
56 * TODO: add javadoc
57 *
58 * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
59 */
60public class DeviceManager implements Log
61{
62
63 private final long DEFAULT_TIMEOUT_SEC = 1;
64
65 // the logger
66 private LogService m_log;
67
68 // the bundle context
69 private final BundleContext m_context;
70
71 // the driver selector
72 private DriverSelector m_selector;
73
74 // the driver locators
75 private List<DriverLocator> m_locators;
76
77 // the devices
78 private Map<ServiceReference, Object> m_devices;
79
80 // the drivers
81 private Map<ServiceReference, DriverAttributes> m_drivers;
82
83 // performs all the background actions
84 private ExecutorService m_worker;
85
86 // used to add delayed actions
87 private ScheduledExecutorService m_delayed;
88
89 private Filter m_deviceImplFilter;
90
91 private Filter m_driverImplFilter;
92
93
94 public DeviceManager( BundleContext context )
95 {
96 m_context = context;
97 }
98
99
100 public void debug( String message )
101 {
102 m_log.log( LogService.LOG_DEBUG, message );
103 }
104
105
106 public void info( String message )
107 {
108 m_log.log( LogService.LOG_INFO, message );
109 }
110
111
112 public void warning( String message )
113 {
114 m_log.log( LogService.LOG_WARNING, message );
115 }
116
117
118 public void error( String message, Throwable e )
119 {
120 System.err.println( message );
121 if ( e != null )
122 {
123 e.printStackTrace();
124 }
125 m_log.log( LogService.LOG_ERROR, message, e );
126 }
127
128
129 // dependency manager methods
130 @SuppressWarnings("unused")
131 private void init() throws InvalidSyntaxException
132 {
133 m_locators = Collections.synchronizedList( new ArrayList<DriverLocator>() );
134 m_worker = Executors.newSingleThreadExecutor( new NamedThreadFactory( "Apache Felix Device Manager" ) );
135 m_delayed = Executors.newScheduledThreadPool( 1, new NamedThreadFactory(
136 "Apache Felix Device Manager - delayed" ) );
137 m_deviceImplFilter = Util.createFilter( "(%s=%s)", new Object[]
138 { org.osgi.framework.Constants.OBJECTCLASS, Device.class.getName() } );
139 m_driverImplFilter = Util.createFilter( "(%s=%s)", new Object[]
140 { org.osgi.framework.Constants.OBJECTCLASS, Driver.class.getName() } );
141 }
142
143
144 @SuppressWarnings("unused")
145 private void start()
146 {
147 m_drivers = new HashMap<ServiceReference, DriverAttributes>();
148 m_devices = new HashMap<ServiceReference, Object>();
149 submit( new WaitForStartFramework() );
150 }
151
152
153 public void stop()
154 {
155 // nothing to do ?
156 }
157
158
159 public void destroy()
160 {
161 m_worker.shutdownNow();
162 m_delayed.shutdownNow();
163 }
164
165
166 // callback methods
167
168 public void locatorAdded( DriverLocator locator )
169 {
170 m_locators.add( locator );
171 debug( "driver locator appeared" );
172 }
173
174
175 public void locatorRemoved( DriverLocator locator )
176 {
177 m_locators.remove( locator );
178 debug( "driver locator lost" );
179 }
180
181
182 public void driverAdded( ServiceReference ref, Object obj )
183 {
184 final Driver driver = Driver.class.cast( obj );
185 m_drivers.put( ref, new DriverAttributes( ref, driver ) );
186
187 debug( "driver appeared: " + Util.showDriver( ref ) );
188 }
189
190
191 public void driverRemoved( ServiceReference ref )
192 {
193 String driverId = String.class.cast( ref.getProperty( Constants.DRIVER_ID ) );
194 debug( "driver lost: " + Util.showDriver( ref ) );
195 m_drivers.remove( driverId );
196
197 // check if devices have become idle
198 // after some time
199 schedule( new CheckForIdleDevices() );
200
201 }
202
203
204 public void deviceAdded( ServiceReference ref, Object device )
205 {
206 m_devices.put( ref, device );
207 debug( "device appeared: " + Util.showDevice( ref ) );
208 submit( new DriverAttachAlgorithm( ref, device ) );
209 }
210
211
212 public void deviceModified( ServiceReference ref, Object device )
213 {
214 debug( "device modified: " + Util.showDevice( ref ) );
215 // nothing further to do ?
216 // DeviceAttributes da = m_devices.get(ref);
217 // submit(new DriverAttachAlgorithm(da));
218 }
219
220
221 public void deviceRemoved( ServiceReference ref )
222 {
223 debug( "device removed: " + Util.showDevice( ref ) );
224 m_devices.remove( ref );
225 // nothing further to do ?
226 // the services that use this
227 // device should track it.
228 }
229
230
231 /**
232 * perform this task as soon as possible.
233 *
234 * @param task
235 * the task
236 */
237 private void submit( Callable<Object> task )
238 {
239 m_worker.submit( new LoggedCall( task ) );
240 }
241
242
243 /**
244 * perform this task after the default delay.
245 *
246 * @param task
247 * the task
248 */
249 private void schedule( Callable<Object> task )
250 {
251 m_delayed.schedule( new DelayedCall( task ), DEFAULT_TIMEOUT_SEC, TimeUnit.SECONDS );
252 }
253
254 // worker callables
255
256 /**
257 * Callable used to start the DeviceManager. It either waits (blocking the
258 * worker thread) for the framework to start, or if it has already started,
259 * returns immediately, freeing up the worker thread.
260 *
261 * @author dennisg
262 *
263 */
264 private class WaitForStartFramework implements Callable<Object>, FrameworkListener
265 {
266
267 private final CountDownLatch m_latch = new CountDownLatch( 1 );
268
269
270 public Object call() throws Exception
271 {
272 boolean addedAsListener = false;
273 if ( m_context.getBundle( 0 ).getState() == Bundle.ACTIVE )
274 {
275 m_latch.countDown();
276 debug( "Starting Device Manager immediately" );
277 }
278 else
279 {
280 m_context.addFrameworkListener( this );
281 addedAsListener = true;
282 debug( "Waiting for framework to start" );
283 }
284
285 m_latch.await();
286 for ( Map.Entry<ServiceReference, Object> entry : m_devices.entrySet() )
287 {
288 submit( new DriverAttachAlgorithm( entry.getKey(), entry.getValue() ) );
289 }
290 // cleanup
291 if ( addedAsListener )
292 {
293 m_context.removeFrameworkListener( this );
294 }
295 return null;
296 }
297
298
299 // FrameworkListener method
300 public void frameworkEvent( FrameworkEvent event )
301 {
302 switch ( event.getType() )
303 {
304 case FrameworkEvent.STARTED:
305 debug( "Framework has started" );
306 m_latch.countDown();
307 break;
308 }
309 }
310
311
312 @Override
313 public String toString()
314 {
315 return getClass().getSimpleName();
316 }
317 }
318
319 private class LoggedCall implements Callable<Object>
320 {
321
322 private final Callable<Object> m_call;
323
324
325 public LoggedCall( Callable<Object> call )
326 {
327 m_call = call;
328 }
329
330
331 private String getName()
332 {
333 return m_call.getClass().getSimpleName();
334 }
335
336
337 public Object call() throws Exception
338 {
339
340 try
341 {
342 return m_call.call();
343 }
344 catch ( Exception e )
345 {
346 error( "call failed: " + getName(), e );
347 throw e;
348 }
349 catch ( Throwable e )
350 {
351 error( "call failed: " + getName(), e );
352 throw new RuntimeException( e );
353 }
354 }
355
356 }
357
358 private class DelayedCall implements Callable<Object>
359 {
360
361 private final Callable<Object> m_call;
362
363
364 public DelayedCall( Callable<Object> call )
365 {
366 m_call = call;
367 }
368
369
370 private String getName()
371 {
372 return m_call.getClass().getSimpleName();
373 }
374
375
376 public Object call() throws Exception
377 {
378 info( "Delayed call: " + getName() );
379 return m_worker.submit( m_call );
380 }
381 }
382
383 /**
384 * Checks for Idle devices, and attaches them
385 *
386 * @author dennisg
387 *
388 */
389 private class CheckForIdleDevices implements Callable<Object>
390 {
391
392 public Object call() throws Exception
393 {
394 debug( "START - check for idle devices" );
395 for ( ServiceReference ref : getIdleDevices() )
396 {
397 info( "IDLE: " + ref.getBundle().getSymbolicName() );
398 submit( new DriverAttachAlgorithm( ref, m_devices.get( ref ) ) );
399 }
400
401 submit( new IdleDriverUninstallAlgorithm() );
402 debug( "STOP - check for idle devices" );
403 return null;
404 }
405
406
407 /**
408 * get a list of all idle devices.
409 *
410 * @return
411 */
412 private List<ServiceReference> getIdleDevices()
413 {
414 List<ServiceReference> list = new ArrayList<ServiceReference>();
415
416 for ( ServiceReference ref : m_devices.keySet() )
417 {
418 info( "checking if idle: " + ref.getBundle().getSymbolicName() );
419
420 final Bundle[] usingBundles = ref.getUsingBundles();
421 for ( Bundle bundle : usingBundles )
422 {
423 if ( isDriverBundle( bundle ) )
424 {
425 info( "used by driver: " + bundle.getSymbolicName() );
426 debug( "not idle: " + ref.getBundle().getSymbolicName() );
Marcel Offermans613a6202009-03-11 22:31:44 +0000427 break;
428 }
Stuart McCulloch0a0508c2009-04-16 09:41:41 +0000429
430 list.add( ref );
431
Marcel Offermans613a6202009-03-11 22:31:44 +0000432 }
433 }
434 return list;
435 }
436 }
437
438
439 private boolean isDriverBundle( Bundle bundle )
440 {
441 ServiceReference[] refs = bundle.getRegisteredServices();
442 for ( ServiceReference ref : refs )
443 {
444 if ( m_driverImplFilter.match( ref ) )
445 {
446 return true;
447 }
448 }
449 return false;
450 }
451
452 /**
453 *
454 * Used to uninstall unused drivers
455 *
456 * @author dennisg
457 *
458 */
459 private class IdleDriverUninstallAlgorithm implements Callable<Object>
460 {
461
462 public Object call() throws Exception
463 {
464
465 info( "cleaning driver cache" );
466 for ( DriverAttributes da : m_drivers.values() )
467 {
468 // just call the tryUninstall; the da itself
469 // will know if it should really uninstall the driver.
470 da.tryUninstall();
471 }
472
473 return null;
474 }
475 }
476
477 private class DriverAttachAlgorithm implements Callable<Object>
478 {
479
480 private final ServiceReference m_ref;
481
482 private final Device m_device;
483
484 private List<DriverAttributes> m_included;
485
486 private List<DriverAttributes> m_excluded;
487
488 private final DriverLoader m_driverLoader;
489
490 private DriverAttributes m_finalDriver;
491
492
493 public DriverAttachAlgorithm( ServiceReference ref, Object obj )
494 {
495 m_ref = ref;
496 if ( m_deviceImplFilter.match( ref ) )
497 {
498 m_device = Device.class.cast( obj );
499 }
500 else
501 {
502 m_device = null;
503 }
504
505 m_driverLoader = new DriverLoader( DeviceManager.this, m_context );
506 }
507
508
509 @SuppressWarnings("all")
510 private Dictionary createDictionary( ServiceReference ref )
511 {
512 final Properties p = new Properties();
513
514 for ( String key : ref.getPropertyKeys() )
515 {
516 p.put( key, ref.getProperty( key ) );
517 }
518 return p;
519 }
520
521
522 @SuppressWarnings("all")
523 public Object call() throws Exception
524 {
525 info( "finding suitable driver for: " + Util.showDevice( m_ref ) );
526
527 final Dictionary dict = createDictionary( m_ref );
528
529 // first create a copy of all the drivers that are already there.
530 // during the process, drivers will be added, but also excluded.
531 m_included = new ArrayList<DriverAttributes>( m_drivers.values() );
532 m_excluded = new ArrayList<DriverAttributes>();
533
534 // first find matching driver bundles
535 // if there are no driver locators
536 // we'll have to do with the drivers that where
537 // added 'manually'
538 List<String> driverIds = m_driverLoader.findDrivers( m_locators, dict );
539
540 // remove the driverIds that are already available
541 for ( DriverAttributes da : m_drivers.values() )
542 {
543 driverIds.remove( da.getDriverId() );
544 }
545 driverIds.removeAll( m_drivers.keySet() );
546 try
547 {
548 return driverAttachment( dict, driverIds.toArray( new String[0] ) );
549 }
550 finally
551 {
552 // unload loaded drivers
553 // that were unnecessarily loaded
554 m_driverLoader.unload( m_finalDriver );
555 }
556 }
557
558
559 @SuppressWarnings("all")
560 private Object driverAttachment( Dictionary dict, String[] driverIds ) throws Exception
561 {
562 m_finalDriver = null;
563
564 // remove the excluded drivers
565 m_included.removeAll( m_excluded );
566
567 // now load the drivers
568 List<ServiceReference> driverRefs = m_driverLoader.loadDrivers( m_locators, driverIds );
569 // these are the possible driver references that have been added
570 // add the to the list of included drivers
571 for ( ServiceReference serviceReference : driverRefs )
572 {
573 DriverAttributes da = m_drivers.get( serviceReference );
574 if ( da != null )
575 {
576 m_included.add( da );
577 }
578 }
579
580 // now start matching all drivers
581 final DriverMatcher mi = new DriverMatcher( DeviceManager.this );
582
583 for ( DriverAttributes driver : m_included )
584 {
585 try
586 {
587 int match = driver.match( m_ref );
588 if ( match <= Device.MATCH_NONE )
589 continue;
590 mi.add( match, driver );
591 }
592 catch ( Throwable t )
593 {
594 error( "match threw an exception", new Exception( t ) );
595 }
596 }
597
598 // get the best match
599 Match bestMatch;
600
601 // local copy
602 final DriverSelector selector = m_selector;
603 if ( selector != null )
604 {
605 bestMatch = mi.selectBestMatch( m_ref, selector );
606 }
607 else
608 {
609 bestMatch = mi.getBestMatch();
610 }
611
612 if ( bestMatch == null )
613 {
614 noDriverFound();
615 // really return
616 return null;
617 }
618
619 String driverId = String.class.cast( bestMatch.getDriver().getProperty( Constants.DRIVER_ID ) );
620
621 debug( "best match: " + driverId );
622 m_finalDriver = m_drivers.get( bestMatch.getDriver() );
623
624 if ( m_finalDriver == null )
625 {
626 error( "we found a driverId, but not the corresponding driver: " + driverId, null );
627 noDriverFound();
628 return null;
629 }
630
631 // here we get serious...
632 try
633 {
634 debug( "attaching to: " + driverId );
635 String newDriverId = m_finalDriver.attach( m_ref );
636 if ( newDriverId == null )
637 {
638 // successful attach
639 return null;
640 }
641 // its a referral
642 info( "attach led to a referral to: " + newDriverId );
643 m_excluded.add( m_finalDriver );
644 return driverAttachment( dict, new String[]
645 { newDriverId } );
646 }
647 catch ( Throwable t )
648 {
649 error( "attach failed due to an exception", t );
650 }
651 m_excluded.add( m_finalDriver );
652 return driverAttachment( dict, driverIds );
653 }
654
655
656 private void noDriverFound()
657 {
658 debug( "no suitable driver found for: " + Util.showDevice( m_ref ) );
659 if ( m_device != null )
660 {
661 m_device.noDriverFound();
662 }
663 }
664
665
666 @Override
667 public String toString()
668 {
669 return getClass().getSimpleName();// + ": " +
670 // Util.showDevice(m_ref);
671 }
672
673 }
674}