001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/io/ecwapi/ECWFileCache.java $ 002 /*---------------- FILE HEADER ------------------------------------------ 003 004 This file is part of deegree. 005 Copyright (C) 2007 by: 006 Planetek Italia s.r.l, Bari, Italia 007 http://www.planetek.it 008 009 This library is free software; you can redistribute it and/or 010 modify it under the terms of the GNU Lesser General Public 011 License as published by the Free Software Foundation; either 012 version 2.1 of the License, or (at your option) any later version. 013 014 This library is distributed in the hope that it will be useful, 015 but WITHOUT ANY WARRANTY; without even the implied warranty of 016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 017 Lesser General Public License for more details. 018 019 You should have received a copy of the GNU Lesser General Public 020 License along with this library; if not, write to the Free Software 021 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 022 023 ---------------------------------------------------------------------------*/ 024 package org.deegree.io.ecwapi; 025 026 import java.util.HashSet; 027 import java.util.Hashtable; 028 029 import com.ermapper.ecw.JNCSException; 030 import com.ermapper.ecw.JNCSFile; 031 032 /** 033 * 034 * 035 * 036 * @version $Revision: 6806 $ 037 * @author <a href="mailto:stutte@planetek.it">Jens Stutte</a> 038 * @author last edited by: $Author: apoth $ 039 * 040 * @version 1.0. $Revision: 6806 $, $Date: 2007-05-04 09:01:16 +0200 (Fr, 04 Mai 2007) $ 041 * 042 * @since 2.0 043 */ 044 public class ECWFileCache { 045 046 private static Hashtable<String,HashSet<ECWFile>> ECWNamedFileCache = new Hashtable<String,HashSet<ECWFile>>(); 047 048 private static HashSet<ECWFile> ECWAllFiles = new HashSet<ECWFile>(); 049 050 private static Hashtable<JNCSFile,ECWFile> ECWJNCSFileRef = new Hashtable<JNCSFile,ECWFile>(); 051 052 public static long EXPIRATION_PERIOD_MS = 600000; 053 054 public static long MAX_NUM_OPEN = 10; 055 056 // A private inner class that describes a cache entry 057 private static class ECWFile { 058 java.util.Date LastAccess; 059 060 JNCSFile myFile; 061 062 long lockingThread; 063 064 ECWFile( String fileName ) throws JNCSException { 065 myFile = new JNCSFile( fileName, false ); 066 LastAccess = new java.util.Date(); 067 ECWAllFiles.add( this ); 068 ECWJNCSFileRef.put( myFile, this ); 069 lockingThread = 0; 070 } 071 } 072 073 // Retrieve the thread id. 074 private static long getThreadId() { 075 return Thread.currentThread().getId(); 076 } 077 078 // Find the opened instances for a file name 079 private static HashSet<ECWFile> findNamedFileSet( String fileName ) { 080 HashSet<ECWFile> files = ECWNamedFileCache.get( fileName ); 081 if ( files == null ) { 082 files = new HashSet<ECWFile>(); 083 ECWNamedFileCache.put( fileName, files ); 084 } 085 return files; 086 } 087 088 // lock the file for the current thread, opens new instance 089 // if nothing present in cache 090 private static ECWFile lockFile( String fileName ) throws JNCSException { 091 // Is there an unused instance in the cache? 092 HashSet<ECWFile> files = findNamedFileSet( fileName ); 093 ECWFile[] fa = files.toArray( new ECWFile[files.size()] ); 094 for ( int i = 0; i < fa.length; i++ ) { 095 if ( fa[i].lockingThread == 0 || fa[i].lockingThread == getThreadId() ) { 096 fa[i].lockingThread = getThreadId(); 097 fa[i].LastAccess = new java.util.Date(); 098 return fa[i]; 099 } 100 } 101 // no: create new one 102 ECWFile f = new ECWFile( fileName ); 103 files.add( f ); 104 f.lockingThread = getThreadId(); 105 106 return f; 107 } 108 109 // release file for use 110 private static void unlockFile( ECWFile file ) { 111 if ( file != null ) { 112 file.lockingThread = 0; 113 } 114 } 115 116 // close (and delete) file access instance 117 private static void closeFile( ECWFile file ) { 118 findNamedFileSet( file.myFile.fileName ).remove( file ); 119 ECWAllFiles.remove( file ); 120 ECWJNCSFileRef.remove( file.myFile ); 121 file.myFile.close( true ); 122 } 123 124 // Find and close expired cache entries. 125 private static void closeExpired() { 126 synchronized ( ECWNamedFileCache ) { 127 ECWFile oldest = null; 128 int numOpen = 0; 129 ECWFile[] fa = ECWAllFiles.toArray( new ECWFile[ECWAllFiles.size()] ); 130 java.util.Date now = new java.util.Date(); 131 for ( int i = 0; i < fa.length; i++ ) { 132 if ( fa[i].lockingThread == 0 ) { 133 numOpen++; 134 if ( now.getTime() - fa[i].LastAccess.getTime() > EXPIRATION_PERIOD_MS ) { 135 closeFile( fa[i] ); 136 } else if ( oldest == null 137 || oldest.LastAccess.getTime() > fa[i].LastAccess.getTime() ) { 138 oldest = fa[i]; 139 } 140 } 141 } 142 if ( oldest != null && numOpen > MAX_NUM_OPEN ) { 143 closeFile( oldest ); 144 } 145 } 146 } 147 148 /** Claim access to an ECW file. 149 * <p> 150 * If there is a non-expired instance in the cache, re-uses and locks this. 151 * Otherwise adds a new one to the cache. 152 * <p> 153 * CAUTION: This cache DOES NOT HANDLE NESTED LOCKS/UNLOCKS ! This means, 154 * that after having called claimAcces, you MUST call releaseFile before 155 * ANY OTHER OPERATION ON THE CACHE within the same thread. 156 * <p> 157 * NOTE: There is no periodical cleanup of this cache. The expiration 158 * method is called only during new calls to claimAccess/releaseFile. 159 * So server memory consumption may remain high for a much longer time 160 * than the defined ExpirationPeriod. 161 */ 162 public static JNCSFile claimAccess( String fileName ) throws JNCSException { 163 synchronized ( ECWNamedFileCache ) { 164 // close old instances 165 closeExpired(); 166 ECWFile f = lockFile( fileName ); 167 return f.myFile; 168 } 169 } 170 171 /** Release access to an ECW file. 172 * <p> 173 * Unlocks the cache entry. 174 * Calls also the expiration method for cleanup of 175 * the instances that exceed MaxNumOpen. 176 */ 177 public static void releaseFile( JNCSFile myfile ) { 178 synchronized ( ECWNamedFileCache ) { 179 unlockFile( ECWJNCSFileRef.get( myfile ) ); 180 closeExpired(); 181 } 182 } 183 184 /** Set expiration period 185 * <p> 186 * Set the time in milliseconds that a cache entry remains valid. 187 * Calls also the expiration method for cleanup. 188 * <p> 189 * Default value: 600000 190 */ 191 public static void setExpirationPeriod( long MilliSecs ) { 192 EXPIRATION_PERIOD_MS = MilliSecs; 193 closeExpired(); 194 } 195 196 /** Set maximum number of unused file instances 197 * <p> 198 * This parameter describes the maximum number of UNLOCKED entries in the 199 * cache (locked instances are not counted). 200 * Calls also the expiration method for cleanup. 201 * <p> 202 * Default value: 10 203 */ 204 public static void setMaxNumOpen( long MaxOpen ) { 205 MAX_NUM_OPEN = MaxOpen; 206 closeExpired(); 207 } 208 }