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: */
017: package org.apache.commons.vfs.cache;
018:
019: import org.apache.commons.logging.Log;
020: import org.apache.commons.logging.LogFactory;
021: import org.apache.commons.vfs.FileName;
022: import org.apache.commons.vfs.FileObject;
023: import org.apache.commons.vfs.FileSystem;
024: import org.apache.commons.vfs.VfsLog;
025: import org.apache.commons.vfs.impl.DefaultFileSystemManager;
026: import org.apache.commons.vfs.util.Messages;
027:
028: import java.lang.ref.Reference;
029: import java.lang.ref.ReferenceQueue;
030: import java.lang.ref.SoftReference;
031: import java.util.HashMap;
032: import java.util.Iterator;
033: import java.util.Map;
034:
035: /**
036: * This implementation caches every file as long as it is strongly reachable by
037: * the java vm. As soon as the vm needs memory - every softly reachable file
038: * will be discarded.
039: *
040: * @author <a href="mailto:imario@apache.org">Mario Ivankovits</a>
041: * @version $Revision: 551086 $ $Date: 2005-09-30 09:02:41 +0200 (Fr, 30 Sep
042: * 2005) $
043: * @see SoftReference
044: */
045: public class SoftRefFilesCache extends AbstractFilesCache {
046: /**
047: * The logger to use.
048: */
049: private Log log = LogFactory.getLog(SoftRefFilesCache.class);
050:
051: private final Map filesystemCache = new HashMap();
052: private final Map refReverseMap = new HashMap(100);
053: private final ReferenceQueue refqueue = new ReferenceQueue();
054:
055: private SoftRefReleaseThread softRefReleaseThread = null;
056:
057: /**
058: * This thread will listen on the ReferenceQueue and remove the entry in the
059: * filescache as soon as the vm removes the reference
060: */
061: private class SoftRefReleaseThread extends Thread {
062: private boolean requestEnd = false;
063:
064: private SoftRefReleaseThread() {
065: setName(SoftRefReleaseThread.class.getName());
066: setDaemon(true);
067: }
068:
069: public void run() {
070: loop: while (!requestEnd
071: && !Thread.currentThread().isInterrupted()) {
072: try {
073: Reference ref = refqueue.remove(1000);
074: if (ref == null) {
075: continue;
076: }
077:
078: FileSystemAndNameKey key;
079: synchronized (refReverseMap) {
080: key = (FileSystemAndNameKey) refReverseMap
081: .get(ref);
082: }
083:
084: if (key != null) {
085: if (removeFile(key)) {
086: filesystemClose(key.getFileSystem());
087: }
088: }
089: } catch (InterruptedException e) {
090: if (!requestEnd) {
091: VfsLog
092: .warn(
093: getLogger(),
094: log,
095: Messages
096: .getString("vfs.impl/SoftRefReleaseThread-interrupt.info"));
097: }
098: break loop;
099: }
100: }
101: }
102: }
103:
104: public SoftRefFilesCache() {
105: }
106:
107: private void startThread() {
108: if (softRefReleaseThread != null) {
109: throw new IllegalStateException(
110: Messages
111: .getString("vfs.impl/SoftRefReleaseThread-already-running.warn"));
112: }
113:
114: softRefReleaseThread = new SoftRefReleaseThread();
115: softRefReleaseThread.start();
116: }
117:
118: private void endThread() {
119: if (softRefReleaseThread != null) {
120: softRefReleaseThread.requestEnd = true;
121: softRefReleaseThread.interrupt();
122: softRefReleaseThread = null;
123: }
124: }
125:
126: public void putFile(final FileObject file) {
127: if (log.isDebugEnabled()) {
128: log.debug("putFile: " + file.getName());
129: }
130:
131: Map files = getOrCreateFilesystemCache(file.getFileSystem());
132:
133: Reference ref = createReference(file, refqueue);
134: FileSystemAndNameKey key = new FileSystemAndNameKey(file
135: .getFileSystem(), file.getName());
136:
137: synchronized (files) {
138: files.put(file.getName(), ref);
139: synchronized (refReverseMap) {
140: refReverseMap.put(ref, key);
141: }
142: }
143: }
144:
145: protected Reference createReference(FileObject file,
146: ReferenceQueue refqueue) {
147: return new SoftReference(file, refqueue);
148: }
149:
150: public FileObject getFile(final FileSystem filesystem,
151: final FileName name) {
152: Map files = getOrCreateFilesystemCache(filesystem);
153:
154: synchronized (files) {
155: Reference ref = (Reference) files.get(name);
156: if (ref == null) {
157: return null;
158: }
159:
160: FileObject fo = (FileObject) ref.get();
161: if (fo == null) {
162: removeFile(filesystem, name);
163: }
164: return fo;
165: }
166: }
167:
168: public void clear(FileSystem filesystem) {
169: Map files = getOrCreateFilesystemCache(filesystem);
170:
171: boolean closeFilesystem;
172:
173: synchronized (files) {
174: synchronized (refReverseMap) {
175: Iterator iterKeys = refReverseMap.values().iterator();
176: while (iterKeys.hasNext()) {
177: FileSystemAndNameKey key = (FileSystemAndNameKey) iterKeys
178: .next();
179: if (key.getFileSystem() == filesystem) {
180: iterKeys.remove();
181: files.remove(key.getFileName());
182: }
183: }
184:
185: closeFilesystem = files.size() < 1;
186: }
187: }
188:
189: if (closeFilesystem) {
190: filesystemClose(filesystem);
191: }
192: }
193:
194: private void filesystemClose(FileSystem filesystem) {
195: if (log.isDebugEnabled()) {
196: log.debug("close fs: " + filesystem.getRootName());
197: }
198: synchronized (filesystemCache) {
199: filesystemCache.remove(filesystem);
200: if (filesystemCache.size() < 1) {
201: endThread();
202: }
203: }
204: ((DefaultFileSystemManager) getContext().getFileSystemManager())
205: ._closeFileSystem(filesystem);
206: }
207:
208: public void close() {
209: super .close();
210:
211: endThread();
212:
213: // files.clear();
214: synchronized (filesystemCache) {
215: filesystemCache.clear();
216: }
217:
218: synchronized (refReverseMap) {
219: refReverseMap.clear();
220: }
221: }
222:
223: public void removeFile(FileSystem filesystem, FileName name) {
224: if (removeFile(new FileSystemAndNameKey(filesystem, name))) {
225: filesystemClose(filesystem);
226: }
227: }
228:
229: public void touchFile(FileObject file) {
230: }
231:
232: private boolean removeFile(final FileSystemAndNameKey key) {
233: if (log.isDebugEnabled()) {
234: log.debug("removeFile: " + key.getFileName());
235: }
236:
237: Map files = getOrCreateFilesystemCache(key.getFileSystem());
238:
239: synchronized (files) {
240: Object ref = files.remove(key.getFileName());
241: if (ref != null) {
242: synchronized (refReverseMap) {
243: refReverseMap.remove(ref);
244: }
245: }
246:
247: return files.size() < 1;
248: }
249: }
250:
251: protected Map getOrCreateFilesystemCache(final FileSystem filesystem) {
252: synchronized (filesystemCache) {
253: if (filesystemCache.size() < 1) {
254: startThread();
255: }
256:
257: Map files = (Map) filesystemCache.get(filesystem);
258: if (files == null) {
259: files = new HashMap();
260: filesystemCache.put(filesystem, files);
261: }
262:
263: return files;
264: }
265: }
266: }
|