001 //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_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: 10660 $ 037 * @author <a href="mailto:stutte@planetek.it">Jens Stutte</a> 038 * @author last edited by: $Author: apoth $ 039 * 040 * @version 1.0. $Revision: 10660 $, $Date: 2008-03-24 22:39:54 +0100 (Mo, 24 Mär 2008) $ 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 ) 091 throws JNCSException { 092 // Is there an unused instance in the cache? 093 HashSet<ECWFile> files = findNamedFileSet( fileName ); 094 ECWFile[] fa = files.toArray( new ECWFile[files.size()] ); 095 for ( int i = 0; i < fa.length; i++ ) { 096 if ( fa[i].lockingThread == 0 || fa[i].lockingThread == getThreadId() ) { 097 fa[i].lockingThread = getThreadId(); 098 fa[i].LastAccess = new java.util.Date(); 099 return fa[i]; 100 } 101 } 102 // no: create new one 103 ECWFile f = new ECWFile( fileName ); 104 files.add( f ); 105 f.lockingThread = getThreadId(); 106 107 return f; 108 } 109 110 // release file for use 111 private static void unlockFile( ECWFile file ) { 112 if ( file != null ) { 113 file.lockingThread = 0; 114 } 115 } 116 117 // close (and delete) file access instance 118 private static void closeFile( ECWFile file ) { 119 findNamedFileSet( file.myFile.fileName ).remove( file ); 120 ECWAllFiles.remove( file ); 121 ECWJNCSFileRef.remove( file.myFile ); 122 file.myFile.close( true ); 123 } 124 125 // Find and close expired cache entries. 126 private static void closeExpired() { 127 synchronized ( ECWNamedFileCache ) { 128 ECWFile oldest = null; 129 int numOpen = 0; 130 ECWFile[] fa = ECWAllFiles.toArray( new ECWFile[ECWAllFiles.size()] ); 131 java.util.Date now = new java.util.Date(); 132 for ( int i = 0; i < fa.length; i++ ) { 133 if ( fa[i].lockingThread == 0 ) { 134 numOpen++; 135 if ( now.getTime() - fa[i].LastAccess.getTime() > EXPIRATION_PERIOD_MS ) { 136 closeFile( fa[i] ); 137 } else if ( oldest == null || 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 /** 149 * Claim access to an ECW file. 150 * <p> 151 * If there is a non-expired instance in the cache, re-uses and locks this. Otherwise adds a new 152 * one to the cache. 153 * <p> 154 * CAUTION: This cache DOES NOT HANDLE NESTED LOCKS/UNLOCKS ! This means, that after having 155 * called claimAcces, you MUST call releaseFile before ANY OTHER OPERATION ON THE CACHE within 156 * the same thread. 157 * <p> 158 * NOTE: There is no periodical cleanup of this cache. The expiration method is called only 159 * during new calls to claimAccess/releaseFile. So server memory consumption may remain high for 160 * a much longer time than the defined ExpirationPeriod. 161 */ 162 public static JNCSFile claimAccess( String fileName ) 163 throws JNCSException { 164 synchronized ( ECWNamedFileCache ) { 165 // close old instances 166 closeExpired(); 167 ECWFile f = lockFile( fileName ); 168 return f.myFile; 169 } 170 } 171 172 /** 173 * Release access to an ECW file. 174 * <p> 175 * Unlocks the cache entry. Calls also the expiration method for cleanup of the instances that 176 * exceed MaxNumOpen. 177 */ 178 public static void releaseFile( JNCSFile myfile ) { 179 synchronized ( ECWNamedFileCache ) { 180 unlockFile( ECWJNCSFileRef.get( myfile ) ); 181 closeExpired(); 182 } 183 } 184 185 /** 186 * Set expiration period 187 * <p> 188 * Set the time in milliseconds that a cache entry remains valid. Calls also the expiration 189 * method for cleanup. 190 * <p> 191 * Default value: 600000 192 */ 193 public static void setExpirationPeriod( long MilliSecs ) { 194 EXPIRATION_PERIOD_MS = MilliSecs; 195 closeExpired(); 196 } 197 198 /** 199 * Set maximum number of unused file instances 200 * <p> 201 * This parameter describes the maximum number of UNLOCKED entries in the cache (locked 202 * instances are not counted). Calls also the expiration method for cleanup. 203 * <p> 204 * Default value: 10 205 */ 206 public static void setMaxNumOpen( long MaxOpen ) { 207 MAX_NUM_OPEN = MaxOpen; 208 closeExpired(); 209 } 210 }