1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.configuration2;
18
19 import static org.junit.jupiter.api.Assertions.assertEquals;
20 import static org.junit.jupiter.api.Assertions.assertFalse;
21 import static org.junit.jupiter.api.Assertions.assertInstanceOf;
22 import static org.junit.jupiter.api.Assertions.assertNotSame;
23 import static org.junit.jupiter.api.Assertions.assertSame;
24 import static org.junit.jupiter.api.Assertions.assertTrue;
25 import static org.junit.jupiter.api.Assertions.fail;
26
27 import java.io.File;
28 import java.util.ArrayList;
29 import java.util.Collection;
30 import java.util.Collections;
31 import java.util.List;
32 import java.util.concurrent.CountDownLatch;
33
34 import org.apache.commons.configuration2.SynchronizerTestImpl.Methods;
35 import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
36 import org.apache.commons.configuration2.builder.fluent.Parameters;
37 import org.apache.commons.configuration2.ex.ConfigurationException;
38 import org.apache.commons.configuration2.io.FileHandler;
39 import org.apache.commons.configuration2.tree.ImmutableNode;
40 import org.apache.commons.configuration2.tree.InMemoryNodeModel;
41 import org.apache.commons.configuration2.tree.NodeStructureHelper;
42 import org.junit.jupiter.api.BeforeEach;
43 import org.junit.jupiter.api.Test;
44
45
46
47
48
49 public class TestBaseHierarchicalConfigurationSynchronization {
50
51
52
53
54 private static final class SubNodeAccessThread extends Thread {
55
56
57 private final HierarchicalConfiguration<ImmutableNode> config;
58
59
60 private final CountDownLatch latch;
61
62
63 private final String keySub;
64
65
66 private final String keyProp;
67
68
69 private String value;
70
71
72
73
74
75
76
77
78
79 public SubNodeAccessThread(final HierarchicalConfiguration<ImmutableNode> c, final CountDownLatch startLatch, final String keySubConfig,
80 final String keyProperty) {
81 config = c;
82 latch = startLatch;
83 keySub = keySubConfig;
84 keyProp = keyProperty;
85 }
86
87 @Override
88 public void run() {
89 try {
90 latch.await();
91 final HierarchicalConfiguration<ImmutableNode> subConfig = config.configurationAt(keySub, true);
92 value = subConfig.getString(keyProp);
93 } catch (final InterruptedException iex) {
94
95 }
96 }
97
98
99
100
101 public void verify() {
102 try {
103 join();
104 } catch (final InterruptedException e) {
105 fail("Waiting was interrupted: " + e);
106 }
107 assertEquals("I'm complex!", value);
108 }
109 }
110
111
112
113
114
115
116
117 private static boolean isDetached(final HierarchicalConfiguration<ImmutableNode> c) {
118 final SubnodeConfiguration subnodeConfig = assertInstanceOf(SubnodeConfiguration.class, c);
119 final InMemoryNodeModel nodeModel = subnodeConfig.getRootNodeModel();
120 return nodeModel.isTrackedNodeDetached(subnodeConfig.getRootSelector());
121 }
122
123
124 private SynchronizerTestImpl sync;
125
126
127 private File testFile;
128
129
130 private BaseHierarchicalConfiguration config;
131
132 @BeforeEach
133 public void setUp() throws Exception {
134 final XMLConfiguration c = new XMLConfiguration();
135 testFile = ConfigurationAssert.getTestFile("test.xml");
136 new FileHandler(c).load(testFile);
137 sync = new SynchronizerTestImpl();
138 c.setSynchronizer(sync);
139 config = c;
140 }
141
142
143
144
145 @Test
146 void testAddNodesSynchronized() {
147 final ImmutableNode node = NodeStructureHelper.createNode("newNode", "true");
148 config.addNodes("test.addNodes", Collections.singleton(node));
149 sync.verify(Methods.BEGIN_WRITE, Methods.END_WRITE);
150 }
151
152
153
154
155 @Test
156 void testChildConfigurationsAtSynchronized() {
157 final List<HierarchicalConfiguration<ImmutableNode>> subs = config.childConfigurationsAt("clear");
158 assertFalse(subs.isEmpty());
159 sync.verify(Methods.BEGIN_READ, Methods.END_READ);
160 }
161
162
163
164
165 @Test
166 void testClearTreeSynchronized() {
167 config.clearTree("clear");
168 sync.verify(Methods.BEGIN_WRITE, Methods.END_WRITE);
169 }
170
171
172
173
174 @Test
175 void testCloneCopySubnodeData() {
176 final BaseHierarchicalConfiguration conf2 = new BaseHierarchicalConfiguration(config);
177
178 final HierarchicalConfiguration<ImmutableNode> sub = conf2.configurationAt("element2.subelement", true);
179 @SuppressWarnings("unchecked")
180 final HierarchicalConfiguration<ImmutableNode> copy = (HierarchicalConfiguration<ImmutableNode>) conf2.clone();
181 final HierarchicalConfiguration<ImmutableNode> sub2 = copy.configurationAt("element2.subelement", true);
182
183 copy.clearTree("element2");
184 assertTrue(isDetached(sub2));
185 assertFalse(isDetached(sub));
186 }
187
188
189
190
191 @Test
192 void testCloneSynchronized() {
193 final BaseHierarchicalConfiguration clone = (BaseHierarchicalConfiguration) config.clone();
194 sync.verify(Methods.BEGIN_READ, Methods.END_READ);
195 assertNotSame(config.getSynchronizer(), clone.getSynchronizer());
196 }
197
198
199
200
201 @Test
202 void testConfigurationAtSynchronized() {
203 final HierarchicalConfiguration<ImmutableNode> sub = config.configurationAt("element2");
204 assertEquals("I'm complex!", sub.getString("subelement.subsubelement"));
205 sync.verify(Methods.BEGIN_READ, Methods.END_READ, Methods.BEGIN_READ, Methods.END_READ);
206 }
207
208
209
210
211 @Test
212 void testConfigurationsAtSynchronized() {
213 final List<HierarchicalConfiguration<ImmutableNode>> subs = config.configurationsAt("list.item");
214 assertFalse(subs.isEmpty());
215 sync.verify(Methods.BEGIN_READ, Methods.END_READ);
216 }
217
218
219
220
221 @Test
222 void testCopyConstructorSynchronized() {
223 final BaseHierarchicalConfiguration copy = new BaseHierarchicalConfiguration(config);
224 sync.verify(Methods.BEGIN_READ, Methods.END_READ);
225 assertNotSame(sync, copy.getSynchronizer());
226 }
227
228
229
230
231 @Test
232 void testGetMaxIndexSynchronized() {
233 assertTrue(config.getMaxIndex("list.item") > 0);
234 sync.verify(Methods.BEGIN_READ, Methods.END_READ);
235 }
236
237
238
239
240 @Test
241 void testGetRootElementNameSynchronized() {
242 assertEquals("testconfig", config.getRootElementName());
243 sync.verify(Methods.BEGIN_READ, Methods.END_READ);
244 }
245
246
247
248
249 @Test
250 void testReadOnlyAccessToSubConfigurations() throws ConfigurationException {
251 final FileBasedConfigurationBuilder<XMLConfiguration> builder = new FileBasedConfigurationBuilder<>(XMLConfiguration.class);
252 builder.configure(new Parameters().fileBased().setFile(testFile));
253 config = builder.getConfiguration();
254
255 final CountDownLatch startLatch = new CountDownLatch(1);
256 final Collection<SubNodeAccessThread> threads = new ArrayList<>();
257 for (int i = 0; i < 4; i++) {
258 final SubNodeAccessThread t = new SubNodeAccessThread(config, startLatch, "element2", "subelement.subsubelement");
259 t.start();
260 threads.add(t);
261 }
262 for (int i = 0; i < 4; i++) {
263 final SubNodeAccessThread t = new SubNodeAccessThread(config, startLatch, "element2.subelement", "subsubelement");
264 t.start();
265 threads.add(t);
266 }
267
268 startLatch.countDown();
269 for (final SubNodeAccessThread t : threads) {
270 t.verify();
271 }
272 }
273
274
275
276
277 @Test
278 void testSubnodeUpdate() {
279 config.addProperty("element2.test", Boolean.TRUE);
280 final HierarchicalConfiguration<ImmutableNode> sub = config.configurationAt("element2", true);
281 final HierarchicalConfiguration<ImmutableNode> subsub = sub.configurationAt("subelement", true);
282 config.clearTree("element2.subelement");
283 assertFalse(isDetached(sub));
284 assertTrue(isDetached(subsub));
285 }
286
287
288
289
290 @Test
291 void testSubnodeUpdateBySubnode() {
292 final HierarchicalConfiguration<ImmutableNode> sub = config.configurationAt("element2", true);
293 final HierarchicalConfiguration<ImmutableNode> subsub = sub.configurationAt("subelement", true);
294 final HierarchicalConfiguration<ImmutableNode> sub2 = config.configurationAt("element2.subelement", true);
295 sub.clearTree("subelement");
296 assertTrue(isDetached(sub2));
297 assertTrue(isDetached(subsub));
298 }
299
300
301
302
303 @Test
304 void testSubsetSynchronized() {
305 final Configuration subset = config.subset("test");
306 sync.verify(Methods.BEGIN_READ, Methods.END_READ);
307 assertSame(sync, subset.getSynchronizer());
308 }
309 }