View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.vfs2.provider.zip;
18  
19  import static org.junit.jupiter.api.Assertions.assertNotNull;
20  import static org.junit.jupiter.api.Assertions.assertTrue;
21  
22  import java.io.FileNotFoundException;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.nio.file.Files;
26  import java.nio.file.Path;
27  import java.nio.file.Paths;
28  import java.nio.file.StandardCopyOption;
29  import java.util.Locale;
30  import java.util.regex.Pattern;
31  
32  import javax.xml.parsers.DocumentBuilder;
33  import javax.xml.parsers.DocumentBuilderFactory;
34  import javax.xml.parsers.ParserConfigurationException;
35  
36  import org.apache.commons.vfs2.FileObject;
37  import org.apache.commons.vfs2.FileSystemException;
38  import org.apache.commons.vfs2.FileSystemManager;
39  import org.apache.commons.vfs2.VFS;
40  import org.junit.jupiter.api.AfterEach;
41  import org.junit.jupiter.api.BeforeEach;
42  import org.junit.jupiter.api.Test;
43  import org.w3c.dom.Document;
44  import org.xml.sax.SAXException;
45  
46  /**
47   * Tests that we can use JAXP to parse an input stream living inside a Zip file.
48   */
49  public class ParseXmlInZipTest {
50  
51      private Locale defaultLocale;
52  
53      @BeforeEach
54      public void changeDefaultLocale() {
55          // save origin default Locale
56          defaultLocale = Locale.getDefault();
57  
58          // change default Locale to US. Prevent Regular Matching fail.
59          Locale.setDefault(new Locale("en", "US"));
60      }
61  
62      private Path createTempFile() throws IOException {
63          final Path zipFile = Paths.get("src/test/resources/test-data/read-xml-tests.zip");
64          final Path newZipFile = Files.createTempFile(getClass().getSimpleName(), ".zip");
65          newZipFile.toFile().deleteOnExit();
66          Files.copy(zipFile, newZipFile, StandardCopyOption.REPLACE_EXISTING);
67          return newZipFile;
68      }
69  
70      private DocumentBuilder newDocumentBuilder(final FileObject containerFile, final FileObject sourceFile,
71              final String pathToXsdInZip) throws IOException {
72          final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
73          final boolean validate = pathToXsdInZip != null;
74          documentBuilderFactory.setValidating(validate);
75          documentBuilderFactory.setNamespaceAware(true);
76          if (validate) {
77              documentBuilderFactory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage",
78                      "http://www.w3.org/2001/XMLSchema");
79              @SuppressWarnings("resource")
80              final FileObject schema = containerFile.resolveFile(pathToXsdInZip);
81              if (!schema.exists()) {
82                  schema.close();
83                  throw new FileNotFoundException(schema.toString());
84              }
85              documentBuilderFactory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaSource",
86                      schema.getContent().getInputStream());
87          }
88          DocumentBuilder documentBuilder = null;
89          try {
90              documentBuilder = documentBuilderFactory.newDocumentBuilder();
91              documentBuilder.setEntityResolver(new TestEntityResolver(containerFile, sourceFile));
92          } catch (final ParserConfigurationException e) {
93              throw new IOException("Cannot read Java Connector configuration: " + e, e);
94          }
95          documentBuilder.setErrorHandler(new TestErrorHandler(containerFile + " - " + sourceFile));
96          return documentBuilder;
97      }
98  
99      @AfterEach
100     public void resetDefaultLocale() {
101         Locale.setDefault(defaultLocale);
102     }
103 
104     @Test
105     public void testParseXmlInZip() throws IOException, SAXException {
106         final Path newZipFile = createTempFile();
107         final String xmlFilePath = "zip:file:" + newZipFile.toAbsolutePath() + "!/read-xml-tests/file1.xml";
108         final FileSystemManager manager = VFS.getManager();
109         try (FileObject zipFileObject = manager.resolveFile(xmlFilePath)) {
110             try (InputStream inputStream = zipFileObject.getContent().getInputStream()) {
111                 final Document document = newDocumentBuilder(zipFileObject, zipFileObject, null).parse(inputStream);
112                 assertNotNull(document);
113             }
114         }
115     }
116 
117     @Test
118     public void testResolveAndParseBiggerXmlInZip() throws IOException, SAXException {
119         // File is > 64 bytes
120         // In this case, we want to make sure that the XML document does NOT fit in the internal buffer used to parse
121         // the XML declaration and see if that affects JAXP when it uses its "rewind" input stream.
122         testResolveAndParseXmlInZip("read-xml-tests/file3-bigger.xml", null);
123     }
124 
125     @Test
126     public void testResolveAndParseInvalidXml() throws IOException, SAXException {
127         try {
128             testResolveAndParseXmlInZip("read-xml-tests/name-invalid.xml", "/read-xml-tests/name.xsd");
129         } catch (final SAXException e) {
130             final Pattern p = Pattern.compile("Invalid content was found starting with element.+FOO");
131             assertTrue(p.matcher(e.toString()).find());
132         }
133     }
134 
135     @Test
136     public void testResolveAndParseNotWellFormedXml() throws IOException {
137         try {
138             testResolveAndParseXmlInZip("read-xml-tests/name-not-well-formed.xml", "/read-xml-tests/name.xsd");
139         } catch (final SAXException e) {
140             assertTrue(
141                     e.toString().contains("XML document structures must start and end within the same entity."));
142         }
143     }
144 
145     @Test
146     public void testResolveAndParseXmlInZip() throws IOException, SAXException {
147         // File is < 64 bytes
148         // In this case, we want to make sure that the XML document DOES fit in the internal buffer used to parse
149         // the XML declaration and see if that affects JAXP when it uses its "rewind" input stream.
150         testResolveAndParseXmlInZip("read-xml-tests/file1.xml", null);
151     }
152 
153     private void testResolveAndParseXmlInZip(final String xmlPathInZip, final String xsdPathInZip)
154             throws IOException, FileSystemException, SAXException {
155         final Path newZipFile = createTempFile();
156         final String zipFilePath = "zip:file:" + newZipFile.toAbsolutePath();
157         final FileSystemManager manager = VFS.getManager();
158         try (FileObject zipFileObject = manager.resolveFile(zipFilePath)) {
159             try (FileObject xmlFileObject = zipFileObject.resolveFile(xmlPathInZip)) {
160                 try (InputStream inputStream = xmlFileObject.getContent().getInputStream()) {
161                     final Document document = newDocumentBuilder(zipFileObject, xmlFileObject, xsdPathInZip)
162                             .parse(inputStream);
163                     assertNotNull(document);
164                 }
165             }
166         }
167     }
168 
169     @Test
170     public void testResolveAndParseXmlInZipWithOneXmlSchema() throws IOException, SAXException {
171         testResolveAndParseXmlInZip("read-xml-tests/name-with-xsd-ref.xml", "/read-xml-tests/name.xsd");
172     }
173 
174     @Test
175     public void testResolveAndParseXmlInZipWithTwoXmlSchema() throws IOException, SAXException {
176         testResolveAndParseXmlInZip("read-xml-tests/person.xml", "/read-xml-tests/person.xsd");
177     }
178 
179 }