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;
18  
19  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
20  import static org.junit.jupiter.api.Assertions.assertEquals;
21  import static org.junit.jupiter.api.Assertions.assertThrows;
22  import static org.junit.jupiter.api.Assertions.assertTrue;
23  
24  import java.io.Closeable;
25  import java.io.File;
26  import java.io.IOException;
27  import java.io.InputStream;
28  import java.io.OutputStream;
29  import java.nio.charset.StandardCharsets;
30  import java.nio.file.Files;
31  import java.nio.file.Path;
32  import java.util.concurrent.atomic.AtomicBoolean;
33  
34  import org.apache.commons.io.IOUtils;
35  import org.apache.commons.lang3.ArrayUtils;
36  import org.apache.commons.lang3.StringUtils;
37  import org.apache.commons.lang3.function.FailableFunction;
38  import org.apache.commons.vfs2.FileContent;
39  import org.apache.commons.vfs2.FileObject;
40  import org.apache.commons.vfs2.FileSystemManager;
41  import org.apache.commons.vfs2.VFS;
42  import org.junit.jupiter.api.Test;
43  
44  /**
45   * {@code DefaultFileContentTest} tests for bug-VFS-614. This bug involves the stream implementation closing the stream
46   * after reading to the end of the buffer, which broke marking.
47   */
48  public class DefaultFileContentTest {
49  
50      private static final String expected = "testing";
51  
52      /**
53       * Test VFS-724 should be done on a website which render a page with no content size. Note the getSize() is
54       * currently the value sent back by the server then zero usually means no content length attached.
55       */
56      @Test
57      public void testGetZeroContents() throws IOException {
58          final FileSystemManager fsManager = VFS.getManager();
59          try (FileObject fo = fsManager.resolveFile(new File("."), "src/test/resources/test-data/size-0-file.bin");
60                  final FileContent content = fo.getContent()) {
61              assertEquals(0, content.getSize());
62              assertTrue(content.isEmpty());
63              assertEquals(StringUtils.EMPTY, content.getString(StandardCharsets.UTF_8));
64              assertEquals(StringUtils.EMPTY, content.getString(StandardCharsets.UTF_8.name()));
65              assertArrayEquals(ArrayUtils.EMPTY_BYTE_ARRAY, content.getByteArray());
66          }
67      }
68  
69      private void testInputStreamBufferSize(final int bufferSize) throws Exception {
70          final Path temp = Files.createTempFile("temp-file-name", ".tmp");
71          final FileSystemManager fileSystemManager = VFS.getManager();
72  
73          try (FileObject file = fileSystemManager.resolveFile(temp.toAbsolutePath().toString())) {
74              file.getContent().getInputStream(bufferSize);
75          }
76      }
77  
78      @Test
79      public void testInputStreamBufferSize0() throws Exception {
80          testInputStreamBufferSize(0);
81      }
82  
83      @Test
84      public void testInputStreamBufferSize1() throws Exception {
85          testInputStreamBufferSize(1);
86      }
87  
88      @Test
89      public void testInputStreamBufferSizeNegative() {
90          assertThrows(IllegalArgumentException.class, () -> testInputStreamBufferSize(-2));
91      }
92  
93      @Test
94      public void testInputStreamClosedInADifferentThread() throws Exception {
95          testStreamClosedInADifferentThread(FileContent::getInputStream);
96      }
97  
98      @Test
99      public void testMarkingWhenReadingEOS() throws Exception {
100         final Path temp = Files.createTempFile("temp-file-name", ".tmp");
101         final FileSystemManager fileSystemManager = VFS.getManager();
102 
103         try (FileObject file = fileSystemManager.resolveFile(temp.toAbsolutePath().toString())) {
104             try (OutputStream outputStream = file.getContent().getOutputStream()) {
105                 outputStream.write(expected.getBytes());
106                 outputStream.flush();
107             }
108             try (InputStream stream = file.getContent().getInputStream()) {
109                 int readCount = 0;
110                 if (stream.markSupported()) {
111                     for (int i = 0; i < 10; i++) {
112                         stream.mark(0);
113                         final byte[] data = new byte[100];
114                         readCount = stream.read(data, 0, 7);
115                         stream.read();
116                         assertEquals(7, readCount);
117                         assertEquals(expected, new String(data).trim());
118                         readCount = stream.read(data, 8, 10);
119                         assertEquals(-1, readCount);
120                         stream.reset();
121                     }
122                 }
123             }
124         }
125     }
126 
127     @Test
128     public void testMarkingWorks() throws Exception {
129         final Path temp = Files.createTempFile("temp-file-name", ".tmp");
130         final FileSystemManager fileSystemManager = VFS.getManager();
131 
132         try (FileObject file = fileSystemManager.resolveFile(temp.toAbsolutePath().toString())) {
133             try (OutputStream outputStream = file.getContent().getOutputStream()) {
134                 outputStream.write(expected.getBytes());
135                 outputStream.flush();
136             }
137             try (InputStream stream = file.getContent().getInputStream()) {
138                 if (stream.markSupported()) {
139                     for (int i = 0; i < 10; i++) {
140                         stream.mark(0);
141                         final byte[] data = new byte[100];
142                         stream.read(data, 0, 7);
143                         stream.read();
144                         assertEquals(expected, new String(data).trim());
145                         stream.reset();
146                     }
147                 }
148             }
149         }
150     }
151 
152     private void testOutputStreamBufferSize(final int bufferSize) throws Exception {
153         final Path temp = Files.createTempFile("temp-file-name", ".tmp");
154         final FileSystemManager fileSystemManager = VFS.getManager();
155 
156         try (FileObject file = fileSystemManager.resolveFile(temp.toAbsolutePath().toString())) {
157             file.getContent().getOutputStream(bufferSize).close();
158         }
159     }
160 
161     @Test
162     public void testOutputStreamBufferSize0() throws Exception {
163         testOutputStreamBufferSize(0);
164     }
165 
166     @Test
167     public void testOutputStreamBufferSize1() throws Exception {
168         testOutputStreamBufferSize(1);
169     }
170 
171     @Test
172     public void testOutputStreamBufferSizeNegative() {
173         assertThrows(IllegalArgumentException.class, () -> testOutputStreamBufferSize(-1));
174     }
175 
176     @Test
177     public void testOutputStreamBufferSizeNegativeWithAppendFlag() throws Exception {
178         final Path temp = Files.createTempFile("temp-file-name", ".tmp");
179         final FileSystemManager fileSystemManager = VFS.getManager();
180 
181         try (FileObject file = fileSystemManager.resolveFile(temp.toAbsolutePath().toString())) {
182             assertThrows(IllegalArgumentException.class, () -> file.getContent().getOutputStream(true, -1));
183         }
184     }
185 
186     @Test
187     public void testOutputStreamClosedInADifferentThread() throws Exception {
188         testStreamClosedInADifferentThread(FileContent::getOutputStream);
189     }
190 
191     private <T extends Closeable> void testStreamClosedInADifferentThread(final FailableFunction<FileContent, T, IOException> getStream) throws Exception {
192         final Path temp = Files.createTempFile("temp-file-name", ".tmp");
193         final FileSystemManager fileSystemManager = VFS.getManager();
194 
195         try (FileObject file = fileSystemManager.resolveFile(temp.toAbsolutePath().toString())) {
196             final T stream = getStream.apply(file.getContent());
197             final AtomicBoolean check = new AtomicBoolean();
198             final Thread thread = new Thread(() -> {
199                 IOUtils.closeQuietly(stream);
200                 check.set(true);
201             });
202             thread.start();
203             thread.join();
204             assertTrue(check.get());
205         }
206     }
207 
208 }