blob: f734da1b913e2f660d0e3f12312a09285cc49358 [file] [log] [blame]
Felix Meschberger2fd5b582007-12-10 10:32:29 +00001/*
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00002 * 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
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +000022import java.io.IOException;
23import java.security.SecureRandom;
Carsten Ziegeler41683982007-12-27 08:35:27 +000024import java.util.*;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +000025
26import org.apache.felix.cm.PersistenceManager;
27import org.apache.felix.cm.file.FilePersistenceManager;
Carsten Ziegeler41683982007-12-27 08:35:27 +000028import org.osgi.framework.*;
29import org.osgi.service.cm.*;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +000030import org.osgi.service.log.LogService;
31import org.osgi.util.tracker.ServiceTracker;
32
33
34/**
35 * The <code>ConfigurationManager</code> is the central class in this
36 * implementation of the Configuration Admin Service Specification. As such it
37 * has the following tasks:
38 * <ul>
39 * <li>It is a <code>BundleActivator</code> which is called when the bundle
40 * is started and stopped.
41 * <li>It is a <code>BundleListener</code> which gets informed when the
42 * states of bundles change. Mostly this is needed to unbind any bound
43 * configuration in case a bundle is uninstalled.
44 * <li>It is a <code>ServiceListener</code> which gets informed when
45 * <code>ManagedService</code> and <code>ManagedServiceFactory</code>
46 * services are registered and unregistered. This is used to provide
47 * configuration to these services. As a service listener it also listens for
48 * {@link PersistenceManager} instances being registered to support different
49 * configuration persistence layers.
50 * <li>A {@link ConfigurationAdminFactory} instance is registered as the
51 * <code>ConfigurationAdmin</code> service.
52 * <li>A {@link FilePersistenceManager} instance is registered as a default
53 * {@link PersistenceManager}.
54 * <li>Last but not least this instance manages all tasks laid out in the
55 * specification such as maintaining configuration, taking care of configuration
56 * events, etc.
57 * </ul>
58 * <p>
59 * The default {@link FilePersistenceManager} is configured with a configuration
60 * location taken from the <code>felix.cm.dir</code> framework property. If
61 * this property is not set the <code>config</code> directory in the current
62 * working directory as specified in the <code>user.dir</code> system property
63 * is used.
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +000064 */
65public class ConfigurationManager implements BundleActivator, BundleListener
66{
67
68 /**
69 * The name of the bundle context property defining the location for the
70 * configuration files (value is "felix.cm.dir").
Felix Meschberger2fd5b582007-12-10 10:32:29 +000071 *
Carsten Ziegeler7853b9a2008-01-11 16:30:24 +000072 * @see #start(BundleContext)
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +000073 */
74 public static final String CM_CONFIG_DIR = "felix.cm.dir";
75
Felix Meschberger08282c32009-01-28 07:01:55 +000076 /**
77 * The name of the bundle context property defining the maximum log level
78 * (value is "felix.cm.loglevel"). The log level setting is only used if
79 * there is no OSGi LogService available. Otherwise this setting is ignored.
80 * <p>
81 * This value of this property is expected to be an integer number
82 * corresponding to the log level values of the OSGi LogService. That is 1
83 * for errors, 2 for warnings, 3 for informational messages and 4 for debug
84 * messages. The default value is 2, such that only warnings and errors are
85 * logged in the absence of a LogService.
86 */
87 public static final String CM_LOG_LEVEL = "felix.cm.loglevel";
88
Felix Meschberger85b355d2007-08-31 07:17:38 +000089 // The name of the LogService (not using the class, which might be missing)
90 private static final String LOG_SERVICE_NAME = "org.osgi.service.log.LogService";
Felix Meschberger2fd5b582007-12-10 10:32:29 +000091
Felix Meschberger08282c32009-01-28 07:01:55 +000092 private static final int CM_LOG_LEVEL_DEFAULT = 2;
Felix Meschberger5dfaa962009-08-14 19:26:01 +000093
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +000094 // random number generator to create configuration PIDs for factory
95 // configurations
Felix Meschberger417f66c2011-02-04 11:25:23 +000096 private static Random numberGenerator;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +000097
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +000098 // the BundleContext of the Configuration Admin Service bundle
99 private BundleContext bundleContext;
100
Felix Meschbergerdb90b1b2007-09-06 08:18:10 +0000101 // the service registration of the configuration admin
Felix Meschberger4b26df92011-02-01 12:41:45 +0000102 private volatile ServiceRegistration configurationAdminRegistration;
Felix Meschberger2fd5b582007-12-10 10:32:29 +0000103
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000104 // the ServiceTracker to emit log services (see log(int, String, Throwable))
105 private ServiceTracker logTracker;
106
107 // the ConfigurationEvent listeners
108 private ServiceTracker configurationListenerTracker;
109
110 // service tracker for managed services
111 private ServiceTracker managedServiceTracker;
112
113 // service tracker for managed service factories
114 private ServiceTracker managedServiceFactoryTracker;
115
116 // PersistenceManager services
117 private ServiceTracker persistenceManagerTracker;
118
119 // the thread used to schedule tasks required to run asynchronously
120 private UpdateThread updateThread;
121
Felix Meschbergerb68d1cf2010-08-26 08:46:36 +0000122 // the thread used to schedule events to be dispatched asynchronously
123 private UpdateThread eventThread;
124
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000125 /**
126 * The actual list of {@link PersistenceManager persistence managers} to use
127 * when looking for configuration data. This list is built from the
128 * {@link #persistenceManagerMap}, which is ordered according to the
129 * {@link RankingComparator}.
130 */
131 private PersistenceManager[] persistenceManagers;
132
133 // the persistenceManagerTracker.getTrackingCount when the
134 // persistenceManagers were last got
135 private int pmtCount;
136
137 // the cache of Factory instances mapped by their factory PID
Felix Meschberger6a698df2009-08-16 18:38:46 +0000138 private final Map factories = new HashMap();
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000139
140 // the cache of Configuration instances mapped by their PID
Felix Meschberger5d3e7ac2009-08-14 21:45:10 +0000141 // have this always set to prevent NPE on bundle shutdown
142 private final Map configurations = new HashMap();
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000143
Felix Meschberger3f9e4da2009-08-17 07:52:39 +0000144 /**
145 * The map of dynamic configuration bindings. This maps the
146 * PID of the dynamically bound configuration or factory to its bundle
147 * location.
148 * <p>
149 * On bundle startup this map is loaded from persistence and validated
150 * against the locations of installed bundles: Entries pointing to bundle
151 * locations not currently installed are removed.
152 * <p>
153 * The map is written to persistence on each change.
154 */
155 private DynamicBindings dynamicBindings;
156
Felix Meschberger08282c32009-01-28 07:01:55 +0000157 // the maximum log level when no LogService is available
158 private int logLevel = CM_LOG_LEVEL_DEFAULT;
Felix Meschberger5dfaa962009-08-14 19:26:01 +0000159
Felix Meschberger2c2a5be2009-03-26 16:47:01 +0000160 // flag indicating whether BundleChange events should be consumed (FELIX-979)
161 private volatile boolean handleBundleEvents;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000162
163 public void start( BundleContext bundleContext )
164 {
165 // track the log service using a ServiceTracker
Felix Meschberger85b355d2007-08-31 07:17:38 +0000166 logTracker = new ServiceTracker( bundleContext, LOG_SERVICE_NAME , null );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000167 logTracker.open();
Felix Meschberger5dfaa962009-08-14 19:26:01 +0000168
Felix Meschberger08282c32009-01-28 07:01:55 +0000169 // assign the log level
170 String logLevelProp = bundleContext.getProperty( CM_LOG_LEVEL );
171 if ( logLevelProp == null )
172 {
173 logLevel = CM_LOG_LEVEL_DEFAULT;
174 }
175 else
176 {
177 try
178 {
179 logLevel = Integer.parseInt( logLevelProp );
180 }
181 catch ( NumberFormatException nfe )
182 {
183 logLevel = CM_LOG_LEVEL_DEFAULT;
184 }
185 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000186
187 // set up some fields
188 this.bundleContext = bundleContext;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000189
190 // configurationlistener support
191 configurationListenerTracker = new ServiceTracker( bundleContext, ConfigurationListener.class.getName(), null );
192 configurationListenerTracker.open();
193
194 // initialize the asynchonous updater thread
Felix Meschbergerb68d1cf2010-08-26 08:46:36 +0000195 ThreadGroup tg = new ThreadGroup( "Configuration Admin Service" );
196 tg.setDaemon( true );
197 this.updateThread = new UpdateThread( this, tg, "CM Configuration Updater" );
198 this.eventThread = new UpdateThread( this, tg, "CM Event Dispatcher" );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000199
200 // set up the location (might throw IllegalArgumentException)
201 try
202 {
Felix Meschberger86a0d172007-07-04 07:15:01 +0000203 FilePersistenceManager fpm = new FilePersistenceManager( bundleContext, bundleContext
204 .getProperty( CM_CONFIG_DIR ) );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000205 Hashtable props = new Hashtable();
206 props.put( Constants.SERVICE_PID, fpm.getClass().getName() );
207 props.put( Constants.SERVICE_DESCRIPTION, "Platform Filesystem Persistence Manager" );
208 props.put( Constants.SERVICE_VENDOR, "Apache Software Foundation" );
209 props.put( Constants.SERVICE_RANKING, new Integer( Integer.MIN_VALUE ) );
210 bundleContext.registerService( PersistenceManager.class.getName(), fpm, props );
Felix Meschberger3f9e4da2009-08-17 07:52:39 +0000211
212 // setup dynamic configuration bindings
213 dynamicBindings = new DynamicBindings( bundleContext, fpm );
214 }
215 catch ( IOException ioe )
216 {
217 log( LogService.LOG_ERROR, "Failure setting up dynamic configuration bindings", ioe );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000218 }
219 catch ( IllegalArgumentException iae )
220 {
221 log( LogService.LOG_ERROR, "Cannot create the FilePersistenceManager", iae );
222 }
223
224 // register as bundle and service listener
Felix Meschberger2c2a5be2009-03-26 16:47:01 +0000225 handleBundleEvents = true;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000226 bundleContext.addBundleListener( this );
227
228 // get all persistence managers to begin with
Felix Meschberger5d3e7ac2009-08-14 21:45:10 +0000229 pmtCount = 1; // make sure to get the persistence managers at least once
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000230 persistenceManagerTracker = new ServiceTracker( bundleContext, PersistenceManager.class.getName(), null );
231 persistenceManagerTracker.open();
232
233 // create and register configuration admin - start after PM tracker ...
234 ConfigurationAdminFactory caf = new ConfigurationAdminFactory( this );
235 Hashtable props = new Hashtable();
236 props.put( Constants.SERVICE_PID, "org.apache.felix.cm.ConfigurationAdmin" );
237 props.put( Constants.SERVICE_DESCRIPTION, "Configuration Admin Service Specification 1.2 Implementation" );
238 props.put( Constants.SERVICE_VENDOR, "Apache Software Foundation" );
Felix Meschbergerdb90b1b2007-09-06 08:18:10 +0000239 configurationAdminRegistration = bundleContext.registerService( ConfigurationAdmin.class.getName(), caf, props );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000240
Felix Meschberger4b26df92011-02-01 12:41:45 +0000241 // start processing the event queues only after registering the service
242 // see FELIX-2813 for details
243 this.updateThread.start();
244 this.eventThread.start();
245
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000246 // start handling ManagedService[Factory] services
Carsten Ziegeler41683982007-12-27 08:35:27 +0000247 managedServiceTracker = new ManagedServiceTracker(this);
248 managedServiceFactoryTracker = new ManagedServiceFactoryTracker(this);
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000249 }
250
251
252 public void stop( BundleContext bundleContext )
253 {
Felix Meschbergerdb90b1b2007-09-06 08:18:10 +0000254
Felix Meschberger5d3e7ac2009-08-14 21:45:10 +0000255 // stop handling bundle events immediately
256 handleBundleEvents = false;
257
Felix Meschberger4b26df92011-02-01 12:41:45 +0000258 // stop queue processing before unregistering the service
259 // see FELIX-2813 for details
260 if ( updateThread != null )
261 {
262 updateThread.terminate();
263 }
264 if ( eventThread != null )
265 {
266 eventThread.terminate();
267 }
268
Felix Meschbergerdb90b1b2007-09-06 08:18:10 +0000269 // immediately unregister the Configuration Admin before cleaning up
Felix Meschberger10568352009-01-15 08:57:11 +0000270 // clearing the field before actually unregistering the service
271 // prevents IllegalStateException in getServiceReference() if
272 // the field is not null but the service already unregistered
273 if (configurationAdminRegistration != null) {
274 ServiceRegistration reg = configurationAdminRegistration;
275 configurationAdminRegistration = null;
276 reg.unregister();
277 }
Felix Meschberger2fd5b582007-12-10 10:32:29 +0000278
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000279 // stop handling ManagedService[Factory] services
280 managedServiceFactoryTracker.close();
281 managedServiceTracker.close();
282
283 // don't care for PersistenceManagers any more
284 persistenceManagerTracker.close();
285
286 // stop listening for events
287 bundleContext.removeBundleListener( this );
288
289 if ( configurationListenerTracker != null )
290 {
291 configurationListenerTracker.close();
292 }
293
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000294 if ( logTracker != null )
295 {
296 logTracker.close();
297 }
298
Felix Meschberger6a698df2009-08-16 18:38:46 +0000299 // just ensure the configuration cache is empty
Felix Meschberger5d3e7ac2009-08-14 21:45:10 +0000300 synchronized ( configurations )
301 {
302 configurations.clear();
303 }
304
Felix Meschberger6a698df2009-08-16 18:38:46 +0000305 // just ensure the factory cache is empty
306 synchronized ( factories )
307 {
308 factories.clear();
309 }
310
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000311 this.bundleContext = null;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000312 }
313
314
315 // ---------- Configuration caching support --------------------------------
316
317 ConfigurationImpl getCachedConfiguration( String pid )
318 {
319 synchronized ( configurations )
320 {
321 return ( ConfigurationImpl ) configurations.get( pid );
322 }
323 }
324
325
Felix Meschberger6a698df2009-08-16 18:38:46 +0000326 ConfigurationImpl[] getCachedConfigurations()
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000327 {
328 synchronized ( configurations )
329 {
Felix Meschberger6a698df2009-08-16 18:38:46 +0000330 return ( ConfigurationImpl[] ) configurations.values().toArray(
331 new ConfigurationImpl[configurations.size()] );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000332 }
333 }
334
335
Felix Meschberger2941ef92007-08-20 13:15:16 +0000336 ConfigurationImpl cacheConfiguration( ConfigurationImpl configuration )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000337 {
338 synchronized ( configurations )
339 {
Felix Meschberger2941ef92007-08-20 13:15:16 +0000340 Object existing = configurations.get( configuration.getPid() );
341 if ( existing != null )
342 {
343 return ( ConfigurationImpl ) existing;
344 }
345
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000346 configurations.put( configuration.getPid(), configuration );
Felix Meschberger2941ef92007-08-20 13:15:16 +0000347 return configuration;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000348 }
349 }
350
351
352 void removeConfiguration( ConfigurationImpl configuration )
353 {
354 synchronized ( configurations )
355 {
356 configurations.remove( configuration.getPid() );
357 }
358 }
359
360
Felix Meschberger6a698df2009-08-16 18:38:46 +0000361 Factory getCachedFactory( String factoryPid )
362 {
363 synchronized ( factories )
364 {
365 return ( Factory ) factories.get( factoryPid );
366 }
367 }
368
369
370 Factory[] getCachedFactories()
371 {
372 synchronized ( factories )
373 {
374 return ( Factory[] ) factories.values().toArray( new Factory[factories.size()] );
375 }
376 }
377
378
379 void cacheFactory( Factory factory )
380 {
381 synchronized ( factories )
382 {
383 factories.put( factory.getFactoryPid(), factory );
384 }
385 }
386
387
Felix Meschberger3f9e4da2009-08-17 07:52:39 +0000388 // ---------- ConfigurationAdminImpl support
Felix Meschberger6a698df2009-08-16 18:38:46 +0000389
Felix Meschberger3f9e4da2009-08-17 07:52:39 +0000390 void setDynamicBundleLocation( final String pid, final String location )
391 {
392 if ( dynamicBindings != null )
393 {
394 try
395 {
396 dynamicBindings.putLocation( pid, location );
397 }
398 catch ( IOException ioe )
399 {
400 log( LogService.LOG_ERROR, "Failed storing dynamic configuration binding for " + pid + " to "
401 + location, ioe );
402 }
403 }
404 }
405
406
407 String getDynamicBundleLocation( final String pid )
408 {
409 if ( dynamicBindings != null )
410 {
411 return dynamicBindings.getLocation( pid );
412 }
413
414 return null;
415 }
416
417
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000418 ConfigurationImpl createFactoryConfiguration( ConfigurationAdminImpl configurationAdmin, String factoryPid )
419 throws IOException
420 {
Felix Meschberger007c50e2011-10-20 12:39:38 +0000421 return createFactoryConfiguration( factoryPid, configurationAdmin.getBundle().getLocation() );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000422 }
423
424
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000425 ConfigurationImpl createFactoryConfiguration( String factoryPid, String location ) throws IOException
426 {
Felix Meschberger007c50e2011-10-20 12:39:38 +0000427 return createConfiguration( createPid( factoryPid ), factoryPid, location );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000428 }
429
430
Felix Meschberger2941ef92007-08-20 13:15:16 +0000431 ConfigurationImpl getExistingConfiguration( String pid ) throws IOException
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000432 {
Felix Meschberger2941ef92007-08-20 13:15:16 +0000433 ConfigurationImpl config = getCachedConfiguration( pid );
434 if ( config != null )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000435 {
Felix Meschberger2941ef92007-08-20 13:15:16 +0000436 return config;
437 }
Felix Meschberger2fd5b582007-12-10 10:32:29 +0000438
Felix Meschberger2941ef92007-08-20 13:15:16 +0000439 PersistenceManager[] pmList = getPersistenceManagers();
440 for ( int i = 0; i < pmList.length; i++ )
441 {
442 if ( pmList[i].exists( pid ) )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000443 {
Felix Meschberger2941ef92007-08-20 13:15:16 +0000444 Dictionary props = pmList[i].load( pid );
445 config = new ConfigurationImpl( this, pmList[i], props );
446 return cacheConfiguration( config );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000447 }
448 }
Felix Meschberger2fd5b582007-12-10 10:32:29 +0000449
Felix Meschberger2941ef92007-08-20 13:15:16 +0000450 // neither the cache nor any persistence manager has configuration
451 return null;
452 }
Felix Meschberger2fd5b582007-12-10 10:32:29 +0000453
454
Felix Meschberger2941ef92007-08-20 13:15:16 +0000455 ConfigurationImpl getConfiguration( String pid, String bundleLocation ) throws IOException
456 {
457 // check for existing (cached or persistent) configuration
458 ConfigurationImpl config = getExistingConfiguration( pid );
459 if ( config != null )
460 {
461 return config;
462 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000463
Felix Meschberger2941ef92007-08-20 13:15:16 +0000464 // else create new configuration also setting the bundle location
Felix Meschbergerfbc7dc12008-08-06 08:25:58 +0000465 // and cache the new configuration
466 config = createConfiguration( pid, null, bundleLocation );
467 return cacheConfiguration( config );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000468 }
469
470
471 ConfigurationImpl[] listConfigurations( ConfigurationAdminImpl configurationAdmin, String filterString )
472 throws IOException, InvalidSyntaxException
473 {
474 Filter filter = null;
475 if ( filterString != null )
476 {
477 filter = bundleContext.createFilter( filterString );
478 }
479
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000480 List configList = new ArrayList();
481
482 PersistenceManager[] pmList = getPersistenceManagers();
483 for ( int i = 0; i < pmList.length; i++ )
484 {
485 Enumeration configs = pmList[i].getDictionaries();
486 while ( configs.hasMoreElements() )
487 {
488 Dictionary config = ( Dictionary ) configs.nextElement();
489
490 // ignore non-Configuration dictionaries
Felix Meschberger86a0d172007-07-04 07:15:01 +0000491 String pid = ( String ) config.get( Constants.SERVICE_PID );
492 if ( pid == null )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000493 {
494 continue;
495 }
496
Felix Meschberger007c50e2011-10-20 12:39:38 +0000497 // CM 1.4 / 104.13.2.3 Permission required
498 if ( !configurationAdmin.hasPermission( ( String ) config
499 .get( ConfigurationAdmin.SERVICE_BUNDLELOCATION ) ) )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000500 {
Felix Meschberger007c50e2011-10-20 12:39:38 +0000501 continue;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000502 }
503
504 // check filter
505 if ( filter == null || filter.match( config ) )
506 {
Felix Meschberger86a0d172007-07-04 07:15:01 +0000507 // ensure the service.pid and returned a cached config if available
508 ConfigurationImpl cfg = getCachedConfiguration( pid );
509 if ( cfg == null )
510 {
511 cfg = new ConfigurationImpl( this, pmList[i], config );
512 }
513
Felix Meschberger0c4e7042008-08-06 07:41:48 +0000514 // FELIX-611: Ignore configuration objects without props
515 if ( !cfg.isNew() )
516 {
517 configList.add( cfg );
518 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000519 }
520 }
521 }
522
Felix Meschberger8faceff2007-07-04 07:19:48 +0000523 return ( ConfigurationImpl[] ) configList.toArray( new ConfigurationImpl[configList
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000524 .size()] );
525 }
526
527
Felix Meschbergercefe5eb2009-08-19 12:37:32 +0000528 void deleted( ConfigurationImpl config )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000529 {
530 // remove the configuration from the cache
531 removeConfiguration( config );
Felix Meschberger9967fbb2010-08-25 18:13:25 +0000532 fireConfigurationEvent( ConfigurationEvent.CM_DELETED, config.getPid(), config.getFactoryPid() );
533 updateThread.schedule( new DeleteConfiguration( config ) );
534 if ( isLogEnabled( LogService.LOG_DEBUG ) )
535 {
536 log( LogService.LOG_DEBUG, "DeleteConfiguration(" + config.getPid() + ") scheduled", null );
537 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000538 }
539
540
Felix Meschbergerce67d732009-08-20 06:26:35 +0000541 void updated( ConfigurationImpl config, boolean fireEvent )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000542 {
Felix Meschberger9967fbb2010-08-25 18:13:25 +0000543 if ( fireEvent )
544 {
545 fireConfigurationEvent( ConfigurationEvent.CM_UPDATED, config.getPid(), config.getFactoryPid() );
546 }
547 updateThread.schedule( new UpdateConfiguration( config ) );
548 if ( isLogEnabled( LogService.LOG_DEBUG ) )
549 {
550 log( LogService.LOG_DEBUG, "UpdateConfiguration(" + config.getPid() + ") scheduled", null );
551 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000552 }
553
554
Felix Meschberger007c50e2011-10-20 12:39:38 +0000555 void locationChanged( ConfigurationImpl config, String oldLocation )
556 {
557 fireConfigurationEvent( ConfigurationEvent.CM_LOCATION_CHANGED, config.getPid(), config.getFactoryPid() );
558 if ( oldLocation != null )
559 {
560 updateThread.schedule( new LocationChanged( config, oldLocation ) );
561 }
562 }
563
564
Felix Meschberger66423332007-08-22 08:46:34 +0000565 void fireConfigurationEvent( int type, String pid, String factoryPid )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000566 {
Felix Meschberger9967fbb2010-08-25 18:13:25 +0000567 FireConfigurationEvent event = new FireConfigurationEvent( type, pid, factoryPid );
568 if ( event.hasConfigurationEventListeners() )
569 {
Felix Meschbergerb68d1cf2010-08-26 08:46:36 +0000570 eventThread.schedule( event );
Felix Meschberger9967fbb2010-08-25 18:13:25 +0000571 }
572 else if ( isLogEnabled( LogService.LOG_DEBUG ) )
573 {
574 log( LogService.LOG_DEBUG, "No ConfigurationListeners to send " + event.getTypeName() + " event to.", null );
575 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000576 }
577
578
579 // ---------- BundleListener -----------------------------------------------
580
581 public void bundleChanged( BundleEvent event )
582 {
Felix Meschberger2c2a5be2009-03-26 16:47:01 +0000583 if ( event.getType() == BundleEvent.UNINSTALLED && handleBundleEvents )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000584 {
Felix Meschberger6a698df2009-08-16 18:38:46 +0000585 final String location = event.getBundle().getLocation();
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000586
Felix Meschberger6a698df2009-08-16 18:38:46 +0000587 // we only reset dynamic bindings, which are only present in
588 // cached configurations, hence only consider cached configs here
589 final ConfigurationImpl[] configs = getCachedConfigurations();
590 for ( int i = 0; i < configs.length; i++ )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000591 {
Felix Meschberger6a698df2009-08-16 18:38:46 +0000592 final ConfigurationImpl cfg = configs[i];
Felix Meschberger41cce522009-08-19 05:54:40 +0000593 if ( location.equals( cfg.getDynamicBundleLocation() ) )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000594 {
Felix Meschberger007c50e2011-10-20 12:39:38 +0000595 cfg.setDynamicBundleLocation( null, true );
Felix Meschberger6a698df2009-08-16 18:38:46 +0000596 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000597 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000598 }
599 }
600
601
602 // ---------- internal -----------------------------------------------------
603
604 private PersistenceManager[] getPersistenceManagers()
605 {
Felix Meschbergerd71f4102007-07-06 15:34:32 +0000606 int currentPmtCount = persistenceManagerTracker.getTrackingCount();
607 if ( persistenceManagers == null || currentPmtCount > pmtCount )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000608 {
609
Felix Meschbergerdf26ae82009-08-14 19:50:43 +0000610 List pmList = new ArrayList();
Felix Meschbergerd71f4102007-07-06 15:34:32 +0000611 PersistenceManager[] pm;
612
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000613 ServiceReference[] refs = persistenceManagerTracker.getServiceReferences();
614 if ( refs == null || refs.length == 0 )
615 {
Felix Meschbergerd71f4102007-07-06 15:34:32 +0000616 pm = new PersistenceManager[0];
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000617 }
Felix Meschbergerd71f4102007-07-06 15:34:32 +0000618 else
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000619 {
Felix Meschbergerd71f4102007-07-06 15:34:32 +0000620 // sort the references according to the cmRanking property
Felix Meschberger007c50e2011-10-20 12:39:38 +0000621 if ( refs.length > 1 )
Felix Meschbergerd71f4102007-07-06 15:34:32 +0000622 {
Felix Meschberger007c50e2011-10-20 12:39:38 +0000623 Arrays.sort( refs, RankingComparator.SRV_RANKING );
Felix Meschbergerd71f4102007-07-06 15:34:32 +0000624 }
625
626 // create the service array from the sorted set of referenecs
Felix Meschberger007c50e2011-10-20 12:39:38 +0000627 for ( int i = 0; i < refs.length; i++ )
Felix Meschbergerd71f4102007-07-06 15:34:32 +0000628 {
Felix Meschberger007c50e2011-10-20 12:39:38 +0000629 Object service = persistenceManagerTracker.getService( refs[i] );
Felix Meschbergerdf26ae82009-08-14 19:50:43 +0000630 if ( service != null )
631 {
Felix Meschbergera86cdfc2010-08-25 09:32:36 +0000632 pmList.add( new CachingPersistenceManagerProxy( ( PersistenceManager ) service ) );
Felix Meschbergerdf26ae82009-08-14 19:50:43 +0000633 }
Felix Meschbergerd71f4102007-07-06 15:34:32 +0000634 }
Felix Meschbergerdf26ae82009-08-14 19:50:43 +0000635
636 pm = ( PersistenceManager[] ) pmList.toArray( new PersistenceManager[pmList.size()] );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000637 }
638
Felix Meschbergerdf26ae82009-08-14 19:50:43 +0000639 pmtCount = pm.length;
Felix Meschbergerd71f4102007-07-06 15:34:32 +0000640 persistenceManagers = pm;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000641 }
642
643 return persistenceManagers;
644 }
645
646
Felix Meschbergerb4f83e42009-01-15 08:53:36 +0000647 private ServiceReference getServiceReference()
648 {
649 ServiceRegistration reg = configurationAdminRegistration;
650 return ( reg != null ) ? reg.getReference() : null;
651 }
652
653
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000654 private void configure( ServiceReference sr, ManagedService service )
655 {
Felix Meschberger851c6412009-08-16 18:43:26 +0000656 String[] pids = getServicePid( sr );
657 if ( pids != null )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000658 {
Felix Meschberger851c6412009-08-16 18:43:26 +0000659 for ( int i = 0; i < pids.length; i++ )
660 {
661 ManagedServiceUpdate update = new ManagedServiceUpdate( pids[i], sr, service );
Felix Meschberger6a698df2009-08-16 18:38:46 +0000662 updateThread.schedule( update );
Felix Meschberger9967fbb2010-08-25 18:13:25 +0000663 if ( isLogEnabled( LogService.LOG_DEBUG ) )
664 {
665 log( LogService.LOG_DEBUG, "ManagedServiceUpdate(" + pids[i] + ") scheduled", null );
666 }
Felix Meschberger6a698df2009-08-16 18:38:46 +0000667 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000668 }
Felix Meschberger851c6412009-08-16 18:43:26 +0000669 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000670
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000671
672 private void configure( ServiceReference sr, ManagedServiceFactory service )
673 {
Felix Meschberger851c6412009-08-16 18:43:26 +0000674 String[] pids = getServicePid( sr );
675 if ( pids != null )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000676 {
Felix Meschberger851c6412009-08-16 18:43:26 +0000677 for ( int i = 0; i < pids.length; i++ )
678 {
679 ManagedServiceFactoryUpdate update = new ManagedServiceFactoryUpdate( pids[i], sr, service );
Felix Meschberger6a698df2009-08-16 18:38:46 +0000680 updateThread.schedule( update );
Felix Meschberger9967fbb2010-08-25 18:13:25 +0000681 if ( isLogEnabled( LogService.LOG_DEBUG ) )
682 {
683 log( LogService.LOG_DEBUG, "ManagedServiceFactoryUpdate(" + pids[i] + ") scheduled", null );
684 }
Felix Meschberger6a698df2009-08-16 18:38:46 +0000685 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000686 }
Felix Meschberger851c6412009-08-16 18:43:26 +0000687 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000688
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000689
Felix Meschbergerfbc7dc12008-08-06 08:25:58 +0000690 /**
691 * Factory method to create a new configuration object. The configuration
692 * object returned is not stored in configuration cache and only persisted
693 * if the <code>factoryPid</code> parameter is <code>null</code>.
Felix Meschberger5dfaa962009-08-14 19:26:01 +0000694 *
Felix Meschbergerfbc7dc12008-08-06 08:25:58 +0000695 * @param pid
696 * The PID of the new configuration object. Must not be
697 * <code>null</code>.
698 * @param factoryPid
699 * The factory PID of the new configuration. Not
Felix Meschberger6a698df2009-08-16 18:38:46 +0000700 * <code>null</code> if the new configuration object belongs to a
701 * factory. The configuration object will not be persisted if
Felix Meschbergerfbc7dc12008-08-06 08:25:58 +0000702 * this parameter is not <code>null</code>.
703 * @param bundleLocation
704 * The bundle location of the bundle to which the configuration
Felix Meschberger6a698df2009-08-16 18:38:46 +0000705 * belongs or <code>null</code> if the configuration is not bound
706 * yet.
Felix Meschbergerfbc7dc12008-08-06 08:25:58 +0000707 * @return The new configuration object
708 * @throws IOException
709 * May be thrown if an error occurrs persisting the new
710 * configuration object.
711 */
Felix Meschberger2941ef92007-08-20 13:15:16 +0000712 ConfigurationImpl createConfiguration( String pid, String factoryPid, String bundleLocation ) throws IOException
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000713 {
Felix Meschbergerfbc7dc12008-08-06 08:25:58 +0000714 return new ConfigurationImpl( this, getPersistenceManagers()[0], pid, factoryPid, bundleLocation );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000715 }
716
717
718 Factory getFactory( String factoryPid ) throws IOException
719 {
Felix Meschbergerf4631322008-03-10 12:32:35 +0000720 Factory factory = getCachedFactory( factoryPid );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000721 if ( factory != null )
722 {
723 return factory;
724 }
725
726 PersistenceManager[] pmList = getPersistenceManagers();
727 for ( int i = 0; i < pmList.length; i++ )
728 {
729 if ( Factory.exists( pmList[i], factoryPid ) )
730 {
Felix Meschberger3f9e4da2009-08-17 07:52:39 +0000731 factory = Factory.load( this, pmList[i], factoryPid );
Felix Meschbergerf4631322008-03-10 12:32:35 +0000732 cacheFactory( factory );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000733 return factory;
734 }
735 }
736
737 // if getting here, there is no configuration yet, optionally create new
738 return createFactory( factoryPid );
739 }
740
741
742 Factory createFactory( String factoryPid )
743 {
Felix Meschberger3f9e4da2009-08-17 07:52:39 +0000744 Factory factory = new Factory( this, getPersistenceManagers()[0], factoryPid );
Felix Meschbergerf4631322008-03-10 12:32:35 +0000745 cacheFactory( factory );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000746 return factory;
747 }
748
749
Felix Meschberger2941ef92007-08-20 13:15:16 +0000750 /**
751 * Calls the registered configuration plugins on the given configuration
Felix Meschberger41cce522009-08-19 05:54:40 +0000752 * properties from the given configuration object unless the configuration
753 * has just been created and not been updated yet.
Felix Meschberger2fd5b582007-12-10 10:32:29 +0000754 *
Felix Meschberger41cce522009-08-19 05:54:40 +0000755 * @param props The configuraiton properties run through the registered
756 * ConfigurationPlugin services. This may be <code>null</code>
757 * in which case this method just immediately returns.
Felix Meschberger5dfaa962009-08-14 19:26:01 +0000758 * @param targetPid The identification of the configuration update used to
759 * select the plugins according to their cm.target service
760 * property
Felix Meschberger2941ef92007-08-20 13:15:16 +0000761 * @param sr The service reference of the managed service (factory) which
Felix Meschberger5dfaa962009-08-14 19:26:01 +0000762 * is to be updated with configuration
Felix Meschberger2941ef92007-08-20 13:15:16 +0000763 * @param cfg The configuration object whose properties have to be passed
Felix Meschberger5dfaa962009-08-14 19:26:01 +0000764 * through the plugins
Felix Meschberger2941ef92007-08-20 13:15:16 +0000765 */
Felix Meschberger41cce522009-08-19 05:54:40 +0000766 private void callPlugins( final Dictionary props, final String targetPid, final ServiceReference sr,
767 final ConfigurationImpl cfg )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000768 {
Felix Meschberger2941ef92007-08-20 13:15:16 +0000769 // guard against NPE for new configuration never updated
770 if (props == null) {
Felix Meschberger41cce522009-08-19 05:54:40 +0000771 return;
Felix Meschberger2941ef92007-08-20 13:15:16 +0000772 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000773
774 ServiceReference[] plugins = null;
775 try
776 {
Felix Meschberger5dfaa962009-08-14 19:26:01 +0000777 String filter = "(|(!(cm.target=*))(cm.target=" + targetPid + "))";
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000778 plugins = bundleContext.getServiceReferences( ConfigurationPlugin.class.getName(), filter );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000779 }
780 catch ( InvalidSyntaxException ise )
781 {
782 // no filter, no exception ...
783 }
784
785 // abort early if there are no plugins
786 if ( plugins == null || plugins.length == 0 )
787 {
Felix Meschberger41cce522009-08-19 05:54:40 +0000788 return;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000789 }
790
791 // sort the plugins by their service.cmRanking
Felix Meschberger007c50e2011-10-20 12:39:38 +0000792 if ( plugins.length > 1 )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000793 {
Felix Meschberger007c50e2011-10-20 12:39:38 +0000794 Arrays.sort( plugins, RankingComparator.CM_RANKING );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000795 }
796
797 // call the plugins in order
Felix Meschberger007c50e2011-10-20 12:39:38 +0000798 for ( int i = 0; i < plugins.length; i++ )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000799 {
Felix Meschberger007c50e2011-10-20 12:39:38 +0000800 ServiceReference pluginRef = plugins[i];
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000801 ConfigurationPlugin plugin = ( ConfigurationPlugin ) bundleContext.getService( pluginRef );
Felix Meschberger007c50e2011-10-20 12:39:38 +0000802 if ( plugin != null )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000803 {
Felix Meschberger007c50e2011-10-20 12:39:38 +0000804 try
805 {
806 plugin.modifyConfiguration( sr, props );
807 }
808 catch ( Throwable t )
809 {
810 log( LogService.LOG_ERROR, "Unexpected problem calling configuration plugin "
811 + toString( pluginRef ), t );
812 }
813 finally
814 {
815 // ensure ungetting the plugin
816 bundleContext.ungetService( pluginRef );
817 }
818 cfg.setAutoProperties( props, false );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000819 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000820 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000821 }
822
823
824 /**
825 * Creates a PID for the given factoryPid
Felix Meschberger2fd5b582007-12-10 10:32:29 +0000826 *
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000827 * @param factoryPid
828 * @return
829 */
830 private static String createPid( String factoryPid )
831 {
Felix Meschberger417f66c2011-02-04 11:25:23 +0000832 Random ng = numberGenerator;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000833 if ( ng == null )
834 {
Felix Meschberger417f66c2011-02-04 11:25:23 +0000835 // FELIX-2771 Secure Random not available on Mika
836 try
837 {
838 ng = new SecureRandom();
839 }
840 catch ( Throwable t )
841 {
842 // fall back to Random
843 ng = new Random();
844 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000845 }
846
847 byte[] randomBytes = new byte[16];
848 ng.nextBytes( randomBytes );
849 randomBytes[6] &= 0x0f; /* clear version */
850 randomBytes[6] |= 0x40; /* set to version 4 */
851 randomBytes[8] &= 0x3f; /* clear variant */
852 randomBytes[8] |= 0x80; /* set to IETF variant */
853
854 StringBuffer buf = new StringBuffer( factoryPid.length() + 1 + 36 );
855
856 // prefix the new pid with the factory pid
857 buf.append( factoryPid ).append( "." );
858
859 // serialize the UUID into the buffer
860 for ( int i = 0; i < randomBytes.length; i++ )
861 {
862
863 if ( i == 4 || i == 6 || i == 8 || i == 10 )
864 {
865 buf.append( '-' );
866 }
867
868 int val = randomBytes[i] & 0xff;
869 buf.append( Integer.toHexString( val >> 4 ) );
870 buf.append( Integer.toHexString( val & 0xf ) );
871 }
872
873 return buf.toString();
874 }
875
876
Felix Meschberger9967fbb2010-08-25 18:13:25 +0000877 boolean isLogEnabled( int level )
878 {
879 return level <= logLevel;
880 }
881
882
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000883 void log( int level, String message, Throwable t )
884 {
Felix Meschberger08282c32009-01-28 07:01:55 +0000885 // log using the LogService if available
Felix Meschberger85b355d2007-08-31 07:17:38 +0000886 Object log = logTracker.getService();
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000887 if ( log != null )
888 {
Felix Meschbergerb4f83e42009-01-15 08:53:36 +0000889 ( ( LogService ) log ).log( getServiceReference(), level, message, t );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000890 return;
891 }
892
Felix Meschberger08282c32009-01-28 07:01:55 +0000893 // Otherwise only log if more serious than the configured level
Felix Meschberger9967fbb2010-08-25 18:13:25 +0000894 if ( isLogEnabled( level ) )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000895 {
Felix Meschberger08282c32009-01-28 07:01:55 +0000896 String code;
897 switch ( level )
898 {
899 case LogService.LOG_INFO:
900 code = "*INFO *";
901 break;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000902
Felix Meschberger08282c32009-01-28 07:01:55 +0000903 case LogService.LOG_WARNING:
904 code = "*WARN *";
905 break;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000906
Felix Meschberger08282c32009-01-28 07:01:55 +0000907 case LogService.LOG_ERROR:
908 code = "*ERROR*";
909 break;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000910
Felix Meschberger08282c32009-01-28 07:01:55 +0000911 case LogService.LOG_DEBUG:
912 default:
913 code = "*DEBUG*";
914 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000915
Felix Meschberger08282c32009-01-28 07:01:55 +0000916 System.err.println( code + " " + message );
917 if ( t != null )
918 {
919 t.printStackTrace( System.err );
920 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000921 }
922 }
923
Felix Meschberger851c6412009-08-16 18:43:26 +0000924
925 /**
926 * Returns the <code>service.pid</code> property of the service reference as
927 * an array of strings or <code>null</code> if the service reference does
928 * not have a service PID property.
929 * <p>
930 * The service.pid property may be a single string, in which case a single
931 * element array is returned. If the property is an array of string, this
932 * array is returned. If the property is a collection it is assumed to be a
933 * collection of strings and the collection is converted to an array to be
934 * returned. Otherwise (also if the property is not set) <code>null</code>
935 * is returned.
936 *
937 * @throws NullPointerException
938 * if reference is <code>null</code>
939 * @throws ArrayStoreException
940 * if the service pid is a collection and not all elements are
941 * strings.
942 */
943 static String[] getServicePid( ServiceReference reference )
944 {
945 Object pidObj = reference.getProperty( Constants.SERVICE_PID );
946 if ( pidObj instanceof String )
947 {
948 return new String[]
949 { ( String ) pidObj };
950 }
951 else if ( pidObj instanceof String[] )
952 {
953 return ( String[] ) pidObj;
954 }
955 else if ( pidObj instanceof Collection )
956 {
957 Collection pidCollection = ( Collection ) pidObj;
958 return ( String[] ) pidCollection.toArray( new String[pidCollection.size()] );
959 }
960
961 return null;
962 }
963
Felix Meschberger41cce522009-08-19 05:54:40 +0000964
965 static String toString( ServiceReference ref )
966 {
967 String[] ocs = ( String[] ) ref.getProperty( "objectClass" );
Felix Meschbergerdc8a5532011-02-18 13:11:03 +0000968 StringBuffer buf = new StringBuffer("[");
Felix Meschberger41cce522009-08-19 05:54:40 +0000969 for ( int i = 0; i < ocs.length; i++ )
970 {
Felix Meschbergerdc8a5532011-02-18 13:11:03 +0000971 buf.append(ocs[i]);
Felix Meschberger41cce522009-08-19 05:54:40 +0000972 if ( i < ocs.length - 1 )
Felix Meschbergerdc8a5532011-02-18 13:11:03 +0000973 buf.append(", ");
Felix Meschberger41cce522009-08-19 05:54:40 +0000974 }
975
Felix Meschbergerdc8a5532011-02-18 13:11:03 +0000976 buf.append( ", id=" ).append( ref.getProperty( Constants.SERVICE_ID ) );
977
978 Bundle provider = ref.getBundle();
979 if ( provider != null )
980 {
981 buf.append( ", bundle=" ).append( provider.getBundleId() );
982 }
983 else
984 {
985 buf.append( ", unregistered" );
986 }
987
988 buf.append( "]" );
989 return buf.toString();
Felix Meschberger41cce522009-08-19 05:54:40 +0000990 }
991
Felix Meschbergerce67d732009-08-20 06:26:35 +0000992
993 void handleCallBackError( final Throwable error, final ServiceReference target, final ConfigurationImpl config )
994 {
995 if ( error instanceof ConfigurationException )
996 {
997 final ConfigurationException ce = ( ConfigurationException ) error;
998 if ( ce.getProperty() != null )
999 {
1000 log( LogService.LOG_ERROR, toString( target ) + ": Updating configuration property " + ce.getProperty()
1001 + " caused a problem: " + ce.getReason(), ce );
1002 }
1003 else
1004 {
1005 log( LogService.LOG_ERROR, toString( target ) + ": Updating configuration caused a problem: "
1006 + ce.getReason(), ce );
Felix Meschbergerce67d732009-08-20 06:26:35 +00001007 }
1008 }
1009 else
1010 {
1011 {
1012 log( LogService.LOG_ERROR, toString( target ) + ": Unexpected problem updating " + config, error );
1013 }
1014
1015 }
1016 }
1017
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001018
Felix Meschberger007c50e2011-10-20 12:39:38 +00001019 /**
1020 * Checks whether the bundle is allowed to receive the configuration
1021 * with the given location binding.
1022 * <p>
1023 * This method implements the logic defined CM 1.4 / 104.4.1:
1024 * <ul>
1025 * <li>If the location is <code>null</code> (the configuration is not
1026 * bound yet), assume the bundle is allowed</li>
1027 * <li>If the location is a single location (no leading "?"), require
1028 * the bundle's location to match</li>
1029 * <li>If the location is a multi-location (leading "?"), assume the
1030 * bundle is allowed if there is no security manager. If there is a
1031 * security manager, check whether the bundle has "target" permission
1032 * on this location.</li>
1033 * </ul>
1034 */
1035 boolean canReceive( final Bundle bundle, final String location )
1036 {
1037 if ( location == null )
1038 {
1039 return true;
1040 }
1041 else if ( location.startsWith( "?" ) )
1042 {
1043 // multi-location
1044 if ( System.getSecurityManager() != null )
1045 {
1046 return bundle.hasPermission( new ConfigurationPermission( location, ConfigurationPermission.TARGET ) );
1047 }
1048 return true;
1049 }
1050 else
1051 {
1052 // single location, must match
1053 return location.equals( bundle.getLocation() );
1054 }
1055 }
1056
1057 // ---------- inner classes
1058
1059 private ServiceHelper createServiceHelper( ConfigurationImpl config )
1060 {
1061 if ( config.getFactoryPid() == null )
1062 {
1063 return new ManagedServiceHelper( config );
1064 }
1065 return new ManagedServiceFactoryHelper( config );
1066 }
1067
1068 private abstract class ServiceHelper
1069 {
1070 protected final ConfigurationImpl config;
1071
1072 private final Dictionary properties;
1073
1074 protected ServiceHelper( ConfigurationImpl config )
1075 {
1076 this.config = config;
1077 this.properties = config.getProperties( true );
1078 }
1079
1080 final ServiceReference[] getServices( )
1081 {
1082 try
1083 {
1084 ServiceReference[] refs = doGetServices();
1085 if ( refs != null && refs.length > 1 )
1086 {
1087 Arrays.sort( refs, RankingComparator.SRV_RANKING );
1088 }
1089 return refs;
1090 }
1091 catch ( InvalidSyntaxException ise )
1092 {
1093 log( LogService.LOG_ERROR, "Service selection filter is invalid to update " + config, ise );
1094 }
1095 return null;
1096 }
1097
1098
1099 protected abstract ServiceReference[] doGetServices() throws InvalidSyntaxException;
1100
1101
1102 abstract void provide( ServiceReference service );
1103
1104
1105 abstract void remove( ServiceReference service );
1106
1107
1108 protected Dictionary getProperties( String targetPid, ServiceReference service )
1109 {
1110 Dictionary props = new CaseInsensitiveDictionary( this.properties );
1111 callPlugins( props, targetPid, service, config );
1112 return props;
1113 }
1114 }
1115
1116 private class ManagedServiceHelper extends ServiceHelper
1117 {
1118
1119 protected ManagedServiceHelper( ConfigurationImpl config )
1120 {
1121 super( config );
1122 }
1123
1124
1125 public ServiceReference[] doGetServices() throws InvalidSyntaxException
1126 {
1127 return bundleContext.getServiceReferences( ManagedService.class.getName(), "(" + Constants.SERVICE_PID
1128 + "=" + config.getPid() + ")" );
1129 }
1130
1131
1132 public void provide( ServiceReference service )
1133 {
1134 ManagedService srv = ( ManagedService ) bundleContext.getService( service );
1135 if ( srv != null )
1136 {
1137 try
1138 {
1139 Dictionary props = getProperties( this.config.getPid(), service );
1140 srv.updated( props );
1141 }
1142 catch ( Throwable t )
1143 {
1144 handleCallBackError( t, service, config );
1145 }
1146 finally
1147 {
1148 bundleContext.ungetService( service );
1149 }
1150 }
1151 }
1152
1153
1154 public void remove( ServiceReference service )
1155 {
1156 ManagedService srv = ( ManagedService ) bundleContext.getService( service );
1157 try
1158 {
1159 srv.updated( null );
1160 }
1161 catch ( Throwable t )
1162 {
1163 handleCallBackError( t, service, config );
1164 }
1165 finally
1166 {
1167 bundleContext.ungetService( service );
1168 }
1169 }
1170
1171 }
1172
1173 private class ManagedServiceFactoryHelper extends ServiceHelper
1174 {
1175
1176 protected ManagedServiceFactoryHelper( ConfigurationImpl config )
1177 {
1178 super( config );
1179 // TODO Auto-generated constructor stub
1180 }
1181
1182
1183 public ServiceReference[] doGetServices() throws InvalidSyntaxException
1184 {
1185 return bundleContext.getServiceReferences( ManagedServiceFactory.class.getName(), "("
1186 + Constants.SERVICE_PID + "=" + config.getFactoryPid() + ")" );
1187 }
1188
1189
1190 public void provide( ServiceReference service )
1191 {
1192 ManagedServiceFactory srv = ( ManagedServiceFactory ) bundleContext.getService( service );
1193 if ( srv != null )
1194 {
1195 try
1196 {
1197 Dictionary props = getProperties( this.config.getFactoryPid(), service );
1198 srv.updated( config.getPid(), props );
1199 }
1200 catch ( Throwable t )
1201 {
1202 handleCallBackError( t, service, config );
1203 }
1204 finally
1205 {
1206 bundleContext.ungetService( service );
1207 }
1208 }
1209 }
1210
1211
1212 public void remove( ServiceReference service )
1213 {
1214 ManagedServiceFactory srv = ( ManagedServiceFactory ) bundleContext.getService( service );
1215 try
1216 {
1217 srv.deleted( config.getPid() );
1218 }
1219 catch ( Throwable t )
1220 {
1221 handleCallBackError( t, service, config );
1222 }
1223 finally
1224 {
1225 bundleContext.ungetService( service );
1226 }
1227 }
1228
1229 }
1230
1231 /**
1232 * The <code>ManagedServiceUpdate</code> updates a freshly registered
1233 * <code>ManagedService</code> with a specific configuration. If a
1234 * ManagedService is registered with multiple PIDs an instance of this
1235 * class is used for each registered PID.
1236 */
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001237 private class ManagedServiceUpdate implements Runnable
1238 {
Felix Meschberger41cce522009-08-19 05:54:40 +00001239 private final String pid;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001240
Felix Meschberger41cce522009-08-19 05:54:40 +00001241 private final ServiceReference sr;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001242
Felix Meschberger41cce522009-08-19 05:54:40 +00001243 private final ManagedService service;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001244
Felix Meschberger41cce522009-08-19 05:54:40 +00001245 private final ConfigurationImpl config;
1246
1247 private final Dictionary rawProperties;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001248
Felix Meschbergercb46b9e2009-08-31 14:33:15 +00001249 private final long lastModificationTime;
1250
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001251 ManagedServiceUpdate( String pid, ServiceReference sr, ManagedService service )
1252 {
1253 this.pid = pid;
1254 this.sr = sr;
1255 this.service = service;
Felix Meschberger41cce522009-08-19 05:54:40 +00001256
1257 // get or load configuration for the pid
1258 ConfigurationImpl config = null;
1259 Dictionary rawProperties = null;
Felix Meschbergercb46b9e2009-08-31 14:33:15 +00001260 long lastModificationTime = -1;
Felix Meschberger41cce522009-08-19 05:54:40 +00001261 try
1262 {
1263 config = getExistingConfiguration( pid );
Felix Meschbergercb46b9e2009-08-31 14:33:15 +00001264 if ( config != null )
1265 {
1266 synchronized ( config )
1267 {
1268 rawProperties = config.getProperties( true );
1269 lastModificationTime = config.getLastModificationTime();
1270 }
Felix Meschberger41cce522009-08-19 05:54:40 +00001271 }
1272 }
1273 catch ( IOException ioe )
1274 {
1275 log( LogService.LOG_ERROR, "Error loading configuration for " + pid, ioe );
1276 }
1277
1278 this.config = config;
1279 this.rawProperties = rawProperties;
Felix Meschbergercb46b9e2009-08-31 14:33:15 +00001280 this.lastModificationTime = lastModificationTime;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001281 }
1282
1283
1284 public void run()
1285 {
Felix Meschbergercb46b9e2009-08-31 14:33:15 +00001286 // only update configuration if lastModificationTime is less than
1287 // lastUpdateTime
Felix Meschberger41cce522009-08-19 05:54:40 +00001288 Dictionary properties = rawProperties;
Felix Meschberger007c50e2011-10-20 12:39:38 +00001289 if ( properties != null && config != null && lastModificationTime < config.getLastUpdatedTime() )
Felix Meschbergercb46b9e2009-08-31 14:33:15 +00001290 {
Felix Meschberger9967fbb2010-08-25 18:13:25 +00001291 if ( isLogEnabled( LogService.LOG_DEBUG ) )
1292 {
1293 log( LogService.LOG_DEBUG, "Configuration " + config.getPid() + " at modification #"
1294 + config.getLastModificationTime() + " has already been updated to update #"
1295 + config.getLastUpdatedTime() + ", nothing to be done anymore.", null );
1296 }
Felix Meschbergercb46b9e2009-08-31 14:33:15 +00001297 return;
1298 }
1299
1300 // check configuration and call plugins if existing
Felix Meschberger41cce522009-08-19 05:54:40 +00001301 if ( config != null )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001302 {
Felix Meschberger9967fbb2010-08-25 18:13:25 +00001303 if ( isLogEnabled( LogService.LOG_DEBUG ) )
1304 {
Felix Meschberger007c50e2011-10-20 12:39:38 +00001305 log( LogService.LOG_DEBUG, "Updating configuration " + pid + " to modification #"
Felix Meschberger9967fbb2010-08-25 18:13:25 +00001306 + config.getLastModificationTime(), null );
1307 }
Felix Meschbergercb46b9e2009-08-31 14:33:15 +00001308
Felix Meschberger41243192009-01-14 19:59:58 +00001309 Bundle serviceBundle = sr.getBundle();
1310 if ( serviceBundle == null )
1311 {
Felix Meschberger9967fbb2010-08-25 18:13:25 +00001312 if ( isLogEnabled( LogService.LOG_INFO ) )
1313 {
1314 log( LogService.LOG_INFO, "Service for PID " + pid
1315 + " seems to already have been unregistered, not updating with configuration", null );
1316 }
Felix Meschberger41243192009-01-14 19:59:58 +00001317 return;
1318 }
1319
Felix Meschberger007c50e2011-10-20 12:39:38 +00001320 // CM 1.4 / 104.13.2.2
1321 if ( !canReceive( serviceBundle, config.getBundleLocation() ) )
Felix Meschberger2941ef92007-08-20 13:15:16 +00001322 {
Felix Meschberger007c50e2011-10-20 12:39:38 +00001323 log( LogService.LOG_ERROR,
1324 "Cannot use configuration " + pid + " for " + ConfigurationManager.toString( sr )
1325 + ": No visibility to configuration bound to " + config.getBundleLocation(), null );
Felix Meschberger2941ef92007-08-20 13:15:16 +00001326 return;
1327 }
1328
Felix Meschberger007c50e2011-10-20 12:39:38 +00001329 // 104.4.2 Dynamic Binding
1330 config.tryBindLocation( serviceBundle.getLocation() );
Felix Meschberger2941ef92007-08-20 13:15:16 +00001331
1332 // prepare the configuration for the service (call plugins)
Felix Meschberger41cce522009-08-19 05:54:40 +00001333 callPlugins( properties, pid, sr, config );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001334 }
1335 else
1336 {
Felix Meschberger2941ef92007-08-20 13:15:16 +00001337 // 104.5.3 ManagedService.updated must be called with null
1338 // if no configuration is available
Felix Meschberger41cce522009-08-19 05:54:40 +00001339 properties = null;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001340 }
1341
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001342 // update the service with the configuration
1343 try
1344 {
Felix Meschberger41cce522009-08-19 05:54:40 +00001345 service.updated( properties );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001346 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001347 catch ( Throwable t )
1348 {
Felix Meschbergerce67d732009-08-20 06:26:35 +00001349 handleCallBackError( t, sr, config );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001350 }
Felix Meschbergerfd52e312009-08-29 19:44:58 +00001351
1352 // update the lastUpdatedTime if there is configuration
Felix Meschbergercb46b9e2009-08-31 14:33:15 +00001353 if ( config != null && properties != null )
Felix Meschbergerfd52e312009-08-29 19:44:58 +00001354 {
Felix Meschberger8d9851a2010-08-25 13:22:44 +00001355 config.setLastUpdatedTime( lastModificationTime );
Felix Meschberger9967fbb2010-08-25 18:13:25 +00001356 if ( isLogEnabled( LogService.LOG_DEBUG ) )
1357 {
1358 log( LogService.LOG_DEBUG, "Updated configuration " + config.getPid() + " to update #"
1359 + config.getLastUpdatedTime(), null );
1360 }
Felix Meschbergerfd52e312009-08-29 19:44:58 +00001361 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001362 }
Felix Meschberger5dfaa962009-08-14 19:26:01 +00001363
Felix Meschberger432e3872008-03-07 14:58:57 +00001364 public String toString()
1365 {
1366 return "ManagedService Update: pid=" + pid;
1367 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001368 }
1369
Felix Meschberger007c50e2011-10-20 12:39:38 +00001370 /**
1371 * The <code>ManagedServiceFactoryUpdate</code> updates a freshly
1372 * registered <code>ManagedServiceFactory</code> with a specific
1373 * configuration. If a ManagedServiceFactory is registered with
1374 * multiple PIDs an instance of this class is used for each registered
1375 * PID.
1376 */
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001377 private class ManagedServiceFactoryUpdate implements Runnable
1378 {
Felix Meschberger41cce522009-08-19 05:54:40 +00001379 private final String factoryPid;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001380
Felix Meschberger41cce522009-08-19 05:54:40 +00001381 private final ServiceReference sr;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001382
Felix Meschberger41cce522009-08-19 05:54:40 +00001383 private final ManagedServiceFactory service;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001384
Felix Meschberger41cce522009-08-19 05:54:40 +00001385 private final Map configs;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001386
Felix Meschbergercb46b9e2009-08-31 14:33:15 +00001387 private final Map stamps;
1388
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001389 ManagedServiceFactoryUpdate( String factoryPid, ServiceReference sr, ManagedServiceFactory service )
1390 {
1391 this.factoryPid = factoryPid;
1392 this.sr = sr;
1393 this.service = service;
Felix Meschberger41cce522009-08-19 05:54:40 +00001394
1395 Factory factory = null;
1396 Map configs = null;
Felix Meschbergercb46b9e2009-08-31 14:33:15 +00001397 Map stamps = null;
Felix Meschberger41cce522009-08-19 05:54:40 +00001398 try
1399 {
1400 factory = getFactory( factoryPid );
1401 if (factory != null) {
1402 configs = new HashMap();
Felix Meschbergercb46b9e2009-08-31 14:33:15 +00001403 stamps = new HashMap();
Felix Meschberger41cce522009-08-19 05:54:40 +00001404 for ( Iterator pi = factory.getPIDs().iterator(); pi.hasNext(); )
1405 {
1406 final String pid = ( String ) pi.next();
1407 ConfigurationImpl cfg;
1408 try
1409 {
1410 cfg = getExistingConfiguration( pid );
1411 }
1412 catch ( IOException ioe )
1413 {
1414 log( LogService.LOG_ERROR, "Error loading configuration for " + pid, ioe );
1415 continue;
1416 }
1417
1418 // sanity check on the configuration
1419 if ( cfg == null )
1420 {
1421 log( LogService.LOG_ERROR, "Configuration " + pid + " referred to by factory " + factoryPid
1422 + " does not exist", null );
1423 factory.removePID( pid );
1424 factory.storeSilently();
1425 continue;
1426 }
1427 else if ( cfg.isNew() )
1428 {
1429 // Configuration has just been created but not yet updated
1430 // we currently just ignore it and have the update mechanism
1431 // provide the configuration to the ManagedServiceFactory
1432 // As of FELIX-612 (not storing new factory configurations)
1433 // this should not happen. We keep this for added stability
1434 // but raise the logging level to error.
1435 log( LogService.LOG_ERROR, "Ignoring new configuration pid=" + pid, null );
1436 continue;
1437 }
1438 else if ( !factoryPid.equals( cfg.getFactoryPid() ) )
1439 {
1440 log( LogService.LOG_ERROR, "Configuration " + pid + " referred to by factory " + factoryPid
1441 + " seems to belong to factory " + cfg.getFactoryPid(), null );
1442 factory.removePID( pid );
1443 factory.storeSilently();
1444 continue;
1445 }
1446
1447 // get the configuration properties for later
Felix Meschbergercb46b9e2009-08-31 14:33:15 +00001448 synchronized ( cfg )
1449 {
1450 configs.put( cfg, cfg.getProperties( true ) );
1451 stamps.put( cfg, new Long( cfg.getLastModificationTime() ) );
1452 }
Felix Meschberger41cce522009-08-19 05:54:40 +00001453 }
1454 }
1455 }
1456 catch ( IOException ioe )
1457 {
1458 log( LogService.LOG_ERROR, "Cannot get factory mapping for factory PID " + factoryPid, ioe );
1459 }
1460
Felix Meschberger41cce522009-08-19 05:54:40 +00001461 this.configs = configs;
Felix Meschbergercb46b9e2009-08-31 14:33:15 +00001462 this.stamps = stamps;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001463 }
1464
1465
1466 public void run()
1467 {
Felix Meschberger41243192009-01-14 19:59:58 +00001468 Bundle serviceBundle = sr.getBundle();
1469 if ( serviceBundle == null )
1470 {
Felix Meschberger9967fbb2010-08-25 18:13:25 +00001471 if ( isLogEnabled( LogService.LOG_INFO ) )
1472 {
1473 log( LogService.LOG_INFO, "ManagedServiceFactory for factory PID " + factoryPid
1474 + " seems to already have been unregistered, not updating with factory", null );
1475 }
Felix Meschberger41243192009-01-14 19:59:58 +00001476 return;
1477 }
1478
Felix Meschberger41cce522009-08-19 05:54:40 +00001479 for ( Iterator ci=configs.entrySet().iterator(); ci.hasNext(); )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001480 {
Felix Meschberger41cce522009-08-19 05:54:40 +00001481 final Map.Entry entry = (Map.Entry) ci.next();
1482 final ConfigurationImpl cfg = (ConfigurationImpl) entry.getKey();
1483 final Dictionary properties = (Dictionary) entry.getValue();
Felix Meschbergercb46b9e2009-08-31 14:33:15 +00001484 final long lastModificationTime = ( ( Long ) stamps.get( cfg ) ).longValue();
1485
Felix Meschberger007c50e2011-10-20 12:39:38 +00001486 if ( lastModificationTime <= cfg.getLastUpdatedTime() )
Felix Meschbergercb46b9e2009-08-31 14:33:15 +00001487 {
Felix Meschberger9967fbb2010-08-25 18:13:25 +00001488 if ( isLogEnabled( LogService.LOG_DEBUG ) )
1489 {
1490 log( LogService.LOG_DEBUG, "Configuration " + cfg.getPid() + " at modification #"
1491 + cfg.getLastModificationTime() + " has already been updated to update #"
1492 + cfg.getLastUpdatedTime() + ", nothing to be done anymore.", null );
1493 }
Felix Meschberger007c50e2011-10-20 12:39:38 +00001494 continue;
Felix Meschbergercb46b9e2009-08-31 14:33:15 +00001495 }
1496
Felix Meschberger9967fbb2010-08-25 18:13:25 +00001497 if ( isLogEnabled( LogService.LOG_DEBUG ) )
1498 {
1499 log( LogService.LOG_DEBUG, "Updating configuration " + cfg.getPid() + " to modification #"
1500 + cfg.getLastModificationTime(), null );
1501 }
Felix Meschberger5dfaa962009-08-14 19:26:01 +00001502
Felix Meschberger007c50e2011-10-20 12:39:38 +00001503 // CM 1.4 / 104.13.2.1
1504 if ( !canReceive( serviceBundle, cfg.getBundleLocation() ) )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001505 {
Felix Meschberger007c50e2011-10-20 12:39:38 +00001506 log( LogService.LOG_ERROR,
1507 "Cannot use configuration " + cfg.getPid() + " for " + ConfigurationManager.toString( sr )
1508 + ": No visibility to configuration bound to " + cfg.getBundleLocation(), null );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001509 continue;
1510 }
1511
Felix Meschberger007c50e2011-10-20 12:39:38 +00001512 // 104.4.2 Dynamic Binding
1513 cfg.tryBindLocation( serviceBundle.getLocation() );
Felix Meschberger41cce522009-08-19 05:54:40 +00001514
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001515 // prepare the configuration for the service (call plugins)
Felix Meschberger5dfaa962009-08-14 19:26:01 +00001516 // call the plugins with cm.target set to the service's factory PID
1517 // (clarification in Section 104.9.1 of Compendium 4.2)
Felix Meschberger41cce522009-08-19 05:54:40 +00001518 callPlugins( properties, factoryPid, sr, cfg );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001519
Felix Meschbergerce67d732009-08-20 06:26:35 +00001520 // update the service with the configuration (if non-null)
1521 if ( properties != null )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001522 {
Felix Meschberger9967fbb2010-08-25 18:13:25 +00001523 if ( isLogEnabled( LogService.LOG_DEBUG ) )
1524 {
1525 log( LogService.LOG_DEBUG, sr + ": Updating configuration pid=" + cfg.getPid(), null );
1526 }
1527
Felix Meschbergerce67d732009-08-20 06:26:35 +00001528 try
Felix Meschberger2941ef92007-08-20 13:15:16 +00001529 {
Felix Meschberger41cce522009-08-19 05:54:40 +00001530 service.updated( cfg.getPid(), properties );
Felix Meschberger2941ef92007-08-20 13:15:16 +00001531 }
Felix Meschbergerce67d732009-08-20 06:26:35 +00001532 catch ( Throwable t )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001533 {
Felix Meschbergerce67d732009-08-20 06:26:35 +00001534 handleCallBackError( t, sr, cfg );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001535 }
Felix Meschbergerfd52e312009-08-29 19:44:58 +00001536
1537 // update the lastUpdatedTime
Felix Meschberger8d9851a2010-08-25 13:22:44 +00001538 cfg.setLastUpdatedTime( lastModificationTime );
Felix Meschberger9967fbb2010-08-25 18:13:25 +00001539
1540 if ( isLogEnabled( LogService.LOG_DEBUG ) )
1541 {
1542 log( LogService.LOG_DEBUG, "Updated configuration " + cfg.getPid() + " to update #"
1543 + cfg.getLastUpdatedTime(), null );
1544 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001545 }
1546 }
1547 }
Felix Meschberger5dfaa962009-08-14 19:26:01 +00001548
1549
Felix Meschberger432e3872008-03-07 14:58:57 +00001550 public String toString()
1551 {
1552 return "ManagedServiceFactory Update: factoryPid=" + factoryPid;
1553 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001554 }
1555
Felix Meschberger007c50e2011-10-20 12:39:38 +00001556
1557 /**
1558 * The <code>UpdateConfiguration</code> is used to update
1559 * <code>ManagedService[Factory]</code> services with the configuration
1560 * they are subscribed to. This may cause the configuration to be
1561 * supplied to multiple services.
1562 */
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001563 private class UpdateConfiguration implements Runnable
1564 {
1565
Felix Meschberger41cce522009-08-19 05:54:40 +00001566 private final ConfigurationImpl config;
Felix Meschberger007c50e2011-10-20 12:39:38 +00001567 private final ServiceHelper helper;
Felix Meschbergerfd52e312009-08-29 19:44:58 +00001568 private final long lastModificationTime;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001569
1570
Felix Meschberger9967fbb2010-08-25 18:13:25 +00001571 UpdateConfiguration( final ConfigurationImpl config )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001572 {
1573 this.config = config;
Felix Meschbergerfd52e312009-08-29 19:44:58 +00001574 synchronized ( config )
1575 {
Felix Meschberger007c50e2011-10-20 12:39:38 +00001576 this.helper = createServiceHelper( config );
Felix Meschbergerfd52e312009-08-29 19:44:58 +00001577 this.lastModificationTime = config.getLastModificationTime();
1578 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001579 }
1580
1581
1582 public void run()
1583 {
Felix Meschberger007c50e2011-10-20 12:39:38 +00001584 if ( lastModificationTime <= config.getLastUpdatedTime() )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001585 {
Felix Meschberger9967fbb2010-08-25 18:13:25 +00001586 if ( isLogEnabled( LogService.LOG_DEBUG ) )
1587 {
Felix Meschberger007c50e2011-10-20 12:39:38 +00001588 log( LogService.LOG_DEBUG,
1589 "Configuration " + config.getPid() + " at modification #" + config.getLastModificationTime()
1590 + " has already been updated to update #" + config.getLastUpdatedTime()
1591 + ", nothing to be done anymore.", null );
1592 }
1593 return;
1594 }
1595
1596 if ( isLogEnabled( LogService.LOG_DEBUG ) )
1597 {
1598 log(
1599 LogService.LOG_DEBUG,
1600 "Updating configuration " + config.getPid() + " to modification #"
Felix Meschberger9967fbb2010-08-25 18:13:25 +00001601 + config.getLastModificationTime(), null );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001602 }
Felix Meschberger5dfaa962009-08-14 19:26:01 +00001603
Felix Meschberger007c50e2011-10-20 12:39:38 +00001604 final ServiceReference[] srList = helper.getServices();
1605 if ( srList != null )
Felix Meschberger41cce522009-08-19 05:54:40 +00001606 {
Felix Meschberger007c50e2011-10-20 12:39:38 +00001607 // optionally bind dynamically to the first service
1608 config.tryBindLocation( srList[0].getBundle().getLocation() );
1609
1610 final String configBundleLocation = config.getBundleLocation();
1611
1612 // provide configuration to all services from the
1613 // correct bundle
Felix Meschberger41cce522009-08-19 05:54:40 +00001614 for ( int i = 0; i < srList.length; i++ )
1615 {
Felix Meschberger007c50e2011-10-20 12:39:38 +00001616 final ServiceReference ref = srList[i];
1617
1618 // CM 1.4 / 104.13.2.2
1619 if ( !canReceive( ref.getBundle(), configBundleLocation ) )
Felix Meschberger41cce522009-08-19 05:54:40 +00001620 {
Felix Meschberger007c50e2011-10-20 12:39:38 +00001621 log( LogService.LOG_ERROR, "Cannot use configuration " + config.getPid() + " for "
1622 + ConfigurationManager.toString( ref ) + ": No visibility to configuration bound to "
1623 + configBundleLocation, null );
1624 continue;
1625 }
1626
1627 helper.provide( ref );
1628
1629 if ( isLogEnabled( LogService.LOG_DEBUG ) )
1630 {
1631 log( LogService.LOG_DEBUG,
1632 "Updated configuration " + config.getPid() + " to update #" + config.getLastUpdatedTime(),
1633 null );
Felix Meschberger41cce522009-08-19 05:54:40 +00001634 }
1635 }
Felix Meschberger6f5b69e2009-08-19 09:39:21 +00001636
Felix Meschberger007c50e2011-10-20 12:39:38 +00001637 // update the lastUpdatedTime
1638 config.setLastUpdatedTime( lastModificationTime );
Felix Meschberger6f5b69e2009-08-19 09:39:21 +00001639 }
Felix Meschberger41cce522009-08-19 05:54:40 +00001640 }
1641
1642
Felix Meschberger432e3872008-03-07 14:58:57 +00001643 public String toString()
1644 {
1645 return "Update: pid=" + config.getPid();
1646 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001647 }
1648
Felix Meschberger007c50e2011-10-20 12:39:38 +00001649
1650 /**
1651 * The <code>DeleteConfiguration</code> class is used to inform
1652 * <code>ManagedService[Factory]</code> services of a configuration
1653 * being deleted.
1654 */
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001655 private class DeleteConfiguration implements Runnable
1656 {
Felix Meschberger66423332007-08-22 08:46:34 +00001657
Felix Meschbergerce67d732009-08-20 06:26:35 +00001658 private final ConfigurationImpl config;
Felix Meschbergerc12db8c2009-08-19 06:43:59 +00001659 private final String configLocation;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001660
1661
Felix Meschberger9967fbb2010-08-25 18:13:25 +00001662 DeleteConfiguration( ConfigurationImpl config )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001663 {
Felix Meschbergerce67d732009-08-20 06:26:35 +00001664 /*
1665 * NOTE: We keep the configuration because it might be cleared just
1666 * after calling this method. The pid and factoryPid fields are
1667 * final and cannot be reset.
1668 */
1669 this.config = config;
Felix Meschberger007c50e2011-10-20 12:39:38 +00001670 this.configLocation = config.getBundleLocation();
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001671 }
1672
1673
1674 public void run()
1675 {
Felix Meschbergerce67d732009-08-20 06:26:35 +00001676 final String pid = config.getPid();
1677 final String factoryPid = config.getFactoryPid();
Felix Meschberger007c50e2011-10-20 12:39:38 +00001678 final ServiceHelper helper = createServiceHelper( config );
Felix Meschbergerce67d732009-08-20 06:26:35 +00001679
Felix Meschberger007c50e2011-10-20 12:39:38 +00001680 ServiceReference[] srList = helper.getServices( );
1681 if ( srList != null )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001682 {
Felix Meschberger007c50e2011-10-20 12:39:38 +00001683 for ( int i = 0; i < srList.length; i++ )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001684 {
Felix Meschberger007c50e2011-10-20 12:39:38 +00001685 final ServiceReference sr = srList[i];
1686 if ( canReceive( sr.getBundle(), configLocation ) )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001687 {
Felix Meschberger007c50e2011-10-20 12:39:38 +00001688 helper.remove( sr );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001689 }
1690 }
1691 }
Felix Meschberger007c50e2011-10-20 12:39:38 +00001692
1693 if ( factoryPid != null )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001694 {
Felix Meschberger007c50e2011-10-20 12:39:38 +00001695 // remove the pid from the factory
1696 try
1697 {
1698 Factory factory = getFactory( factoryPid );
1699 factory.removePID( pid );
1700 factory.store();
1701 }
1702 catch ( IOException ioe )
1703 {
1704 log( LogService.LOG_ERROR, "Failed removing " + pid + " from the factory " + factoryPid, ioe );
1705 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001706 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001707 }
Felix Meschberger5dfaa962009-08-14 19:26:01 +00001708
Felix Meschberger432e3872008-03-07 14:58:57 +00001709 public String toString()
1710 {
Felix Meschbergerce67d732009-08-20 06:26:35 +00001711 return "Delete: pid=" + config.getPid();
Felix Meschberger432e3872008-03-07 14:58:57 +00001712 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001713 }
1714
Felix Meschberger007c50e2011-10-20 12:39:38 +00001715 private class LocationChanged implements Runnable
1716 {
1717 private final ConfigurationImpl config;
1718 private final String oldLocation;
1719
1720
1721 LocationChanged( ConfigurationImpl config, String oldLocation )
1722 {
1723 this.config = config;
1724 this.oldLocation = oldLocation;
1725 }
1726
1727
1728 public void run()
1729 {
1730 ServiceHelper helper = createServiceHelper( this.config );
1731 ServiceReference[] srList = helper.getServices( );
1732 if ( srList != null )
1733 {
1734 // make sure the config is dynamically bound to the first
1735 // service if it has been unbound causing this update
1736 config.tryBindLocation( srList[0].getBundle().getLocation() );
1737
1738 for ( int i = 0; i < srList.length; i++ )
1739 {
1740 final ServiceReference sr = srList[i];
1741 final boolean wasVisible = canReceive( sr.getBundle(), oldLocation );
1742 final boolean isVisible = canReceive( sr.getBundle(), config.getBundleLocation() );
1743 if ( wasVisible && !isVisible )
1744 {
1745 // call deleted method
1746 helper.remove( sr );
1747 }
1748 else if ( !wasVisible && isVisible )
1749 {
1750 // call updated method
1751 helper.provide( sr );
1752 }
1753 }
1754 }
1755 }
1756
1757
1758 public String toString()
1759 {
1760 return "Location Changed (pid=" + config.getPid() + "): " + oldLocation + " ==> "
1761 + config.getBundleLocation();
1762 }
1763 }
1764
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001765 private class FireConfigurationEvent implements Runnable
1766 {
Felix Meschberger9967fbb2010-08-25 18:13:25 +00001767 private final int type;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001768
Felix Meschberger9967fbb2010-08-25 18:13:25 +00001769 private final String pid;
Felix Meschberger2fd5b582007-12-10 10:32:29 +00001770
Felix Meschberger9967fbb2010-08-25 18:13:25 +00001771 private final String factoryPid;
1772
1773 private final ServiceReference[] listenerReferences;
1774
1775 private final ConfigurationListener[] listeners;
1776
1777 private final Bundle[] listenerProvider;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001778
1779
Felix Meschberger9967fbb2010-08-25 18:13:25 +00001780 private FireConfigurationEvent( final int type, final String pid, final String factoryPid)
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001781 {
1782 this.type = type;
Felix Meschberger66423332007-08-22 08:46:34 +00001783 this.pid = pid;
1784 this.factoryPid = factoryPid;
Felix Meschberger9967fbb2010-08-25 18:13:25 +00001785
1786 final ServiceReference[] srs = configurationListenerTracker.getServiceReferences();
1787 if ( srs == null || srs.length == 0 )
1788 {
1789 this.listenerReferences = null;
1790 this.listeners = null;
1791 this.listenerProvider = null;
1792 }
1793 else
1794 {
1795 this.listenerReferences = srs;
1796 this.listeners = new ConfigurationListener[srs.length];
1797 this.listenerProvider = new Bundle[srs.length];
1798 for ( int i = 0; i < srs.length; i++ )
1799 {
1800 this.listeners[i] = ( ConfigurationListener ) configurationListenerTracker.getService( srs[i] );
1801 this.listenerProvider[i] = srs[i].getBundle();
1802 }
1803 }
1804 }
1805
1806
1807 boolean hasConfigurationEventListeners()
1808 {
1809 return this.listenerReferences != null;
1810 }
1811
1812
1813 String getTypeName()
1814 {
1815 switch ( type )
1816 {
1817 case ConfigurationEvent.CM_DELETED:
1818 return "CM_DELETED";
1819 case ConfigurationEvent.CM_UPDATED:
1820 return "CM_UPDATED";
Felix Meschberger007c50e2011-10-20 12:39:38 +00001821 case ConfigurationEvent.CM_LOCATION_CHANGED:
1822 return "CM_LOCATION_CHANGED";
Felix Meschberger9967fbb2010-08-25 18:13:25 +00001823 default:
1824 return "<UNKNOWN(" + type + ")>";
1825 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001826 }
1827
1828
1829 public void run()
1830 {
Felix Meschberger9967fbb2010-08-25 18:13:25 +00001831 final String typeName = getTypeName();
1832 final ConfigurationEvent event = new ConfigurationEvent( getServiceReference(), type, factoryPid, pid );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001833
Felix Meschberger9967fbb2010-08-25 18:13:25 +00001834 for ( int i = 0; i < listeners.length; i++ )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001835 {
Felix Meschberger9967fbb2010-08-25 18:13:25 +00001836 if ( listenerProvider[i].getState() == Bundle.ACTIVE )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001837 {
Felix Meschberger9967fbb2010-08-25 18:13:25 +00001838 if ( isLogEnabled( LogService.LOG_DEBUG ) )
1839 {
1840 log( LogService.LOG_DEBUG, "Sending " + typeName + " event for " + pid + " to "
1841 + ConfigurationManager.toString( listenerReferences[i] ), null );
1842 }
1843
1844 try
1845 {
1846 listeners[i].configurationEvent( event );
1847 }
1848 catch ( Throwable t )
1849 {
1850 log( LogService.LOG_ERROR, "Unexpected problem delivery configuration event to "
1851 + ConfigurationManager.toString( listenerReferences[i] ), t );
1852 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001853 }
1854 }
1855 }
Felix Meschberger5dfaa962009-08-14 19:26:01 +00001856
Felix Meschberger432e3872008-03-07 14:58:57 +00001857 public String toString()
1858 {
1859 return "Fire ConfigurationEvent: pid=" + pid;
1860 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001861 }
1862
Felix Meschberger41cce522009-08-19 05:54:40 +00001863 private static class ManagedServiceTracker extends ServiceTracker
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001864 {
Carsten Ziegeler41683982007-12-27 08:35:27 +00001865
Felix Meschberger41cce522009-08-19 05:54:40 +00001866 private final ConfigurationManager cm;
1867
1868
1869 ManagedServiceTracker( ConfigurationManager cm )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001870 {
Felix Meschberger41cce522009-08-19 05:54:40 +00001871 super( cm.bundleContext, ManagedService.class.getName(), null );
Carsten Ziegeler41683982007-12-27 08:35:27 +00001872 this.cm = cm;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001873 open();
1874 }
1875
1876
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001877 public Object addingService( ServiceReference reference )
1878 {
Felix Meschberger2fd5b582007-12-10 10:32:29 +00001879 Object serviceObject = super.addingService( reference );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001880
1881 // configure the managed service
Felix Meschberger2fd5b582007-12-10 10:32:29 +00001882 if ( serviceObject instanceof ManagedService )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001883 {
Carsten Ziegeler41683982007-12-27 08:35:27 +00001884 cm.configure(reference, ( ManagedService ) serviceObject);
Felix Meschberger2fd5b582007-12-10 10:32:29 +00001885 }
1886 else
1887 {
Carsten Ziegeler41683982007-12-27 08:35:27 +00001888 cm.log( LogService.LOG_WARNING, "Service " + serviceObject + " is not a ManagedService", null );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001889 }
1890
Felix Meschberger2fd5b582007-12-10 10:32:29 +00001891 return serviceObject;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001892 }
1893 }
1894
Felix Meschberger41cce522009-08-19 05:54:40 +00001895 private static class ManagedServiceFactoryTracker extends ServiceTracker
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001896 {
Felix Meschberger41cce522009-08-19 05:54:40 +00001897 private final ConfigurationManager cm;
1898
1899
1900 ManagedServiceFactoryTracker( ConfigurationManager cm )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001901 {
Felix Meschberger41cce522009-08-19 05:54:40 +00001902 super( cm.bundleContext, ManagedServiceFactory.class.getName(), null );
1903 this.cm = cm;
1904 open();
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001905 }
1906
1907
1908 public Object addingService( ServiceReference reference )
1909 {
Felix Meschberger2fd5b582007-12-10 10:32:29 +00001910 Object serviceObject = super.addingService( reference );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001911
Felix Meschberger2fd5b582007-12-10 10:32:29 +00001912 // configure the managed service factory
1913 if ( serviceObject instanceof ManagedServiceFactory )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001914 {
Carsten Ziegeler41683982007-12-27 08:35:27 +00001915 cm.configure( reference, ( ManagedServiceFactory ) serviceObject );
Felix Meschberger2fd5b582007-12-10 10:32:29 +00001916 }
1917 else
1918 {
Carsten Ziegeler41683982007-12-27 08:35:27 +00001919 cm.log( LogService.LOG_WARNING, "Service " + serviceObject + " is not a ManagedServiceFactory", null );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001920 }
1921
Felix Meschberger2fd5b582007-12-10 10:32:29 +00001922 return serviceObject;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001923 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001924 }
Felix Meschberger007c50e2011-10-20 12:39:38 +00001925
1926
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +00001927}