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 java.io.InputStream;
20  import java.util.ArrayList;
21  
22  import org.apache.commons.vfs2.FileSystemException;
23  import org.apache.commons.vfs2.RandomAccessContent;
24  
25  /**
26   * Holds the data which needs to be local to the current thread
27   */
28  class FileContentThreadData {
29  
30      private ArrayList<InputStream> inputStreamList;
31      private ArrayList<RandomAccessContent> randomAccessContentList;
32      private DefaultFileContent.FileContentOutputStream outputStream;
33  
34      FileContentThreadData() {
35      }
36  
37      void add(final InputStream inputStream) {
38          if (this.inputStreamList == null) {
39              this.inputStreamList = new ArrayList<>();
40          }
41          this.inputStreamList.add(inputStream);
42      }
43  
44      void add(final RandomAccessContent randomAccessContent) {
45          if (this.randomAccessContentList == null) {
46              this.randomAccessContentList = new ArrayList<>();
47          }
48          this.randomAccessContentList.add(randomAccessContent);
49      }
50  
51      /**
52       * Closes the output stream.
53       *
54       * @throws FileSystemException if an IO error occurs.
55       */
56      void closeOutputStream() throws FileSystemException {
57          outputStream.close();
58          outputStream = null;
59      }
60  
61      DefaultFileContent.FileContentOutputStream getOutputStream() {
62          return this.outputStream;
63      }
64  
65      boolean hasInputStream() {
66          return this.inputStreamList != null && !this.inputStreamList.isEmpty();
67      }
68  
69      boolean hasRandomAccessContent() {
70          return randomAccessContentList != null && !randomAccessContentList.isEmpty();
71      }
72  
73      boolean hasStreams() {
74          return hasInputStream() || outputStream != null || hasRandomAccessContent();
75      }
76  
77      InputStream removeInputStream(final int pos) {
78          return this.inputStreamList.remove(pos);
79      }
80  
81      void remove(final InputStream inputStream) {
82          // this null-check (as well as the one in the other `remove` method) should not
83          // be needed because `remove` is called only in `DefaultFileContent.endInput` which
84          // should only be called after an input stream has been created and hence the `inputStreamList`
85          // variable initialized. However, `DefaultFileContent` uses this class per thread -
86          // so it is possible to get a stream, pass it to another thread and close it there -
87          // and that would lead to a NPE here if it weren't for that check. This "solution" here -
88          // adding a null-check - is really "bad" in the sense that it will fix a crash but there will
89          // be a leak because the input stream won't be removed from the original thread's `inputStreamList`.
90          // See https://github.com/apache/commons-vfs/pull/166 for more context.
91          // TODO: fix this problem
92          if (this.inputStreamList != null) {
93              this.inputStreamList.remove(inputStream);
94          }
95      }
96  
97      Object removeRandomAccessContent(final int pos) {
98          return this.randomAccessContentList.remove(pos);
99      }
100 
101     void remove(final RandomAccessContent randomAccessContent) {
102         if (this.randomAccessContentList != null) {
103             this.randomAccessContentList.remove(randomAccessContent);
104         }
105     }
106 
107     void setOutputStream(final DefaultFileContent.FileContentOutputStream outputStream) {
108         this.outputStream = outputStream;
109     }
110 }