SLING-4272 : Issues in handling of configurations wrt update handling and write back
[sling-org-apache-sling-installer-it.git] / src / test / java / org / apache / sling / installer / it / ConfigInstallTest.java
1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.sling.installer.it;
18
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertNotNull;
21 import static org.junit.Assert.assertNull;
22
23 import java.util.ArrayList;
24 import java.util.Dictionary;
25 import java.util.Hashtable;
26 import java.util.LinkedList;
27 import java.util.List;
28 import java.util.concurrent.atomic.AtomicInteger;
29
30 import org.apache.sling.installer.api.InstallableResource;
31 import org.apache.sling.installer.api.event.InstallationEvent;
32 import org.apache.sling.installer.api.event.InstallationListener;
33 import org.junit.After;
34 import org.junit.Before;
35 import org.junit.Test;
36 import org.junit.runner.RunWith;
37 import org.ops4j.pax.exam.Option;
38 import org.ops4j.pax.exam.junit.PaxExam;
39 import org.osgi.framework.Bundle;
40 import org.osgi.framework.ServiceRegistration;
41 import org.osgi.service.cm.Configuration;
42 import org.osgi.service.cm.ConfigurationAdmin;
43 import org.osgi.service.cm.ConfigurationEvent;
44 import org.osgi.service.cm.ConfigurationListener;
45
46 @RunWith(PaxExam.class)
47
48 public class ConfigInstallTest extends OsgiInstallerTestBase implements ConfigurationListener {
49
50 private final static long TIMEOUT = 5000L;
51 private final List<ConfigurationEvent> events = new LinkedList<ConfigurationEvent>();
52 private List<ServiceRegistration<?>> serviceRegistrations = new ArrayList<ServiceRegistration<?>>();
53 private int installationEvents = 0;
54 private static final AtomicInteger counter = new AtomicInteger();
55
56 @org.ops4j.pax.exam.Configuration
57 public Option[] config() {
58 return defaultConfiguration();
59 }
60
61 @Before
62 public void setUp() {
63 installationEvents = 0;
64 setupInstaller();
65 events.clear();
66 serviceRegistrations.clear();
67 serviceRegistrations.add(bundleContext.registerService(ConfigurationListener.class.getName(), this, null));
68
69 final InstallationListener il = new InstallationListener() {
70 public void onEvent(InstallationEvent event) {
71 installationEvents++;
72 }
73 };
74 serviceRegistrations.add(bundleContext.registerService(InstallationListener.class.getName(), il, null));
75 }
76
77 @Override
78 @After
79 public void tearDown() {
80 super.tearDown();
81 for(ServiceRegistration<?> reg : serviceRegistrations) {
82 reg.unregister();
83 }
84 serviceRegistrations.clear();
85 }
86
87 private String uniqueID() {
88 return counter.incrementAndGet() + "_" + System.currentTimeMillis();
89 }
90
91 /**
92 * @see org.osgi.service.cm.ConfigurationListener#configurationEvent(org.osgi.service.cm.ConfigurationEvent)
93 */
94 public void configurationEvent(ConfigurationEvent e) {
95 synchronized ( events ) {
96 events.add(e);
97 }
98 }
99
100 @Test
101 public void testInstallConfigWindowsPath() throws Exception {
102 final Dictionary<String, Object> cfgData = new Hashtable<String, Object>();
103 cfgData.put("foo", "bar");
104 final String cfgPid = getClass().getSimpleName() + "." + uniqueID();
105 assertNull("Config " + cfgPid + " must not be found before test", findConfiguration(cfgPid));
106
107 // install configs
108 //Following patterns work fine. Problem occurs one with windows seprator
109 // - "c:/foo/bar/"
110 // - "/foo/bar/"
111 final String uri = "c:\\foo\bar\\";
112 final InstallableResource result = new MockInstallableResource(uri + cfgPid, copy(cfgData), null, null, 100);
113 final InstallableResource[] rsrc = new InstallableResource[] {result};
114 installer.updateResources(URL_SCHEME, rsrc, null);
115
116 Configuration cfg = waitForConfiguration("After installing", cfgPid, TIMEOUT, true);
117 assertEquals("Config value must match", "bar", cfg.getProperties().get("foo"));
118
119 // remove again
120 installer.updateResources(URL_SCHEME, null, new String[] {rsrc[0].getId()});
121 waitForConfiguration("After removing for the second time", cfgPid, TIMEOUT, false);
122 }
123
124 @Test
125 public void testInstallAndRemoveConfig() throws Exception {
126 final Dictionary<String, Object> cfgData = new Hashtable<String, Object>();
127 cfgData.put("foo", "bar");
128 final String cfgPid = getClass().getSimpleName() + "." + uniqueID();
129 assertNull("Config " + cfgPid + " must not be found before test", findConfiguration(cfgPid));
130
131 // install config
132 final InstallableResource[] rsrc = getInstallableResource(cfgPid, cfgData);
133 installer.updateResources(URL_SCHEME, rsrc, null);
134
135 Configuration cfg = waitForConfiguration("After installing", cfgPid, TIMEOUT, true);
136 assertEquals("Config value must match", "bar", cfg.getProperties().get("foo"));
137
138 // remove resource
139 installer.updateResources(URL_SCHEME, null, new String[] {rsrc[0].getId()});
140 waitForConfiguration("After removing", cfgPid, TIMEOUT, false);
141
142 // Reinstalling with same digest must work
143 installer.updateResources(URL_SCHEME, rsrc, null);
144 cfg = waitForConfiguration("After reinstalling", cfgPid, TIMEOUT, true);
145 assertEquals("Config value must match", "bar", cfg.getProperties().get("foo"));
146
147 // remove again
148 installer.updateResources(URL_SCHEME, null, new String[] {rsrc[0].getId()});
149 waitForConfiguration("After removing for the second time", cfgPid, TIMEOUT, false);
150
151 }
152
153 @Test
154 public void testInstallUpdateRemoveConfigResource() throws Exception {
155 final Dictionary<String, Object> cfgData = new Hashtable<String, Object>();
156 cfgData.put("foo", "bar");
157 final String cfgPid = getClass().getSimpleName() + "." + uniqueID();
158 assertNull("Config " + cfgPid + " must not be found before test", findConfiguration(cfgPid));
159
160 // install config
161 final InstallableResource rsrc = new InstallableResource("/configA/" + cfgPid,
162 null, cfgData, null, InstallableResource.TYPE_PROPERTIES, 10);
163 installer.updateResources(URL_SCHEME, new InstallableResource[] {rsrc}, null);
164
165 // get config
166 final Configuration cfg = waitForConfiguration("After installing", cfgPid, TIMEOUT, true);
167 assertEquals("Config value must match", "bar", cfg.getProperties().get("foo"));
168
169 // create second configuration
170 final Dictionary<String, Object> secondData = new Hashtable<String, Object>();
171 secondData.put("foo", "bla");
172 final InstallableResource rsrc2 = new InstallableResource("/configB/" + cfgPid,
173 null, secondData, null, InstallableResource.TYPE_PROPERTIES, 20);
174 installer.updateResources(URL_SCHEME, new InstallableResource[] {rsrc2}, null);
175
176 sleep(200);
177
178 // get updated config
179 final Configuration secondCfg = waitForConfiguration("After updating", cfgPid, TIMEOUT, true);
180 assertEquals("Config value must match", "bla", secondCfg.getProperties().get("foo"));
181
182 // remove config
183 installer.updateResources(URL_SCHEME, null, new String[] {"/configB/" + cfgPid});
184
185 sleep(200);
186
187 final Configuration origCfg = waitForConfiguration("After deleting", cfgPid, TIMEOUT, true);
188 assertEquals("Config value must match", "bar", origCfg.getProperties().get("foo"));
189 }
190
191 @Test
192 public void testInstallUpdateRemoveTemplateConfigResource() throws Exception {
193 final Dictionary<String, Object> cfgData = new Hashtable<String, Object>();
194 cfgData.put("foo", "bar");
195 cfgData.put(InstallableResource.RESOURCE_IS_TEMPLATE, "true");
196 final String cfgPid = getClass().getSimpleName() + "." + uniqueID();
197 assertNull("Config " + cfgPid + " must not be found before test", findConfiguration(cfgPid));
198
199 // install config
200 final InstallableResource rsrc = new InstallableResource("/configA/" + cfgPid,
201 null, cfgData, null, InstallableResource.TYPE_PROPERTIES, 10);
202 installer.updateResources(URL_SCHEME, new InstallableResource[] {rsrc}, null);
203
204 // get config
205 final Configuration cfg = waitForConfiguration("After installing", cfgPid, TIMEOUT, true);
206 assertEquals("Config value must match", "bar", cfg.getProperties().get("foo"));
207
208 // create second configuration
209 final Dictionary<String, Object> secondData = new Hashtable<String, Object>();
210 secondData.put("foo", "bla");
211 final InstallableResource rsrc2 = new InstallableResource("/configB/" + cfgPid,
212 null, secondData, null, InstallableResource.TYPE_PROPERTIES, 20);
213 installer.updateResources(URL_SCHEME, new InstallableResource[] {rsrc2}, null);
214
215 sleep(200);
216
217 // get updated config
218 final Configuration secondCfg = waitForConfiguration("After updating", cfgPid, TIMEOUT, true);
219 assertEquals("Config value must match", "bla", secondCfg.getProperties().get("foo"));
220
221 // remove config
222 installer.updateResources(URL_SCHEME, null, new String[] {"/configB/" + cfgPid});
223
224 sleep(200);
225
226 final Configuration noCfg = waitForConfiguration("After deleting", cfgPid, TIMEOUT, false);
227 assertNull("Configuration should be removed", noCfg);
228 }
229
230 @Test
231 public void testInstallUpdateRemoveConfigFactoryResource() throws Exception {
232 final Dictionary<String, Object> cfgData = new Hashtable<String, Object>();
233 cfgData.put("foo", "bar");
234 final String cfgFactoryPid = getClass().getSimpleName() + "." + uniqueID();
235 final String alias = "alias" + uniqueID();
236 assertNull("Factory config " + cfgFactoryPid + " must not be found before test", findFactoryConfiguration(cfgFactoryPid));
237
238 // install factory config
239 final InstallableResource rsrc = new InstallableResource("/configA/" + cfgFactoryPid + "-" + alias,
240 null, cfgData, null, InstallableResource.TYPE_PROPERTIES, 10);
241 installer.updateResources(URL_SCHEME, new InstallableResource[] {rsrc}, null);
242
243 // get factory config
244 final Configuration cfg = waitForFactoryConfiguration("After installing", cfgFactoryPid, TIMEOUT, true);
245 assertEquals("Config value must match", "bar", cfg.getProperties().get("foo"));
246
247 // create second factory configuration with same alias
248 final Dictionary<String, Object> secondData = new Hashtable<String, Object>();
249 secondData.put("foo", "bla");
250 final InstallableResource rsrc2 = new InstallableResource("/configB/" + cfgFactoryPid + "-" + alias,
251 null, secondData, null, InstallableResource.TYPE_PROPERTIES, 20);
252 installer.updateResources(URL_SCHEME, new InstallableResource[] {rsrc2}, null);
253
254 sleep(200);
255
256 // get updated factory config
257 final Configuration secondCfg = waitForFactoryConfiguration("After updating", cfgFactoryPid, TIMEOUT, true);
258 assertEquals("Config value must match", "bla", secondCfg.getProperties().get("foo"));
259
260 // remove factory config
261 installer.updateResources(URL_SCHEME, null, new String[] {"/configB/" + cfgFactoryPid + "-" + alias});
262
263 sleep(200);
264
265 final Configuration origCfg = waitForFactoryConfiguration("After deleting", cfgFactoryPid, TIMEOUT, true);
266 assertEquals("Config value must match", "bar", origCfg.getProperties().get("foo"));
267 }
268
269 @Test
270 public void testInstallUpdateRemoveTemplateConfigFactoryResource() throws Exception {
271 final Dictionary<String, Object> cfgData = new Hashtable<String, Object>();
272 cfgData.put("foo", "bar");
273 cfgData.put(InstallableResource.RESOURCE_IS_TEMPLATE, "true");
274 final String cfgFactoryPid = getClass().getSimpleName() + "." + uniqueID();
275 final String alias = "alias" + uniqueID();
276 assertNull("Factory config " + cfgFactoryPid + " must not be found before test", findFactoryConfiguration(cfgFactoryPid));
277
278 // install factory config
279 final InstallableResource rsrc = new InstallableResource("/configA/" + cfgFactoryPid + "-" + alias,
280 null, cfgData, null, InstallableResource.TYPE_PROPERTIES, 10);
281 installer.updateResources(URL_SCHEME, new InstallableResource[] {rsrc}, null);
282
283 // get factory config
284 final Configuration cfg = waitForFactoryConfiguration("After installing", cfgFactoryPid, TIMEOUT, true);
285 assertEquals("Config value must match", "bar", cfg.getProperties().get("foo"));
286
287 // create second factory configuration
288 final Dictionary<String, Object> secondData = new Hashtable<String, Object>();
289 secondData.put("foo", "bla");
290 final InstallableResource rsrc2 = new InstallableResource("/configB/" + cfgFactoryPid + "-" + alias,
291 null, secondData, null, InstallableResource.TYPE_PROPERTIES, 20);
292 installer.updateResources(URL_SCHEME, new InstallableResource[] {rsrc2}, null);
293
294 sleep(200);
295
296 // get updated factory config
297 final Configuration secondCfg = waitForFactoryConfiguration("After updating", cfgFactoryPid, TIMEOUT, true);
298 assertEquals("Config value must match", "bla", secondCfg.getProperties().get("foo"));
299
300 // remove config
301 installer.updateResources(URL_SCHEME, null, new String[] {"/configB/" + cfgFactoryPid + "-" + alias});
302
303 sleep(200);
304
305 final Configuration noCfg = waitForFactoryConfiguration("After deleting", cfgFactoryPid, TIMEOUT, false);
306 assertNull("Factory configuration should be removed", noCfg);
307 }
308
309 @Test
310 public void testInstallUpdateRemoveConfig() throws Exception {
311 final Dictionary<String, Object> cfgData = new Hashtable<String, Object>();
312 cfgData.put("foo", "bar");
313 final String cfgPid = getClass().getSimpleName() + "." + uniqueID();
314 assertNull("Config " + cfgPid + " must not be found before test", findConfiguration(cfgPid));
315
316 // install config
317 final InstallableResource rsrc = new InstallableResource("/configA/" + cfgPid,
318 null, cfgData, null, InstallableResource.TYPE_PROPERTIES, 10);
319 installer.updateResources(URL_SCHEME, new InstallableResource[] {rsrc}, null);
320
321 // get config
322 final Configuration cfg = waitForConfiguration("After installing", cfgPid, TIMEOUT, true);
323 assertEquals("Config value must match", "bar", cfg.getProperties().get("foo"));
324
325 // update configuration
326 final Dictionary<String, Object> secondData = new Hashtable<String, Object>();
327 secondData.put("foo", "bla");
328 cfg.update(secondData);
329
330 sleep(200);
331
332 // get updated config
333 final Configuration secondCfg = waitForConfiguration("After updating", cfgPid, TIMEOUT, true);
334 assertEquals("Config value must match", "bla", secondCfg.getProperties().get("foo"));
335
336 // remove config
337 secondCfg.delete();
338
339 sleep(200);
340
341 final Configuration origCfg = waitForConfiguration("After deleting", cfgPid, TIMEOUT, true);
342 assertEquals("Config value must match", "bar", origCfg.getProperties().get("foo"));
343 }
344
345 @Test
346 public void testInstallUpdateRemoveTemplateConfig() throws Exception {
347 final Dictionary<String, Object> cfgData = new Hashtable<String, Object>();
348 cfgData.put("foo", "bar");
349 cfgData.put(InstallableResource.RESOURCE_IS_TEMPLATE, "true");
350 final String cfgPid = getClass().getSimpleName() + "." + uniqueID();
351 assertNull("Config " + cfgPid + " must not be found before test", findConfiguration(cfgPid));
352
353 // install config
354 final InstallableResource rsrc = new InstallableResource("/configA/" + cfgPid,
355 null, cfgData, null, InstallableResource.TYPE_PROPERTIES, 10);
356 installer.updateResources(URL_SCHEME, new InstallableResource[] {rsrc}, null);
357
358 // get config
359 final Configuration cfg = waitForConfiguration("After installing", cfgPid, TIMEOUT, true);
360 assertEquals("Config value must match", "bar", cfg.getProperties().get("foo"));
361
362 // update configuration
363 final Dictionary<String, Object> secondData = new Hashtable<String, Object>();
364 secondData.put("foo", "bla");
365 cfg.update(secondData);
366
367 sleep(200);
368
369 // get updated config
370 final Configuration secondCfg = waitForConfiguration("After updating", cfgPid, TIMEOUT, true);
371 assertEquals("Config value must match", "bla", secondCfg.getProperties().get("foo"));
372
373 // remove config
374 secondCfg.delete();
375
376 sleep(200);
377
378 final Configuration noCfg = waitForConfiguration("After deleting", cfgPid, TIMEOUT, false);
379 assertNull("Configuration should be removed", noCfg);
380 }
381
382 @Test
383 public void testInstallUpdateRemoveConfigFactory() throws Exception {
384 final Dictionary<String, Object> cfgData = new Hashtable<String, Object>();
385 cfgData.put("foo", "bar");
386 final String cfgFactoryPid = getClass().getSimpleName() + "." + uniqueID();
387 final String alias = "alias" + uniqueID();
388 assertNull("Factory config " + cfgFactoryPid + " must not be found before test", findFactoryConfiguration(cfgFactoryPid));
389
390 // install factory config
391 final InstallableResource rsrc = new InstallableResource("/configA/" + cfgFactoryPid + "-" + alias,
392 null, cfgData, null, InstallableResource.TYPE_PROPERTIES, 10);
393 installer.updateResources(URL_SCHEME, new InstallableResource[] {rsrc}, null);
394
395 // get factory config
396 final Configuration cfg = waitForFactoryConfiguration("After installing", cfgFactoryPid, TIMEOUT, true);
397 assertEquals("Config value must match", "bar", cfg.getProperties().get("foo"));
398
399 // update configuration
400 final Dictionary<String, Object> secondData = new Hashtable<String, Object>();
401 secondData.put("foo", "bla");
402 cfg.update(secondData);
403
404 sleep(200);
405
406 // get updated factory config
407 final Configuration secondCfg = waitForFactoryConfiguration("After updating", cfgFactoryPid, TIMEOUT, true);
408 assertEquals("Config value must match", "bla", secondCfg.getProperties().get("foo"));
409
410 // remove factory config
411 secondCfg.delete();
412 sleep(200);
413
414 final Configuration origCfg = waitForFactoryConfiguration("After deleting", cfgFactoryPid, TIMEOUT, true);
415 assertEquals("Config value must match", "bar", origCfg.getProperties().get("foo"));
416 }
417
418 @Test
419 public void testInstallUpdateRemoveTemplateConfigFactory() throws Exception {
420 final Dictionary<String, Object> cfgData = new Hashtable<String, Object>();
421 cfgData.put("foo", "bar");
422 cfgData.put(InstallableResource.RESOURCE_IS_TEMPLATE, "true");
423 final String cfgFactoryPid = getClass().getSimpleName() + "." + uniqueID();
424 final String alias = "alias" + uniqueID();
425 assertNull("Factory config " + cfgFactoryPid + " must not be found before test", findFactoryConfiguration(cfgFactoryPid));
426
427 // install factory config
428 final InstallableResource rsrc = new InstallableResource("/configA/" + cfgFactoryPid + "-" + alias,
429 null, cfgData, null, InstallableResource.TYPE_PROPERTIES, 10);
430 installer.updateResources(URL_SCHEME, new InstallableResource[] {rsrc}, null);
431
432 // get factory config
433 final Configuration cfg = waitForFactoryConfiguration("After installing", cfgFactoryPid, TIMEOUT, true);
434 assertEquals("Config value must match", "bar", cfg.getProperties().get("foo"));
435
436 // update configuration
437 final Dictionary<String, Object> secondData = new Hashtable<String, Object>();
438 secondData.put("foo", "bla");
439 cfg.update(secondData);
440
441 sleep(200);
442
443 // get updated factory config
444 final Configuration secondCfg = waitForFactoryConfiguration("After updating", cfgFactoryPid, TIMEOUT, true);
445 assertEquals("Config value must match", "bla", secondCfg.getProperties().get("foo"));
446
447 // remove config
448 secondCfg.delete();
449 sleep(200);
450
451 final Configuration noCfg = waitForFactoryConfiguration("After deleting", cfgFactoryPid, TIMEOUT, false);
452 assertNull("Factory configuration should be removed", noCfg);
453 }
454
455 @Test
456 public void testDeferredConfigInstall() throws Exception {
457 // get config admin bundle and wait for service
458 final Bundle configAdmin = this.getConfigAdminBundle();
459 assertNotNull("ConfigAdmin bundle must be found", configAdmin);
460 waitForConfigAdmin(true);
461
462 // check that configuration is not available
463 final String cfgPid = getClass().getSimpleName() + ".deferred." + uniqueID();
464 assertNull("Config " + cfgPid + " must not be found before test", findConfiguration(cfgPid));
465 // create new configuration object
466 final Dictionary<String, Object> cfgData = new Hashtable<String, Object>();
467 cfgData.put("foo", "bar");
468
469 // Configuration installs must be deferred if ConfigAdmin service is stopped
470 configAdmin.stop();
471 waitForConfigAdmin(false);
472
473 // add new configuration
474 final InstallableResource[] rsrc = getInstallableResource(cfgPid, cfgData);
475 installationEvents = 0;
476 installer.updateResources(URL_SCHEME, rsrc, null);
477 waitForInstallationEvents(2);
478 configAdmin.start();
479 waitForConfigAdmin(true);
480 waitForConfiguration("Config must be installed once ConfigurationAdmin restarts",
481 cfgPid, TIMEOUT, true);
482
483 // Remove config and check
484 installer.updateResources(URL_SCHEME, null, new String[] {rsrc[0].getId()});
485 waitForConfiguration("Config must be removed once ConfigurationAdmin restarts",
486 cfgPid, TIMEOUT, false);
487 }
488
489 @Test
490 public void testDeferredConfigRemove() throws Exception {
491 // get config admin bundle and wait for service
492 final Bundle configAdmin = this.getConfigAdminBundle();
493 assertNotNull("ConfigAdmin bundle must be found", configAdmin);
494 waitForConfigAdmin(true);
495
496 // check that configuration is not available
497 final String cfgPid = getClass().getSimpleName() + ".deferred." + uniqueID();
498 assertNull("Config " + cfgPid + " must not be found before test", findConfiguration(cfgPid));
499
500 // create and install new configuration object, verify
501 final Dictionary<String, Object> cfgData = new Hashtable<String, Object>();
502 cfgData.put("foo", "bar");
503 final InstallableResource[] rsrc = getInstallableResource(cfgPid, cfgData);
504 installationEvents = 0;
505 installer.updateResources(URL_SCHEME, rsrc, null);
506 waitForInstallationEvents(2);
507 waitForConfiguration("Config must be installed before stopping ConfigurationAdmin",
508 cfgPid, TIMEOUT, true);
509
510 // Configuration uninstalls must be deferred if ConfigAdmin service is stopped
511 configAdmin.stop();
512 waitForConfigAdmin(false);
513
514 // remove configuration
515 installationEvents = 0;
516 installer.updateResources(URL_SCHEME, null, new String[] {rsrc[0].getId()});
517 waitForInstallationEvents(2);
518 configAdmin.start();
519 waitForConfigAdmin(true);
520 waitForConfiguration("Config must be removed once ConfigurationAdmin restarts",
521 cfgPid, TIMEOUT, false);
522 }
523
524 @Test
525 public void testReinstallSameConfig() throws Exception {
526 final Dictionary<String, Object> cfgData = new Hashtable<String, Object>();
527 cfgData.put("foo", "bar");
528 final String cfgPid = getClass().getSimpleName() + ".reinstall." + uniqueID();
529 assertNull("Config " + cfgPid + " must not be found before test", findConfiguration(cfgPid));
530
531 // Install config directly
532 ConfigurationAdmin ca = waitForConfigAdmin(true);
533 final Configuration c = ca.getConfiguration(cfgPid);
534 c.update(cfgData);
535 waitForConfigValue("After manual installation", cfgPid, TIMEOUT, "foo", "bar");
536 waitForCondition("Expected one ConfigurationEvents since beginning of test", TIMEOUT, new ConfigCondition(cfgPid, 1));
537
538 installer.updateResources(URL_SCHEME, getInstallableResource(cfgPid, cfgData), null);
539
540 // Reinstalling with a change must be executed
541 cfgData.put("foo", "changed");
542 installer.updateResources(URL_SCHEME, getInstallableResource(cfgPid, cfgData), null);
543 waitForConfigValue("After changing value", cfgPid, TIMEOUT, "foo", "changed");
544 waitForCondition("Expected two ConfigurationEvents since beginning of test", TIMEOUT, new ConfigCondition(cfgPid, 2));
545 }
546
547 protected final class ConfigCondition extends Condition {
548
549 private final String pid;
550
551 private final int maxCount;
552
553 public ConfigCondition(final String pid, final int count) {
554 this.pid = pid;
555 this.maxCount = count;
556 }
557
558 @Override
559 boolean isTrue() throws Exception {
560 int count = 0;
561 synchronized ( events ) {
562 for(final ConfigurationEvent e : events) {
563 if ( pid.equals(e.getPid()) ) {
564 count++;
565 }
566 }
567 }
568 return count == maxCount;
569 }
570
571 @Override
572 String additionalInfo() {
573 final StringBuilder sb = new StringBuilder("Expected ");
574 sb.append(maxCount);
575 sb.append(" events for ");
576 sb.append(pid);
577 sb.append(". Received events: [");
578 boolean first = true;
579 synchronized ( events ) {
580 for(final ConfigurationEvent e : events) {
581 if ( !first) {
582 sb.append(", ");
583 }
584 first = false;
585 sb.append(e.getPid());
586 sb.append(':');
587 sb.append(e.getType());
588 }
589 }
590 sb.append("]");
591 return sb.toString();
592 }
593 }
594
595 private void waitForInstallationEvents(final int howMany) throws Exception {
596 final Condition c = new Condition() {
597 @Override
598 boolean isTrue() throws Exception {
599 return installationEvents >= howMany;
600 }
601 };
602 waitForCondition("Wait for " + howMany + " installation events", TIMEOUT, c);
603 }
604 }