blob: 676c33a226d7a29f156ad7700886ccb488848b23 [file] [log] [blame]
Carsten Ziegeler6c6f25b2007-08-15 10:04:02 +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
22import java.io.IOException;
23import java.util.Dictionary;
24import java.util.Hashtable;
25
26import org.apache.felix.cm.PersistenceManager;
27import org.osgi.framework.Constants;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +000028import org.osgi.service.cm.Configuration;
29import org.osgi.service.cm.ConfigurationAdmin;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +000030import org.osgi.service.log.LogService;
Carsten Ziegelereb80d512007-08-15 11:17:41 +000031
Felix Meschberger93c409a2009-01-19 10:47:59 +000032
Carsten Ziegelereb80d512007-08-15 11:17:41 +000033/**
Felix Meschberger93c409a2009-01-19 10:47:59 +000034 * The <code>ConfigurationImpl</code> is the backend implementation of the
35 * Configuration Admin Service Specification <i>Configuration object</i>
36 * (section 104.4). Instances of this class are shared by multiple instances of
37 * the {@link ConfigurationAdapter} class, whose instances are actually returned
38 * to clients.
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +000039 */
Felix Meschbergeref470042009-08-19 05:52:41 +000040class ConfigurationImpl extends ConfigurationBase
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +000041{
42
Felix Meschbergerfd52e312009-08-29 19:44:58 +000043 /*
44 * Concurrency note: There is a slight (but real) chance of a race condition
45 * between a configuration update and a ManagedService[Factory] registration.
46 * Per the specification a ManagedService must be called with configuration
47 * or null when registered and a ManagedService must be called with currently
48 * existing configuration when registered. Also the ManagedService[Factory]
49 * must be updated when the configuration is updated.
50 *
51 * Consider now this situation of two threads T1 and T2:
52 *
53 * T1. create and update configuration
54 * ConfigurationImpl.update persists configuration and sets field
55 * Thread preempted
56 *
57 * T2. ManagedServiceUpdate constructor reads configuration
58 * Uses configuration already persisted by T1 for update
59 * Schedules task to update service with the configuration
60 *
61 * T1. Runs again creating the UpdateConfiguration task with the
62 * configuration persisted before being preempted
63 * Schedules task to update service
64 *
65 * Update Thread:
66 * Updates ManagedService with configuration prepared by T2
67 * Updates ManagedService with configuration prepared by T1
68 *
69 * The correct behaviour would be here, that the second call to update
70 * would not take place.
71 *
72 * This concurrency safety is implemented with the help of the
73 * lastModificationTime field updated by the configure(Dictionary) method
74 * when setting the properties field and the lastUpdatedTime field updated
75 * in the Update Thread after calling the update(Dictionary) method of
76 * the ManagedService[Factory] service.
77 *
78 * The UpdateConfiguration task compares the lastModificationTime to the
79 * lastUpdateTime. If the configuration has been modified after being
80 * updated the last time, it is updated in the ManagedService[Factory]. If
81 * the configuration has already been updated since being modified (as in
82 * the case above), the UpdateConfiguration thread does not call the update
83 * method (but still sends the CM_UPDATED event).
84 *
85 * See also FELIX-1542.
Felix Meschbergerf23eb072009-08-31 14:04:33 +000086 *
87 * FELIX-1545 provides further update to the concurrency situation defining
88 * three more failure cases:
89 *
90 * (1) System.currentTimeMillis() may be too coarse graind to protect
91 * against race condition.
92 * (2) ManagedService update sets last update time regardless of whether
93 * configuration was provided or not. This may cause a configuration
94 * update to be lost.
95 * (3) ManagedService update does not respect last update time which
96 * in turn may cause duplicate configuration delivery.
Felix Meschbergerfd52e312009-08-29 19:44:58 +000097 */
98
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +000099 /**
Felix Meschberger2941ef92007-08-20 13:15:16 +0000100 * The name of a synthetic property stored in the persisted configuration
101 * data to indicate that the configuration data is new, that is created but
102 * never updated (value is "_felix_.cm.newConfiguration").
103 * <p>
104 * This special property is stored by the
105 * {@link #ConfigurationImpl(ConfigurationManager, PersistenceManager, String, String, String)}
106 * constructor, when the configuration is first created and persisted and is
107 * interpreted by the
108 * {@link #ConfigurationImpl(ConfigurationManager, PersistenceManager, Dictionary)}
109 * method when the configuration data is loaded in a new object.
110 * <p>
111 * The goal of this property is to keep the information on whether
112 * configuration data is new (but persisted as per the spec) or has already
113 * been assigned with possible no data.
114 */
115 private static final String CONFIGURATION_NEW = "_felix_.cm.newConfiguration";
Felix Meschbergerdf26ae82009-08-14 19:50:43 +0000116
Felix Meschberger2941ef92007-08-20 13:15:16 +0000117 /**
Felix Meschberger007c50e2011-10-20 12:39:38 +0000118 * The factory PID of this configuration or <code>null</code> if this
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000119 * is not a factory configuration.
120 */
Felix Meschbergerdf26ae82009-08-14 19:50:43 +0000121 private final String factoryPID;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000122
123 /**
Felix Meschberger007c50e2011-10-20 12:39:38 +0000124 * The statically bound bundle location, which is set explicitly by calling
125 * the Configuration.setBundleLocation(String) method or when the
126 * configuration was created with the two-argument method.
127 */
128 private volatile String staticBundleLocation;
129
130 /**
131 * The bundle location from dynamic binding. This value is set as the
132 * configuration or factory is assigned to a ManagedService[Factory].
133 */
134 private volatile String dynamicBundleLocation;
135
136 /**
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000137 * The configuration data of this configuration instance. This is a private
138 * copy of the properties of which a copy is made when the
Felix Meschbergeref470042009-08-19 05:52:41 +0000139 * {@link #getProperties()} method is called. This field is
140 * <code>null</code> if the configuration has been created and never been
141 * updated with acutal configuration properties.
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000142 */
Felix Meschbergerdf26ae82009-08-14 19:50:43 +0000143 private volatile CaseInsensitiveDictionary properties;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000144
Felix Meschbergerf4631322008-03-10 12:32:35 +0000145 /**
Felix Meschbergeref470042009-08-19 05:52:41 +0000146 * Flag indicating that this configuration has been deleted.
147 *
148 * @see #isDeleted()
Felix Meschbergerf4631322008-03-10 12:32:35 +0000149 */
Felix Meschbergeref470042009-08-19 05:52:41 +0000150 private volatile boolean isDeleted;
151
Felix Meschbergerfd52e312009-08-29 19:44:58 +0000152 /**
Felix Meschbergerf23eb072009-08-31 14:04:33 +0000153 * Current configuration modification counter. This field is incremented
154 * each time the {@link #properties} field is set (in the constructor or the
155 * {@link #configure(Dictionary)} method. field.
Felix Meschbergerfd52e312009-08-29 19:44:58 +0000156 */
157 private volatile long lastModificationTime;
158
159 /**
Felix Meschbergerf23eb072009-08-31 14:04:33 +0000160 * Value of the {@link #lastModificationTime} counter at the time the non-
161 * <code>null</code> properties of this configuration have been updated to a
162 * ManagedService[Factory]. This field is initialized to -1 in the
163 * constructors and set to the value of the {@link #lastModificationTime} by
164 * the {@link #setLastUpdatedTime()} method called from the respective task
165 * updating the configuration.
Felix Meschbergerfd52e312009-08-29 19:44:58 +0000166 *
167 * @see #lastModificationTime
168 */
169 private volatile long lastUpdatedTime;
170
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000171
172 ConfigurationImpl( ConfigurationManager configurationManager, PersistenceManager persistenceManager,
173 Dictionary properties )
174 {
Felix Meschberger007c50e2011-10-20 12:39:38 +0000175 super( configurationManager, persistenceManager, ( String ) properties.remove( Constants.SERVICE_PID ) );
Felix Meschbergerdf26ae82009-08-14 19:50:43 +0000176
Felix Meschbergerdf26ae82009-08-14 19:50:43 +0000177 this.factoryPID = ( String ) properties.remove( ConfigurationAdmin.SERVICE_FACTORYPID );
Felix Meschbergeref470042009-08-19 05:52:41 +0000178 this.isDeleted = false;
Felix Meschbergerfd52e312009-08-29 19:44:58 +0000179 this.lastUpdatedTime = -1;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000180
Felix Meschberger007c50e2011-10-20 12:39:38 +0000181 // set bundle location from persistence and/or check for dynamic binding
182 this.staticBundleLocation = ( String ) properties.remove( ConfigurationAdmin.SERVICE_BUNDLELOCATION ) ;
183 this.dynamicBundleLocation = configurationManager.getDynamicBundleLocation( getBaseId() );
184
Felix Meschberger2941ef92007-08-20 13:15:16 +0000185 // set the properties internally
186 configureFromPersistence( properties );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000187 }
188
189
190 ConfigurationImpl( ConfigurationManager configurationManager, PersistenceManager persistenceManager, String pid,
Felix Meschberger2941ef92007-08-20 13:15:16 +0000191 String factoryPid, String bundleLocation ) throws IOException
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000192 {
Felix Meschberger007c50e2011-10-20 12:39:38 +0000193 super( configurationManager, persistenceManager, pid );
Felix Meschbergerdf26ae82009-08-14 19:50:43 +0000194
Felix Meschberger2941ef92007-08-20 13:15:16 +0000195 this.factoryPID = factoryPid;
Felix Meschbergeref470042009-08-19 05:52:41 +0000196 this.isDeleted = false;
Felix Meschbergerfd52e312009-08-29 19:44:58 +0000197 this.lastUpdatedTime = -1;
Felix Meschberger2941ef92007-08-20 13:15:16 +0000198
Felix Meschberger007c50e2011-10-20 12:39:38 +0000199 // set bundle location from persistence and/or check for dynamic binding
200 this.staticBundleLocation = bundleLocation;
201 this.dynamicBundleLocation = configurationManager.getDynamicBundleLocation( getBaseId() );
202
Felix Meschbergerf23eb072009-08-31 14:04:33 +0000203 // first "update"
204 this.properties = null;
205 setLastModificationTime();
206
Felix Meschbergerfbc7dc12008-08-06 08:25:58 +0000207 // this is a new configuration object, store immediately unless
208 // the new configuration object is created from a factory, in which
209 // case the configuration is only stored when first updated
Felix Meschbergeref470042009-08-19 05:52:41 +0000210 if ( factoryPid == null )
211 {
Felix Meschbergerfbc7dc12008-08-06 08:25:58 +0000212 Dictionary props = new Hashtable();
Felix Meschbergeref470042009-08-19 05:52:41 +0000213 setAutoProperties( props, true );
Felix Meschbergerfbc7dc12008-08-06 08:25:58 +0000214 props.put( CONFIGURATION_NEW, Boolean.TRUE );
215 persistenceManager.store( pid, props );
216 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000217 }
218
219
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000220 public void delete() throws IOException
221 {
Felix Meschbergeref470042009-08-19 05:52:41 +0000222 this.isDeleted = true;
223 getPersistenceManager().delete( this.getPid() );
Felix Meschbergercefe5eb2009-08-19 12:37:32 +0000224 getConfigurationManager().setDynamicBundleLocation( this.getPid(), null );
225 getConfigurationManager().deleted( this );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000226 }
227
228
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000229 public String getPid()
230 {
Felix Meschbergeref470042009-08-19 05:52:41 +0000231 return getBaseId();
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000232 }
233
234
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000235 public String getFactoryPid()
236 {
Carsten Ziegelereb80d512007-08-15 11:17:41 +0000237 return factoryPID;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000238 }
239
240
Felix Meschberger007c50e2011-10-20 12:39:38 +0000241
242
243 /**
244 * Returns the "official" bundle location as visible from the outside
245 * world of code calling into the Configuration.getBundleLocation() method.
246 * <p>
247 * In other words: The {@link #getStaticBundleLocation()} is returned if
248 * not <code>null</code>. Otherwise the {@link #getDynamicBundleLocation()}
249 * is returned (which may also be <code>null</code>).
250 */
251 String getBundleLocation()
252 {
253 if ( staticBundleLocation != null )
254 {
255 return staticBundleLocation;
256 }
257
258 return dynamicBundleLocation;
259 }
260
261
262 String getDynamicBundleLocation()
263 {
264 return dynamicBundleLocation;
265 }
266
267
268 String getStaticBundleLocation()
269 {
270 return staticBundleLocation;
271 }
272
273
274 void setStaticBundleLocation( final String bundleLocation )
275 {
276 // CM 1.4; needed for bundle location change at the end
277 final String oldBundleLocation = getBundleLocation();
278
279 // 104.15.2.8 The bundle location will be set persistently
280 this.staticBundleLocation = bundleLocation;
281 storeSilently();
282
283 // check whether the dynamic bundle location is different from the
284 // static now. If so, the dynamic bundle location has to be
285 // removed.
286 if ( bundleLocation != null && getDynamicBundleLocation() != null
287 && !bundleLocation.equals( getDynamicBundleLocation() ) )
288 {
289 setDynamicBundleLocation( null, false );
290 }
291
292 // CM 1.4
293 this.getConfigurationManager().locationChanged( this, oldBundleLocation );
294 }
295
296
297 void setDynamicBundleLocation( final String bundleLocation, final boolean dispatchConfiguration )
298 {
299 // CM 1.4; needed for bundle location change at the end
300 final String oldBundleLocation = getBundleLocation();
301
302 this.dynamicBundleLocation = bundleLocation;
303 this.getConfigurationManager().setDynamicBundleLocation( this.getBaseId(), bundleLocation );
304
305 // CM 1.4
306 if ( dispatchConfiguration )
307 {
308 this.getConfigurationManager().locationChanged( this, oldBundleLocation );
309
310 }
311 }
312
313
314 /**
315 * Dynamically binds this configuration to the given location unless
316 * the configuration is already bound (statically or dynamically). In
317 * the case of this configuration to be dynamically bound a
318 * <code>CM_LOCATION_CHANGED</code> event is dispatched.
319 */
320 boolean tryBindLocation( final String bundleLocation )
321 {
322 if ( this.getBundleLocation() == null )
323 {
324 setDynamicBundleLocation( bundleLocation, true );
325 }
326
327 return true;
328 }
329
330
Felix Meschbergera0903df2009-01-19 10:40:28 +0000331 /**
332 * Returns an optionally deep copy of the properties of this configuration
333 * instance.
334 * <p>
335 * This method returns a copy of the internal dictionary. If the
336 * <code>deepCopy</code> parameter is true array and collection values are
337 * copied into new arrays or collections. Otherwise just a new dictionary
338 * referring to the same objects is returned.
Felix Meschbergerdf26ae82009-08-14 19:50:43 +0000339 *
Felix Meschbergera0903df2009-01-19 10:40:28 +0000340 * @param deepCopy
341 * <code>true</code> if a deep copy is to be returned.
342 * @return
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000343 */
Felix Meschbergera0903df2009-01-19 10:40:28 +0000344 public Dictionary getProperties( boolean deepCopy )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000345 {
346 // no properties yet
Carsten Ziegelereb80d512007-08-15 11:17:41 +0000347 if ( properties == null )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000348 {
349 return null;
350 }
351
Felix Meschbergera0903df2009-01-19 10:40:28 +0000352 CaseInsensitiveDictionary props = new CaseInsensitiveDictionary( properties, deepCopy );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000353
354 // fix special properties (pid, factory PID, bundle location)
Carsten Ziegelereb80d512007-08-15 11:17:41 +0000355 setAutoProperties( props, false );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000356
357 return props;
358 }
359
360
361 /* (non-Javadoc)
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000362 * @see org.osgi.service.cm.Configuration#update()
363 */
364 public void update() throws IOException
365 {
Felix Meschbergerdf26ae82009-08-14 19:50:43 +0000366 PersistenceManager localPersistenceManager = getPersistenceManager();
367 if ( localPersistenceManager != null )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000368 {
369 // read configuration from persistence (again)
Felix Meschberger049cc072011-02-04 20:02:03 +0000370 if ( localPersistenceManager.exists( getPid() ) )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000371 {
Felix Meschberger049cc072011-02-04 20:02:03 +0000372 Dictionary properties = localPersistenceManager.load( getPid() );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000373
Felix Meschberger049cc072011-02-04 20:02:03 +0000374 // ensure serviceReference pid
375 String servicePid = ( String ) properties.get( Constants.SERVICE_PID );
376 if ( servicePid != null && !getPid().equals( servicePid ) )
377 {
378 throw new IOException( "PID of configuration file does match requested PID; expected " + getPid()
379 + ", got " + servicePid );
380 }
381
382 configureFromPersistence( properties );
383 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000384
Felix Meschberger02f457d2009-08-20 06:27:17 +0000385 // update the service but do not fire an CM_UPDATED event
386 getConfigurationManager().updated( this, false );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000387 }
388 }
389
390
391 /* (non-Javadoc)
392 * @see org.osgi.service.cm.Configuration#update(java.util.Dictionary)
393 */
394 public void update( Dictionary properties ) throws IOException
395 {
Felix Meschbergerdf26ae82009-08-14 19:50:43 +0000396 PersistenceManager localPersistenceManager = getPersistenceManager();
397 if ( localPersistenceManager != null )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000398 {
399 CaseInsensitiveDictionary newProperties = new CaseInsensitiveDictionary( properties );
400
Felix Meschbergeref470042009-08-19 05:52:41 +0000401 setAutoProperties( newProperties, true );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000402
Felix Meschbergera0e8e332009-08-18 07:52:51 +0000403 // persist new configuration
Felix Meschbergeref470042009-08-19 05:52:41 +0000404 localPersistenceManager.store( getPid(), newProperties );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000405
Felix Meschbergerfbc7dc12008-08-06 08:25:58 +0000406 // if this is a factory configuration, update the factory with
407 String factoryPid = getFactoryPid();
408 if ( factoryPid != null )
409 {
Felix Meschbergeref470042009-08-19 05:52:41 +0000410 // If this is a new factory configuration, we also have to add
411 // it to the configuration manager cache
412 if ( isNew() )
413 {
414 getConfigurationManager().cacheConfiguration( this );
415 }
416
417 Factory factory = getConfigurationManager().getFactory( factoryPid );
Felix Meschbergerfbc7dc12008-08-06 08:25:58 +0000418 if ( factory.addPID( getPid() ) )
419 {
420 // only write back if the pid was not already registered
421 // with the factory
Felix Meschbergera0e8e332009-08-18 07:52:51 +0000422 try
423 {
424 factory.store();
425 }
426 catch ( IOException ioe )
427 {
Felix Meschbergeref470042009-08-19 05:52:41 +0000428 getConfigurationManager().log( LogService.LOG_ERROR,
429 "Failure storing factory " + factoryPid + " with new configuration " + getPid(), ioe );
Felix Meschbergera0e8e332009-08-18 07:52:51 +0000430 }
Felix Meschbergerfbc7dc12008-08-06 08:25:58 +0000431 }
432 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000433
Felix Meschbergera0e8e332009-08-18 07:52:51 +0000434 // finally assign the configuration for use
435 configure( newProperties );
Felix Meschbergerdf26ae82009-08-14 19:50:43 +0000436
Felix Meschberger02f457d2009-08-20 06:27:17 +0000437 // update the service and fire an CM_UPDATED event
438 getConfigurationManager().updated( this, true );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000439 }
440 }
441
442
443 //---------- Object overwrites --------------------------------------------
444
445 public boolean equals( Object obj )
446 {
447 if ( obj == this )
448 {
449 return true;
450 }
451
452 if ( obj instanceof Configuration )
453 {
Felix Meschbergeref470042009-08-19 05:52:41 +0000454 return getPid().equals( ( ( Configuration ) obj ).getPid() );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000455 }
456
457 return false;
458 }
459
460
461 public int hashCode()
462 {
Felix Meschbergeref470042009-08-19 05:52:41 +0000463 return getPid().hashCode();
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000464 }
465
466
467 public String toString()
468 {
Felix Meschbergeref470042009-08-19 05:52:41 +0000469 return "Configuration PID=" + getPid() + ", factoryPID=" + factoryPID + ", bundleLocation=" + getBundleLocation();
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000470 }
471
472
Felix Meschbergeref470042009-08-19 05:52:41 +0000473 // ---------- private helper -----------------------------------------------
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000474
475 void store() throws IOException
476 {
Felix Meschbergera0903df2009-01-19 10:40:28 +0000477 // we don't need a deep copy, since we are not modifying
478 // any value in the dictionary itself. we are just adding
479 // properties to it, which are required for storing
480 Dictionary props = getProperties( false );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000481
482 // if this is a new configuration, we just use an empty Dictionary
483 if ( props == null )
484 {
485 props = new Hashtable();
486
Felix Meschberger6a698df2009-08-16 18:38:46 +0000487 // add automatic properties including the bundle location (if
488 // statically bound)
Felix Meschbergeref470042009-08-19 05:52:41 +0000489 setAutoProperties( props, true );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000490 }
Felix Meschbergeref470042009-08-19 05:52:41 +0000491 else
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000492 {
Felix Meschbergeref470042009-08-19 05:52:41 +0000493 replaceProperty( props, ConfigurationAdmin.SERVICE_BUNDLELOCATION, getStaticBundleLocation() );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000494 }
495
496 // only store now, if this is not a new configuration
Felix Meschbergeref470042009-08-19 05:52:41 +0000497 getPersistenceManager().store( getPid(), props );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000498 }
499
500
Felix Meschberger0c4e7042008-08-06 07:41:48 +0000501 /**
Felix Meschbergerf23eb072009-08-31 14:04:33 +0000502 * Increments the last modification counter of this configuration to cause
503 * the ManagedService or ManagedServiceFactory subscribed to this
504 * configuration to be updated.
Felix Meschbergerfd52e312009-08-29 19:44:58 +0000505 * <p>
Felix Meschbergerf23eb072009-08-31 14:04:33 +0000506 * This method is intended to only be called by the constructor(s) of this
507 * class and the {@link #update(Dictionary)} method to indicate to the
508 * update threads, the configuration is ready for distribution.
509 * <p>
510 * Setting the properties field and incrementing this counter should be
511 * done synchronized on this instance.
512 */
513 void setLastModificationTime( )
514 {
515 this.lastModificationTime++;
516 }
517
518
519 /**
520 * Returns the modification counter of the last modification of the
521 * properties of this configuration object.
522 * <p>
523 * This value may be compared to the {@link #getLastUpdatedTime()} to decide
524 * whether to update the ManagedService[Factory] or not.
525 * <p>
526 * Getting the properties of this configuration and this counter should be
527 * done synchronized on this instance.
Felix Meschbergerfd52e312009-08-29 19:44:58 +0000528 */
529 long getLastModificationTime()
530 {
531 return lastModificationTime;
532 }
533
Felix Meschbergerf23eb072009-08-31 14:04:33 +0000534
Felix Meschbergerfd52e312009-08-29 19:44:58 +0000535 /**
Felix Meschbergerf23eb072009-08-31 14:04:33 +0000536 * Returns the modification counter of the last update of this configuration
537 * to the subscribing ManagedService or ManagedServiceFactory. This value
538 * may be compared to the {@link #getLastModificationTime()} to decide
539 * whether the configuration should be updated or not.
Felix Meschbergerfd52e312009-08-29 19:44:58 +0000540 */
541 long getLastUpdatedTime()
542 {
543 return lastUpdatedTime;
544 }
545
Felix Meschbergerf23eb072009-08-31 14:04:33 +0000546
Felix Meschbergerfd52e312009-08-29 19:44:58 +0000547 /**
Felix Meschberger8d9851a2010-08-25 13:22:44 +0000548 * Sets the last update time field to the given value of the last
549 * modification time to indicate the version of configuration properties
550 * that have been updated in a ManagedService[Factory].
Felix Meschbergerfd52e312009-08-29 19:44:58 +0000551 * <p>
552 * This method should only be called from the Update Thread after supplying
553 * the configuration to the ManagedService[Factory].
Felix Meschberger8d9851a2010-08-25 13:22:44 +0000554 *
555 * @param lastModificationTime The value of the
556 * {@link #getLastModificationTime() last modification time field} at
557 * which the properties have been extracted from the configuration to
558 * be supplied to the service.
Felix Meschbergerfd52e312009-08-29 19:44:58 +0000559 */
Felix Meschberger8d9851a2010-08-25 13:22:44 +0000560 void setLastUpdatedTime( long lastModificationTime )
Felix Meschbergerfd52e312009-08-29 19:44:58 +0000561 {
Felix Meschberger8d9851a2010-08-25 13:22:44 +0000562 synchronized ( this )
563 {
Felix Meschberger007c50e2011-10-20 12:39:38 +0000564 if ( this.lastUpdatedTime < lastModificationTime )
565 {
566 this.lastUpdatedTime = lastModificationTime;
567 }
Felix Meschbergerf23eb072009-08-31 14:04:33 +0000568 }
Felix Meschbergerfd52e312009-08-29 19:44:58 +0000569 }
570
Felix Meschberger8d9851a2010-08-25 13:22:44 +0000571
Felix Meschbergerfd52e312009-08-29 19:44:58 +0000572 /**
Felix Meschberger0c4e7042008-08-06 07:41:48 +0000573 * Returns <code>false</code> if this configuration contains configuration
574 * properties. Otherwise <code>true</code> is returned and this is a
575 * newly creted configuration object whose {@link #update(Dictionary)}
576 * method has never been called.
577 */
Felix Meschberger2941ef92007-08-20 13:15:16 +0000578 boolean isNew()
579 {
580 return properties == null;
581 }
582
583
Felix Meschbergerdf26ae82009-08-14 19:50:43 +0000584 /**
585 * Returns <code>true</code> if this configuration has already been deleted
586 * on the persistence.
587 */
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000588 boolean isDeleted()
589 {
Felix Meschbergeref470042009-08-19 05:52:41 +0000590 return isDeleted;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000591 }
592
593
Felix Meschberger2941ef92007-08-20 13:15:16 +0000594 private void configureFromPersistence( Dictionary properties )
595 {
596 // if the this is not an empty/new configuration, accept the properties
597 // otherwise just set the properties field to null
598 if ( properties.get( CONFIGURATION_NEW ) == null )
599 {
600 configure( properties );
601 }
602 else
603 {
Felix Meschbergera0e8e332009-08-18 07:52:51 +0000604 configure( null );
Felix Meschberger2941ef92007-08-20 13:15:16 +0000605 }
606 }
Felix Meschbergerdf26ae82009-08-14 19:50:43 +0000607
Felix Meschbergera0e8e332009-08-18 07:52:51 +0000608 private void configure( final Dictionary properties )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000609 {
Felix Meschbergera0e8e332009-08-18 07:52:51 +0000610 final CaseInsensitiveDictionary newProperties;
611 if ( properties == null )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000612 {
Felix Meschbergera0e8e332009-08-18 07:52:51 +0000613 newProperties = null;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000614 }
615 else
616 {
Felix Meschbergera0e8e332009-08-18 07:52:51 +0000617 // remove predefined properties
618 clearAutoProperties( properties );
619
620 // ensure CaseInsensitiveDictionary
621 if ( properties instanceof CaseInsensitiveDictionary )
622 {
623 newProperties = ( CaseInsensitiveDictionary ) properties;
624 }
625 else
626 {
627 newProperties = new CaseInsensitiveDictionary( properties );
628 }
629 }
630
Felix Meschbergerfd52e312009-08-29 19:44:58 +0000631 synchronized ( this )
632 {
633 this.properties = newProperties;
Felix Meschbergerf23eb072009-08-31 14:04:33 +0000634 setLastModificationTime();
Felix Meschbergerfd52e312009-08-29 19:44:58 +0000635 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000636 }
637
638
639 void setAutoProperties( Dictionary properties, boolean withBundleLocation )
640 {
641 // set pid and factory pid in the properties
Felix Meschbergeref470042009-08-19 05:52:41 +0000642 replaceProperty( properties, Constants.SERVICE_PID, getPid() );
Felix Meschberger2941ef92007-08-20 13:15:16 +0000643 replaceProperty( properties, ConfigurationAdmin.SERVICE_FACTORYPID, factoryPID );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000644
645 // bundle location is not set here
646 if ( withBundleLocation )
647 {
Felix Meschbergeref470042009-08-19 05:52:41 +0000648 replaceProperty( properties, ConfigurationAdmin.SERVICE_BUNDLELOCATION, getStaticBundleLocation() );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000649 }
650 else
651 {
652 properties.remove( ConfigurationAdmin.SERVICE_BUNDLELOCATION );
653 }
654 }
655
656
Felix Meschbergeref470042009-08-19 05:52:41 +0000657 static void clearAutoProperties( Dictionary properties )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000658 {
659 properties.remove( Constants.SERVICE_PID );
660 properties.remove( ConfigurationAdmin.SERVICE_FACTORYPID );
661 properties.remove( ConfigurationAdmin.SERVICE_BUNDLELOCATION );
662 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000663}