blob: 1c48117836d42598e8fdc3d9789aadce65612173 [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 Meschberger4f269292011-10-21 13:52:31 +0000401 getConfigurationManager().log( LogService.LOG_DEBUG, "Updating config {0} with {1}", new Object[]
402 { getPid(), newProperties } );
403
Felix Meschbergeref470042009-08-19 05:52:41 +0000404 setAutoProperties( newProperties, true );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000405
Felix Meschbergera0e8e332009-08-18 07:52:51 +0000406 // persist new configuration
Felix Meschbergeref470042009-08-19 05:52:41 +0000407 localPersistenceManager.store( getPid(), newProperties );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000408
Felix Meschbergerfbc7dc12008-08-06 08:25:58 +0000409 // if this is a factory configuration, update the factory with
410 String factoryPid = getFactoryPid();
411 if ( factoryPid != null )
412 {
Felix Meschbergeref470042009-08-19 05:52:41 +0000413 // If this is a new factory configuration, we also have to add
414 // it to the configuration manager cache
415 if ( isNew() )
416 {
417 getConfigurationManager().cacheConfiguration( this );
418 }
419
420 Factory factory = getConfigurationManager().getFactory( factoryPid );
Felix Meschbergerfbc7dc12008-08-06 08:25:58 +0000421 if ( factory.addPID( getPid() ) )
422 {
423 // only write back if the pid was not already registered
424 // with the factory
Felix Meschbergera0e8e332009-08-18 07:52:51 +0000425 try
426 {
427 factory.store();
428 }
429 catch ( IOException ioe )
430 {
Felix Meschbergeref470042009-08-19 05:52:41 +0000431 getConfigurationManager().log( LogService.LOG_ERROR,
Felix Meschberger4f269292011-10-21 13:52:31 +0000432 "Failure storing factory {0} with new configuration {1}", new Object[]
433 { factoryPid, getPid(), ioe } );
Felix Meschbergera0e8e332009-08-18 07:52:51 +0000434 }
Felix Meschbergerfbc7dc12008-08-06 08:25:58 +0000435 }
436 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000437
Felix Meschbergera0e8e332009-08-18 07:52:51 +0000438 // finally assign the configuration for use
439 configure( newProperties );
Felix Meschbergerdf26ae82009-08-14 19:50:43 +0000440
Felix Meschberger02f457d2009-08-20 06:27:17 +0000441 // update the service and fire an CM_UPDATED event
442 getConfigurationManager().updated( this, true );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000443 }
444 }
445
446
447 //---------- Object overwrites --------------------------------------------
448
449 public boolean equals( Object obj )
450 {
451 if ( obj == this )
452 {
453 return true;
454 }
455
456 if ( obj instanceof Configuration )
457 {
Felix Meschbergeref470042009-08-19 05:52:41 +0000458 return getPid().equals( ( ( Configuration ) obj ).getPid() );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000459 }
460
461 return false;
462 }
463
464
465 public int hashCode()
466 {
Felix Meschbergeref470042009-08-19 05:52:41 +0000467 return getPid().hashCode();
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000468 }
469
470
471 public String toString()
472 {
Felix Meschbergeref470042009-08-19 05:52:41 +0000473 return "Configuration PID=" + getPid() + ", factoryPID=" + factoryPID + ", bundleLocation=" + getBundleLocation();
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000474 }
475
476
Felix Meschbergeref470042009-08-19 05:52:41 +0000477 // ---------- private helper -----------------------------------------------
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000478
479 void store() throws IOException
480 {
Felix Meschbergera0903df2009-01-19 10:40:28 +0000481 // we don't need a deep copy, since we are not modifying
482 // any value in the dictionary itself. we are just adding
483 // properties to it, which are required for storing
484 Dictionary props = getProperties( false );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000485
486 // if this is a new configuration, we just use an empty Dictionary
487 if ( props == null )
488 {
489 props = new Hashtable();
490
Felix Meschberger6a698df2009-08-16 18:38:46 +0000491 // add automatic properties including the bundle location (if
492 // statically bound)
Felix Meschbergeref470042009-08-19 05:52:41 +0000493 setAutoProperties( props, true );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000494 }
Felix Meschbergeref470042009-08-19 05:52:41 +0000495 else
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000496 {
Felix Meschbergeref470042009-08-19 05:52:41 +0000497 replaceProperty( props, ConfigurationAdmin.SERVICE_BUNDLELOCATION, getStaticBundleLocation() );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000498 }
499
500 // only store now, if this is not a new configuration
Felix Meschbergeref470042009-08-19 05:52:41 +0000501 getPersistenceManager().store( getPid(), props );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000502 }
503
504
Felix Meschberger0c4e7042008-08-06 07:41:48 +0000505 /**
Felix Meschbergerf23eb072009-08-31 14:04:33 +0000506 * Increments the last modification counter of this configuration to cause
507 * the ManagedService or ManagedServiceFactory subscribed to this
508 * configuration to be updated.
Felix Meschbergerfd52e312009-08-29 19:44:58 +0000509 * <p>
Felix Meschbergerf23eb072009-08-31 14:04:33 +0000510 * This method is intended to only be called by the constructor(s) of this
511 * class and the {@link #update(Dictionary)} method to indicate to the
512 * update threads, the configuration is ready for distribution.
513 * <p>
514 * Setting the properties field and incrementing this counter should be
515 * done synchronized on this instance.
516 */
517 void setLastModificationTime( )
518 {
519 this.lastModificationTime++;
520 }
521
522
523 /**
524 * Returns the modification counter of the last modification of the
525 * properties of this configuration object.
526 * <p>
527 * This value may be compared to the {@link #getLastUpdatedTime()} to decide
528 * whether to update the ManagedService[Factory] or not.
529 * <p>
530 * Getting the properties of this configuration and this counter should be
531 * done synchronized on this instance.
Felix Meschbergerfd52e312009-08-29 19:44:58 +0000532 */
533 long getLastModificationTime()
534 {
535 return lastModificationTime;
536 }
537
Felix Meschbergerf23eb072009-08-31 14:04:33 +0000538
Felix Meschbergerfd52e312009-08-29 19:44:58 +0000539 /**
Felix Meschbergerf23eb072009-08-31 14:04:33 +0000540 * Returns the modification counter of the last update of this configuration
541 * to the subscribing ManagedService or ManagedServiceFactory. This value
542 * may be compared to the {@link #getLastModificationTime()} to decide
543 * whether the configuration should be updated or not.
Felix Meschbergerfd52e312009-08-29 19:44:58 +0000544 */
545 long getLastUpdatedTime()
546 {
547 return lastUpdatedTime;
548 }
549
Felix Meschbergerf23eb072009-08-31 14:04:33 +0000550
Felix Meschbergerfd52e312009-08-29 19:44:58 +0000551 /**
Felix Meschberger8d9851a2010-08-25 13:22:44 +0000552 * Sets the last update time field to the given value of the last
553 * modification time to indicate the version of configuration properties
554 * that have been updated in a ManagedService[Factory].
Felix Meschbergerfd52e312009-08-29 19:44:58 +0000555 * <p>
556 * This method should only be called from the Update Thread after supplying
557 * the configuration to the ManagedService[Factory].
Felix Meschberger8d9851a2010-08-25 13:22:44 +0000558 *
559 * @param lastModificationTime The value of the
560 * {@link #getLastModificationTime() last modification time field} at
561 * which the properties have been extracted from the configuration to
562 * be supplied to the service.
Felix Meschbergerfd52e312009-08-29 19:44:58 +0000563 */
Felix Meschberger8d9851a2010-08-25 13:22:44 +0000564 void setLastUpdatedTime( long lastModificationTime )
Felix Meschbergerfd52e312009-08-29 19:44:58 +0000565 {
Felix Meschberger8d9851a2010-08-25 13:22:44 +0000566 synchronized ( this )
567 {
Felix Meschberger007c50e2011-10-20 12:39:38 +0000568 if ( this.lastUpdatedTime < lastModificationTime )
569 {
570 this.lastUpdatedTime = lastModificationTime;
571 }
Felix Meschbergerf23eb072009-08-31 14:04:33 +0000572 }
Felix Meschbergerfd52e312009-08-29 19:44:58 +0000573 }
574
Felix Meschberger8d9851a2010-08-25 13:22:44 +0000575
Felix Meschbergerfd52e312009-08-29 19:44:58 +0000576 /**
Felix Meschberger0c4e7042008-08-06 07:41:48 +0000577 * Returns <code>false</code> if this configuration contains configuration
578 * properties. Otherwise <code>true</code> is returned and this is a
579 * newly creted configuration object whose {@link #update(Dictionary)}
580 * method has never been called.
581 */
Felix Meschberger2941ef92007-08-20 13:15:16 +0000582 boolean isNew()
583 {
584 return properties == null;
585 }
586
587
Felix Meschbergerdf26ae82009-08-14 19:50:43 +0000588 /**
589 * Returns <code>true</code> if this configuration has already been deleted
590 * on the persistence.
591 */
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000592 boolean isDeleted()
593 {
Felix Meschbergeref470042009-08-19 05:52:41 +0000594 return isDeleted;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000595 }
596
597
Felix Meschberger2941ef92007-08-20 13:15:16 +0000598 private void configureFromPersistence( Dictionary properties )
599 {
600 // if the this is not an empty/new configuration, accept the properties
601 // otherwise just set the properties field to null
602 if ( properties.get( CONFIGURATION_NEW ) == null )
603 {
604 configure( properties );
605 }
606 else
607 {
Felix Meschbergera0e8e332009-08-18 07:52:51 +0000608 configure( null );
Felix Meschberger2941ef92007-08-20 13:15:16 +0000609 }
610 }
Felix Meschbergerdf26ae82009-08-14 19:50:43 +0000611
Felix Meschbergera0e8e332009-08-18 07:52:51 +0000612 private void configure( final Dictionary properties )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000613 {
Felix Meschbergera0e8e332009-08-18 07:52:51 +0000614 final CaseInsensitiveDictionary newProperties;
615 if ( properties == null )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000616 {
Felix Meschbergera0e8e332009-08-18 07:52:51 +0000617 newProperties = null;
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000618 }
619 else
620 {
Felix Meschbergera0e8e332009-08-18 07:52:51 +0000621 // remove predefined properties
622 clearAutoProperties( properties );
623
624 // ensure CaseInsensitiveDictionary
625 if ( properties instanceof CaseInsensitiveDictionary )
626 {
627 newProperties = ( CaseInsensitiveDictionary ) properties;
628 }
629 else
630 {
631 newProperties = new CaseInsensitiveDictionary( properties );
632 }
633 }
634
Felix Meschbergerfd52e312009-08-29 19:44:58 +0000635 synchronized ( this )
636 {
637 this.properties = newProperties;
Felix Meschbergerf23eb072009-08-31 14:04:33 +0000638 setLastModificationTime();
Felix Meschbergerfd52e312009-08-29 19:44:58 +0000639 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000640 }
641
642
643 void setAutoProperties( Dictionary properties, boolean withBundleLocation )
644 {
645 // set pid and factory pid in the properties
Felix Meschbergeref470042009-08-19 05:52:41 +0000646 replaceProperty( properties, Constants.SERVICE_PID, getPid() );
Felix Meschberger2941ef92007-08-20 13:15:16 +0000647 replaceProperty( properties, ConfigurationAdmin.SERVICE_FACTORYPID, factoryPID );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000648
649 // bundle location is not set here
650 if ( withBundleLocation )
651 {
Felix Meschbergeref470042009-08-19 05:52:41 +0000652 replaceProperty( properties, ConfigurationAdmin.SERVICE_BUNDLELOCATION, getStaticBundleLocation() );
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000653 }
654 else
655 {
656 properties.remove( ConfigurationAdmin.SERVICE_BUNDLELOCATION );
657 }
658 }
659
660
Felix Meschbergeref470042009-08-19 05:52:41 +0000661 static void clearAutoProperties( Dictionary properties )
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000662 {
663 properties.remove( Constants.SERVICE_PID );
664 properties.remove( ConfigurationAdmin.SERVICE_FACTORYPID );
665 properties.remove( ConfigurationAdmin.SERVICE_BUNDLELOCATION );
666 }
Felix Meschbergeradd2b4a2007-04-11 18:12:33 +0000667}