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  
18  package org.apache.commons.vfs2.provider.zip;
19  
20  import java.io.File;
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.nio.charset.StandardCharsets;
24  
25  import org.apache.commons.io.FileUtils;
26  import org.apache.commons.io.IOUtils;
27  import org.apache.commons.vfs2.FileObject;
28  import org.apache.commons.vfs2.FileSystemException;
29  import org.apache.commons.vfs2.FileSystemManager;
30  import org.apache.commons.vfs2.VFS;
31  import org.junit.Assert;
32  import org.junit.Ignore;
33  import org.junit.Test;
34  
35  public class ZipFileObjectTestCase {
36  
37      private static final String NESTED_FILE_1 = "/read-xml-tests/file1.xml";
38      private static final String NESTED_FILE_2 = "/read-xml-tests/file2.xml";
39  
40      private void assertDelete(final File fileObject) {
41          Assert.assertTrue("Could not delete file", fileObject.delete());
42      }
43  
44      private File createTempFile() throws IOException {
45          final File zipFile = new File("src/test/resources/test-data/read-xml-tests.zip");
46          final File newZipFile = File.createTempFile(getClass().getSimpleName(), ".zip");
47          newZipFile.deleteOnExit();
48          FileUtils.copyFile(zipFile, newZipFile);
49          return newZipFile;
50      }
51  
52      private void getInputStreamAndAssert(final FileObject fileObject, final String expectedId)
53              throws FileSystemException, IOException {
54          readAndAssert(fileObject, fileObject.getContent().getInputStream(), expectedId);
55      }
56  
57      private void readAndAssert(final FileObject fileObject, final InputStream inputStream, final String expectedId)
58              throws IOException {
59          final String streamData = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
60          final String fileObjectString = fileObject.toString();
61          Assert.assertNotNull(fileObjectString, streamData);
62          Assert.assertEquals(
63                  fileObjectString, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<Root"
64                          + expectedId + ">foo" + expectedId + "</Root" + expectedId + ">\r\n",
65                  streamData);
66      }
67  
68      private void resolveReadAssert(final FileObject zipFileObject, final String path)
69              throws IOException, FileSystemException {
70          try (final FileObject zipFileObject2 = zipFileObject.resolveFile(path)) {
71              try (final InputStream inputStream = zipFileObject2.getContent().getInputStream()) {
72                  readAndAssert(zipFileObject2, inputStream, "2");
73              }
74          }
75      }
76  
77      /**
78       * Tests that when we read a file inside a file Zip and leave it open, we can still delete the Zip after we clean up
79       * the Zip file.
80       *
81       * @throws IOException
82       */
83      @Test
84      @Ignore("Shows that leaving a stream open and not closing any resource leaves the container file locked")
85      public void testLeaveNestedFileOpen() throws IOException {
86          final File newZipFile = createTempFile();
87          final FileSystemManager manager = VFS.getManager();
88          try (final FileObject zipFileObject = manager.resolveFile("zip:file:" + newZipFile.getAbsolutePath())) {
89              @SuppressWarnings({ "resource" })
90              final FileObject zipFileObject1 = zipFileObject.resolveFile(NESTED_FILE_1);
91              getInputStreamAndAssert(zipFileObject1, "1");
92          }
93          assertDelete(newZipFile);
94      }
95  
96      /**
97       * Tests that we can read more than one file within a Zip file, especially after closing each FileObject.
98       *
99       * @throws IOException
100      */
101     @Test
102     public void testReadingFilesInZipFile() throws IOException {
103         final File newZipFile = createTempFile();
104         final FileSystemManager manager = VFS.getManager();
105         try (final FileObject zipFileObject = manager.resolveFile("zip:file:" + newZipFile.getAbsolutePath())) {
106             try (final FileObject zipFileObject1 = zipFileObject.resolveFile(NESTED_FILE_1)) {
107                 try (final InputStream inputStream = zipFileObject1.getContent().getInputStream()) {
108                     readAndAssert(zipFileObject1, inputStream, "1");
109                 }
110             }
111             resolveReadAssert(zipFileObject, NESTED_FILE_2);
112         }
113         assertDelete(newZipFile);
114     }
115 
116     /**
117      * Tests that we can get a stream from one file in a zip file, then close another file from the same zip, then
118      * process the initial input stream.
119      *
120      * @throws IOException
121      */
122     @Test
123     public void testReadingOneAfterClosingAnotherFile() throws IOException {
124         final File newZipFile = createTempFile();
125         final FileSystemManager manager = VFS.getManager();
126         final FileObject zipFileObject1;
127         final InputStream inputStream1;
128         try (final FileObject zipFileObject = manager.resolveFile("zip:file:" + newZipFile.getAbsolutePath())) {
129             // leave resources open
130             zipFileObject1 = zipFileObject.resolveFile(NESTED_FILE_1);
131             inputStream1 = zipFileObject1.getContent().getInputStream();
132         }
133         // The zip file is "closed", but we read from the stream now.
134         readAndAssert(zipFileObject1, inputStream1, "1");
135         // clean up
136         zipFileObject1.close();
137         assertDelete(newZipFile);
138     }
139 
140     /**
141      * Tests that we can get a stream from one file in a zip file, then close another file from the same zip, then
142      * process the initial input stream. If our internal reference counting is correct, the test passes.
143      *
144      * @throws IOException
145      */
146     @Test
147     public void testReadingOneAfterClosingAnotherStream() throws IOException {
148         final File newZipFile = createTempFile();
149         final FileSystemManager manager = VFS.getManager();
150         final FileObject zipFileObject1;
151         final InputStream inputStream1;
152         try (final FileObject zipFileObject = manager.resolveFile("zip:file:" + newZipFile.getAbsolutePath())) {
153             // leave resources open (note that internal counters are updated)
154             zipFileObject1 = zipFileObject.resolveFile(NESTED_FILE_1);
155             inputStream1 = zipFileObject1.getContent().getInputStream();
156             resolveReadAssert(zipFileObject, NESTED_FILE_2);
157         }
158         // The Zip file is "closed", but we read from the stream now, which currently fails.
159         // Why aren't internal counters preventing the stream from closing?
160         readAndAssert(zipFileObject1, inputStream1, "1");
161         // clean up
162         zipFileObject1.close();
163         assertDelete(newZipFile);
164     }
165 
166     /**
167      * Test read file with special name in a zip file
168      */
169     @Test
170     public void testReadSpecialNameFileInZipFile() throws FileSystemException {
171 
172         final File testFile = new File("src/test/resources/test-data/special_fileName.zip");
173         final String[] fileNames = {"file.txt", "file^.txt", "file~.txt", "file?.txt", "file@.txt", "file$.txt",
174                                     "file*.txt", "file&.txt", "file#.txt", "file%.txt", "file!.txt"};
175         final FileSystemManager manager = VFS.getManager();
176         final String baseUrl = "zip:file:"+testFile.getAbsolutePath();
177 
178         // test
179         try (final FileObject fileObject = manager.resolveFile(baseUrl)) {
180             // test getChildren() number equal
181             Assert.assertEquals(fileObject.getChildren().length, fileNames.length);
182 
183             // test getChild(String)
184             for (final String fileName : fileNames) {
185                 Assert.assertNotNull("can't read file " + fileName, fileObject.getChild(fileName));
186             }
187         }
188     }
189 
190     /**
191      * Tests that we can resolve a file in a Zip file, then close the container zip, which should still let us delete
192      * the Zip file.
193      *
194      * @throws IOException
195      */
196     @Test
197     public void testResolveNestedFileWithoutCleanup() throws IOException {
198         final File newZipFile = createTempFile();
199         final FileSystemManager manager = VFS.getManager();
200         try (final FileObject zipFileObject = manager.resolveFile("zip:file:" + newZipFile.getAbsolutePath())) {
201             @SuppressWarnings({ "unused", "resource" })
202             // We resolve a nested file and do nothing else.
203             final FileObject zipFileObject1 = zipFileObject.resolveFile(NESTED_FILE_1);
204         }
205         assertDelete(newZipFile);
206     }
207 }