1 /*
2  * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 package transform;
25 
26 import java.io.StringReader;
27 import java.io.StringWriter;
28 import java.util.concurrent.CyclicBarrier;
29 import java.util.concurrent.ExecutorService;
30 import java.util.concurrent.Executors;
31 import java.util.concurrent.TimeUnit;
32 import java.util.concurrent.atomic.AtomicBoolean;
33 
34 import javax.xml.transform.Source;
35 import javax.xml.transform.Templates;
36 import javax.xml.transform.Transformer;
37 import javax.xml.transform.TransformerFactory;
38 import javax.xml.transform.stream.StreamResult;
39 import javax.xml.transform.stream.StreamSource;
40 
41 import org.testng.annotations.Test;
42 import static org.testng.Assert.assertTrue;
43 import static jaxp.library.JAXPTestUtilities.runWithAllPerm;
44 
45 /*
46  * @test
47  * @bug 8167179
48  * @library /javax/xml/jaxp/libs
49  * @run testng/othervm -DrunSecMngr=true -Djava.security.manager=allow transform.NamespacePrefixTest
50  * @run testng/othervm transform.NamespacePrefixTest
51  * @summary This class tests the generation of namespace prefixes
52  */
53 public class NamespacePrefixTest {
54 
55     @Test
testReuseTemplates()56     public void testReuseTemplates() throws Exception {
57         final TransformerFactory tf = TransformerFactory.newInstance();
58         final Source xslsrc = new StreamSource(new StringReader(XSL));
59         final Templates tmpl = tf.newTemplates(xslsrc);
60         for (int i = 0; i < TRANSF_COUNT; i++) {
61             checkResult(doTransformation(tmpl.newTransformer()));
62         }
63     }
64 
65     @Test
testReuseTransformer()66     public void testReuseTransformer() throws Exception {
67         final TransformerFactory tf = TransformerFactory.newInstance();
68         final Source xslsrc = new StreamSource(new StringReader(XSL));
69         final Transformer t = tf.newTransformer(xslsrc);
70         for (int i = 0; i < TRANSF_COUNT; i++) {
71             checkResult(doTransformation(t));
72         }
73     }
74 
75     @Test
testConcurrentTransformations()76     public void testConcurrentTransformations() throws Exception {
77         final TransformerFactory tf = TransformerFactory.newInstance();
78         final Source xslsrc = new StreamSource(new StringReader(XSL));
79         final Templates tmpl = tf.newTemplates(xslsrc);
80         concurrentTestPassed.set(true);
81 
82         // Execute multiple TestWorker tasks
83         for (int id = 0; id < THREADS_COUNT; id++) {
84             EXECUTOR.execute(new TransformerThread(tmpl.newTransformer(), id));
85         }
86         // Initiate shutdown of previously submitted task
87         runWithAllPerm(EXECUTOR::shutdown);
88         // Wait for termination of submitted tasks
89         if (!EXECUTOR.awaitTermination(THREADS_COUNT, TimeUnit.SECONDS)) {
90             // If not all tasks terminates during the time out force them to shutdown
91             runWithAllPerm(EXECUTOR::shutdownNow);
92         }
93         // Check if all transformation threads generated the correct namespace prefix
94         assertTrue(concurrentTestPassed.get());
95     }
96 
97     // Do one transformation with the provided transformer
doTransformation(Transformer t)98     private static String doTransformation(Transformer t) throws Exception {
99         StringWriter resWriter = new StringWriter();
100         Source xmlSrc = new StreamSource(new StringReader(XML));
101         t.transform(xmlSrc, new StreamResult(resWriter));
102         return resWriter.toString();
103     }
104 
105     // Check if the transformation result string contains the
106     // element with the exact namespace prefix generated.
checkResult(String result)107     private static void checkResult(String result) {
108         // Check prefix of 'Element2' element, it should always be the same
109         assertTrue(result.contains(EXPECTED_CONTENT));
110     }
111 
112     // Check if the transformation result string contains the element with
113     // the exact namespace prefix generated by current thread.
114     // If the expected prefix is not found and there was no failures observed by
115     // other test threads then mark concurrent test as failed.
checkThreadResult(String result, int id)116     private static void checkThreadResult(String result, int id) {
117         boolean res = result.contains(EXPECTED_CONTENT);
118         System.out.printf("%d: transformation result: %s%n", id, res ? "Pass" : "Fail");
119         if (!res) {
120             System.out.printf("%d result:%s%n", id, result);
121         }
122         concurrentTestPassed.compareAndSet(true, res);
123     }
124 
125     // TransformerThread task that does the transformation similar
126     // to testReuseTransformer test method
127     private class TransformerThread implements Runnable {
128 
129         private final Transformer transformer;
130         private final int id;
131 
TransformerThread(Transformer transformer, int id)132         TransformerThread(Transformer transformer, int id) {
133             this.transformer = transformer;
134             this.id = id;
135         }
136 
137         @Override
run()138         public void run() {
139             try {
140                 System.out.printf("%d: waiting for barrier%n", id);
141                 //Synchronize startup of all tasks
142                 BARRIER.await();
143                 System.out.printf("%d: starting transformation%n", id);
144                 checkThreadResult(doTransformation(transformer), id);
145             } catch (Exception ex) {
146                 throw new RuntimeException("TransformerThread " + id + " failed", ex);
147             }
148         }
149     }
150 
151     // Number of subsequent transformations
152     private static final int TRANSF_COUNT = 10;
153 
154     // Number of transformer threads running concurently
155     private static final int THREADS_COUNT = 10;
156 
157     // Variable for storing the concurrent transformation test result. It is
158     // updated by transformer threads
159     private static final AtomicBoolean concurrentTestPassed = new AtomicBoolean(true);
160 
161     // Cyclic barrier for threads startup synchronization
162     private static final CyclicBarrier BARRIER = new CyclicBarrier(THREADS_COUNT);
163 
164     // Thread pool
165     private static final ExecutorService EXECUTOR = Executors.newCachedThreadPool();
166 
167     // XSL that transforms XML and produces unique namespace prefixes for each element
168     private final static String XSL = "<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n"
169             + " <xsl:template match=\"node()|@*\" priority=\"1\">\n"
170             + "     <xsl:copy>\n"
171             + "       <xsl:apply-templates select=\"node()|@*\"/>\n"
172             + "     </xsl:copy>\n"
173             + " </xsl:template>\n"
174             + " <xsl:template match=\"*\" priority=\"2\">\n"
175             + "  <xsl:element name=\"{name()}\" namespace=\"{namespace-uri()}\">\n"
176             + "   <xsl:apply-templates select=\"node()|@*\"/>\n"
177             + "  </xsl:element>\n"
178             + " </xsl:template>\n"
179             + "</xsl:stylesheet>";
180 
181     // Simple XML content with root and two child elements
182     private final static String XML = "<TestRoot xmlns=\"test.xmlns\">\n"
183             + "  <Element1 xmlns=\"test.xmlns\">\n"
184             + "  </Element1>\n"
185             + "  <Element2 xmlns=\"test.xmlns\">\n"
186             + "  </Element2>\n"
187             + "</TestRoot>";
188 
189     // With thread local namespace prefix index each transformation result should
190     // be the same and contain the same prefix for Element2
191     private final static String EXPECTED_CONTENT = "</ns2:Element2>";
192 
193 }
194