001 //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_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 }