1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.configuration;
19
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertFalse;
22 import static org.junit.Assert.assertNotNull;
23 import static org.junit.Assert.assertTrue;
24
25 import java.io.File;
26 import java.io.FileReader;
27 import java.io.FileWriter;
28 import java.io.IOException;
29 import java.io.Reader;
30 import java.io.Writer;
31
32 import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine;
33 import org.apache.commons.lang.text.StrLookup;
34 import org.junit.Test;
35
36 public class TestDynamicCombinedConfiguration
37 {
38 private static String PATTERN = "${sys:Id}";
39 private static String PATTERN1 = "target/test-classes/testMultiConfiguration_${sys:Id}.xml";
40 private static String DEFAULT_FILE = "target/test-classes/testMultiConfiguration_default.xml";
41 private static final File MULTI_TENENT_FILE = new File(
42 "conf/testMultiTenentConfigurationBuilder4.xml");
43 private static final File MULTI_DYNAMIC_FILE = new File(
44 "conf/testMultiTenentConfigurationBuilder5.xml");
45
46
47 private static final int THREAD_COUNT = 3;
48
49
50 private static final int LOOP_COUNT = 100;
51
52 @Test
53 public void testConfiguration() throws Exception
54 {
55 DynamicCombinedConfiguration config = new DynamicCombinedConfiguration();
56 XPathExpressionEngine engine = new XPathExpressionEngine();
57 config.setExpressionEngine(engine);
58 config.setKeyPattern(PATTERN);
59 config.setDelimiterParsingDisabled(true);
60 MultiFileHierarchicalConfiguration multi = new MultiFileHierarchicalConfiguration(PATTERN1);
61 multi.setExpressionEngine(engine);
62 config.addConfiguration(multi, "Multi");
63 XMLConfiguration xml = new XMLConfiguration();
64 xml.setExpressionEngine(engine);
65 xml.setDelimiterParsingDisabled(true);
66 xml.setFile(new File(DEFAULT_FILE));
67 xml.load();
68 config.addConfiguration(xml, "Default");
69
70 verify("1001", config, 15);
71 verify("1002", config, 25);
72 verify("1003", config, 35);
73 verify("1004", config, 50);
74 assertEquals("a,b,c", config.getString("split/list3/@values"));
75 assertEquals(0, config.getMaxIndex("split/list3/@values"));
76 assertEquals("a\\,b\\,c", config.getString("split/list4/@values"));
77 assertEquals("a,b,c", config.getString("split/list1"));
78 assertEquals(0, config.getMaxIndex("split/list1"));
79 assertEquals("a\\,b\\,c", config.getString("split/list2"));
80 }
81
82 @Test
83 public void testConcurrentGetAndReload() throws Exception
84 {
85 System.getProperties().remove("Id");
86 DefaultConfigurationBuilder factory = new DefaultConfigurationBuilder();
87 factory.setFile(MULTI_TENENT_FILE);
88 CombinedConfiguration config = factory.getConfiguration(true);
89
90 assertEquals(config.getString("rowsPerPage"), "50");
91 Thread testThreads[] = new Thread[THREAD_COUNT];
92 int failures[] = new int[THREAD_COUNT];
93
94 for (int i = 0; i < testThreads.length; ++i)
95 {
96 testThreads[i] = new ReloadThread(config, failures, i, LOOP_COUNT, false, null, "50");
97 testThreads[i].start();
98 }
99
100 int totalFailures = 0;
101 for (int i = 0; i < testThreads.length; ++i)
102 {
103 testThreads[i].join();
104 totalFailures += failures[i];
105 }
106 assertTrue(totalFailures + " failures Occurred", totalFailures == 0);
107 }
108
109 @Test
110 public void testConcurrentGetAndReload2() throws Exception
111 {
112 System.getProperties().remove("Id");
113 DefaultConfigurationBuilder factory = new DefaultConfigurationBuilder();
114 factory.setFile(MULTI_TENENT_FILE);
115 CombinedConfiguration config = factory.getConfiguration(true);
116
117 assertEquals(config.getString("rowsPerPage"), "50");
118
119 Thread testThreads[] = new Thread[THREAD_COUNT];
120 int failures[] = new int[THREAD_COUNT];
121 System.setProperty("Id", "2002");
122 assertEquals(config.getString("rowsPerPage"), "25");
123 for (int i = 0; i < testThreads.length; ++i)
124 {
125 testThreads[i] = new ReloadThread(config, failures, i, LOOP_COUNT, false, null, "25");
126 testThreads[i].start();
127 }
128
129 int totalFailures = 0;
130 for (int i = 0; i < testThreads.length; ++i)
131 {
132 testThreads[i].join();
133 totalFailures += failures[i];
134 }
135 System.getProperties().remove("Id");
136 assertTrue(totalFailures + " failures Occurred", totalFailures == 0);
137 }
138
139 @Test
140 public void testConcurrentGetAndReloadMultipleClients() throws Exception
141 {
142 System.getProperties().remove("Id");
143 DefaultConfigurationBuilder factory = new DefaultConfigurationBuilder();
144 factory.setFile(MULTI_TENENT_FILE);
145 CombinedConfiguration config = factory.getConfiguration(true);
146
147 assertEquals(config.getString("rowsPerPage"), "50");
148
149 Thread testThreads[] = new Thread[THREAD_COUNT];
150 int failures[] = new int[THREAD_COUNT];
151 String[] ids = new String[] {null, "2002", "3001", "3002", "3003"};
152 String[] expected = new String[] {"50", "25", "15", "25", "50"};
153 for (int i = 0; i < testThreads.length; ++i)
154 {
155 testThreads[i] = new ReloadThread(config, failures, i, LOOP_COUNT, true, ids[i], expected[i]);
156 testThreads[i].start();
157 }
158
159 int totalFailures = 0;
160 for (int i = 0; i < testThreads.length; ++i)
161 {
162 testThreads[i].join();
163 totalFailures += failures[i];
164 }
165 System.getProperties().remove("Id");
166 if (totalFailures != 0)
167 {
168 System.out.println("Failures:");
169 for (int i = 0; i < testThreads.length; ++i)
170 {
171 System.out.println("Thread " + i + " " + failures[i]);
172 }
173 }
174 assertTrue(totalFailures + " failures Occurred", totalFailures == 0);
175 }
176
177 @Test
178 public void testConcurrentGetAndReloadFile() throws Exception
179 {
180 final int threadCount = 25;
181 System.getProperties().remove("Id");
182
183 File input = new File("target/test-classes/testMultiDynamic_default.xml");
184 File output = new File("target/test-classes/testwrite/testMultiDynamic_default.xml");
185 output.delete();
186 output.getParentFile().mkdir();
187 copyFile(input, output);
188
189 DefaultConfigurationBuilder factory = new DefaultConfigurationBuilder();
190 factory.setFile(MULTI_DYNAMIC_FILE);
191 CombinedConfiguration config = factory.getConfiguration(true);
192
193 assertEquals(config.getString("Product/FIIndex/FI[@id='123456781']"), "ID0001");
194
195 ReaderThread testThreads[] = new ReaderThread[threadCount];
196 for (int i = 0; i < testThreads.length; ++i)
197 {
198 testThreads[i] = new ReaderThread(config);
199 testThreads[i].start();
200 }
201
202 Thread.sleep(2000);
203
204 input = new File("target/test-classes/testMultiDynamic_default2.xml");
205 copyFile(input, output);
206
207 Thread.sleep(2000);
208 String id = config.getString("Product/FIIndex/FI[@id='123456782']");
209 assertNotNull("File did not reload, id is null", id);
210 String rows = config.getString("rowsPerPage");
211 assertTrue("Incorrect value for rowsPerPage", "25".equals(rows));
212
213 for (int i = 0; i < testThreads.length; ++i)
214 {
215 testThreads[i].shutdown();
216 testThreads[i].join();
217 }
218 for (int i = 0; i < testThreads.length; ++i)
219 {
220 assertFalse(testThreads[i].failed());
221 }
222 assertEquals("ID0002", config.getString("Product/FIIndex/FI[@id='123456782']"));
223 output.delete();
224 }
225
226
227 private class ReloadThread extends Thread
228 {
229 CombinedConfiguration combined;
230 int[] failures;
231 int index;
232 int count;
233 String expected;
234 String id;
235 boolean useId;
236
237 ReloadThread(CombinedConfiguration config, int[] failures, int index, int count,
238 boolean useId, String id, String expected)
239 {
240 combined = config;
241 this.failures = failures;
242 this.index = index;
243 this.count = count;
244 this.expected = expected;
245 this.id = id;
246 this.useId = useId;
247 }
248 @Override
249 public void run()
250 {
251 failures[index] = 0;
252
253 if (useId)
254 {
255 ThreadLookup.setId(id);
256 }
257 for (int i = 0; i < count; i++)
258 {
259 try
260 {
261 String value = combined.getString("rowsPerPage", null);
262 if (value == null || !value.equals(expected))
263 {
264 ++failures[index];
265 }
266 }
267 catch (Exception ex)
268 {
269 ++failures[index];
270 }
271 }
272 }
273 }
274
275 private class ReaderThread extends Thread
276 {
277 private boolean running = true;
278 private boolean failed = false;
279 CombinedConfiguration combined;
280
281 public ReaderThread(CombinedConfiguration c)
282 {
283 combined = c;
284 }
285
286 @Override
287 public void run()
288 {
289 while (running)
290 {
291 String bcId = combined.getString("Product/FIIndex/FI[@id='123456781']");
292 if ("ID0001".equalsIgnoreCase(bcId))
293 {
294 if (failed)
295 {
296 System.out.println("Thread failed, but recovered");
297 }
298 failed = false;
299 }
300 else
301 {
302 failed = true;
303 }
304 }
305 }
306
307 public boolean failed()
308 {
309 return failed;
310 }
311
312 public void shutdown()
313 {
314 running = false;
315 }
316
317 }
318
319 private void verify(String key, DynamicCombinedConfiguration config, int rows)
320 {
321 System.setProperty("Id", key);
322 assertTrue(config.getInt("rowsPerPage") == rows);
323 }
324
325 private void copyFile(File input, File output) throws IOException
326 {
327 Reader reader = new FileReader(input);
328 Writer writer = new FileWriter(output);
329 char[] buffer = new char[4096];
330 int n = 0;
331 while (-1 != (n = reader.read(buffer)))
332 {
333 writer.write(buffer, 0, n);
334 }
335 reader.close();
336 writer.close();
337 }
338
339 public static class ThreadLookup extends StrLookup
340 {
341 private static ThreadLocal<String> id = new ThreadLocal<String>();
342
343
344
345 public ThreadLookup()
346 {
347
348 }
349
350 public static void setId(String value)
351 {
352 id.set(value);
353 }
354
355 @Override
356 public String lookup(String key)
357 {
358 if (key == null || !key.equals("Id"))
359 {
360 return null;
361 }
362 String value = System.getProperty("Id");
363 if (value != null)
364 {
365 return value;
366 }
367 return id.get();
368
369 }
370 }
371 }