1 28 package net.sf.jasperreports.engine.util; 29 30 import java.io.File ; 31 import java.io.FileNotFoundException ; 32 import java.io.IOException ; 33 import java.io.RandomAccessFile ; 34 35 import org.apache.commons.logging.Log; 36 import org.apache.commons.logging.LogFactory; 37 38 import net.sf.jasperreports.engine.JRRuntimeException; 39 40 41 54 public class JRSwapFile 55 { 56 private static final Log log = LogFactory.getLog(JRSwapFile.class); 57 58 private final File swapFile; 59 protected final RandomAccessFile file; 60 private final int blockSize; 61 private final int minGrowCount; 62 private final LongQueue freeBlocks; 63 64 65 74 public JRSwapFile(String directory, int blockSize, int minGrowCount) 75 { 76 try 77 { 78 String filename = "swap_" + System.identityHashCode(this) + "_" + System.currentTimeMillis(); 79 swapFile = new File (directory, filename); 80 if (log.isDebugEnabled()) 81 { 82 log.debug("Creating swap file " + swapFile.getPath()); 83 } 84 boolean fileExists = swapFile.exists(); 85 swapFile.deleteOnExit(); 86 file = new RandomAccessFile (swapFile, "rw"); 87 88 this.blockSize = blockSize; 89 this.minGrowCount = minGrowCount; 90 freeBlocks = new LongQueue(minGrowCount); 91 92 if (fileExists) 93 { 94 file.setLength(0); 95 if (log.isDebugEnabled()) 96 { 97 log.debug("Swap file " + swapFile.getPath() + " exists, truncating"); 98 } 99 } 100 } 101 catch (FileNotFoundException e) 102 { 103 throw new JRRuntimeException(e); 104 } 105 catch (IOException e) 106 { 107 throw new JRRuntimeException(e); 108 } 109 } 110 111 112 119 public SwapHandle write(byte[] data) throws IOException 120 { 121 int blockCount = (data.length - 1) / blockSize + 1; 122 long[] offsets = reserveFreeBlocks(blockCount); 123 int lastBlockSize = (data.length - 1) % blockSize + 1; 124 SwapHandle handle = new SwapHandle(offsets, lastBlockSize); 125 for (int i = 0; i < blockCount; ++i) 126 { 127 int dataSize = i < blockCount - 1 ? blockSize : lastBlockSize; 128 int dataOffset = i * blockSize; 129 write(data, dataSize, dataOffset, offsets[i]); 130 } 131 132 return handle; 133 } 134 135 136 protected void write(byte[] data, int dataSize, int dataOffset, long fileOffset) throws IOException 137 { 138 synchronized (this) 139 { 140 file.seek(fileOffset); 141 file.write(data, dataOffset, dataSize); 142 } 143 } 144 145 146 154 public byte[] read(SwapHandle handle, boolean free) throws IOException 155 { 156 long[] offsets = handle.getOffsets(); 157 int totalLength = (offsets.length - 1) * blockSize + handle.getLastSize(); 158 byte[] data = new byte[totalLength]; 159 160 for (int i = 0; i < offsets.length; ++i) 161 { 162 int dataOffset = i * blockSize; 163 int dataLength = i < offsets.length - 1 ? blockSize : handle.getLastSize(); 164 read(data, dataOffset, dataLength, offsets[i]); 165 } 166 167 if (free) 168 { 169 freeBlocks(offsets); 170 } 171 172 return data; 173 } 174 175 176 protected void read(byte[] data, int dataOffset, int dataLength, long fileOffset) throws IOException 177 { 178 synchronized (this) 179 { 180 file.seek(fileOffset); 181 file.readFully(data, dataOffset, dataLength); 182 } 183 } 184 185 186 191 public void free(SwapHandle handle) 192 { 193 freeBlocks(handle.getOffsets()); 194 } 195 196 197 200 public void dispose() 201 { 202 synchronized (this) 203 { 204 if (swapFile.exists()) 205 { 206 if (log.isDebugEnabled()) 207 { 208 log.debug("Disposing swap file " + swapFile.getPath()); 209 } 210 211 try 212 { 213 file.close(); 214 } 215 catch (IOException e) 216 { 217 log.warn("Not able to close swap file " + swapFile.getPath()); 218 } 219 220 if (!swapFile.delete()) 221 { 222 log.warn("Not able to delete swap file " + swapFile.getPath()); 223 } 224 } 225 } 226 } 227 228 229 protected void finalize() throws Throwable 230 { 231 dispose(); 232 super.finalize(); 233 } 234 235 236 protected synchronized long[] reserveFreeBlocks(int blockCount) throws IOException 237 { 238 int growCount = blockCount - freeBlocks.size(); 239 if (growCount > 0) 240 { 241 if (growCount < minGrowCount) 242 { 243 growCount = minGrowCount; 244 } 245 246 long length = file.length(); 247 long newLength = length + growCount * blockSize; 248 if (log.isDebugEnabled()) 249 { 250 log.debug("Growing swap file " + swapFile.getPath() + " with " + growCount + " blocks x " + blockSize + " bytes to size " + newLength); 251 } 252 file.setLength(newLength); 253 254 for (int i = 0; i < growCount; ++i) 255 { 256 freeBlocks.addLast(length + i * blockSize); 257 } 258 } 259 260 long[] offsets = new long[blockCount]; 261 for (int i = 0; i < blockCount; i++) 262 { 263 offsets[i] = freeBlocks.popFirst(); 264 } 265 return offsets; 266 } 267 268 269 protected synchronized void freeBlocks(long []offsets) 270 { 271 for (int i = offsets.length - 1; i >= 0; --i) 272 { 273 freeBlocks.addFirst(offsets[i]); 274 } 275 } 276 277 278 protected static class LongQueue 279 { 280 private final int minGrowCount; 281 private long[] vals; 282 private int size; 283 private int first; 284 private int last; 285 286 public LongQueue(int minGrowCount) 287 { 288 this.minGrowCount = minGrowCount; 289 vals = new long[minGrowCount]; 290 size = 0; 291 first = 0; 292 last = 0; 293 } 294 295 public void addFirst(long val) 296 { 297 growIfFull(); 298 299 --first; 300 if (first == -1) 301 { 302 first = vals.length - 1; 303 } 304 vals[first] = val; 305 ++size; 306 } 307 308 public void addLast(long val) 309 { 310 growIfFull(); 311 312 vals[last] = val; 313 ++size; 314 ++last; 315 if (last == vals.length) 316 { 317 last = 0; 318 } 319 } 320 321 public long popFirst() 322 { 323 if (size == 0) 324 { 325 throw new JRRuntimeException("Queue underflow"); 326 } 327 328 long val = vals[first]; 329 ++first; 330 if (first == vals.length) 331 { 332 first = 0; 333 } 334 --size; 335 336 return val; 337 } 338 339 protected void growIfFull() 340 { 341 int valsLenght = vals.length; 342 if (size == valsLenght) 343 { 344 int newLength = (valsLenght * 3) / 2 + 1; 345 if (newLength - valsLenght < minGrowCount) 346 { 347 newLength = valsLenght + minGrowCount; 348 } 349 350 long[] newVals = new long[newLength]; 351 System.arraycopy(vals, first, newVals, 0, valsLenght - first); 352 if (last > 0) 353 { 354 System.arraycopy(vals, 0, newVals, valsLenght - first, last); 355 } 356 357 vals = newVals; 358 first = 0; 359 last = valsLenght; 360 } 361 } 362 363 public int size() 364 { 365 return size; 366 } 367 } 368 369 public static class SwapHandle 370 { 371 private final long[] offsets; 372 private final int lastSize; 373 374 public SwapHandle(long[] offsets, int lastSize) 375 { 376 this.offsets = offsets; 377 this.lastSize = lastSize; 378 } 379 380 public long[] getOffsets() 381 { 382 return offsets; 383 } 384 385 public int getLastSize() 386 { 387 return lastSize; 388 } 389 } 390 } 391 | Popular Tags |