1 /*
2  * Copyright (c) 2015, 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.FilePermission;
27 import java.io.IOException;
28 import java.io.StringReader;
29 import java.io.StringWriter;
30 import java.nio.file.Files;
31 import java.nio.file.Path;
32 import java.nio.file.Paths;
33 import javax.xml.transform.Source;
34 import javax.xml.transform.Transformer;
35 import javax.xml.transform.TransformerException;
36 import javax.xml.transform.TransformerFactory;
37 import javax.xml.transform.URIResolver;
38 import javax.xml.transform.stream.StreamResult;
39 import javax.xml.transform.stream.StreamSource;
40 
41 import org.testng.Assert;
42 import org.testng.annotations.DataProvider;
43 import org.testng.annotations.Listeners;
44 import org.testng.annotations.Test;
45 
46 import static org.testng.Assert.assertEquals;
47 import static jaxp.library.JAXPTestUtilities.runWithAllPerm;
48 import static jaxp.library.JAXPTestUtilities.clearSystemProperty;
49 import static jaxp.library.JAXPTestUtilities.setSystemProperty;
50 import static jaxp.library.JAXPTestUtilities.tryRunWithTmpPermission;
51 import static jaxp.library.JAXPTestUtilities.getSystemProperty;
52 
53 /*
54  * @test
55  * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest
56  * @compile DocumentExtFunc.java
57  * @run testng/othervm -DrunSecMngr=true transform.XSLTFunctionsTest
58  * @run testng/othervm transform.XSLTFunctionsTest
59  * @summary This class contains tests for XSLT functions.
60  */
61 
62 @Listeners({jaxp.library.FilePolicy.class})
63 public class XSLTFunctionsTest {
64     /**
65      * @bug 8165116
66      * Verifies that redirect works properly when extension function is enabled
67      *
68      * @param xml the XML source
69      * @param xsl the stylesheet that redirect output to a file
70      * @param output the output file
71      * @param redirect the redirect file
72      * @throws Exception if the test fails
73      **/
74     @Test(dataProvider = "redirect")
testRedirect(String xml, String xsl, String output, String redirect)75     public void testRedirect(String xml, String xsl, String output, String redirect) throws Exception {
76 
77         TransformerFactory tf = TransformerFactory.newInstance();
78         tf.setFeature(ORACLE_ENABLE_EXTENSION_FUNCTION, true);
79         Transformer t = tf.newTransformer(new StreamSource(new StringReader(xsl)));
80 
81         //Transform the xml
82         tryRunWithTmpPermission(
83                 () -> t.transform(new StreamSource(new StringReader(xml)), new StreamResult(new StringWriter())),
84                 new FilePermission(output, "write"), new FilePermission(redirect, "write"));
85 
86         // Verifies that the output is redirected successfully
87         String userDir = getSystemProperty("user.dir");
88         Path pathOutput = Paths.get(userDir, output);
89         Path pathRedirect = Paths.get(userDir, redirect);
90         Assert.assertTrue(Files.exists(pathOutput));
91         Assert.assertTrue(Files.exists(pathRedirect));
92         System.out.println("Output to " + pathOutput + " successful.");
93         System.out.println("Redirect to " + pathRedirect + " successful.");
94         Files.deleteIfExists(pathOutput);
95         Files.deleteIfExists(pathRedirect);
96     }
97 
98     /**
99      * @bug 8161454
100      * Verifies that the new / correct name is supported, as is the old / incorrect
101      * one for compatibility
102      */
103     @Test
testNameChange()104     public void testNameChange() {
105 
106         boolean feature;
107         TransformerFactory tf = TransformerFactory.newInstance();
108         feature = tf.getFeature(ORACLE_ENABLE_EXTENSION_FUNCTION);
109         System.out.println("Default setting: " + feature);
110         // The default: true if no SecurityManager, false otherwise
111         Assert.assertTrue(feature == getDefault());
112 
113         setSystemProperty(SP_ENABLE_EXTENSION_FUNCTION, getDefaultOpposite());
114         tf = TransformerFactory.newInstance();
115         feature = tf.getFeature(ORACLE_ENABLE_EXTENSION_FUNCTION);
116         System.out.println("After setting " + SP_ENABLE_EXTENSION_FUNCTION + ": " + feature);
117         clearSystemProperty(SP_ENABLE_EXTENSION_FUNCTION);
118         // old/incorrect name is still supported
119         Assert.assertTrue(feature != getDefault());
120 
121         setSystemProperty(SP_ENABLE_EXTENSION_FUNCTION_SPEC, getDefaultOpposite());
122         tf = TransformerFactory.newInstance();
123         feature = tf.getFeature(ORACLE_ENABLE_EXTENSION_FUNCTION);
124         System.out.println("After setting " + SP_ENABLE_EXTENSION_FUNCTION_SPEC + ": " + feature);
125         clearSystemProperty(SP_ENABLE_EXTENSION_FUNCTION_SPEC);
126         // new/correct name is effective
127         Assert.assertTrue(feature != getDefault());
128     }
129 
130     final boolean isSecure;
131     {
132         String runSecMngr = getSystemProperty("runSecMngr");
133         isSecure = runSecMngr != null && runSecMngr.equals("true");
134     }
135 
136     // The default: true if no SecurityManager, false otherwise
getDefault()137     private boolean getDefault() {
138         if (isSecure) {
139             return false;
140         } else {
141             return true;
142         }
143     }
144 
145     // Gets a String value that is opposite to the default value
getDefaultOpposite()146     private String getDefaultOpposite() {
147         if (isSecure) {
148             return "true";
149         } else {
150             return "false";
151         }
152     }
153 
154     /**
155      * @bug 8062518 8153082
156      * Verifies that a reference to the DTM created by XSLT document function is
157      * actually read from the DTM by an extension function.
158      * @param xml Content of xml file to process
159      * @param xsl stylesheet content that loads external document {@code externalDoc}
160      *        with XSLT 'document' function and then reads it with
161      *        DocumentExtFunc.test() function
162      * @param externalDoc Content of the external xml document
163      * @param expectedResult Expected transformation result
164      **/
165     @Test(dataProvider = "document")
testDocument(final String xml, final String xsl, final String externalDoc, final String expectedResult)166     public void testDocument(final String xml, final String xsl,
167                              final String externalDoc, final String expectedResult) throws Exception {
168         // Prepare sources for transormation
169         Source src = new StreamSource(new StringReader(xml));
170         Source xslsrc = new StreamSource(new StringReader(xsl));
171 
172         // Create factory and transformer
173         TransformerFactory tf = TransformerFactory.newInstance();
174         tf.setFeature(ORACLE_ENABLE_EXTENSION_FUNCTION, true);
175         tf.setAttribute(EXTENSION_CLASS_LOADER,
176                 runWithAllPerm(() -> Thread.currentThread().getContextClassLoader()));
177         Transformer t = tf.newTransformer( xslsrc );
178         t.setErrorListener(tf.getErrorListener());
179 
180         // Set URI Resolver to return the newly constructed xml
181         // stream source object from xml test string
182         t.setURIResolver(new URIResolver() {
183             @Override
184             public Source resolve(String href, String base)
185                     throws TransformerException {
186                 if (href.contains("externalDoc")) {
187                     return new StreamSource(new StringReader(externalDoc));
188                 } else {
189                     return new StreamSource(new StringReader(xml));
190                 }
191             }
192         });
193 
194         // Prepare output stream
195         StringWriter xmlResultString = new StringWriter();
196         StreamResult xmlResultStream = new StreamResult(xmlResultString);
197 
198         //Transform the xml
199         t.transform(src, xmlResultStream);
200 
201         // If the document can't be accessed and the bug is in place then
202         // reported exception will be thrown during transformation
203         System.out.println("Transformation result:"+xmlResultString.toString().trim());
204 
205         // Check the result - it should contain two (node name, node values) entries -
206         // one for original document, another for a document created with
207         // call to 'document' function
208         assertEquals(xmlResultString.toString().trim(), expectedResult);
209     }
210 
211     @DataProvider(name = "document")
documentTestData()212     public static Object[][] documentTestData() {
213         return new Object[][] {
214             {documentTestXml, documentTestXsl, documentTestExternalDoc, documentTesteExpectedResult},
215         };
216     }
217 
218     @DataProvider(name = "redirect")
getData()219     public static Object[][] getData() {
220         return new Object[][] {
221             {documentTestXml, xslRedirect, "testoutput.xml", "testredirect.xml"},
222         };
223     }
224 
225     static final String documentTestXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Test>Doc</Test>";
226 
227     static final String documentTestExternalDoc = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Test>External Doc</Test>";
228 
229     static final String documentTestXsl = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
230             + "<xsl:transform version=\"1.0\""
231             + " xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" "
232             + " xmlns:cfunc=\"http://xml.apache.org/xalan/java/\">"
233             + "<xsl:template match=\"/\">"
234             + "<xsl:element name=\"root\">"
235             + "<xsl:variable name=\"other_doc\" select=\"document(&#39;externalDoc&#39;)\"/>"
236             + "<!-- Source -->"
237             + "<xsl:value-of select=\"cfunc:transform.DocumentExtFunc.test(/Test)\"/>"
238             + "<!-- document() -->"
239             + "<xsl:value-of select=\"cfunc:transform.DocumentExtFunc.test($other_doc/Test)\"/>"
240             + "</xsl:element></xsl:template></xsl:transform>";
241 
242     static final String documentTesteExpectedResult = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
243                                                     + "<root>[Test:Doc][Test:External Doc]</root>";
244 
245     static String xslRedirect = " <xsl:stylesheet \n"
246             + "   xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n"
247             + "   xmlns:xsltc=\"http://xml.apache.org/xalan/xsltc\"\n"
248             + "   xmlns:redirect=\"http://xml.apache.org/xalan/redirect\"\n"
249             + "   extension-element-prefixes=\"xsltc redirect\"\n"
250             + "   version=\"1.0\">\n"
251             + "   <xsl:template match=\"/\">\n"
252             + "     <xsl:text>This goes to standard output</xsl:text>\n"
253             + "     <xsltc:output file=\"testoutput.xml\">\n"
254             + "       <xsl:text>This ends up in the file 'testoutput.xml'</xsl:text>\n"
255             + "     </xsltc:output>\n"
256             + "     <redirect:write file=\"testredirect.xml\">\n"
257             + "       <xsl:text>This ends up in the file 'testredirect.xml'</xsl:text>\n"
258             + "     </redirect:write>\n"
259             + "   </xsl:template>\n"
260             + "</xsl:stylesheet>";
261 
262     public static final String ORACLE_JAXP_PROPERTY_PREFIX =
263         "http://www.oracle.com/xml/jaxp/properties/";
264     /**
265      * Feature enableExtensionFunctions
266      */
267     public static final String ORACLE_ENABLE_EXTENSION_FUNCTION =
268             ORACLE_JAXP_PROPERTY_PREFIX + "enableExtensionFunctions";
269     static final String SP_ENABLE_EXTENSION_FUNCTION = "javax.xml.enableExtensionFunctions";
270     // This is the correct name by the spec
271     static final String SP_ENABLE_EXTENSION_FUNCTION_SPEC = "jdk.xml.enableExtensionFunctions";
272     private static final String EXTENSION_CLASS_LOADER = "jdk.xml.transform.extensionClassLoader";
273 }
274