1   /*
2    * Copyright 2001-2004 (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    * $Id: TestThreading.java,v 1.4 2004/06/25 08:03:47 maartenc Exp $
8    */
9   
10  package org.dom4j;
11  
12  import com.clarkware.junitperf.LoadTest;
13  import com.clarkware.junitperf.TimedTest;
14  
15  import java.text.FieldPosition;
16  import java.text.SimpleDateFormat;
17  import java.util.Date;
18  
19  import junit.extensions.RepeatedTest;
20  import junit.framework.Test;
21  import junit.framework.TestSuite;
22  
23  
24  /*** A test harness to test the dom4j package in a threaded environment
25    *
26    * @author <a href="mailto:ddlucas@lse.com">David Lucas</a>
27    * @version $Revision: 1.4 $
28    */
29  public class TestThreading extends AbstractTestCase {
30      public TestThreading(String name) {
31          super(name);
32      }
33  
34      final static boolean debug=true;
35  
36      final static java.lang.ThreadLocal formatterCache = new java.lang.ThreadLocal();
37      final static String seperator=" - ";
38      final static FieldPosition FIELD_ZERO = new FieldPosition(0);
39  
40      private final static void preformat(StringBuffer strBuf, String context) {
41          long now = System.currentTimeMillis();
42          Date currentTime = new Date(now);
43          SimpleDateFormat formatter = (SimpleDateFormat) formatterCache.get();
44          if (formatter == null) {
45              formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS zzz");
46              formatterCache.set(formatter);
47          }
48          strBuf.append("[");
49          formatter.format(currentTime, strBuf, FIELD_ZERO);
50          strBuf.append(" (").append(now).append(") ]");
51  
52          strBuf.append(seperator);
53          strBuf.append(getThreadId());
54          strBuf.append(seperator);
55          strBuf.append(context);
56          strBuf.append(seperator);
57      }
58  
59      private static final String getThreadId() {
60        String tid = Thread.currentThread().getName();
61        return tid;
62      }
63  
64      public static final void debug(String context, String msg ) {
65        if (debug) {
66          StringBuffer sb = new StringBuffer();
67          preformat(sb,context);
68          sb.append(msg);
69          System.out.println(sb.toString());
70        }
71      }
72  
73      /***
74       * static initializer
75       */
76      static {
77      }
78  
79      /***
80       *  The JUnit setup method
81       */
82      protected void setUp() { }
83  
84  
85      /***
86       *  The teardown method for JUnit
87       */
88      protected void tearDown() { }
89  
90  
91      /***
92       * This test combines many different types of operations on DOM4J in a
93       * threaded environment.  If a problem occurs with threading, the tests will
94       * fail.  This was used to help isolate an internal threading issue.
95       * Unfortunately it may not always create the condition necessary to break
96       * un-thread-safe code.  This is due to the nature of the machine, JVM, and
97       * application and if the conditions are right.  Typically the problems of
98       * multithreading occur due to an unprotected HashMap or ArrayList in a class
99       * being used by more than one thread.  Also, most developers think that
100      * their class or object instance will only be used by one thread.  But if
101      * a factory or singleton caches a class or instance, it can quickly become
102      * an unsafe environment.  Hence this test to assist in locating threading
103      * issues.
104      */
105     public void testCombo() {
106       int loop = 10;
107       try {
108         debug("testCombo", "beginning test...");
109 
110         long begin = System.currentTimeMillis();
111         String value=null;
112         String expected=null;
113         String xml=null;
114         Document doc=null;
115         Element root=null;
116         Element item=null;
117         Element newItem=null;
118         QName qn = null;
119         Namespace ns = null;
120         long now=0;
121 
122         xml = "<ROOT xmlns:t0=\"http://www.lse.com/t0\" >" +
123                 "  <ctx><type>Context</type></ctx>" +
124                 "  <A><B><C><D>This is a TEST</D></C></B></A>" +
125                 "  <t0:Signon><A>xyz</A><t0:Cust>customer</t0:Cust></t0:Signon>" +
126                 "</ROOT>";
127 
128 
129         for (int i = 0; i < loop; i++) {
130             doc = DocumentHelper.parseText(xml);
131 
132             root = doc.getRootElement();
133             ns = Namespace.get("t0","http://www.lse.com/t0");
134             qn = QName.get("Signon",ns);
135             item = root.element(qn);
136             value = item.asXML();
137             expected="<t0:Signon xmlns:t0=\"http://www.lse.com/t0\"><A>xyz</A><t0:Cust>customer</t0:Cust></t0:Signon>";
138             assertEquals("test t0:Signon ",expected,value);
139 
140             qn = root.getQName("Test");
141             newItem = DocumentHelper.createElement(qn);
142             now=System.currentTimeMillis();
143             newItem.setText(String.valueOf(now));
144             root.add(newItem);
145 
146             qn = root.getQName("Test2");
147             newItem = DocumentHelper.createElement(qn);
148             now=System.currentTimeMillis();
149             newItem.setText(String.valueOf(now));
150             root.add(newItem);
151 
152             item=root.element(qn);
153             item.detach();
154             item.setQName(qn);
155             root.add(item);
156             value = item.asXML();
157             expected="<Test2>"+now+"</Test2>";
158             assertEquals("test Test2 ",expected,value);
159 
160             qn = root.getQName("Test3");
161             newItem = DocumentHelper.createElement(qn);
162             now=System.currentTimeMillis();
163             newItem.setText(String.valueOf(now));
164             root.add(newItem);
165 
166             item=root.element(qn);
167             item.detach();
168             item.setQName(qn);
169             root.add(item);
170             value = item.asXML();
171             expected="<Test3>"+now+"</Test3>";
172             assertEquals("test Test3 ",expected,value);
173 
174             qn = item.getQName("Test4");
175             newItem = DocumentHelper.createElement(qn);
176             now=System.currentTimeMillis();
177             newItem.setText(String.valueOf(now));
178             root.add(newItem);
179 
180             item=root.element(qn);
181             item.detach();
182             item.setQName(qn);
183             root.add(item);
184             value = item.asXML();
185             expected="<Test4>"+now+"</Test4>";
186             assertEquals("test Test4 ",expected,value);
187         }
188         double duration = System.currentTimeMillis()-begin;
189         double avg = duration / loop;
190         debug("testCombo",
191               "*** duration="+duration+" , loop="+loop+" , avg="+avg);
192       }
193       catch (Exception e) {
194         e.printStackTrace();
195         assertTrue("Exception in test: "+e.getMessage(),false);
196       }
197     }
198 
199 
200     /***
201      * This test isolates QNameCache in a multithreaded environment.
202      */
203     public void testQNameCache() {
204       int loop = 100;
205       try {
206         debug("testQNameCache", "beginning test...");
207 
208         long begin = System.currentTimeMillis();
209         String value=null;
210         String expected=null;
211         String xml=null;
212         Document doc=null;
213         Element root=null;
214         Element item=null;
215         Element newItem=null;
216         QName qn = null;
217         Namespace ns = null;
218         long now=0;
219 
220         xml = "<ROOT xmlns:t0=\"http://www.lse.com/t0\" >" +
221                 "  <ctx><type>Context</type></ctx>" +
222                 "  <A><B><C><D>This is a TEST</D></C></B></A>" +
223                 "  <t0:Signon><A>xyz</A><t0:Cust>customer</t0:Cust></t0:Signon>" +
224                 "</ROOT>";
225 
226 
227         for (int i = 0; i < loop; i++) {
228             doc = DocumentHelper.parseText(xml);
229             root = doc.getRootElement();
230 
231             qn=DocumentHelper.createQName("test");
232             value = fetchValue(qn);
233             expected="<test/>";
234             assertEquals("test test ",expected,value);
235 
236             //creat it again
237             qn=DocumentHelper.createQName("test");
238             value = fetchValue(qn);
239             expected="<test/>";
240             assertEquals("test test again ",expected,value);
241 
242             qn=root.getQName("t0:Signon");
243             value= fetchValue(qn);
244             expected="<t0:Signon xmlns:t0=\"http://www.lse.com/t0\"/>";
245             assertEquals("test t0:Signon ",expected,value);
246 
247         }
248         double duration = System.currentTimeMillis()-begin;
249         double avg = duration / loop;
250         debug("testQNameCache",
251               "*** duration="+duration+" , loop="+loop+" , avg="+avg);
252       }
253       catch (Exception e) {
254         e.printStackTrace();
255         assertTrue("Exception in test: "+e.getMessage(),false);
256       }
257     }
258 
259 
260     /***
261      * This method creates a value that can be expected during a test
262      * @param qn
263      * @return
264      */
265     public String fetchValue(QName qn) {
266       String value=null;
267 
268       StringBuffer sb = new StringBuffer();
269       sb.append("<");
270       String prefix=qn.getNamespacePrefix();
271       if (prefix!=null && prefix.length()>0) {
272         sb.append(prefix).append(":");
273       }
274       sb.append(qn.getName());
275       String uri=qn.getNamespaceURI();
276       if (uri!=null && uri.length()>0) {
277         sb.append(" xmlns");
278         if (prefix!=null && prefix.length()>0){
279           sb.append(":").append(prefix);
280         }
281         sb.append("=\"").append(uri).append("\"");
282       }
283       sb.append("/>");
284 
285       value=sb.toString();
286       return value;
287     }
288 
289     /***
290      *  Assembles and returns a test suite.
291      *
292      *@return    The  suite
293      */
294     public static Test suite() {
295         TestSuite suite = new TestSuite();
296         suite.addTest(makeRepeatedLoadTest(5, 10, "testCombo"));
297         suite.addTest(makeRepeatedLoadTest(5, 10, "testQNameCache"));
298         return suite;
299     }
300 
301 
302 
303     /***
304      *  JUnit method to exercise test via threads and loops
305      *
306      *@param  users       Number of users to simulate (i.e. Threads).
307      *@param  iterations  Number of iterations per user ( repeat the test x times).
308      *@param  method      method to execute (testXXX).
309      *@return             A Junit test
310      */
311     protected static Test makeRepeatedLoadTest(int users, int iterations, String testMethod) {
312         long maxElapsedTime = 120000 + (1000 * users * iterations);
313 
314         Test testCase = new TestThreading(testMethod);
315 
316         Test repeatedTest = new RepeatedTest(testCase, iterations);
317         Test loadTest = new LoadTest(repeatedTest, users);
318         Test timedTest = new TimedTest(loadTest, maxElapsedTime);
319 
320         return timedTest;
321     }
322 
323     /***
324      *  The main program for the DOM4JThreadTest class
325      *
326      *@param  args  The command line arguments
327      */
328     public static void main(String[] args) {
329         String[] testCaseName = {TestThreading.class.getName()};
330         junit.textui.TestRunner.main(testCaseName);
331     }
332 
333 
334 }
335 
336 
337 
338 
339 /*
340  * Redistribution and use of this software and associated documentation
341  * ("Software"), with or without modification, are permitted provided
342  * that the following conditions are met:
343  *
344  * 1. Redistributions of source code must retain copyright
345  *    statements and notices.  Redistributions must also contain a
346  *    copy of this document.
347  *
348  * 2. Redistributions in binary form must reproduce the
349  *    above copyright notice, this list of conditions and the
350  *    following disclaimer in the documentation and/or other
351  *    materials provided with the distribution.
352  *
353  * 3. The name "DOM4J" must not be used to endorse or promote
354  *    products derived from this Software without prior written
355  *    permission of MetaStuff, Ltd.  For written permission,
356  *    please contact dom4j-info@metastuff.com.
357  *
358  * 4. Products derived from this Software may not be called "DOM4J"
359  *    nor may "DOM4J" appear in their names without prior written
360  *    permission of MetaStuff, Ltd. DOM4J is a registered
361  *    trademark of MetaStuff, Ltd.
362  *
363  * 5. Due credit should be given to the DOM4J Project - 
364  *    http://www.dom4j.org
365  *
366  * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS
367  * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
368  * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
369  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
370  * METASTUFF, LTD. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
371  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
372  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
373  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
374  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
375  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
376  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
377  * OF THE POSSIBILITY OF SUCH DAMAGE.
378  *
379  * Copyright 2001-2004 (C) MetaStuff, Ltd. All Rights Reserved.
380  *
381  * $Id: TestThreading.java,v 1.4 2004/06/25 08:03:47 maartenc Exp $
382  */