001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.vfs2.provider; 018 019import java.util.Map; 020import java.util.TreeMap; 021import java.util.stream.Stream; 022 023import org.apache.commons.vfs2.FileName; 024import org.apache.commons.vfs2.FileObject; 025import org.apache.commons.vfs2.FileSystem; 026import org.apache.commons.vfs2.FileSystemConfigBuilder; 027import org.apache.commons.vfs2.FileSystemException; 028import org.apache.commons.vfs2.FileSystemOptions; 029import org.apache.commons.vfs2.provider.local.GenericFileNameParser; 030 031/** 032 * A partial {@link FileProvider} implementation. Takes care of managing the file systems created by the provider. 033 */ 034public abstract class AbstractFileProvider extends AbstractVfsContainer implements FileProvider { 035 036 private static final AbstractFileSystem[] EMPTY_ABSTRACT_FILE_SYSTEMS = {}; 037 038 /** 039 * The cached file systems. 040 * <p> 041 * This is a mapping from {@link FileSystemKey} (root URI and options) to {@link FileSystem}. 042 * </p> 043 */ 044 private final Map<FileSystemKey, FileSystem> fileSystemMap = new TreeMap<>(); // @GuardedBy("self") 045 046 private FileNameParser fileNameParser; 047 048 /** 049 * Constructs a new instance for subclasses. 050 */ 051 public AbstractFileProvider() { 052 fileNameParser = GenericFileNameParser.getInstance(); 053 } 054 055 /** 056 * Adds a file system to those cached by this provider. 057 * <p> 058 * The file system may implement {@link VfsComponent}, in which case it is initialized. 059 * </p> 060 * 061 * @param key The root file of the file system, part of the cache key. 062 * @param fs the file system to add. 063 * @throws FileSystemException if any error occurs. 064 */ 065 protected void addFileSystem(final Comparable<?> key, final FileSystem fs) throws FileSystemException { 066 // Add to the container and initialize 067 addComponent(fs); 068 final FileSystemKey treeKey = new FileSystemKey(key, fs.getFileSystemOptions()); 069 ((AbstractFileSystem) fs).setCacheKey(treeKey); 070 synchronized (fileSystemMap) { 071 fileSystemMap.put(treeKey, fs); 072 } 073 } 074 075 /** 076 * Closes the file systems created by this provider. 077 */ 078 @Override 079 public void close() { 080 synchronized (fileSystemMap) { 081 fileSystemMap.clear(); 082 } 083 super.close(); 084 } 085 086 /** 087 * Closes the FileSystem. 088 * 089 * @param fileSystem The FileSystem to close. 090 */ 091 public void closeFileSystem(final FileSystem fileSystem) { 092 final AbstractFileSystem fs = (AbstractFileSystem) fileSystem; 093 094 final FileSystemKey key = fs.getCacheKey(); 095 if (key != null) { 096 synchronized (fileSystemMap) { 097 fileSystemMap.remove(key); 098 } 099 } 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}