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 }