1   
2   
3   
4   
5   
6   
7   
8   package org.dom4j;
9   
10  import com.clarkware.junitperf.LoadTest;
11  import com.clarkware.junitperf.TimedTest;
12  
13  import junit.extensions.RepeatedTest;
14  
15  import junit.framework.Test;
16  import junit.framework.TestSuite;
17  
18  import junit.textui.TestRunner;
19  
20  import java.text.FieldPosition;
21  import java.text.SimpleDateFormat;
22  import java.util.Date;
23  
24  /***
25   * A test harness to test the dom4j package in a threaded environment
26   * 
27   * @author <a href="mailto:ddlucas@lse.com">David Lucas </a>
28   * @version $Revision: 1.3 $
29   */
30  public class ThreadingTest extends AbstractTestCase {
31      private static final ThreadLocal FORMATTER_CACHE = new ThreadLocal();
32  
33      private static final String SEPERATOR = " - ";
34  
35      private static final FieldPosition FIELD_ZERO = new FieldPosition(0);
36  
37      public ThreadingTest(String name) {
38          super(name);
39      }
40  
41      private static void preformat(StringBuffer strBuf, String context) {
42          long now = System.currentTimeMillis();
43          Date currentTime = new Date(now);
44          SimpleDateFormat formatter = (SimpleDateFormat) FORMATTER_CACHE.get();
45  
46          if (formatter == null) {
47              formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS zzz");
48              FORMATTER_CACHE.set(formatter);
49          }
50  
51          strBuf.append("[");
52          formatter.format(currentTime, strBuf, FIELD_ZERO);
53          strBuf.append(" (").append(now).append(") ]");
54  
55          strBuf.append(SEPERATOR);
56          strBuf.append(getThreadId());
57          strBuf.append(SEPERATOR);
58          strBuf.append(context);
59          strBuf.append(SEPERATOR);
60      }
61  
62      private static String getThreadId() {
63          String tid = Thread.currentThread().getName();
64  
65          return tid;
66      }
67  
68      /***
69       * This test combines many different types of operations on DOM4J in a
70       * threaded environment. If a problem occurs with threading, the tests will
71       * fail. This was used to help isolate an internal threading issue.
72       * Unfortunately it may not always create the condition necessary to break
73       * un-thread-safe code. This is due to the nature of the machine, JVM, and
74       * application and if the conditions are right. Typically the problems of
75       * multithreading occur due to an unprotected HashMap or ArrayList in a
76       * class being used by more than one thread. Also, most developers think
77       * that their class or object instance will only be used by one thread. But
78       * if a factory or singleton caches a class or instance, it can quickly
79       * become an unsafe environment. Hence this test to assist in locating
80       * threading issues.
81       */
82      public void testCombo() {
83          int loop = 10;
84  
85          try {
86              long begin = System.currentTimeMillis();
87              String value = null;
88              String expected = null;
89              String xml = null;
90              Document doc = null;
91              Element root = null;
92              Element item = null;
93              Element newItem = null;
94              QName qn = null;
95              Namespace ns = null;
96              long now = 0;
97  
98              xml = "<ROOT xmlns:t0=\"http://www.lse.com/t0\" >"
99                      + "  <ctx><type>Context</type></ctx>"
100                     + "  <A><B><C><D>This is a TEST</D></C></B></A>"
101                     + "  <t0:Signon><A>xyz</A><t0:Cust>customer</t0:Cust>"
102                     + "</t0:Signon></ROOT>";
103 
104             for (int i = 0; i < loop; i++) {
105                 doc = DocumentHelper.parseText(xml);
106 
107                 root = doc.getRootElement();
108                 ns = Namespace.get("t0", "http://www.lse.com/t0");
109                 qn = QName.get("Signon", ns);
110                 item = root.element(qn);
111                 value = item.asXML();
112                 expected = "<t0:Signon xmlns:t0=\"http://www.lse.com/t0\">"
113                         + "<A>xyz</A><t0:Cust>customer</t0:Cust></t0:Signon>";
114                 assertEquals("test t0:Signon ", expected, value);
115 
116                 qn = root.getQName("Test");
117                 newItem = DocumentHelper.createElement(qn);
118                 now = System.currentTimeMillis();
119                 newItem.setText(String.valueOf(now));
120                 root.add(newItem);
121 
122                 qn = root.getQName("Test2");
123                 newItem = DocumentHelper.createElement(qn);
124                 now = System.currentTimeMillis();
125                 newItem.setText(String.valueOf(now));
126                 root.add(newItem);
127 
128                 item = root.element(qn);
129                 item.detach();
130                 item.setQName(qn);
131                 root.add(item);
132                 value = item.asXML();
133                 expected = "<Test2>" + now + "</Test2>";
134                 assertEquals("test Test2 ", expected, value);
135 
136                 qn = root.getQName("Test3");
137                 newItem = DocumentHelper.createElement(qn);
138                 now = System.currentTimeMillis();
139                 newItem.setText(String.valueOf(now));
140                 root.add(newItem);
141 
142                 item = root.element(qn);
143                 item.detach();
144                 item.setQName(qn);
145                 root.add(item);
146                 value = item.asXML();
147                 expected = "<Test3>" + now + "</Test3>";
148                 assertEquals("test Test3 ", expected, value);
149 
150                 qn = item.getQName("Test4");
151                 newItem = DocumentHelper.createElement(qn);
152                 now = System.currentTimeMillis();
153                 newItem.setText(String.valueOf(now));
154                 root.add(newItem);
155 
156                 item = root.element(qn);
157                 item.detach();
158                 item.setQName(qn);
159                 root.add(item);
160                 value = item.asXML();
161                 expected = "<Test4>" + now + "</Test4>";
162                 assertEquals("test Test4 ", expected, value);
163             }
164 
165             double duration = System.currentTimeMillis() - begin;
166             double avg = duration / loop;
167         } catch (Exception e) {
168             e.printStackTrace();
169             assertTrue("Exception in test: " + e.getMessage(), false);
170         }
171     }
172 
173     /***
174      * This test isolates QNameCache in a multithreaded environment.
175      */
176     public void testQNameCache() {
177         int loop = 100;
178 
179         try {
180             long begin = System.currentTimeMillis();
181             String value = null;
182             String expected = null;
183             String xml = null;
184             Document doc = null;
185             Element root = null;
186             Element item = null;
187             Element newItem = null;
188             QName qn = null;
189             Namespace ns = null;
190             long now = 0;
191 
192             xml = "<ROOT xmlns:t0=\"http://www.lse.com/t0\" >"
193                     + "  <ctx><type>Context</type></ctx>"
194                     + "  <A><B><C><D>This is a TEST</D></C></B></A>"
195                     + "  <t0:Signon><A>xyz</A><t0:Cust>customer</t0:Cust>"
196                     + "</t0:Signon></ROOT>";
197 
198             for (int i = 0; i < loop; i++) {
199                 doc = DocumentHelper.parseText(xml);
200                 root = doc.getRootElement();
201 
202                 qn = DocumentHelper.createQName("test");
203                 value = fetchValue(qn);
204                 expected = "<test/>";
205                 assertEquals("test test ", expected, value);
206 
207                 
208                 qn = DocumentHelper.createQName("test");
209                 value = fetchValue(qn);
210                 expected = "<test/>";
211                 assertEquals("test test again ", expected, value);
212 
213                 qn = root.getQName("t0:Signon");
214                 value = fetchValue(qn);
215                 expected = "<t0:Signon xmlns:t0=\"http://www.lse.com/t0\"/>";
216                 assertEquals("test t0:Signon ", expected, value);
217             }
218 
219             double duration = System.currentTimeMillis() - begin;
220             double avg = duration / loop;
221         } catch (Exception e) {
222             e.printStackTrace();
223             assertTrue("Exception in test: " + e.getMessage(), false);
224         }
225     }
226 
227     /***
228      * This method creates a value that can be expected during a test
229      * 
230      * @param qn
231      * 
232      * @return
233      */
234     public String fetchValue(QName qn) {
235         String value = null;
236 
237         StringBuffer sb = new StringBuffer();
238         sb.append("<");
239 
240         String prefix = qn.getNamespacePrefix();
241 
242         if ((prefix != null) && (prefix.length() > 0)) {
243             sb.append(prefix).append(":");
244         }
245 
246         sb.append(qn.getName());
247 
248         String uri = qn.getNamespaceURI();
249 
250         if ((uri != null) && (uri.length() > 0)) {
251             sb.append(" xmlns");
252 
253             if ((prefix != null) && (prefix.length() > 0)) {
254                 sb.append(":").append(prefix);
255             }
256 
257             sb.append("=\"").append(uri).append("\"");
258         }
259 
260         sb.append("/>");
261 
262         value = sb.toString();
263 
264         return value;
265     }
266 
267     /***
268      * Assembles and returns a test suite.
269      * 
270      * @return The suite
271      */
272     public static Test suite() {
273         TestSuite suite = new TestSuite();
274         suite.addTest(makeRepeatedLoadTest(5, 10, "testCombo"));
275         suite.addTest(makeRepeatedLoadTest(5, 10, "testQNameCache"));
276 
277         return suite;
278     }
279 
280     /***
281      * JUnit method to exercise test via threads and loops
282      * 
283      * @param users
284      *            Number of users to simulate (i.e. Threads).
285      * @param iterations
286      *            Number of iterations per user ( repeat the test x times).
287      * @param testMethod
288      *            method to execute (testXXX).
289      * 
290      * @return A Junit test
291      */
292     protected static Test makeRepeatedLoadTest(int users, int iterations,
293             String testMethod) {
294         long maxElapsedTime = 120000 + (1000 * users * iterations);
295 
296         Test testCase = new ThreadingTest(testMethod);
297 
298         Test repeatedTest = new RepeatedTest(testCase, iterations);
299         Test loadTest = new LoadTest(repeatedTest, users);
300         Test timedTest = new TimedTest(loadTest, maxElapsedTime);
301 
302         return timedTest;
303     }
304 
305     public static void main(String[] args) {
306         TestRunner.run(ThreadingTest.class);
307     }
308 }
309 
310 
311 
312 
313 
314 
315 
316 
317 
318 
319 
320 
321 
322 
323 
324 
325 
326 
327 
328 
329 
330 
331 
332 
333 
334 
335 
336 
337 
338 
339 
340 
341 
342 
343 
344 
345