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