1 /*
   2  * Copyright (c) 2014, 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 package test.auctionportal;
  24 
  25 import static com.sun.org.apache.xerces.internal.impl.Constants.SP_ENTITY_EXPANSION_LIMIT;
  26 import static com.sun.org.apache.xerces.internal.impl.Constants.SP_MAX_OCCUR_LIMIT;
  27 import static com.sun.org.apache.xerces.internal.jaxp.JAXPConstants.JAXP_SCHEMA_LANGUAGE;
  28 import static com.sun.org.apache.xerces.internal.jaxp.JAXPConstants.JAXP_SCHEMA_SOURCE;
  29 import static org.testng.Assert.assertTrue;
  30 import java.io.File;
  31 import java.io.FileInputStream;
  32 import java.io.FileOutputStream;
  33 import java.io.IOException;
  34 import java.io.InputStream;
  35 import java.nio.file.Files;
  36 import java.nio.file.Path;
  37 import java.nio.file.Paths;
  38 import static javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING;
  39 import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI;
  40 import javax.xml.parsers.DocumentBuilder;
  41 import javax.xml.parsers.DocumentBuilderFactory;
  42 import javax.xml.parsers.ParserConfigurationException;
  43 import javax.xml.parsers.SAXParser;
  44 import javax.xml.parsers.SAXParserFactory;
  45 import javax.xml.transform.TransformerException;
  46 import javax.xml.transform.TransformerFactory;
  47 import javax.xml.transform.dom.DOMSource;
  48 import javax.xml.transform.stream.StreamResult;
  49 import static jaxp.library.JAXPTestUtilities.compareDocumentWithGold;
  50 import static jaxp.library.JAXPTestUtilities.failCleanup;
  51 import static jaxp.library.JAXPTestUtilities.failUnexpected;
  52 import static org.testng.Assert.assertFalse;
  53 
  54 import org.testng.annotations.Test;
  55 import org.w3c.dom.Document;
  56 import org.xml.sax.SAXException;
  57 import org.xml.sax.SAXParseException;
  58 import static test.auctionportal.HiBidConstants.CLASS_DIR;
  59 import static test.auctionportal.HiBidConstants.GOLDEN_DIR;
  60 import static test.auctionportal.HiBidConstants.XML_DIR;
  61 
  62 /**
  63  * This is a test class for the Auction portal HiBid.com.
  64  */
  65 public class AuctionItemRepository {
  66     /**
  67      * XML file for parsing. 
  68      */
  69     private final static String ENTITY_XML = XML_DIR + "entity.xml";
  70     
  71     /**
  72      * Feature name.
  73      */
  74     private final static String FEATURE_NAME = "http://xml.org/sax/features/namespace-prefixes";
  75 
  76     /**
  77      * Setting the EntityExpansion Limit to 128000 and checks if the XML
  78      * document that has more than two levels of entity expansion is parsed or
  79      * not. Previous system property was changed to jdk.xml.entityExpansionLimit
  80      * see http://docs.oracle.com/javase/tutorial/jaxp/limits/limits.html.
  81      */
  82     @Test
  83     public void testEntityExpansionSAXPos() {
  84         try {
  85             SAXParserFactory factory = SAXParserFactory.newInstance();
  86             // Secure processing will limit XML processing to conform to 
  87             // implementation limits.
  88             factory.setFeature(FEATURE_SECURE_PROCESSING, true);
  89             // Set entityExpansionLimit as 2 should expect fatalError
  90             System.setProperty(SP_ENTITY_EXPANSION_LIMIT, String.valueOf(128000));
  91             SAXParser parser = factory.newSAXParser();
  92 
  93             MyErrorHandler fatalHandler = new MyErrorHandler();
  94             parser.parse(new File(ENTITY_XML), fatalHandler);
  95             assertFalse(fatalHandler.isAnyError()); 
  96         } catch (ParserConfigurationException | SAXException | IOException e) {
  97             failUnexpected(e);
  98         }
  99     }
 100     /**
 101      * Setting the EntityExpansion Limit to 2 and checks if the XML
 102      * document that has more than two levels of entity expansion is parsed or
 103      * not. Previous system property was changed to jdk.xml.entityExpansionLimit
 104      * see http://docs.oracle.com/javase/tutorial/jaxp/limits/limits.html.
 105      */
 106     @Test(expectedExceptions = SAXParseException.class)
 107     public void testEntityExpansionSAXNeg() throws SAXParseException {
 108         // 
 109         try {
 110             SAXParserFactory factory = SAXParserFactory.newInstance();
 111             // Secure processing will limit XML processing to conform to 
 112             // implementation limits.
 113             factory.setFeature(FEATURE_SECURE_PROCESSING, true);
 114             // Set entityExpansionLimit as 2 should expect SAXParseException
 115             System.setProperty(SP_ENTITY_EXPANSION_LIMIT, String.valueOf(2));
 116             SAXParser parser = factory.newSAXParser();
 117 
 118             MyErrorHandler fatalHandler = new MyErrorHandler();
 119             parser.parse(new File(ENTITY_XML), fatalHandler);
 120         } catch (SAXParseException e) {
 121             throw e;
 122         } catch (ParserConfigurationException | SAXException | IOException e) {
 123             failUnexpected(e);
 124         }
 125     }
 126 
 127     /**
 128      * Testing set MaxOccursLimit to 10000 in the secure processing enabled for 
 129      * SAXParserFactory.
 130      */
 131     @Test
 132     public void testMaxOccurLimitPos() {
 133         String schema_file = XML_DIR + "toys.xsd";
 134         String xml_file = XML_DIR + "toys.xml";
 135 
 136         try (InputStream is = new FileInputStream(xml_file)) {
 137             SAXParserFactory factory = SAXParserFactory.newInstance();
 138             factory.setValidating(true);
 139             factory.setFeature(FEATURE_SECURE_PROCESSING, true);
 140             System.setProperty(SP_MAX_OCCUR_LIMIT, String.valueOf(10000));
 141             SAXParser parser = factory.newSAXParser();
 142             parser.setProperty(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA_NS_URI);
 143             parser.setProperty(JAXP_SCHEMA_SOURCE, new File(schema_file));
 144             MyErrorHandler eh = new MyErrorHandler();
 145             parser.parse(is, eh);
 146             assertFalse(eh.isAnyError());
 147         } catch (ParserConfigurationException | SAXException | IOException e) {
 148             failUnexpected(e);
 149         }
 150     }
 151 
 152     /**
 153      * Use a DocumentBuilder to create a DOM object and see if Secure Processing 
 154      * feature affects the entity expansion.
 155      */
 156     @Test
 157     public void testEntityExpansionDOMPos()  {
 158         try {
 159             DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
 160             dfactory.setFeature(FEATURE_SECURE_PROCESSING, true);
 161             System.setProperty(SP_ENTITY_EXPANSION_LIMIT, String.valueOf(10000));
 162             DocumentBuilder dBuilder = dfactory.newDocumentBuilder();
 163             MyErrorHandler eh = new MyErrorHandler();
 164             dBuilder.setErrorHandler(eh);
 165             dBuilder.parse(ENTITY_XML);
 166             assertFalse(eh.isAnyError());
 167         } catch (ParserConfigurationException | IOException | SAXException e) {
 168             failUnexpected(e);       
 169         }
 170     }
 171 
 172     /**
 173      * Use a DocumentBuilder to create a DOM object and see how does the Secure
 174      * Processing feature and entityExpansionLimit value affects output.
 175      * Negative test that when entityExpansionLimit is too small.
 176      */
 177     @Test(expectedExceptions = SAXParseException.class)
 178     public void testEntityExpansionDOMNeg() throws SAXParseException {
 179         try {
 180             DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
 181             dfactory.setFeature(FEATURE_SECURE_PROCESSING, true);
 182             System.setProperty(SP_ENTITY_EXPANSION_LIMIT, String.valueOf(2));
 183             DocumentBuilder dBuilder = dfactory.newDocumentBuilder();
 184             MyErrorHandler eh = new MyErrorHandler();
 185             dBuilder.setErrorHandler(eh);
 186             dBuilder.parse(ENTITY_XML);
 187         } catch (SAXParseException e) {
 188             throw e;
 189         } catch (ParserConfigurationException | IOException | SAXException e) {
 190             failUnexpected(e);       
 191         }
 192     }
 193 
 194     /**
 195      * Test xi:include with a SAXParserFactory.
 196      */
 197     @Test
 198     public void testXIncludeSAXPos() {
 199         String resultFile = CLASS_DIR + "doc_xinclude.out";
 200         String goldFile = GOLDEN_DIR + "doc_xincludeGold.xml";
 201         String xmlFile = XML_DIR + "doc_xinclude.xml";
 202 
 203         try {
 204             try(FileOutputStream fos = new FileOutputStream(resultFile)) {
 205                 XInclHandler xh = new XInclHandler(fos, null);
 206                 SAXParserFactory spf = SAXParserFactory.newInstance();
 207                 spf.setNamespaceAware(true);
 208                 spf.setXIncludeAware(true);
 209                 spf.setFeature(FEATURE_NAME, true);
 210                 spf.newSAXParser().parse(new File(xmlFile), xh);
 211             }
 212             assertTrue(compareDocumentWithGold(goldFile, resultFile));
 213         } catch (ParserConfigurationException | SAXException | IOException e) {
 214             failUnexpected(e);
 215         } finally {
 216             try {
 217                 Path resultPath = Paths.get(resultFile);
 218                 if (Files.exists(resultPath)) {
 219                     Files.delete(resultPath);
 220                 }
 221             } catch (IOException ex) {
 222                 failCleanup(ex, resultFile);
 223             }
 224         }
 225     }
 226 
 227     /**
 228      * Test the simple case of including a document using xi:include using a
 229      * DocumentBuilder.
 230      */
 231     @Test
 232     public void testXIncludeDOMPos() {
 233         String resultFile = CLASS_DIR + "doc_xincludeDOM.out";
 234         String goldFile = GOLDEN_DIR + "doc_xincludeGold.xml";
 235         String xmlFile = XML_DIR + "doc_xinclude.xml";
 236         try {
 237             try (FileOutputStream fos = new FileOutputStream(resultFile)) {
 238                 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
 239                 dbf.setXIncludeAware(true);
 240                 dbf.setNamespaceAware(true);
 241 
 242                 Document doc = dbf.newDocumentBuilder().parse(new File(xmlFile));
 243                 doc.setXmlStandalone(true);
 244 
 245                 TransformerFactory.newInstance().newTransformer().
 246                         transform(new DOMSource(doc), new StreamResult(fos));
 247             }
 248             assertTrue(compareDocumentWithGold(goldFile, resultFile));
 249         } catch (ParserConfigurationException | SAXException | IOException 
 250                 | TransformerException e) {
 251             failUnexpected(e);
 252         } finally {
 253             try {
 254                 Path resultPath = Paths.get(resultFile);
 255                 if (Files.exists(resultPath)) {
 256                     Files.delete(resultPath);
 257                 }
 258             } catch (IOException ex) {
 259                 failCleanup(ex, resultFile);
 260             }
 261         }
 262     }
 263 
 264     /**
 265      * Test the simple case of including a document using xi:include within a
 266      * xi:fallback using a DocumentBuilder.
 267      */
 268     @Test
 269     public void testXIncludeFallbackDOMPos() {
 270         String resultFile = CLASS_DIR + "doc_fallbackDOM.out";
 271         String goldFile = GOLDEN_DIR + "doc_fallbackGold.xml";
 272         String xmlFile = XML_DIR + "doc_fallback.xml";
 273         try{
 274             try (FileOutputStream fos = new FileOutputStream(resultFile)) {
 275                 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
 276                 dbf.setXIncludeAware(true);
 277                 dbf.setNamespaceAware(true);
 278 
 279                 Document doc = dbf.newDocumentBuilder().parse(new File(xmlFile));
 280                 doc.setXmlStandalone(true);
 281                 TransformerFactory.newInstance().newTransformer()
 282                         .transform(new DOMSource(doc), new StreamResult(fos));
 283             }
 284             assertTrue(compareDocumentWithGold(goldFile, resultFile));
 285         } catch (ParserConfigurationException | SAXException | IOException 
 286                 | TransformerException e) {
 287             failUnexpected(e);
 288         } finally {
 289             try {
 290                 Path resultPath = Paths.get(resultFile);
 291                 if (Files.exists(resultPath)) {
 292                     Files.delete(resultPath);
 293                 }
 294             } catch (IOException ex) {
 295                 failCleanup(ex, resultFile);
 296             }
 297         }
 298     }
 299 
 300     /**
 301      * Test for xi:fallback where the fall back text is parsed as text. This 
 302      * test uses a nested xi:include for the fallback test.
 303      */
 304     @Test
 305     public void testXIncludeFallbackTextPos() {
 306         String resultFile = CLASS_DIR + "doc_fallback_text.out";
 307         String goldFile = GOLDEN_DIR + "doc_fallback_textGold.xml";
 308         String xmlFile = XML_DIR + "doc_fallback_text.xml";
 309 
 310         try{
 311             try (FileOutputStream fos = new FileOutputStream(resultFile)) {
 312                 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
 313                 dbf.setXIncludeAware(true);
 314                 dbf.setNamespaceAware(true);
 315 
 316                 Document doc = dbf.newDocumentBuilder().parse(new File(xmlFile));
 317                 doc.setXmlStandalone(true);
 318                 TransformerFactory.newInstance().newTransformer()
 319                         .transform(new DOMSource(doc), new StreamResult(fos));
 320             }
 321             assertTrue(compareDocumentWithGold(goldFile, resultFile));
 322         } catch (ParserConfigurationException | SAXException | IOException 
 323                 | TransformerException e) {
 324             failUnexpected(e);
 325         } finally {
 326             try {
 327                 Path resultPath = Paths.get(resultFile);
 328                 if (Files.exists(resultPath)) {
 329                     Files.delete(resultPath);
 330                 }
 331             } catch (IOException ex) {
 332                 failCleanup(ex, resultFile);
 333             }
 334         }
 335     }
 336 
 337     /**
 338      * Test the XPointer element() framework with XInclude.
 339      */
 340     @Test
 341     public void testXpointerElementPos() {
 342         String resultFile = CLASS_DIR + "doc_xpointer_element.out";
 343         String goldFile = GOLDEN_DIR + "doc_xpointerGold.xml";
 344         String xmlFile = XML_DIR + "doc_xpointer_element.xml";
 345 
 346         try{
 347             try (FileOutputStream fos = new FileOutputStream(resultFile)) {
 348                 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
 349                 dbf.setXIncludeAware(true);
 350                 dbf.setNamespaceAware(true);
 351 
 352                 DocumentBuilder db = dbf.newDocumentBuilder();
 353 
 354                 TransformerFactory.newInstance().newTransformer()
 355                         .transform(new DOMSource(db.parse(new File(xmlFile))),
 356                                 new StreamResult(fos));
 357             }
 358             assertTrue(compareDocumentWithGold(goldFile, resultFile));
 359         } catch (ParserConfigurationException | SAXException | IOException 
 360                 | TransformerException e) {
 361             failUnexpected(e);
 362         } finally {
 363             try {
 364                 Path resultPath = Paths.get(resultFile);
 365                 if (Files.exists(resultPath)) {
 366                     Files.delete(resultPath);
 367                 }
 368             } catch (IOException ex) {
 369                 failCleanup(ex, resultFile);
 370             }
 371         }
 372     }
 373 
 374     /**
 375      * Test the XPointer framework with a SAX object.
 376      */
 377     @Test
 378     public void testXPointerPos() {
 379         String resultFile = CLASS_DIR + "doc_xpointer.out";
 380         String goldFile = GOLDEN_DIR + "doc_xpointerGold.xml";
 381         String xmlFile = XML_DIR + "doc_xpointer.xml";
 382 
 383         try{
 384             try (FileOutputStream fos = new FileOutputStream(resultFile)) {
 385                 SAXParserFactory spf = SAXParserFactory.newInstance();
 386                 spf.setNamespaceAware(true);
 387                 spf.setXIncludeAware(true);
 388                 spf.setFeature(FEATURE_NAME, true);
 389                 // parse the file
 390                 spf.newSAXParser().parse(new File(xmlFile), new XInclHandler(fos, null));
 391             }
 392             assertTrue(compareDocumentWithGold(goldFile, resultFile));
 393         } catch (ParserConfigurationException | SAXException | IOException e) {
 394             failUnexpected(e);
 395         } finally {
 396             try {
 397                 Path resultPath = Paths.get(resultFile);
 398                 if (Files.exists(resultPath)) {
 399                     Files.delete(resultPath);
 400                 }
 401             } catch (IOException ex) {
 402                 failCleanup(ex, resultFile);
 403             }
 404         }
 405     }
 406 
 407     /**
 408      * Test if xi:include may reference the doc containing the include if the
 409      * parse type is text.
 410      */
 411     @Test
 412     public void testXIncludeLoopPos() {
 413         String resultFile = CLASS_DIR + "doc_xinc_loops.out";
 414         String goldFile = GOLDEN_DIR + "doc_xinc_loopGold.xml";
 415         String xmlFile = XML_DIR + "doc_xinc_loops.xml";
 416 
 417         try{
 418             try (FileOutputStream fos = new FileOutputStream(resultFile)) {
 419                 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
 420                 dbf.setXIncludeAware(true);
 421                 dbf.setNamespaceAware(true);
 422                 DocumentBuilder db = dbf.newDocumentBuilder();
 423                 Document doc = db.parse(new File(xmlFile));
 424                 doc.normalizeDocument();
 425                 doc.setXmlStandalone(true);
 426 
 427                 TransformerFactory.newInstance().newTransformer()
 428                         .transform(new DOMSource(doc), new StreamResult(fos));
 429             }
 430             assertTrue(compareDocumentWithGold(goldFile, resultFile));
 431         } catch (ParserConfigurationException | SAXException | IOException 
 432                 | TransformerException e) {
 433             failUnexpected(e);
 434         } finally {
 435             try {
 436                 Path resultPath = Paths.get(resultFile);
 437                 if (Files.exists(resultPath)) {
 438                     Files.delete(resultPath);
 439                 }
 440             } catch (IOException ex) {
 441                 failCleanup(ex, resultFile);
 442             }
 443         }
 444     }
 445 
 446     /**
 447      * Test if two non nested xi:include elements can include the same document
 448      * with an xi:include statement.
 449      */
 450     @Test
 451     public void testXIncludeNestedPos() {
 452         String resultFile = CLASS_DIR + "schedule.out";
 453         String goldFile = GOLDEN_DIR + "scheduleGold.xml";
 454         String xmlFile = XML_DIR + "schedule.xml";
 455 
 456         try{
 457             try (FileOutputStream fos = new FileOutputStream(resultFile)) {
 458                 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
 459                 dbf.setXIncludeAware(true);
 460                 dbf.setNamespaceAware(true);
 461 
 462                 Document doc = dbf.newDocumentBuilder().parse(new File(xmlFile));
 463                 doc.setXmlStandalone(true);
 464                 TransformerFactory.newInstance().newTransformer()
 465                         .transform(new DOMSource(doc), new StreamResult(fos));
 466             }
 467             assertTrue(compareDocumentWithGold(goldFile, resultFile));
 468         } catch (ParserConfigurationException | SAXException | IOException 
 469                 | TransformerException e) {
 470             failUnexpected(e);
 471         } finally {
 472             try {
 473                 Path resultPath = Paths.get(resultFile);
 474                 if (Files.exists(resultPath)) {
 475                     Files.delete(resultPath);
 476                 }
 477             } catch (IOException ex) {
 478                 failCleanup(ex, resultFile);
 479             }
 480         }
 481     }
 482 }