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