blob: fcfd00b7a1718fb71bf1fd036a66867156fbfffc [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.vfs.cache;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.vfs.FileName;
import org.apache.commons.vfs.FileObject;
import org.apache.commons.vfs.FileSystem;
import org.apache.commons.vfs.VfsLog;
import org.apache.commons.vfs.impl.DefaultFileSystemManager;
import org.apache.commons.vfs.util.Messages;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* This implementation caches every file as long as it is strongly reachable by
* the java vm. As soon as the vm needs memory - every softly reachable file
* will be discarded.
*
* @author <a href="mailto:imario@apache.org">Mario Ivankovits</a>
* @version $Revision$ $Date: 2005-09-30 09:02:41 +0200 (Fr, 30 Sep
* 2005) $
* @see SoftReference
*/
public class SoftRefFilesCache extends AbstractFilesCache
{
private static final int TIMEOUT = 1000;
/**
* The logger to use.
*/
private final Log log = LogFactory.getLog(SoftRefFilesCache.class);
private final Map filesystemCache = new HashMap();
private final Map refReverseMap = new HashMap(100);
private final ReferenceQueue refqueue = new ReferenceQueue();
private SoftRefReleaseThread softRefReleaseThread;
/**
* This thread will listen on the ReferenceQueue and remove the entry in the
* filescache as soon as the vm removes the reference
*/
private final class SoftRefReleaseThread extends Thread
{
private volatile boolean requestEnd; // used for inter-thread communication
private SoftRefReleaseThread()
{
setName(SoftRefReleaseThread.class.getName());
setDaemon(true);
}
public void run()
{
loop: while (!requestEnd && !Thread.currentThread().isInterrupted())
{
try
{
Reference ref = refqueue.remove(TIMEOUT);
if (ref == null)
{
continue;
}
FileSystemAndNameKey key;
synchronized (refReverseMap)
{
key = (FileSystemAndNameKey) refReverseMap.get(ref);
}
if (key != null)
{
if (removeFile(key))
{
/* This is not thread safe
filesystemClose(key.getFileSystem());
*/
}
}
}
catch (InterruptedException e)
{
if (!requestEnd)
{
VfsLog.warn(getLogger(), log,
Messages.getString("vfs.impl/SoftRefReleaseThread-interrupt.info"));
}
break loop;
}
}
}
}
public SoftRefFilesCache()
{
}
private void startThread()
{
if (softRefReleaseThread != null)
{
throw new IllegalStateException(
Messages.getString("vfs.impl/SoftRefReleaseThread-already-running.warn"));
}
softRefReleaseThread = new SoftRefReleaseThread();
softRefReleaseThread.start();
}
private void endThread()
{
if (softRefReleaseThread != null)
{
softRefReleaseThread.requestEnd = true;
softRefReleaseThread.interrupt();
softRefReleaseThread = null;
}
}
public void putFile(final FileObject file)
{
if (log.isDebugEnabled())
{
log.debug("putFile: " + file.getName());
}
Map files = getOrCreateFilesystemCache(file.getFileSystem());
Reference ref = createReference(file, refqueue);
FileSystemAndNameKey key = new FileSystemAndNameKey(file
.getFileSystem(), file.getName());
synchronized (files)
{
Reference old = (Reference) files.put(file.getName(), ref);
synchronized (refReverseMap)
{
if (old != null)
{
refReverseMap.remove(old);
}
refReverseMap.put(ref, key);
}
}
}
protected Reference createReference(FileObject file, ReferenceQueue refqueue)
{
return new SoftReference(file, refqueue);
}
public FileObject getFile(final FileSystem filesystem, final FileName name)
{
Map files = getOrCreateFilesystemCache(filesystem);
synchronized (files)
{
Reference ref = (Reference) files.get(name);
if (ref == null)
{
return null;
}
FileObject fo = (FileObject) ref.get();
if (fo == null)
{
removeFile(filesystem, name);
}
return fo;
}
}
public void clear(FileSystem filesystem)
{
Map files = getOrCreateFilesystemCache(filesystem);
boolean closeFilesystem;
synchronized (files)
{
synchronized (refReverseMap)
{
Iterator iterKeys = refReverseMap.values().iterator();
while (iterKeys.hasNext())
{
FileSystemAndNameKey key = (FileSystemAndNameKey) iterKeys.next();
if (key.getFileSystem() == filesystem)
{
iterKeys.remove();
files.remove(key.getFileName());
}
}
closeFilesystem = files.size() < 1;
}
}
if (closeFilesystem)
{
filesystemClose(filesystem);
}
}
private void filesystemClose(FileSystem filesystem)
{
if (log.isDebugEnabled())
{
log.debug("close fs: " + filesystem.getRootName());
}
synchronized (filesystemCache)
{
filesystemCache.remove(filesystem);
if (filesystemCache.size() < 1)
{
endThread();
}
}
((DefaultFileSystemManager) getContext().getFileSystemManager())
._closeFileSystem(filesystem);
}
public void close()
{
super.close();
endThread();
// files.clear();
synchronized (filesystemCache)
{
filesystemCache.clear();
}
synchronized (refReverseMap)
{
refReverseMap.clear();
}
}
public void removeFile(FileSystem filesystem, FileName name)
{
if (removeFile(new FileSystemAndNameKey(filesystem, name)))
{
filesystemClose(filesystem);
}
}
public void touchFile(FileObject file)
{
}
private boolean removeFile(final FileSystemAndNameKey key)
{
if (log.isDebugEnabled())
{
log.debug("removeFile: " + key.getFileName());
}
Map files = getOrCreateFilesystemCache(key.getFileSystem());
synchronized (files)
{
Object ref = files.remove(key.getFileName());
if (ref != null)
{
synchronized (refReverseMap)
{
refReverseMap.remove(ref);
}
}
return files.size() < 1;
}
}
protected Map getOrCreateFilesystemCache(final FileSystem filesystem)
{
synchronized (filesystemCache)
{
if (filesystemCache.size() < 1)
{
startThread();
}
Map files = (Map) filesystemCache.get(filesystem);
if (files == null)
{
files = new HashMap();
filesystemCache.put(filesystem, files);
}
return files;
}
}
}