1   /*
2    * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
3    *
4    * This software is open source.
5    * See the bottom of this file for the licence.
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                 // creat it again
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  * Redistribution and use of this software and associated documentation
312  * ("Software"), with or without modification, are permitted provided that the
313  * following conditions are met:
314  * 
315  * 1. Redistributions of source code must retain copyright statements and
316  * notices. Redistributions must also contain a copy of this document.
317  * 
318  * 2. Redistributions in binary form must reproduce the above copyright notice,
319  * this list of conditions and the following disclaimer in the documentation
320  * and/or other materials provided with the distribution.
321  * 
322  * 3. The name "DOM4J" must not be used to endorse or promote products derived
323  * from this Software without prior written permission of MetaStuff, Ltd. For
324  * written permission, please contact dom4j-info@metastuff.com.
325  * 
326  * 4. Products derived from this Software may not be called "DOM4J" nor may
327  * "DOM4J" appear in their names without prior written permission of MetaStuff,
328  * Ltd. DOM4J is a registered trademark of MetaStuff, Ltd.
329  * 
330  * 5. Due credit should be given to the DOM4J Project - http://www.dom4j.org
331  * 
332  * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS ``AS IS'' AND
333  * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
334  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
335  * ARE DISCLAIMED. IN NO EVENT SHALL METASTUFF, LTD. OR ITS CONTRIBUTORS BE
336  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
337  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
338  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
339  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
340  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
341  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
342  * POSSIBILITY OF SUCH DAMAGE.
343  * 
344  * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
345  */