View Javadoc
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 }