blob: 920d9d03712f09f7d1122125c664745a75636f40 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.vfs2.cache;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.apache.commons.vfs2.FileName;
import org.apache.commons.vfs2.FileObject;
import org.apache.commons.vfs2.FileSystem;
/**
* A simple {@link org.apache.commons.vfs2.FilesCache FilesCache} implementation.
* <p>
* This implementation caches every file with no expire or limit.
* All files and filesystems are hard reachable references. This implementation
* holds a list of filesystem specific {@linkplain ConcurrentHashMap ConcurrentHashMaps} in
* the main cache map.
* <p>
* Cached {@linkplain FileObject FileObjects} as well as {@linkplain FileSystem FileSystems}
* are only removed when {@link #clear(FileSystem)} is called (i.e. on filesystem close).
* When the used {@link org.apache.commons.vfs2.FileSystemManager FileSystemManager} is closed,
* it will also {@linkplain #close() close} this cache (which frees all entries).
* <p>
* Despite its name, this is not the fallback implementation used by
* {@link org.apache.commons.vfs2.impl.DefaultFileSystemManager#init() DefaultFileSystemManager#init()}
* anymore.
*/
public class DefaultFilesCache extends AbstractFilesCache
{
/** The FileSystem cache. Keeps one Map for each FileSystem. */
private final ConcurrentMap<FileSystem, ConcurrentMap<FileName, FileObject>> filesystemCache =
new ConcurrentHashMap<FileSystem, ConcurrentMap<FileName, FileObject>>(10);
@Override
public void putFile(final FileObject file)
{
final Map<FileName, FileObject> files = getOrCreateFilesystemCache(file.getFileSystem());
files.put(file.getName(), file);
}
@Override
public boolean putFileIfAbsent(final FileObject file)
{
final ConcurrentMap<FileName, FileObject> files = getOrCreateFilesystemCache(file.getFileSystem());
return files.putIfAbsent(file.getName(), file) == null;
}
@Override
public FileObject getFile(final FileSystem filesystem, final FileName name)
{
// avoid creating filesystem entry for empty filesystem cache:
final Map<FileName, FileObject> files = filesystemCache.get(filesystem);
if (files == null)
{
// cache for filesystem is not known => file is not cached:
return null;
}
return files.get(name); // or null
}
@Override
public void clear(final FileSystem filesystem)
{
// avoid keeping a reference to the FileSystem (key) object
final Map<FileName, FileObject> files = filesystemCache.remove(filesystem);
if (files != null)
{
files.clear(); // help GC
}
}
protected ConcurrentMap<FileName, FileObject> getOrCreateFilesystemCache(final FileSystem filesystem)
{
ConcurrentMap<FileName, FileObject> files = filesystemCache.get(filesystem);
// we loop to make sure we never return null even when concurrent clean is called
while (files == null)
{
filesystemCache.putIfAbsent(filesystem, new ConcurrentHashMap<FileName, FileObject>(200, 0.75f, 8));
files = filesystemCache.get(filesystem);
}
return files;
}
@Override
public void close()
{
super.close();
filesystemCache.clear();
}
@Override
public void removeFile(final FileSystem filesystem, final FileName name)
{
// avoid creating filesystem entry for empty filesystem cache:
final Map<FileName, FileObject> files = filesystemCache.get(filesystem);
if (files != null)
{
files.remove(name);
// This would be too racey:
// if (files.empty()) filesystemCache.remove(filessystem);
}
}
}