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.cache;
018
019import java.util.Map;
020import java.util.concurrent.ConcurrentHashMap;
021import java.util.concurrent.ConcurrentMap;
022
023import org.apache.commons.vfs2.FileName;
024import org.apache.commons.vfs2.FileObject;
025import org.apache.commons.vfs2.FileSystem;
026
027/**
028 * A simple {@link org.apache.commons.vfs2.FilesCache FilesCache} implementation.
029 * <p>
030 * This implementation caches every file with no expire or limit. All files and filesystems are hard reachable
031 * references. This implementation holds a list of filesystem specific {@linkplain ConcurrentHashMap ConcurrentHashMaps}
032 * in the main cache map.
033 * <p>
034 * Cached {@linkplain FileObject FileObjects} as well as {@linkplain FileSystem FileSystems} are only removed when
035 * {@link #clear(FileSystem)} is called (i.e. on filesystem close). When the used
036 * {@link org.apache.commons.vfs2.FileSystemManager FileSystemManager} is closed, it will also {@linkplain #close()
037 * close} this cache (which frees all entries).
038 * <p>
039 * Despite its name, this is not the fallback implementation used by
040 * {@link org.apache.commons.vfs2.impl.DefaultFileSystemManager#init() DefaultFileSystemManager#init()} anymore.
041 */
042public class DefaultFilesCache extends AbstractFilesCache {
043    /** The FileSystem cache. Keeps one Map for each FileSystem. */
044    private final ConcurrentMap<FileSystem, ConcurrentMap<FileName, FileObject>> filesystemCache = new ConcurrentHashMap<>(
045            10);
046
047    @Override
048    public void putFile(final FileObject file) {
049        final Map<FileName, FileObject> files = getOrCreateFilesystemCache(file.getFileSystem());
050        files.put(file.getName(), file);
051    }
052
053    @Override
054    public boolean putFileIfAbsent(final FileObject file) {
055        final ConcurrentMap<FileName, FileObject> files = getOrCreateFilesystemCache(file.getFileSystem());
056        return files.putIfAbsent(file.getName(), file) == null;
057    }
058
059    @Override
060    public FileObject getFile(final FileSystem filesystem, final FileName name) {
061        // avoid creating filesystem entry for empty filesystem cache:
062        final Map<FileName, FileObject> files = filesystemCache.get(filesystem);
063        if (files == null) {
064            // cache for filesystem is not known => file is not cached:
065            return null;
066        }
067
068        return files.get(name); // or null
069    }
070
071    @Override
072    public void clear(final FileSystem filesystem) {
073        // avoid keeping a reference to the FileSystem (key) object
074        final Map<FileName, FileObject> files = filesystemCache.remove(filesystem);
075        if (files != null) {
076            files.clear(); // help GC
077        }
078    }
079
080    protected ConcurrentMap<FileName, FileObject> getOrCreateFilesystemCache(final FileSystem filesystem) {
081        ConcurrentMap<FileName, FileObject> files = filesystemCache.get(filesystem);
082        // we loop to make sure we never return null even when concurrent clean is called
083        while (files == null) {
084            filesystemCache.putIfAbsent(filesystem, new ConcurrentHashMap<FileName, FileObject>(200, 0.75f, 8));
085            files = filesystemCache.get(filesystem);
086        }
087
088        return files;
089    }
090
091    @Override
092    public void close() {
093        super.close();
094
095        filesystemCache.clear();
096    }
097
098    @Override
099    public void removeFile(final FileSystem filesystem, final FileName name) {
100        // avoid creating filesystem entry for empty filesystem cache:
101        final Map<FileName, FileObject> files = filesystemCache.get(filesystem);
102        if (files != null) {
103            files.remove(name);
104            // This would be too racey:
105            // if (files.empty()) filesystemCache.remove(filessystem);
106        }
107    }
108}