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 file systems are hard reachable 031 * references. This implementation holds a list of file system specific {@linkplain ConcurrentHashMap ConcurrentHashMaps} 032 * in the main cache map. 033 * </p> 034 * <p> 035 * Cached {@linkplain FileObject FileObjects} as well as {@linkplain FileSystem FileSystems} are only removed when 036 * {@link #clear(FileSystem)} is called (i.e. on file system close). When the used 037 * {@link org.apache.commons.vfs2.FileSystemManager FileSystemManager} is closed, it will also {@linkplain #close() 038 * close} this cache (which frees all entries). 039 * </p> 040 * <p> 041 * Despite its name, this is not the fallback implementation used by 042 * {@link org.apache.commons.vfs2.impl.DefaultFileSystemManager#init() DefaultFileSystemManager#init()} anymore. 043 * </p> 044 */ 045public class DefaultFilesCache extends AbstractFilesCache { 046 047 private static final float LOAD_FACTOR = 0.75f; 048 private static final int INITIAL_CAPACITY = 200; 049 050 /** The FileSystem cache. Keeps one Map for each FileSystem. */ 051 private final ConcurrentMap<FileSystem, ConcurrentMap<FileName, FileObject>> fileSystemCache = new ConcurrentHashMap<>(10); 052 053 /** 054 * Constructs a new instance. 055 */ 056 public DefaultFilesCache() { 057 // empty 058 } 059 060 @Override 061 public void clear(final FileSystem filesystem) { 062 // avoid keeping a reference to the FileSystem (key) object 063 final Map<FileName, FileObject> files = fileSystemCache.remove(filesystem); 064 if (files != null) { 065 files.clear(); // help GC 066 } 067 } 068 069 @Override 070 public void close() { 071 super.close(); 072 fileSystemCache.clear(); 073 } 074 075 @Override 076 public FileObject getFile(final FileSystem filesystem, final FileName name) { 077 // avoid creating filesystem entry for empty filesystem cache: 078 final Map<FileName, FileObject> files = fileSystemCache.get(filesystem); 079 if (files == null) { 080 // cache for filesystem is not known => file is not cached: 081 return null; 082 } 083 return files.get(name); // or null 084 } 085 086 /** 087 * Gets or creates a Map. 088 * 089 * @param fileSystem the key 090 * @return an existing or new Map. 091 */ 092 protected ConcurrentMap<FileName, FileObject> getOrCreateFilesystemCache(final FileSystem fileSystem) { 093 ConcurrentMap<FileName, FileObject> files = fileSystemCache.get(fileSystem); 094 // we loop to make sure we never return null even when concurrent clean is called 095 while (files == null) { 096 files = fileSystemCache.computeIfAbsent(fileSystem, 097 k -> new ConcurrentHashMap<>(INITIAL_CAPACITY, LOAD_FACTOR, Math.max(2, Runtime.getRuntime().availableProcessors()) / 2)); 098 } 099 return files; 100 } 101 102 @Override 103 public void putFile(final FileObject file) { 104 final Map<FileName, FileObject> files = getOrCreateFilesystemCache(file.getFileSystem()); 105 files.put(file.getName(), file); 106 } 107 108 @Override 109 public boolean putFileIfAbsent(final FileObject file) { 110 final ConcurrentMap<FileName, FileObject> files = getOrCreateFilesystemCache(file.getFileSystem()); 111 return files.putIfAbsent(file.getName(), file) == null; 112 } 113 114 @Override 115 public void removeFile(final FileSystem filesystem, final FileName name) { 116 // avoid creating filesystem entry for empty filesystem cache: 117 final Map<FileName, FileObject> files = fileSystemCache.get(filesystem); 118 if (files != null) { 119 files.remove(name); 120 // This would be too racey: 121 // if (files.empty()) filesystemCache.remove(filessystem); 122 } 123 } 124}