1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.vfs2.cache;
18
19 import java.util.Map;
20 import java.util.concurrent.ConcurrentHashMap;
21 import java.util.concurrent.ConcurrentMap;
22
23 import org.apache.commons.vfs2.FileName;
24 import org.apache.commons.vfs2.FileObject;
25 import org.apache.commons.vfs2.FileSystem;
26
27 /**
28 * A simple {@link org.apache.commons.vfs2.FilesCache FilesCache} implementation.
29 * <p>
30 * This implementation caches every file with no expire or limit. All files and file systems are hard reachable
31 * references. This implementation holds a list of file system specific {@linkplain ConcurrentHashMap ConcurrentHashMaps}
32 * in the main cache map.
33 * </p>
34 * <p>
35 * Cached {@linkplain FileObject FileObjects} as well as {@linkplain FileSystem FileSystems} are only removed when
36 * {@link #clear(FileSystem)} is called (i.e. on file system close). When the used
37 * {@link org.apache.commons.vfs2.FileSystemManager FileSystemManager} is closed, it will also {@linkplain #close()
38 * close} this cache (which frees all entries).
39 * </p>
40 * <p>
41 * Despite its name, this is not the fallback implementation used by
42 * {@link org.apache.commons.vfs2.impl.DefaultFileSystemManager#init() DefaultFileSystemManager#init()} anymore.
43 * </p>
44 */
45 public class DefaultFilesCache extends AbstractFilesCache {
46
47 private static final float LOAD_FACTOR = 0.75f;
48 private static final int INITIAL_CAPACITY = 200;
49
50 /** The FileSystem cache. Keeps one Map for each FileSystem. */
51 private final ConcurrentMap<FileSystem, ConcurrentMap<FileName, FileObject>> fileSystemCache = new ConcurrentHashMap<>(10);
52
53 /**
54 * Constructs a new instance.
55 */
56 public DefaultFilesCache() {
57 // empty
58 }
59
60 @Override
61 public void clear(final FileSystem filesystem) {
62 // avoid keeping a reference to the FileSystem (key) object
63 final Map<FileName, FileObject> files = fileSystemCache.remove(filesystem);
64 if (files != null) {
65 files.clear(); // help GC
66 }
67 }
68
69 @Override
70 public void close() {
71 super.close();
72 fileSystemCache.clear();
73 }
74
75 @Override
76 public FileObject getFile(final FileSystem filesystem, final FileName name) {
77 // avoid creating filesystem entry for empty filesystem cache:
78 final Map<FileName, FileObject> files = fileSystemCache.get(filesystem);
79 if (files == null) {
80 // cache for filesystem is not known => file is not cached:
81 return null;
82 }
83 return files.get(name); // or null
84 }
85
86 /**
87 * Gets or creates a Map.
88 *
89 * @param fileSystem the key
90 * @return an existing or new Map.
91 */
92 protected ConcurrentMap<FileName, FileObject> getOrCreateFilesystemCache(final FileSystem fileSystem) {
93 ConcurrentMap<FileName, FileObject> files = fileSystemCache.get(fileSystem);
94 // we loop to make sure we never return null even when concurrent clean is called
95 while (files == null) {
96 files = fileSystemCache.computeIfAbsent(fileSystem,
97 k -> new ConcurrentHashMap<>(INITIAL_CAPACITY, LOAD_FACTOR, Math.max(2, Runtime.getRuntime().availableProcessors()) / 2));
98 }
99 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 }