001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2010–2011 ApexIdentity Inc. 015 * Portions Copyright 2011-2014 ForgeRock AS. 016 */ 017 018package org.forgerock.openig.io; 019 020import java.io.File; 021import java.io.FileNotFoundException; 022import java.io.IOException; 023import java.io.RandomAccessFile; 024 025/** 026 * A buffer that uses a local file for data storage. 027 * <p> 028 * <strong>Note:</strong> This implementation is not synchronized. If multiple threads access 029 * a buffer concurrently, threads that append to the buffer should synchronize on the instance 030 * of this object. 031 */ 032public class FileBuffer implements Buffer { 033 034 /** File to store buffered data in. */ 035 private RandomAccessFile raf; 036 037 /** Maximum file size, after which an {@link OverflowException} will be thrown. */ 038 private final int limit; 039 040 /** 041 * Constructs a new file buffer. 042 * 043 * @param file the file to use as storage for the buffer. 044 * @param limit the buffer length limit, after which an {@link OverflowException} will be thrown. 045 * @throws FileNotFoundException if the file cannot be created or opened for writing. 046 * @throws SecurityException if a security manager denies access to the specified file. 047 */ 048 public FileBuffer(File file, int limit) throws FileNotFoundException { 049 raf = new RandomAccessFile(file, "rw"); 050 this.limit = limit; 051 } 052 053 @Override 054 public int read(int pos, byte[] b, int off, int len) throws IOException { 055 if (off < 0 || len < 0 || len > b.length - off) { 056 throw new IndexOutOfBoundsException(); 057 } 058 notClosed(); 059 int n = 0; 060 if (pos < raf.length()) { 061 synchronized (raf) { 062 raf.seek(pos); 063 if ((n = raf.read(b, off, len)) == -1) { 064 // obey the contract of buffer reads 065 n = 0; 066 } 067 } 068 } 069 return n; 070 } 071 072 @Override 073 public void append(byte[] b, int off, int len) throws IOException, OverflowException { 074 if (off < 0 || len < 0 || len > b.length - off) { 075 throw new IndexOutOfBoundsException(); 076 } 077 notClosed(); 078 synchronized (raf) { 079 int rafLength = (int) Math.min(Integer.MAX_VALUE, raf.length()); 080 if (rafLength + len > limit) { 081 throw new OverflowException(); 082 } 083 raf.seek(rafLength); 084 raf.write(b, off, len); 085 } 086 } 087 088 @Override 089 public int length() throws IOException { 090 notClosed(); 091 return (int) Math.min(Integer.MAX_VALUE, raf.length()); 092 } 093 094 @Override 095 public void close() throws IOException { 096 if (raf != null) { 097 try { 098 raf.close(); 099 } finally { 100 raf = null; 101 } 102 } 103 } 104 105 @Override 106 public void finalize() throws Throwable { 107 try { 108 close(); 109 } catch (IOException ioe) { 110 // inappropriate to throw an exception when object is being collected 111 } 112 super.finalize(); 113 } 114 115 /** 116 * Throws an {@link IOException} if the buffer is closed. 117 */ 118 private void notClosed() throws IOException { 119 if (raf == null) { 120 throw new IOException("buffer is closed"); 121 } 122 } 123}