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.util.Map;
20 import java.util.TreeMap;
21 import java.util.stream.Stream;
22
23 import org.apache.commons.vfs2.FileName;
24 import org.apache.commons.vfs2.FileObject;
25 import org.apache.commons.vfs2.FileSystem;
26 import org.apache.commons.vfs2.FileSystemConfigBuilder;
27 import org.apache.commons.vfs2.FileSystemException;
28 import org.apache.commons.vfs2.FileSystemOptions;
29 import org.apache.commons.vfs2.provider.local.GenericFileNameParser;
30
31 /**
32 * A partial {@link FileProvider} implementation. Takes care of managing the file systems created by the provider.
33 */
34 public abstract class AbstractFileProvider extends AbstractVfsContainer implements FileProvider {
35
36 private static final AbstractFileSystem[] EMPTY_ABSTRACT_FILE_SYSTEMS = {};
37
38 /**
39 * The cached file systems.
40 * <p>
41 * This is a mapping from {@link FileSystemKey} (root URI and options) to {@link FileSystem}.
42 * </p>
43 */
44 private final Map<FileSystemKey, FileSystem> fileSystemMap = new TreeMap<>(); // @GuardedBy("self")
45
46 private FileNameParser fileNameParser;
47
48 /**
49 * Constructs a new instance for subclasses.
50 */
51 public AbstractFileProvider() {
52 fileNameParser = GenericFileNameParser.getInstance();
53 }
54
55 /**
56 * Adds a file system to those cached by this provider.
57 * <p>
58 * The file system may implement {@link VfsComponent}, in which case it is initialized.
59 * </p>
60 *
61 * @param key The root file of the file system, part of the cache key.
62 * @param fs the file system to add.
63 * @throws FileSystemException if any error occurs.
64 */
65 protected void addFileSystem(final Comparable<?> key, final FileSystem fs) throws FileSystemException {
66 // Add to the container and initialize
67 addComponent(fs);
68 final FileSystemKey treeKey = new FileSystemKey(key, fs.getFileSystemOptions());
69 ((AbstractFileSystem) fs).setCacheKey(treeKey);
70 synchronized (fileSystemMap) {
71 fileSystemMap.put(treeKey, fs);
72 }
73 }
74
75 /**
76 * Closes the file systems created by this provider.
77 */
78 @Override
79 public void close() {
80 synchronized (fileSystemMap) {
81 fileSystemMap.clear();
82 }
83 super.close();
84 }
85
86 /**
87 * Closes the FileSystem.
88 *
89 * @param fileSystem The FileSystem to close.
90 */
91 public void closeFileSystem(final FileSystem fileSystem) {
92 final AbstractFileSystem fs = (AbstractFileSystem) fileSystem;
93
94 final FileSystemKey key = fs.getCacheKey();
95 if (key != null) {
96 synchronized (fileSystemMap) {
97 fileSystemMap.remove(key);
98 }
99 }
100
101 removeComponent(fs);
102 fs.close();
103 }
104
105 /**
106 * Creates a layered file system. This method throws a 'not supported' exception.
107 *
108 * @param scheme The protocol to use to access the file.
109 * @param file a FileObject.
110 * @param fileSystemOptions Options to the file system.
111 * @return A FileObject associated with the new FileSystem.
112 * @throws FileSystemException if an error occurs.
113 */
114 @Override
115 public FileObject createFileSystem(final String scheme, final FileObject file,
116 final FileSystemOptions fileSystemOptions) throws FileSystemException {
117 // Can't create a layered file system
118 throw new FileSystemException("vfs.provider/not-layered-fs.error", scheme);
119 }
120
121 /**
122 * Locates a cached file system.
123 *
124 * @param key The root file of the file system, part of the cache key.
125 * @param fileSystemOptions file system options the file system instance must have, may be null.
126 * @return The file system instance, or null if it is not cached.
127 */
128 protected FileSystem findFileSystem(final Comparable<?> key, final FileSystemOptions fileSystemOptions) {
129 synchronized (fileSystemMap) {
130 return fileSystemMap.get(new FileSystemKey(key, fileSystemOptions));
131 }
132 }
133
134 /**
135 * Frees unused resources.
136 */
137 public void freeUnusedResources() {
138 final AbstractFileSystem[] abstractFileSystems;
139 synchronized (fileSystemMap) {
140 // create snapshot under lock
141 abstractFileSystems = fileSystemMap.values().toArray(EMPTY_ABSTRACT_FILE_SYSTEMS);
142 }
143
144 // process snapshot outside lock
145 Stream.of(abstractFileSystems).filter(AbstractFileSystem::isReleaseable)
146 .forEach(AbstractFileSystem::closeCommunicationLink);
147 }
148
149 /**
150 * Gets the FileSystemConfigBuilder.
151 *
152 * @return the FileSystemConfigBuilder.
153 */
154 @Override
155 public FileSystemConfigBuilder getConfigBuilder() {
156 return null;
157 }
158
159 /**
160 * Gets the file name parser.
161 *
162 * @return the file name parser.
163 */
164 protected FileNameParser getFileNameParser() {
165 return fileNameParser;
166 }
167
168 /**
169 * Parses an absolute URI.
170 *
171 * @param base The base file - if null the {@code uri} needs to be absolute
172 * @param uri The URI to parse.
173 * @return The FileName.
174 * @throws FileSystemException if an error occurs.
175 */
176 @Override
177 public FileName parseUri(final FileName base, final String uri) throws FileSystemException {
178 if (getFileNameParser() != null) {
179 return getFileNameParser().parseUri(getContext(), base, uri);
180 }
181 throw new FileSystemException("vfs.provider/filename-parser-missing.error");
182 }
183
184 /**
185 * Sets the file name parser.
186 *
187 * @param parser a file name parser.
188 */
189 protected void setFileNameParser(final FileNameParser parser) {
190 this.fileNameParser = parser;
191 }
192 }