blob: d6b2190fc10a8db0920fa2183500a24d3b5bc8bd [file] [log] [blame]
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +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.cm.impl;
20
21
22import java.awt.image.ImagingOpException;
23import java.io.IOException;
24import java.security.SecureRandom;
25import java.util.ArrayList;
26import java.util.Comparator;
27import java.util.Dictionary;
28import java.util.Enumeration;
29import java.util.HashMap;
30import java.util.Hashtable;
31import java.util.Iterator;
32import java.util.List;
33import java.util.Map;
34import java.util.Set;
35import java.util.SortedSet;
36import java.util.TreeSet;
37
38import org.apache.felix.cm.PersistenceManager;
39import org.apache.felix.cm.file.FilePersistenceManager;
40import org.osgi.framework.BundleActivator;
41import org.osgi.framework.BundleContext;
42import org.osgi.framework.BundleEvent;
43import org.osgi.framework.BundleListener;
44import org.osgi.framework.Constants;
45import org.osgi.framework.Filter;
46import org.osgi.framework.InvalidSyntaxException;
47import org.osgi.framework.ServiceReference;
48import org.osgi.service.cm.ConfigurationAdmin;
49import org.osgi.service.cm.ConfigurationEvent;
50import org.osgi.service.cm.ConfigurationException;
51import org.osgi.service.cm.ConfigurationListener;
52import org.osgi.service.cm.ConfigurationPlugin;
53import org.osgi.service.cm.ManagedService;
54import org.osgi.service.cm.ManagedServiceFactory;
55import org.osgi.service.log.LogService;
56import org.osgi.util.tracker.ServiceTracker;
57
58
59/**
60 * The <code>ConfigurationManager</code> is the central class in this
61 * implementation of the Configuration Admin Service Specification. As such it
62 * has the following tasks:
63 * <ul>
64 * <li>It is a <code>BundleActivator</code> which is called when the bundle
65 * is started and stopped.
66 * <li>It is a <code>BundleListener</code> which gets informed when the
67 * states of bundles change. Mostly this is needed to unbind any bound
68 * configuration in case a bundle is uninstalled.
69 * <li>It is a <code>ServiceListener</code> which gets informed when
70 * <code>ManagedService</code> and <code>ManagedServiceFactory</code>
71 * services are registered and unregistered. This is used to provide
72 * configuration to these services. As a service listener it also listens for
73 * {@link PersistenceManager} instances being registered to support different
74 * configuration persistence layers.
75 * <li>A {@link ConfigurationAdminFactory} instance is registered as the
76 * <code>ConfigurationAdmin</code> service.
77 * <li>A {@link FilePersistenceManager} instance is registered as a default
78 * {@link PersistenceManager}.
79 * <li>Last but not least this instance manages all tasks laid out in the
80 * specification such as maintaining configuration, taking care of configuration
81 * events, etc.
82 * </ul>
83 * <p>
84 * The default {@link FilePersistenceManager} is configured with a configuration
85 * location taken from the <code>felix.cm.dir</code> framework property. If
86 * this property is not set the <code>config</code> directory in the current
87 * working directory as specified in the <code>user.dir</code> system property
88 * is used.
89 *
90 * @author fmeschbe
91 */
92public class ConfigurationManager implements BundleActivator, BundleListener
93{
94
95 /**
96 * The name of the bundle context property defining the location for the
97 * configuration files (value is "felix.cm.dir").
98 *
99 * @see #FilePersistenceManager(BundleContext)
100 */
101 public static final String CM_CONFIG_DIR = "felix.cm.dir";
102
103 // random number generator to create configuration PIDs for factory
104 // configurations
105 private static SecureRandom numberGenerator;
106
107 // comparator used to keep the ordered persistence manager map
108 private static final Comparator cmRankComp = new RankingComparator( true, ConfigurationPlugin.CM_RANKING );
109
110 // the BundleContext of the Configuration Admin Service bundle
111 private BundleContext bundleContext;
112
113 // the service reference to the ConfigurationAdmin service
114 private ServiceReference configurationAdminReference;
115
116 // the ServiceTracker to emit log services (see log(int, String, Throwable))
117 private ServiceTracker logTracker;
118
119 // the ConfigurationEvent listeners
120 private ServiceTracker configurationListenerTracker;
121
122 // service tracker for managed services
123 private ServiceTracker managedServiceTracker;
124
125 // service tracker for managed service factories
126 private ServiceTracker managedServiceFactoryTracker;
127
128 // PersistenceManager services
129 private ServiceTracker persistenceManagerTracker;
130
131 // the thread used to schedule tasks required to run asynchronously
132 private UpdateThread updateThread;
133
134 /**
135 * The actual list of {@link PersistenceManager persistence managers} to use
136 * when looking for configuration data. This list is built from the
137 * {@link #persistenceManagerMap}, which is ordered according to the
138 * {@link RankingComparator}.
139 */
140 private PersistenceManager[] persistenceManagers;
141
142 // the persistenceManagerTracker.getTrackingCount when the
143 // persistenceManagers were last got
144 private int pmtCount;
145
146 // the cache of Factory instances mapped by their factory PID
147 private Map factories;
148
149 // the cache of Configuration instances mapped by their PID
150 private Map configurations;
151
152
153 public void start( BundleContext bundleContext )
154 {
155 // track the log service using a ServiceTracker
156 logTracker = new ServiceTracker( bundleContext, LogService.class.getName(), null );
157 logTracker.open();
158
159 // set up some fields
160 this.bundleContext = bundleContext;
161 this.factories = new HashMap();
162 this.configurations = new HashMap();
163
164 // configurationlistener support
165 configurationListenerTracker = new ServiceTracker( bundleContext, ConfigurationListener.class.getName(), null );
166 configurationListenerTracker.open();
167
168 // initialize the asynchonous updater thread
169 this.updateThread = new UpdateThread( this );
170 this.updateThread.start();
171
172 // set up the location (might throw IllegalArgumentException)
173 try
174 {
175 FilePersistenceManager fpm = new FilePersistenceManager( bundleContext.getProperty( CM_CONFIG_DIR ) );
176 Hashtable props = new Hashtable();
177 props.put( Constants.SERVICE_PID, fpm.getClass().getName() );
178 props.put( Constants.SERVICE_DESCRIPTION, "Platform Filesystem Persistence Manager" );
179 props.put( Constants.SERVICE_VENDOR, "Apache Software Foundation" );
180 props.put( Constants.SERVICE_RANKING, new Integer( Integer.MIN_VALUE ) );
181 bundleContext.registerService( PersistenceManager.class.getName(), fpm, props );
182 }
183 catch ( IllegalArgumentException iae )
184 {
185 log( LogService.LOG_ERROR, "Cannot create the FilePersistenceManager", iae );
186 }
187
188 // register as bundle and service listener
189 bundleContext.addBundleListener( this );
190
191 // get all persistence managers to begin with
192 pmtCount = 1; // make sure to get the persistence managers at least
193 // once
194 persistenceManagerTracker = new ServiceTracker( bundleContext, PersistenceManager.class.getName(), null );
195 persistenceManagerTracker.open();
196
197 // create and register configuration admin - start after PM tracker ...
198 ConfigurationAdminFactory caf = new ConfigurationAdminFactory( this );
199 Hashtable props = new Hashtable();
200 props.put( Constants.SERVICE_PID, "org.apache.felix.cm.ConfigurationAdmin" );
201 props.put( Constants.SERVICE_DESCRIPTION, "Configuration Admin Service Specification 1.2 Implementation" );
202 props.put( Constants.SERVICE_VENDOR, "Apache Software Foundation" );
203 bundleContext.registerService( ConfigurationAdmin.class.getName(), caf, props );
204
205 // start handling ManagedService[Factory] services
206 managedServiceTracker = new ManagedServiceTracker();
207 managedServiceFactoryTracker = new ManagedServiceFactoryTracker();
208 }
209
210
211 public void stop( BundleContext bundleContext )
212 {
213 // stop handling ManagedService[Factory] services
214 managedServiceFactoryTracker.close();
215 managedServiceTracker.close();
216
217 // don't care for PersistenceManagers any more
218 persistenceManagerTracker.close();
219
220 // stop listening for events
221 bundleContext.removeBundleListener( this );
222
223 if ( configurationListenerTracker != null )
224 {
225 configurationListenerTracker.close();
226 }
227
228 if ( updateThread != null )
229 {
230 // terminate asynchrounous updates
231 updateThread.terminate();
232
233 // wait for all updates to terminate
234 try
235 {
236 updateThread.join();
237 }
238 catch ( InterruptedException ie )
239 {
240 // don't really care
241 }
242 }
243
244 if ( logTracker != null )
245 {
246 logTracker.close();
247 }
248
249 this.bundleContext = null;
250 this.configurations = null;
251 }
252
253
254 // ---------- Configuration caching support --------------------------------
255
256 ConfigurationImpl getCachedConfiguration( String pid )
257 {
258 synchronized ( configurations )
259 {
260 return ( ConfigurationImpl ) configurations.get( pid );
261 }
262 }
263
264
265 Iterator getCachedConfigurations()
266 {
267 synchronized ( configurations )
268 {
269 return configurations.values().iterator();
270 }
271 }
272
273
274 void cacheConfiguration( ConfigurationImpl configuration )
275 {
276 synchronized ( configurations )
277 {
278 configurations.put( configuration.getPid(), configuration );
279 }
280 }
281
282
283 void removeConfiguration( ConfigurationImpl configuration )
284 {
285 synchronized ( configurations )
286 {
287 configurations.remove( configuration.getPid() );
288 }
289 }
290
291
292 // ---------- ConfigurationAdminImpl support -------------------------------
293
294 /*
295 * (non-Javadoc)
296 *
297 * @see org.osgi.service.cm.ConfigurationAdmin#createFactoryConfiguration(java.lang.String)
298 */
299 ConfigurationImpl createFactoryConfiguration( ConfigurationAdminImpl configurationAdmin, String factoryPid )
300 throws IOException
301 {
302 Factory factory = getFactory( factoryPid );
303
304 // check Persmission if factory is bound to another bundle
305 if ( factory.getBundleLocation() != null
306 && !factory.getBundleLocation().equals( configurationAdmin.getBundle().getLocation() ) )
307 {
308 configurationAdmin.checkPermission();
309 }
310
311 // create the configuration
312 String pid = createPid( factoryPid );
313 ConfigurationImpl config = createConfiguration( pid, factoryPid );
314 config.setBundleLocation( configurationAdmin.getBundle().getLocation() );
315
316 // add the configuration to the factory
317 factory.addPID( pid );
318 factory.store();
319
320 return config;
321 }
322
323
324 /*
325 * (non-Javadoc)
326 *
327 * @see org.osgi.service.cm.ConfigurationAdmin#createFactoryConfiguration(java.lang.String,
328 * java.lang.String)
329 */
330 ConfigurationImpl createFactoryConfiguration( String factoryPid, String location ) throws IOException
331 {
332 // create the configuration
333 String pid = createPid( factoryPid );
334 ConfigurationImpl config = createConfiguration( pid, factoryPid );
335 if ( location != null )
336 {
337 config.setBundleLocation( location );
338 }
339
340 // add the configuration to the factory
341 Factory factory = getFactory( factoryPid );
342 factory.addPID( pid );
343 factory.store();
344
345 return config;
346 }
347
348
349 ConfigurationImpl getConfiguration( String pid ) throws IOException
350 {
351 return getConfiguration( pid, true );
352 }
353
354
355 ConfigurationImpl getConfiguration( String pid, String bundleLocation ) throws IOException
356 {
357 ConfigurationImpl config = getConfiguration( pid, false );
358
359 if ( config == null )
360 {
361 config = createConfiguration( pid, null );
362 if ( bundleLocation != null )
363 {
364 config.setBundleLocation( bundleLocation );
365 }
366 }
367
368 return config;
369 }
370
371
372 ConfigurationImpl[] listConfigurations( ConfigurationAdminImpl configurationAdmin, String filterString )
373 throws IOException, InvalidSyntaxException
374 {
375 Filter filter = null;
376 if ( filterString != null )
377 {
378 filter = bundleContext.createFilter( filterString );
379 }
380
381 boolean unprivileged = configurationAdmin != null && !configurationAdmin.hasPermission();
382 String location = unprivileged ? configurationAdmin.getBundle().getLocation() : null;
383
384 List configList = new ArrayList();
385
386 PersistenceManager[] pmList = getPersistenceManagers();
387 for ( int i = 0; i < pmList.length; i++ )
388 {
389 Enumeration configs = pmList[i].getDictionaries();
390 while ( configs.hasMoreElements() )
391 {
392 Dictionary config = ( Dictionary ) configs.nextElement();
393
394 // ignore non-Configuration dictionaries
395 if ( config.get( Constants.SERVICE_PID ) == null )
396 {
397 continue;
398 }
399
400 // ignore this config if not privileged and not bound to bundle
401 if ( unprivileged )
402 {
403 Object boundLocation = config.get( ConfigurationAdmin.SERVICE_BUNDLELOCATION );
404 if ( !location.equals( boundLocation ) )
405 {
406 continue;
407 }
408 }
409
410 // check filter
411 if ( filter == null || filter.match( config ) )
412 {
413 configList.add( new ConfigurationImpl( this, pmList[i], config ) );
414 }
415 }
416 }
417
418 return ( org.apache.felix.cm.impl.ConfigurationImpl[] ) configList.toArray( new ConfigurationImpl[configList
419 .size()] );
420 }
421
422
423 void deleted( ConfigurationImpl config )
424 {
425 // remove the configuration from the cache
426 removeConfiguration( config );
427 updateThread.schedule( new DeleteConfiguration( config ) );
428 }
429
430
431 void updated( ConfigurationImpl config )
432 {
433 updateThread.schedule( new UpdateConfiguration( config ) );
434 }
435
436
437 void fireConfigurationEvent( int type, ConfigurationImpl config )
438 {
439
440 updateThread.schedule( new FireConfigurationEvent( type, config ) );
441 }
442
443
444 // ---------- BundleListener -----------------------------------------------
445
446 public void bundleChanged( BundleEvent event )
447 {
448 if ( event.getType() == BundleEvent.UNINSTALLED )
449 {
450 String location = event.getBundle().getLocation();
451 String filter = "(service.bundleLocation=" + location + ")";
452
453 try
454 {
455 ConfigurationImpl[] configs = listConfigurations( null, filter );
456 if ( configs != null && configs.length > 0 )
457 {
458 for ( int i = 0; i < configs.length; i++ )
459 {
460 configs[i].setBundleLocation( null );
461 try
462 {
463 configs[i].store();
464 }
465 catch ( IOException ioe )
466 {
467 log( LogService.LOG_WARNING,
468 "Problem storing unbound configuration " + configs[i].getPid(), ioe );
469 }
470 }
471 }
472 }
473 catch ( Exception e )
474 {
475 log( LogService.LOG_WARNING, "Problem unbinding configurations for bundle " + location, e );
476 }
477
478 // unbind cached configurations
479 Iterator configurations = getCachedConfigurations();
480 while ( configurations.hasNext() )
481 {
482 ConfigurationImpl cfg = ( ConfigurationImpl ) configurations.next();
483 if ( location.equals( cfg.getBundleLocation() ) )
484 {
485 cfg.setBundleLocation( null );
486 }
487 }
488
489 // TODO: find factories to unbind !!
490 }
491 }
492
493
494 // ---------- internal -----------------------------------------------------
495
496 private PersistenceManager[] getPersistenceManagers()
497 {
498 if ( persistenceManagers == null || persistenceManagerTracker.getTrackingCount() > pmtCount )
499 {
500
501 ServiceReference[] refs = persistenceManagerTracker.getServiceReferences();
502 if ( refs == null || refs.length == 0 )
503 {
504 return new PersistenceManager[0];
505 }
506
507 SortedSet pms = new TreeSet( cmRankComp );
508 for ( int i = 0; i < refs.length; i++ )
509 {
510 pms.add( persistenceManagerTracker.getService( refs[i] ) );
511 }
512
513 persistenceManagers = ( PersistenceManager[] ) pms.toArray( new PersistenceManager[pms.size()] );
514 }
515
516 return persistenceManagers;
517 }
518
519
520 private void configure( ServiceReference sr, ManagedService service )
521 {
522 String pid = ( String ) sr.getProperty( Constants.SERVICE_PID );
523 if ( pid != null )
524 {
525 ManagedServiceUpdate update = new ManagedServiceUpdate( pid, sr, service );
526 updateThread.schedule( update );
527 }
528
529 }
530
531
532 private void configure( ServiceReference sr, ManagedServiceFactory service )
533 {
534 String pid = ( String ) sr.getProperty( Constants.SERVICE_PID );
535 if ( pid != null )
536 {
537 ManagedServiceFactoryUpdate update = new ManagedServiceFactoryUpdate( pid, sr, service );
538 updateThread.schedule( update );
539 }
540
541 }
542
543
544 ConfigurationImpl getConfiguration( String pid, boolean create ) throws IOException
545 {
546 ConfigurationImpl config = getCachedConfiguration( pid );
547 if ( config != null )
548 {
549 return config;
550 }
551
552 PersistenceManager[] pmList = getPersistenceManagers();
553 for ( int i = 0; i < pmList.length; i++ )
554 {
555 if ( pmList[i].exists( pid ) )
556 {
557 Dictionary props = pmList[i].load( pid );
558 config = new ConfigurationImpl( this, pmList[i], props );
559 cacheConfiguration( config );
560 return config;
561 }
562 }
563
564 // if getting here, there is no configuration yet, optionally create new
565 return ( create ) ? createConfiguration( pid, null ) : null;
566 }
567
568
569 ConfigurationImpl createConfiguration( String pid, String factoryPid ) throws IOException
570 {
571 ConfigurationImpl config = new ConfigurationImpl( this, getPersistenceManagers()[0], pid, factoryPid );
572
573 // immediately store the configuration, yet getProperties() must still
574 // return null
575 config.store();
576
577 cacheConfiguration( config );
578
579 return config;
580 }
581
582
583 Factory getFactory( String factoryPid ) throws IOException
584 {
585 Factory factory = ( Factory ) factories.get( factoryPid );
586 if ( factory != null )
587 {
588 return factory;
589 }
590
591 PersistenceManager[] pmList = getPersistenceManagers();
592 for ( int i = 0; i < pmList.length; i++ )
593 {
594 if ( Factory.exists( pmList[i], factoryPid ) )
595 {
596 factory = Factory.load( pmList[i], factoryPid );
597 factories.put( factoryPid, factory );
598 return factory;
599 }
600 }
601
602 // if getting here, there is no configuration yet, optionally create new
603 return createFactory( factoryPid );
604 }
605
606
607 Factory createFactory( String factoryPid )
608 {
609 Factory factory = new Factory( getPersistenceManagers()[0], factoryPid );
610 factories.put( factoryPid, factory );
611 return factory;
612 }
613
614
615 private Dictionary callPlugins( ServiceReference sr, ConfigurationImpl cfg )
616 {
617 Dictionary props = cfg.getProperties();
618
619 ServiceReference[] plugins = null;
620 try
621 {
622 String pid = ( String ) sr.getProperty( Constants.SERVICE_PID );
623 String filter = "(|(!(cm.target=*))(cm.target=" + pid + "))";
624 plugins = bundleContext.getServiceReferences( ConfigurationPlugin.class.getName(), filter );
625
626 }
627 catch ( InvalidSyntaxException ise )
628 {
629 // no filter, no exception ...
630 }
631
632 // abort early if there are no plugins
633 if ( plugins == null || plugins.length == 0 )
634 {
635 return props;
636 }
637
638 // sort the plugins by their service.cmRanking
639 SortedSet pluginSet = new TreeSet( cmRankComp );
640 for ( int i = 0; plugins != null && i < plugins.length; i++ )
641 {
642 pluginSet.add( plugins[i] );
643 }
644
645 // call the plugins in order
646 for ( Iterator pi = pluginSet.iterator(); pi.hasNext(); )
647 {
648 ServiceReference pluginRef = ( ServiceReference ) pi.next();
649 ConfigurationPlugin plugin = ( ConfigurationPlugin ) bundleContext.getService( pluginRef );
650 try
651 {
652 plugin.modifyConfiguration( sr, props );
653 }
654 catch ( Throwable t )
655 {
656 log( LogService.LOG_ERROR, "Unexpected problem calling" + " configuration plugin", t );
657 }
658 finally
659 {
660 // ensure ungetting the plugin
661 bundleContext.ungetService( pluginRef );
662 }
663 cfg.setAutoProperties( props, false );
664 }
665
666 return props;
667 }
668
669
670 /**
671 * Creates a PID for the given factoryPid
672 *
673 * @param factoryPid
674 * @return
675 */
676 private static String createPid( String factoryPid )
677 {
678 SecureRandom ng = numberGenerator;
679 if ( ng == null )
680 {
681 numberGenerator = ng = new SecureRandom();
682 }
683
684 byte[] randomBytes = new byte[16];
685 ng.nextBytes( randomBytes );
686 randomBytes[6] &= 0x0f; /* clear version */
687 randomBytes[6] |= 0x40; /* set to version 4 */
688 randomBytes[8] &= 0x3f; /* clear variant */
689 randomBytes[8] |= 0x80; /* set to IETF variant */
690
691 StringBuffer buf = new StringBuffer( factoryPid.length() + 1 + 36 );
692
693 // prefix the new pid with the factory pid
694 buf.append( factoryPid ).append( "." );
695
696 // serialize the UUID into the buffer
697 for ( int i = 0; i < randomBytes.length; i++ )
698 {
699
700 if ( i == 4 || i == 6 || i == 8 || i == 10 )
701 {
702 buf.append( '-' );
703 }
704
705 int val = randomBytes[i] & 0xff;
706 buf.append( Integer.toHexString( val >> 4 ) );
707 buf.append( Integer.toHexString( val & 0xf ) );
708 }
709
710 return buf.toString();
711 }
712
713
714 void log( int level, String message, Throwable t )
715 {
716 LogService log = ( LogService ) logTracker.getService();
717 if ( log != null )
718 {
719 log.log( configurationAdminReference, level, message, t );
720 return;
721 }
722
723 String code;
724 switch ( level )
725 {
726 case LogService.LOG_INFO:
727 code = "*INFO *";
728 break;
729
730 case LogService.LOG_WARNING:
731 code = "*WARN *";
732 break;
733
734 case LogService.LOG_ERROR:
735 code = "*ERROR*";
736 break;
737
738 case LogService.LOG_DEBUG:
739 default:
740 code = "*DEBUG*";
741 }
742
743 System.err.println( code + " " + message );
744 if ( t != null )
745 {
746 t.printStackTrace( System.err );
747 }
748 }
749
750 // ---------- inner classes ------------------------------------------------
751
752 private class ManagedServiceUpdate implements Runnable
753 {
754 private String pid;
755
756 private ServiceReference sr;
757
758 private ManagedService service;
759
760
761 ManagedServiceUpdate( String pid, ServiceReference sr, ManagedService service )
762 {
763 this.pid = pid;
764 this.sr = sr;
765 this.service = service;
766 }
767
768
769 public void run()
770 {
771 ConfigurationImpl cfg;
772 try
773 {
774 cfg = getConfiguration( pid, sr.getBundle().getLocation() );
775 }
776 catch ( IOException ioe )
777 {
778 log( LogService.LOG_ERROR, "Error loading configuration for " + pid, ioe );
779 return;
780 }
781
782 // 104.3 Ignore duplicate PIDs from other bundles and report them to
783 // the log
784 // 104.4.1 No update call back for PID already bound to another
785 // bundle location
786 String bundleLocation = sr.getBundle().getLocation();
787 if ( cfg.getBundleLocation() != null && !bundleLocation.equals( cfg.getBundleLocation() ) )
788 {
789 log( LogService.LOG_ERROR, "Cannot use configuration for " + pid + " requested by bundle "
790 + sr.getBundle().getLocation() + " but belongs to " + cfg.getBundleLocation(), null );
791 return;
792 }
793
794 // 104.3 Report an error in the log if more than one service with
795 // the same PID asks for the configuration
796 if ( cfg.getServiceReference() != null && !sr.equals( cfg.getServiceReference() ) )
797 {
798 log( LogService.LOG_ERROR, "Configuration for " + pid + " has already been used for service "
799 + cfg.getServiceReference() + " and will now also be given to " + sr, null );
800 }
801 else
802 {
803 // assign the configuration to the service
804 cfg.setServiceReference( sr );
805 }
806
807 // prepare the configuration for the service (call plugins)
808 Dictionary dictionary = callPlugins( sr, cfg );
809
810 // update the service with the configuration
811 try
812 {
813 service.updated( dictionary );
814 }
815 catch ( ConfigurationException ce )
816 {
817 if ( ce.getProperty() != null )
818 {
819 log( LogService.LOG_ERROR, sr + ": Updating configuration property " + ce.getProperty()
820 + " caused a problem: " + ce.getReason(), ce );
821 }
822 else
823 {
824 log( LogService.LOG_ERROR, sr + ": Updating configuration caused a problem: " + ce.getReason(), ce );
825
826 }
827 }
828 catch ( Throwable t )
829 {
830 log( LogService.LOG_ERROR, sr + ": Unexpected problem updating configuration", t );
831 }
832
833 // 104.5.3 CM_UPDATED is sent asynchronously to all
834 // ConfigurationListeners
835 fireConfigurationEvent( ConfigurationEvent.CM_UPDATED, cfg );
836 }
837 }
838
839 private class ManagedServiceFactoryUpdate implements Runnable
840 {
841 private String factoryPid;
842
843 private ServiceReference sr;
844
845 private ManagedServiceFactory service;
846
847
848 ManagedServiceFactoryUpdate( String factoryPid, ServiceReference sr, ManagedServiceFactory service )
849 {
850 this.factoryPid = factoryPid;
851 this.sr = sr;
852 this.service = service;
853 }
854
855
856 public void run()
857 {
858 Factory factory;
859 try
860 {
861 factory = getFactory( factoryPid );
862 }
863 catch ( IOException ioe )
864 {
865 log( LogService.LOG_ERROR, "Cannot get factory mapping for factory PID " + factoryPid, ioe );
866 return;
867 }
868
869 String bundleLocation = sr.getBundle().getLocation();
870 if ( factory.getBundleLocation() == null )
871 {
872 // bind to the location of the service if unbound
873 factory.setBundleLocation( bundleLocation );
874 }
875 else if ( !bundleLocation.equals( factory.getBundleLocation() ) )
876 {
877 // factory PID is bound to another bundle
878 log( LogService.LOG_ERROR, "Cannot use Factory configuration for " + factoryPid
879 + " requested by bundle " + sr.getBundle().getLocation() + " but belongs to "
880 + factory.getBundleLocation(), null );
881 return;
882 }
883
884 Set pids = factory.getPIDs();
885
886 for ( Iterator pi = pids.iterator(); pi.hasNext(); )
887 {
888 String pid = ( String ) pi.next();
889 ConfigurationImpl cfg;
890 try
891 {
892 cfg = getConfiguration( pid, bundleLocation );
893 }
894 catch ( IOException ioe )
895 {
896 log( LogService.LOG_ERROR, "Error loading configuration for " + pid, ioe );
897 continue;
898 }
899
900 // sanity check on the configuration
901 if ( cfg == null )
902 {
903 log( LogService.LOG_ERROR, "Configuration " + pid + " referred to by factory " + factoryPid
904 + " does not exist", null );
905 factory.removePID( pid );
906 factory.storeSilently();
907 continue;
908 }
909 else if ( !factoryPid.equals( cfg.getFactoryPid() ) )
910 {
911 log( LogService.LOG_ERROR, "Configuration " + pid + " referred to by factory " + factoryPid
912 + " does not exist seems to belong to factory " + cfg.getFactoryPid(), null );
913 factory.removePID( pid );
914 factory.storeSilently();
915 continue;
916 }
917
918 // check bundle location of configuration
919 if ( cfg.getBundleLocation() == null )
920 {
921 // bind to the location of the service if unbound
922 cfg.setBundleLocation( bundleLocation );
923 }
924 else if ( !bundleLocation.equals( cfg.getBundleLocation() ) )
925 {
926 // configuration is bound to another bundle
927 log( LogService.LOG_ERROR, "Configuration " + pid + " (factory " + factoryPid
928 + ") belongs to bundle " + cfg.getBundleLocation() + " but was requested for bundle "
929 + bundleLocation, null );
930 continue;
931 }
932
933 // prepare the configuration for the service (call plugins)
934 Dictionary dictionary = callPlugins( sr, cfg );
935
936 // update the service with the configuration
937 try
938 {
939 service.updated( pid, dictionary );
940 }
941 catch ( ConfigurationException ce )
942 {
943 if ( ce.getProperty() != null )
944 {
945 log( LogService.LOG_ERROR, sr + ": Updating configuration property " + ce.getProperty()
946 + " caused a problem: " + ce.getReason(), ce );
947 }
948 else
949 {
950 log( LogService.LOG_ERROR, sr + ": Updating configuration caused a problem: " + ce.getReason(),
951 ce );
952
953 }
954 }
955 catch ( Throwable t )
956 {
957 log( LogService.LOG_ERROR, sr + ": Unexpected problem updating configuration", t );
958 }
959 }
960 }
961 }
962
963 private class UpdateConfiguration implements Runnable
964 {
965
966 private ConfigurationImpl config;
967
968
969 UpdateConfiguration( ConfigurationImpl config )
970 {
971 this.config = config;
972 }
973
974
975 public void run()
976 {
977 try
978 {
979 if ( config.getFactoryPid() == null )
980 {
981 ServiceReference[] sr = bundleContext.getServiceReferences( ManagedService.class.getName(), "("
982 + Constants.SERVICE_PID + "=" + config.getPid() + ")" );
983 if ( sr != null && sr.length > 0 )
984 {
985 ManagedService srv = ( ManagedService ) bundleContext.getService( sr[0] );
986 try
987 {
988 // bind the configuration, fail if bound to another
989 // bundle !!
990 // check bundle location of configuration
991 String bundleLocation = sr[0].getBundle().getLocation();
992 if ( config.getBundleLocation() == null )
993 {
994 // bind to the location of the service if
995 // unbound
996 config.setBundleLocation( bundleLocation );
997 }
998 else if ( !bundleLocation.equals( config.getBundleLocation() ) )
999 {
1000 // configuration is bound to another bundle
1001 log( LogService.LOG_ERROR, "Configuration " + config.getPid() + " belongs to bundle "
1002 + config.getBundleLocation() + " but was requested for bundle " + bundleLocation,
1003 null );
1004 return;
1005 }
1006
1007 srv.updated( config.getProperties() );
1008 }
1009 finally
1010 {
1011 bundleContext.ungetService( sr[0] );
1012 }
1013 }
1014 }
1015 else
1016 {
1017 ServiceReference[] sr = bundleContext.getServiceReferences( ManagedServiceFactory.class.getName(),
1018 "(" + Constants.SERVICE_PID + "=" + config.getFactoryPid() + ")" );
1019 if ( sr != null && sr.length > 0 )
1020 {
1021 ManagedServiceFactory srv = ( ManagedServiceFactory ) bundleContext.getService( sr[0] );
1022 try
1023 {
1024 // bind the configuration, fail if bound to another
1025 // bundle !!
1026 // check bundle location of configuration
1027 String bundleLocation = sr[0].getBundle().getLocation();
1028 if ( config.getBundleLocation() == null )
1029 {
1030 // bind to the location of the service if
1031 // unbound
1032 config.setBundleLocation( bundleLocation );
1033 }
1034 else if ( !bundleLocation.equals( config.getBundleLocation() ) )
1035 {
1036 // configuration is bound to another bundle
1037 log( LogService.LOG_ERROR, "Configuration " + config.getPid() + " (factory "
1038 + config.getFactoryPid() + ") belongs to bundle " + config.getBundleLocation()
1039 + " but was requested for bundle " + bundleLocation, null );
1040 return;
1041 }
1042
1043 srv.updated( config.getPid(), config.getProperties() );
1044 }
1045 finally
1046 {
1047 bundleContext.ungetService( sr[0] );
1048 }
1049 }
1050 }
1051 }
1052 catch ( ConfigurationException ce )
1053 {
1054 if ( ce.getProperty() != null )
1055 {
1056 log( LogService.LOG_ERROR, "Updating configuration property " + ce.getProperty()
1057 + " caused a problem: " + ce.getReason(), ce );
1058 }
1059 else
1060 {
1061 log( LogService.LOG_ERROR, "Updating configuration caused a problem: " + ce.getReason(), ce );
1062
1063 }
1064 }
1065 catch ( Throwable t )
1066 {
1067 log( LogService.LOG_ERROR, "Unexpected problem updating configuration", t );
1068 }
1069
1070 fireConfigurationEvent( ConfigurationEvent.CM_UPDATED, config );
1071 }
1072 }
1073
1074 private class DeleteConfiguration implements Runnable
1075 {
1076 private ConfigurationImpl config;
1077
1078
1079 DeleteConfiguration( ConfigurationImpl config )
1080 {
1081 this.config = config;
1082 }
1083
1084
1085 public void run()
1086 {
1087 try
1088 {
1089 if ( config.getFactoryPid() == null )
1090 {
1091 ServiceReference[] sr = bundleContext.getServiceReferences( ManagedService.class.getName(), "("
1092 + Constants.SERVICE_PID + "=" + config.getPid() + ")" );
1093 if ( sr != null && sr.length > 0 )
1094 {
1095 ManagedService srv = ( ManagedService ) bundleContext.getService( sr[0] );
1096 try
1097 {
1098 srv.updated( null );
1099 }
1100 finally
1101 {
1102 bundleContext.ungetService( sr[0] );
1103 }
1104 }
1105 }
1106 else
1107 {
1108 // remove the pid from the factory
1109 Factory factory = getFactory( config.getFactoryPid() );
1110 factory.removePID( config.getPid() );
1111 factory.store();
1112
1113 ServiceReference[] sr = bundleContext.getServiceReferences( ManagedServiceFactory.class.getName(),
1114 "(" + Constants.SERVICE_PID + "=" + config.getFactoryPid() + ")" );
1115 if ( sr != null && sr.length > 0 )
1116 {
1117 ManagedServiceFactory srv = ( ManagedServiceFactory ) bundleContext.getService( sr[0] );
1118 try
1119 {
1120 srv.deleted( config.getPid() );
1121 }
1122 finally
1123 {
1124 bundleContext.ungetService( sr[0] );
1125 }
1126 }
1127 }
1128 }
1129 catch ( ConfigurationException ce )
1130 {
1131 if ( ce.getProperty() != null )
1132 {
1133 log( LogService.LOG_ERROR, "Updating configuration property " + ce.getProperty()
1134 + " caused a problem: " + ce.getReason(), ce );
1135 }
1136 else
1137 {
1138 log( LogService.LOG_ERROR, "Updating configuration caused a problem: " + ce.getReason(), ce );
1139
1140 }
1141 }
1142 catch ( Throwable t )
1143 {
1144 log( LogService.LOG_ERROR, "Unexpected problem updating configuration", t );
1145 }
1146
1147 fireConfigurationEvent( ConfigurationEvent.CM_DELETED, config );
1148 }
1149 }
1150
1151 private class FireConfigurationEvent implements Runnable
1152 {
1153 private int type;
1154
1155 private ConfigurationImpl config;
1156
1157
1158 FireConfigurationEvent( int type, ConfigurationImpl config )
1159 {
1160 this.type = type;
1161 this.config = config;
1162 }
1163
1164
1165 public void run()
1166 {
1167 // get the listeners
1168 ServiceReference[] srs = configurationListenerTracker.getServiceReferences();
1169 if ( srs == null || srs.length == 0 )
1170 {
1171 return;
1172 }
1173
1174 ConfigurationEvent event = new ConfigurationEvent( configurationAdminReference, type, config
1175 .getFactoryPid(), config.getPid() );
1176
1177 for ( int i = 0; i < srs.length; i++ )
1178 {
1179 ConfigurationListener cl = ( ConfigurationListener ) configurationListenerTracker.getService( srs[i] );
1180 try
1181 {
1182 cl.configurationEvent( event );
1183 }
1184 catch ( Throwable t )
1185 {
1186 log( LogService.LOG_ERROR, "Unexpected problem delivery configuration event to " + srs[i], t );
1187 }
1188 }
1189 }
1190 }
1191
1192 private abstract class AbstractManagedServiceTracker extends ServiceTracker
1193 {
1194 AbstractManagedServiceTracker( String className )
1195 {
1196 super( bundleContext, className, null );
1197 open();
1198 }
1199
1200
1201 public void removedService( ServiceReference reference, Object service )
1202 {
1203 // check whether we can take back the configuration object
1204 String pid = ( String ) reference.getProperty( Constants.SERVICE_PID );
1205 if ( pid != null )
1206 {
1207 ConfigurationImpl cfg = getCachedConfiguration( pid );
1208 if ( cfg != null && reference.equals( cfg.getServiceReference() ) )
1209 {
1210 cfg.setServiceReference( null );
1211 }
1212 }
1213
1214 super.removedService( reference, service );
1215 }
1216 }
1217
1218 private class ManagedServiceTracker extends AbstractManagedServiceTracker
1219 {
1220 ManagedServiceTracker()
1221 {
1222 super( ManagedService.class.getName() );
1223 }
1224
1225
1226 public Object addingService( ServiceReference reference )
1227 {
1228 ManagedService service = ( ManagedService ) super.addingService( reference );
1229
1230 // configure the managed service
1231 if ( service != null )
1232 {
1233 configure( reference, service );
1234 }
1235
1236 return service;
1237 }
1238 }
1239
1240 private class ManagedServiceFactoryTracker extends AbstractManagedServiceTracker
1241 {
1242 ManagedServiceFactoryTracker()
1243 {
1244 super( ManagedServiceFactory.class.getName() );
1245 }
1246
1247
1248 public Object addingService( ServiceReference reference )
1249 {
1250 ManagedServiceFactory service = ( ManagedServiceFactory ) super.addingService( reference );
1251
1252 // configure the managed service
1253 if ( service != null )
1254 {
1255 configure( reference, service );
1256 }
1257
1258 return service;
1259 }
1260 }
1261}