    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  */