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 final 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 (inputStreamList == null) {
39 inputStreamList = new ArrayList<>();
40 }
41 inputStreamList.add(inputStream);
42 }
43
44 void add(final RandomAccessContent randomAccessContent) {
45 if (randomAccessContentList == null) {
46 randomAccessContentList = new ArrayList<>();
47 }
48 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 outputStream;
63 }
64
65 boolean hasInputStream() {
66 return inputStreamList != null && !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 void remove(final InputStream inputStream) {
78 // this null-check (as well as the one in the other `remove` method) should not
79 // be needed because `remove` is called only in `DefaultFileContent.endInput` which
80 // should only be called after an input stream has been created and hence the `inputStreamList`
81 // variable initialized. However, `DefaultFileContent` uses this class per thread -
82 // so it is possible to get a stream, pass it to another thread and close it there -
83 // and that would lead to a NPE here if it weren't for that check. This "solution" here -
84 // adding a null-check - is really "bad" in the sense that it will fix a crash but there will
85 // be a leak because the input stream won't be removed from the original thread's `inputStreamList`.
86 // See https://github.com/apache/commons-vfs/pull/166 for more context.
87 // TODO: fix this problem
88 if (inputStreamList != null) {
89 inputStreamList.remove(inputStream);
90 }
91 }
92
93 void remove(final RandomAccessContent randomAccessContent) {
94 if (randomAccessContentList != null) {
95 randomAccessContentList.remove(randomAccessContent);
96 }
97 }
98
99 InputStream removeInputStream(final int pos) {
100 return inputStreamList.remove(pos);
101 }
102
103 Object removeRandomAccessContent(final int pos) {
104 return randomAccessContentList.remove(pos);
105 }
106
107 void setOutputStream(final DefaultFileContent.FileContentOutputStream outputStream) {
108 this.outputStream = outputStream;
109 }
110 }