001    /**
002     * Copyright (c) 2000-2013 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) ||
080                            ((offset + length) > bytes.length)) {
081    
082                            throw new IndexOutOfBoundsException();
083                    }
084    
085                    if (length == 0) {
086                            return 0;
087                    }
088    
089                    long position = readUntil(_pointer + length);
090    
091                    length = (int)Math.min(length, position - _pointer);
092    
093                    if (length > 0) {
094                            _randomAccessFileCache.seek(_pointer);
095    
096                            _randomAccessFileCache.readFully(bytes, offset, length);
097    
098                            _pointer += length;
099    
100                            return length;
101                    }
102                    else {
103                            return -1;
104                    }
105            }
106    
107            @Override
108            public synchronized void reset() throws IOException {
109                    if (_markPosition != -1) {
110                            seek(_markPosition);
111                    }
112            }
113    
114            public void seek(long position) throws IOException {
115                    if (position < 0) {
116                            throw new IOException("Error while seeking stream");
117                    }
118    
119                    _pointer = position;
120            }
121    
122            @Override
123            protected void finalize() throws Throwable {
124                    super.finalize();
125    
126                    close();
127            }
128    
129            protected long readUntil(long position) throws IOException {
130                    if (position < _length) {
131                            return position;
132                    }
133    
134                    if (_foundEOF) {
135                            return _length;
136                    }
137    
138                    long lengthToRead = position - _length;
139    
140                    _randomAccessFileCache.seek(_length);
141    
142                    byte[] buffer = new byte[1024];
143    
144                    while (lengthToRead > 0) {
145                            int bytesRead = _inputStream.read(
146                                    buffer, 0, (int)Math.min(lengthToRead, buffer.length));
147    
148                            if (bytesRead == -1) {
149                                    _foundEOF = true;
150    
151                                    return _length;
152                            }
153    
154                            _randomAccessFileCache.setLength(
155                                    _randomAccessFileCache.length() + bytesRead);
156    
157                            _randomAccessFileCache.write(buffer, 0, bytesRead);
158    
159                            lengthToRead -= bytesRead;
160    
161                            _length += bytesRead;
162                    }
163    
164                    return position;
165            }
166    
167            private File _file;
168            private boolean _foundEOF;
169            private InputStream _inputStream;
170            private long _length;
171            private long _markPosition = -1;
172            private long _pointer;
173            private RandomAccessFile _randomAccessFileCache;
174    
175    }