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;
021
022import org.apache.commons.vfs2.FileName;
023import org.apache.commons.vfs2.FileObject;
024import org.apache.commons.vfs2.FileSystem;
025import org.apache.commons.vfs2.FileSystemConfigBuilder;
026import org.apache.commons.vfs2.FileSystemException;
027import org.apache.commons.vfs2.FileSystemOptions;
028import org.apache.commons.vfs2.provider.local.GenericFileNameParser;
029
030/**
031 * A partial {@link FileProvider} implementation. Takes care of managing the file systems created by the provider.
032 */
033public abstract class AbstractFileProvider extends AbstractVfsContainer implements FileProvider {
034    private static final AbstractFileSystem[] EMPTY_ABSTRACTFILESYSTEMS = new AbstractFileSystem[0];
035
036    /**
037     * The cached file systems.
038     * <p>
039     * This is a mapping from {@link FileSystemKey} (root URI and options) to {@link FileSystem}.
040     */
041    private final Map<FileSystemKey, FileSystem> fileSystems = new TreeMap<>(); // @GuardedBy("self")
042
043    private FileNameParser parser;
044
045    public AbstractFileProvider() {
046        parser = GenericFileNameParser.getInstance();
047    }
048
049    protected FileNameParser getFileNameParser() {
050        return parser;
051    }
052
053    protected void setFileNameParser(final FileNameParser parser) {
054        this.parser = parser;
055    }
056
057    /**
058     * Closes the file systems created by this provider.
059     */
060    @Override
061    public void close() {
062        synchronized (fileSystems) {
063            fileSystems.clear();
064        }
065
066        super.close();
067    }
068
069    /**
070     * Creates a layered file system. This method throws a 'not supported' exception.
071     *
072     * @param scheme The protocol to use to access the file.
073     * @param file a FileObject.
074     * @param properties Options to the file system.
075     * @return A FileObject associated with the new FileSystem.
076     * @throws FileSystemException if an error occurs.
077     */
078    @Override
079    public FileObject createFileSystem(final String scheme, final FileObject file, final FileSystemOptions properties)
080            throws FileSystemException {
081        // Can't create a layered file system
082        throw new FileSystemException("vfs.provider/not-layered-fs.error", scheme);
083    }
084
085    /**
086     * Adds a file system to those cached by this provider.
087     * <p>
088     * The file system may implement {@link VfsComponent}, in which case it is initialised.
089     *
090     * @param key The root file of the file system, part of the cache key.
091     * @param fs the file system to add.
092     * @throws FileSystemException if any error occurs.
093     */
094    protected void addFileSystem(final Comparable<?> key, final FileSystem fs) throws FileSystemException {
095        // Add to the container and initialize
096        addComponent(fs);
097
098        final FileSystemKey treeKey = new FileSystemKey(key, fs.getFileSystemOptions());
099        ((AbstractFileSystem) fs).setCacheKey(treeKey);
100
101        synchronized (fileSystems) {
102            fileSystems.put(treeKey, fs);
103        }
104    }
105
106    /**
107     * Locates a cached file system.
108     *
109     * @param key The root file of the file system, part of the cache key.
110     * @param fileSystemProps file system options the file system instance must have.
111     * @return The file system instance, or null if it is not cached.
112     */
113    protected FileSystem findFileSystem(final Comparable<?> key, final FileSystemOptions fileSystemProps) {
114        final FileSystemKey treeKey = new FileSystemKey(key, fileSystemProps);
115
116        synchronized (fileSystems) {
117            return fileSystems.get(treeKey);
118        }
119    }
120
121    /**
122     * Returns the FileSystemConfigBuidler.
123     *
124     * @return the FileSystemConfigBuilder.
125     */
126    @Override
127    public FileSystemConfigBuilder getConfigBuilder() {
128        return null;
129    }
130
131    /**
132     * Free unused resources.
133     */
134    public void freeUnusedResources() {
135        AbstractFileSystem[] abstractFileSystems;
136        synchronized (fileSystems) {
137            // create snapshot under lock
138            abstractFileSystems = fileSystems.values().toArray(EMPTY_ABSTRACTFILESYSTEMS);
139        }
140
141        // process snapshot outside lock
142        for (final AbstractFileSystem fs : abstractFileSystems) {
143            if (fs.isReleaseable()) {
144                fs.closeCommunicationLink();
145            }
146        }
147    }
148
149    /**
150     * Close the FileSystem.
151     *
152     * @param filesystem The FileSystem to close.
153     */
154    public void closeFileSystem(final FileSystem filesystem) {
155        final AbstractFileSystem fs = (AbstractFileSystem) filesystem;
156
157        final FileSystemKey key = fs.getCacheKey();
158        if (key != null) {
159            synchronized (fileSystems) {
160                fileSystems.remove(key);
161            }
162        }
163
164        removeComponent(fs);
165        fs.close();
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
182        throw new FileSystemException("vfs.provider/filename-parser-missing.error");
183    }
184}