View Javadoc

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  
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      /** Constant for the number of test threads. */
47      private static final int THREAD_COUNT = 3;
48  
49      /** Constant for the number of loops in the multi-thread tests. */
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         // create a new configuration
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 }