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