001    /**
002     * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.kernel.util;
016    
017    import java.io.File;
018    import java.io.IOException;
019    import java.io.InputStream;
020    import java.io.RandomAccessFile;
021    
022    /**
023     * This class enables any {@link InputStream} to be seekable by caching its data
024     * in a temporary {@link RandomAccessFile}.
025     *
026     * @author Juan González
027     */
028    public class RandomAccessInputStream extends InputStream {
029    
030            public RandomAccessInputStream(InputStream inputStream) throws IOException {
031                    _inputStream = inputStream;
032    
033                    _file = FileUtil.createTempFile();
034    
035                    _randomAccessFileCache = new RandomAccessFile(_file, "rw");
036            }
037    
038            @Override
039            public void close() throws IOException {
040                    super.close();
041    
042                    _randomAccessFileCache.close();
043    
044                    FileUtil.delete(_file);
045            }
046    
047            @Override
048            public synchronized void mark(int readLimit) {
049                    _markPosition = _pointer;
050            }
051    
052            @Override
053            public boolean markSupported() {
054                    return true;
055            }
056    
057            @Override
058            public int read() throws IOException {
059                    long next = _pointer + 1;
060    
061                    long position = readUntil(next);
062    
063                    if (position >= next) {
064                            _randomAccessFileCache.seek(_pointer++);
065    
066                            return _randomAccessFileCache.read();
067                    }
068                    else {
069                            return -1;
070                    }
071            }
072    
073            @Override
074            public int read(byte[] bytes, int offset, int length) throws IOException {
075                    if (bytes == null) {
076                            throw new NullPointerException();
077                    }
078    
079                    if ((offset < 0) || (length < 0) || (offset + length > bytes.length)) {
080                            throw new IndexOutOfBoundsException();
081                    }
082    
083                    if (length == 0) {
084                            return 0;
085                    }
086    
087                    long position = readUntil(_pointer + length);
088    
089                    length = (int)Math.min(length, position - _pointer);
090    
091                    if (length > 0) {
092                            _randomAccessFileCache.seek(_pointer);
093    
094                            _randomAccessFileCache.readFully(bytes, offset, length);
095    
096                            _pointer += length;
097    
098                            return length;
099                    }
100                    else {
101                            return -1;
102                    }
103            }
104    
105            @Override
106            public synchronized void reset() throws IOException {
107                    if (_markPosition != -1) {
108                            seek(_markPosition);
109                    }
110            }
111    
112            public void seek(long position) throws IOException {
113                    if (position < 0) {
114                            throw new IOException("Error while seeking stream");
115                    }
116    
117                    _pointer = position;
118            }
119    
120            @Override
121            protected void finalize() throws Throwable {
122                    super.finalize();
123    
124                    close();
125            }
126    
127            protected long readUntil(long position) throws IOException {
128                    if (position < _length) {
129                            return position;
130                    }
131    
132                    if (_foundEOF) {
133                            return _length;
134                    }
135    
136                    long lengthToRead = position - _length;
137    
138                    _randomAccessFileCache.seek(_length);
139    
140                    byte[] buffer = new byte[1024];
141    
142                    while (lengthToRead > 0) {
143                            int bytesRead = _inputStream.read(
144                                    buffer, 0, (int) Math.min(lengthToRead, buffer.length));
145    
146                            if (bytesRead == -1) {
147                                    _foundEOF = true;
148    
149                                    return _length;
150                            }
151    
152                            _randomAccessFileCache.setLength(
153                                    _randomAccessFileCache.length() + bytesRead);
154    
155                            _randomAccessFileCache.write(buffer, 0, bytesRead);
156    
157                            lengthToRead -= bytesRead;
158    
159                            _length += bytesRead;
160                    }
161    
162                    return position;
163            }
164    
165            private File _file;
166            private boolean _foundEOF;
167            private InputStream _inputStream;
168            private long _length;
169            private long _markPosition = -1;
170            private long _pointer;
171            private RandomAccessFile _randomAccessFileCache;
172    
173    }