001/*
002 * $Id: MappedRandomAccessFile.java 4784 2011-03-15 08:33:00Z blowagie $
003 *
004 * This file is part of the iText (R) project.
005 * Copyright (c) 1998-2011 1T3XT BVBA
006 * Authors: Bruno Lowagie, Paulo Soares, et al.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU Affero General Public License version 3
010 * as published by the Free Software Foundation with the addition of the
011 * following permission added to Section 15 as permitted in Section 7(a):
012 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT,
013 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
014 *
015 * This program is distributed in the hope that it will be useful, but
016 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
017 * or FITNESS FOR A PARTICULAR PURPOSE.
018 * See the GNU Affero General Public License for more details.
019 * You should have received a copy of the GNU Affero General Public License
020 * along with this program; if not, see http://www.gnu.org/licenses or write to
021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
022 * Boston, MA, 02110-1301 USA, or download the license from the following URL:
023 * http://itextpdf.com/terms-of-use/
024 *
025 * The interactive user interfaces in modified source and object code versions
026 * of this program must display Appropriate Legal Notices, as required under
027 * Section 5 of the GNU Affero General Public License.
028 *
029 * In accordance with Section 7(b) of the GNU Affero General Public License,
030 * a covered work must retain the producer line in every PDF that is created
031 * or manipulated using iText.
032 *
033 * You can be released from the requirements of the license by purchasing
034 * a commercial license. Buying such a license is mandatory as soon as you
035 * develop commercial activities involving the iText software without
036 * disclosing the source code of your own applications.
037 * These activities include: offering paid services to customers as an ASP,
038 * serving PDFs on the fly in a web application, shipping iText with a closed
039 * source product.
040 *
041 * For more information, please contact iText Software Corp. at this
042 * address: sales@itextpdf.com
043 */
044package com.itextpdf.text.pdf;
045
046import java.io.FileInputStream;
047import java.io.FileNotFoundException;
048import java.io.IOException;
049import java.lang.reflect.Method;
050import java.nio.BufferUnderflowException;
051import java.nio.MappedByteBuffer;
052import java.nio.channels.FileChannel;
053import java.security.AccessController;
054import java.security.PrivilegedAction;
055
056/**
057 * A {@link java.nio.MappedByteBuffer} wrapped as a {@link java.io.RandomAccessFile}
058 *
059 * @author Joakim Sandstroem
060 * Created on 6.9.2006
061 */
062public class MappedRandomAccessFile {
063
064    private MappedByteBuffer mappedByteBuffer = null;
065    private FileChannel channel = null;
066
067    /**
068     * Constructs a new MappedRandomAccessFile instance
069     * @param filename String
070     * @param mode String r, w or rw
071     * @throws FileNotFoundException
072     * @throws IOException
073     */
074    public MappedRandomAccessFile(String filename, String mode)
075    throws FileNotFoundException, IOException {
076
077        if (mode.equals("rw"))
078            init(
079                    new java.io.RandomAccessFile(filename, mode).getChannel(),
080                    FileChannel.MapMode.READ_WRITE);
081        else
082            init(
083                    new FileInputStream(filename).getChannel(),
084                    FileChannel.MapMode.READ_ONLY);
085
086    }
087
088    /**
089     * initializes the channel and mapped bytebuffer
090     * @param channel FileChannel
091     * @param mapMode FileChannel.MapMode
092     * @throws IOException
093     */
094    private void init(FileChannel channel, FileChannel.MapMode mapMode)
095    throws IOException {
096
097        this.channel = channel;
098        this.mappedByteBuffer = channel.map(mapMode, 0L, channel.size());
099        mappedByteBuffer.load();
100    }
101
102    /**
103     * @since 2.0.8
104     */
105    public FileChannel getChannel() {
106        return channel;
107    }
108
109    /**
110     * @see java.io.RandomAccessFile#read()
111     * @return int next integer or -1 on EOF
112     */
113    public int read() {
114        try {
115            byte b = mappedByteBuffer.get();
116            int n = b & 0xff;
117
118            return n;
119        } catch (BufferUnderflowException e) {
120            return -1; // EOF
121        }
122    }
123
124    /**
125     * @see java.io.RandomAccessFile#read(byte[], int, int)
126     * @param bytes byte[]
127     * @param off int offset
128     * @param len int length
129     * @return int bytes read or -1 on EOF
130     */
131    public int read(byte bytes[], int off, int len) {
132        int pos = mappedByteBuffer.position();
133        int limit = mappedByteBuffer.limit();
134        if (pos == limit)
135            return -1; // EOF
136        int newlimit = pos + len - off;
137        if (newlimit > limit) {
138            len = limit - pos; // don't read beyond EOF
139        }
140        mappedByteBuffer.get(bytes, off, len);
141        return len;
142    }
143
144    /**
145     * @see java.io.RandomAccessFile#getFilePointer()
146     * @return long
147     */
148    public long getFilePointer() {
149        return mappedByteBuffer.position();
150    }
151
152    /**
153     * @see java.io.RandomAccessFile#seek(long)
154     * @param pos long position
155     */
156    public void seek(long pos) {
157        mappedByteBuffer.position((int) pos);
158    }
159
160    /**
161     * @see java.io.RandomAccessFile#length()
162     * @return long length
163     */
164    public long length() {
165        return mappedByteBuffer.limit();
166    }
167
168    /**
169     * @see java.io.RandomAccessFile#close()
170     * Cleans the mapped bytebuffer and closes the channel
171     */
172    public void close() throws IOException {
173        clean(mappedByteBuffer);
174        mappedByteBuffer = null;
175        if (channel != null)
176            channel.close();
177        channel = null;
178    }
179
180    /**
181     * invokes the close method
182     * @see java.lang.Object#finalize()
183     */
184    @Override
185    protected void finalize() throws Throwable {
186        close();
187        super.finalize();
188    }
189
190    /**
191     * invokes the clean method on the ByteBuffer's cleaner
192     * @param buffer ByteBuffer
193     * @return boolean true on success
194     */
195    public static boolean clean(final java.nio.ByteBuffer buffer) {
196        if (buffer == null || !buffer.isDirect())
197            return false;
198
199        Boolean b = (Boolean) AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
200            public Boolean run() {
201                Boolean success = Boolean.FALSE;
202                try {
203                    Method getCleanerMethod = buffer.getClass().getMethod("cleaner", (Class<?>[])null);
204                    getCleanerMethod.setAccessible(true);
205                    Object cleaner = getCleanerMethod.invoke(buffer, (Object[])null);
206                    Method clean = cleaner.getClass().getMethod("clean", (Class<?>[])null);
207                    clean.invoke(cleaner, (Object[])null);
208                    success = Boolean.TRUE;
209                } catch (Exception e) {
210                    // This really is a show stopper on windows
211                    //e.printStackTrace();
212                }
213                return success;
214            }
215        });
216
217        return b.booleanValue();
218    }
219
220}