/* * File : FMFileManagerImpl.java * Created : 12-Feb-2004 * By : parg * * Azureus - a Java Bittorrent client * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details ( see the LICENSE file ). * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package com.aelitis.azureus.core.diskmanager.file.impl; /** * @author parg * */ import java.util.*; import java.io.File; import org.gudy.azureus2.core3.torrent.TOTorrent; import org.gudy.azureus2.core3.util.*; import org.gudy.azureus2.core3.config.*; import com.aelitis.azureus.core.diskmanager.file.*; import com.aelitis.azureus.core.util.CaseSensitiveFileMap; import com.aelitis.azureus.core.util.LinkFileMap; public class FMFileManagerImpl implements FMFileManager { public static final boolean DEBUG = false; protected static FMFileManagerImpl singleton; protected static AEMonitor class_mon = new AEMonitor( "FMFileManager:class" ); public static FMFileManager getSingleton() { try{ class_mon.enter(); if ( singleton == null ){ singleton = new FMFileManagerImpl(); } return( singleton ); }finally{ class_mon.exit(); } } protected LinkedHashMap map; protected AEMonitor map_mon = new AEMonitor( "FMFileManager:Map"); protected HashMap<Object,LinkFileMap> links = new HashMap<Object,LinkFileMap>(); protected AEMonitor links_mon = new AEMonitor( "FMFileManager:Links"); protected boolean limited; protected int limit_size; protected AESemaphore close_queue_sem; protected List close_queue; protected AEMonitor close_queue_mon = new AEMonitor( "FMFileManager:CQ"); protected List files; protected AEMonitor files_mon = new AEMonitor( "FMFileManager:File"); protected FMFileManagerImpl() { limit_size = COConfigurationManager.getIntParameter( "File Max Open" ); limited = limit_size > 0; if ( DEBUG ){ System.out.println( "FMFileManager::init: limit = " + limit_size ); files = new ArrayList(); } map = new LinkedHashMap( limit_size, (float)0.75, true ); // ACCESS order selected - this means oldest if ( limited ){ close_queue_sem = new AESemaphore("FMFileManager::closeqsem"); close_queue = new LinkedList(); Thread t = new AEThread("FMFileManager::closeQueueDispatcher") { public void runSupport() { closeQueueDispatch(); } }; t.setDaemon(true); t.start(); } } protected LinkFileMap getLinksEntry( TOTorrent torrent ) { Object links_key; try{ links_key = torrent.getHashWrapper(); }catch( Throwable e ){ Debug.printStackTrace(e); links_key = ""; } LinkFileMap links_entry = links.get( links_key ); if ( links_entry == null ){ links_entry = new LinkFileMap(); links.put( links_key, links_entry ); } return( links_entry ); } public void setFileLinks( TOTorrent torrent, LinkFileMap new_links ) { try{ links_mon.enter(); LinkFileMap links_entry = getLinksEntry( torrent ); Iterator<LinkFileMap.Entry> it = new_links.entryIterator(); while( it.hasNext()){ LinkFileMap.Entry entry = it.next(); int index = entry.getIndex(); File source = entry.getFromFile(); File target = entry.getToFile(); // System.out.println( "setLink:" + source + " -> " + target ); if ( target != null && !source.equals(target)){ if ( index >= 0 ){ links_entry.put( index, source, target ); }else{ links_entry.putMigration( source, target ); } }else{ links_entry.remove( index, source ); } } }finally{ links_mon.exit(); } } public File getFileLink( TOTorrent torrent, int file_index, File file ) { // this function works on the currently defined links and will only accept // them as valid if their 'from' location matches the 'file' being queried. // if not the origial file is returned, NOT null // These semantics are important during file-move operations as the move-file // logic does not update links until AFTER the move is complete. If we don't // verify the 'from' path in this case then teh old existing linkage overrides // the new destination during the move process and causes it to fail with // 'file already exists'. There is possibly an argument that we shouldn't // take links into account when moving try{ links_mon.enter(); LinkFileMap links_entry = getLinksEntry( torrent ); LinkFileMap.Entry entry = links_entry.getEntry( file_index, file ); File res = null; if ( entry == null ){ res = file; }else{ if ( file.equals( entry.getFromFile())){ res = entry.getToFile(); }else{ res = file; } } // System.out.println( "getLink:" + file + " -> " + res ); return( res ); }finally{ links_mon.exit(); } } public FMFile createFile( FMFileOwner owner, File file, int type ) throws FMFileManagerException { FMFile res; if ( AEDiagnostics.USE_DUMMY_FILE_DATA ){ res = new FMFileTestImpl( owner, this, file, type ); }else{ if ( limited ){ res = new FMFileLimited( owner, this, file, type ); }else{ res = new FMFileUnlimited( owner, this, file, type ); } } if (DEBUG){ try{ files_mon.enter(); files.add( res ); }finally{ files_mon.exit(); } } return( res ); } protected void getSlot( FMFileLimited file ) { // must close the oldest file outside sync block else we'll get possible deadlock FMFileLimited oldest_file = null; try{ map_mon.enter(); if (DEBUG ){ System.out.println( "FMFileManager::getSlot: " + file.getName() +", map_size = " + map.size()); } if ( map.size() >= limit_size ){ Iterator it = map.keySet().iterator(); oldest_file = (FMFileLimited)it.next(); it.remove(); } map.put( file, file ); }finally{ map_mon.exit(); } if ( oldest_file != null ){ closeFile( oldest_file ); } } protected void releaseSlot( FMFileLimited file ) { if ( DEBUG ){ System.out.println( "FMFileManager::releaseSlot: " + file.getName()); } try{ map_mon.enter(); map.remove( file ); }finally{ map_mon.exit(); } } protected void usedSlot( FMFileLimited file ) { if ( DEBUG ){ System.out.println( "FMFileManager::usedSlot: " + file.getName()); } try{ map_mon.enter(); // only update if already present - might not be due to delay in // closing files if ( map.containsKey( file )){ map.put( file, file ); // update MRU } }finally{ map_mon.exit(); } } protected void closeFile( FMFileLimited file ) { if ( DEBUG ){ System.out.println( "FMFileManager::closeFile: " + file.getName()); } try{ close_queue_mon.enter(); close_queue.add( file ); }finally{ close_queue_mon.exit(); } close_queue_sem.release(); } protected void closeQueueDispatch() { while(true){ if ( DEBUG ){ close_queue_sem.reserve(1000); }else{ close_queue_sem.reserve(); } FMFileLimited file = null; try{ close_queue_mon.enter(); if ( close_queue.size() > 0 ){ file = (FMFileLimited)close_queue.remove(0); if ( DEBUG ){ System.out.println( "FMFileManager::closeQ: " + file.getName() + ", rem = " + close_queue.size()); } } }finally{ close_queue_mon.exit(); } if ( file != null ){ try{ file.close(false); }catch( Throwable e ){ Debug.printStackTrace( e ); } } if ( DEBUG ){ try{ files_mon.enter(); int open = 0; for (int i=0;i<files.size();i++){ FMFileLimited f = (FMFileLimited)files.get(i); if ( f.isOpen()){ open++; } } System.out.println( "INFO: files = " + files.size() + ", open = " + open ); }finally{ files_mon.exit(); } } } } protected void generate( IndentWriter writer ) { writer.println( "FMFileManager slots" ); try{ writer.indent(); try{ map_mon.enter(); Iterator it = map.keySet().iterator(); while( it.hasNext()){ FMFileLimited file = (FMFileLimited)it.next(); writer.println( file.getString()); } }finally{ map_mon.exit(); } }finally{ writer.exdent(); } } protected static void generateEvidence( IndentWriter writer ) { getSingleton(); singleton.generate( writer ); } }