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.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 }