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.vfs.cache;
    18
    19  import java.lang.ref.Reference;
    20  import java.lang.ref.ReferenceQueue;
    21  import java.lang.ref.SoftReference;
    22  import java.util.HashMap;
    23  import java.util.Iterator;
    24  import java.util.Map;
    25  import java.util.TreeMap;
    26
    27  import org.apache.commons.logging.Log;
    28  import org.apache.commons.logging.LogFactory;
    29  import org.apache.commons.vfs.FileName;
    30  import org.apache.commons.vfs.FileObject;
    31  import org.apache.commons.vfs.FileSystem;
    32  import org.apache.commons.vfs.VfsLog;
    33  import org.apache.commons.vfs.impl.DefaultFileSystemManager;
    34  import org.apache.commons.vfs.util.Messages;
    35
    36  /***
    37   * This implementation caches every file as long as it is strongly reachable by
    38   * the java vm. As soon as the vm needs memory - every softly reachable file
    39   * will be discarded.
    40   *
    41   * @author <a href="mailto:imario@apache.org">Mario Ivankovits</a>
    42   * @version $Revision: 484648 $ $Date: 2005-09-30 09:02:41 +0200 (Fr, 30 Sep
    43   *          2005) $
    44   * @see SoftReference
    45   */
    46  public class SoftRefFilesCache extends AbstractFilesCache
    47  {
    48  	/***
    49  	 * The logger to use.
    50  	 */
    51  	private Log log = LogFactory.getLog(SoftRefFilesCache.class);
    52
    53  	private final Map filesystemCache = new HashMap();
    54  	private final Map refReverseMap = new HashMap(100);
    55  	private final ReferenceQueue refqueue = new ReferenceQueue();
    56
    57  	private SoftRefReleaseThread softRefReleaseThread = null;
    58
    59  	/***
    60  	 * This thread will listen on the ReferenceQueue and remove the entry in the
    61  	 * filescache as soon as the vm removes the reference
    62  	 */
    63  	private class SoftRefReleaseThread extends Thread
    64  	{
    65  		private boolean requestEnd = false;
    66
    67  		private SoftRefReleaseThread()
    68  		{
    69  			setName(SoftRefReleaseThread.class.getName());
    70  			setDaemon(true);
    71  		}
    72
    73  		public void run()
    74  		{
    75  			loop: while (!requestEnd && !Thread.currentThread().isInterrupted())
    76  			{
    77  				try
    78  				{
    79  					Reference ref = refqueue.remove(1000);
    80  					if (ref == null)
    81  					{
    82  						continue;
    83  					}
    84
    85  					FileSystemAndNameKey key = (FileSystemAndNameKey) refReverseMap
    86  							.get(ref);
    87
    88  					if (key != null)
    89  					{
    90  						if (removeFile(key))
    91  						{
    92  							filesystemClose(key.getFileSystem());
    93  						}
    94  					}
    95  				}
    96  				catch (InterruptedException e)
    97  				{
    98  					if (!requestEnd)
    99  					{
    100 						VfsLog
    101 								.warn(
    102 										getLogger(),
    103 										log,
    104 										Messages
    105 												.getString("vfs.impl/SoftRefReleaseThread-interrupt.info"));
    106 					}
    107 					break loop;
    108 				}
    109 			}
    110 		}
    111 	}
    112
    113 	public SoftRefFilesCache()
    114 	{
    115 	}
    116
    117 	private void startThread()
    118 	{
    119 		if (softRefReleaseThread != null)
    120 		{
    121 			throw new IllegalStateException(
    122 					Messages
    123 							.getString("vfs.impl/SoftRefReleaseThread-already-running.warn"));
    124 		}
    125
    126 		softRefReleaseThread = new SoftRefReleaseThread();
    127 		softRefReleaseThread.start();
    128 	}
    129
    130 	private void endThread()
    131 	{
    132 		if (softRefReleaseThread != null)
    133 		{
    134 			softRefReleaseThread.requestEnd = true;
    135 			softRefReleaseThread.interrupt();
    136 			softRefReleaseThread = null;
    137 		}
    138 	}
    139
    140 	public void putFile(final FileObject file)
    141 	{
    142 		if (log.isDebugEnabled())
    143 		{
    144 			log.debug("putFile: " + file.getName());
    145 		}
    146
    147 		Map files = getOrCreateFilesystemCache(file.getFileSystem());
    148
    149 		Reference ref = createReference(file, refqueue);
    150 		FileSystemAndNameKey key = new FileSystemAndNameKey(file
    151 				.getFileSystem(), file.getName());
    152
    153 		synchronized (files)
    154 		{
    155 			files.put(file.getName(), ref);
    156 			refReverseMap.put(ref, key);
    157 		}
    158 	}
    159
    160 	protected Reference createReference(FileObject file, ReferenceQueue refqueue)
    161 	{
    162 		return new SoftReference(file, refqueue);
    163 	}
    164
    165 	public FileObject getFile(final FileSystem filesystem, final FileName name)
    166 	{
    167 		Map files = getOrCreateFilesystemCache(filesystem);
    168
    169 		synchronized (files)
    170 		{
    171 			Reference ref = (Reference) files.get(name);
    172 			if (ref == null)
    173 			{
    174 				return null;
    175 			}
    176
    177 			FileObject fo = (FileObject) ref.get();
    178 			if (fo == null)
    179 			{
    180 				removeFile(filesystem, name);
    181 			}
    182 			return fo;
    183 		}
    184 	}
    185
    186 	public void clear(FileSystem filesystem)
    187 	{
    188 		Map files = getOrCreateFilesystemCache(filesystem);
    189
    190 		boolean closeFilesystem;
    191
    192 		synchronized (files)
    193 		{
    194 			Iterator iterKeys = refReverseMap.values().iterator();
    195 			while (iterKeys.hasNext())
    196 			{
    197 				FileSystemAndNameKey key = (FileSystemAndNameKey) iterKeys
    198 						.next();
    199 				if (key.getFileSystem() == filesystem)
    200 				{
    201 					iterKeys.remove();
    202 					files.remove(key.getFileName());
    203 				}
    204 			}
    205
    206 			closeFilesystem = files.size() < 1;
    207 		}
    208
    209 		if (closeFilesystem)
    210 		{
    211 			filesystemClose(filesystem);
    212 		}
    213 	}
    214
    215 	private void filesystemClose(FileSystem filesystem)
    216 	{
    217 		if (log.isDebugEnabled())
    218 		{
    219 			log.debug("close fs: " + filesystem.getRootName());
    220 		}
    221 		synchronized (filesystemCache)
    222 		{
    223 			filesystemCache.remove(filesystem);
    224 			if (filesystemCache.size() < 1)
    225 			{
    226 				endThread();
    227 			}
    228 		}
    229 		((DefaultFileSystemManager) getContext().getFileSystemManager())
    230 				._closeFileSystem(filesystem);
    231 	}
    232
    233 	public void close()
    234 	{
    235 		super.close();
    236
    237 		endThread();
    238
    239 		// files.clear();
    240 		synchronized (filesystemCache)
    241 		{
    242 			filesystemCache.clear();
    243 		}
    244 		refReverseMap.clear();
    245 	}
    246
    247 	public void removeFile(FileSystem filesystem, FileName name)
    248 	{
    249 		if (removeFile(new FileSystemAndNameKey(filesystem, name)))
    250 		{
    251 			filesystemClose(filesystem);
    252 		}
    253 	}
    254
    255 	public void touchFile(FileObject file)
    256 	{
    257 	}
    258
    259 	private boolean removeFile(final FileSystemAndNameKey key)
    260 	{
    261 		if (log.isDebugEnabled())
    262 		{
    263 			log.debug("removeFile: " + key.getFileName());
    264 		}
    265
    266 		Map files = getOrCreateFilesystemCache(key.getFileSystem());
    267
    268 		synchronized (files)
    269 		{
    270 			Object ref = files.remove(key.getFileName());
    271 			if (ref != null)
    272 			{
    273 				refReverseMap.remove(ref);
    274 			}
    275
    276 			return files.size() < 1;
    277 		}
    278 	}
    279
    280 	protected Map getOrCreateFilesystemCache(final FileSystem filesystem)
    281 	{
    282 		synchronized (filesystemCache)
    283 		{
    284 			if (filesystemCache.size() < 1)
    285 			{
    286 				startThread();
    287 			}
    288
    289 			Map files = (Map) filesystemCache.get(filesystem);
    290 			if (files == null)
    291 			{
    292 				files = new TreeMap();
    293 				filesystemCache.put(filesystem, files);
    294 			}
    295
    296 			return files;
    297 		}
    298 	}
    299 }