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.io;
016    
017    import java.io.IOException;
018    import java.io.InputStream;
019    
020    /**
021     * Skip the specified offset until it fails three times. This is used to prevent
022     * reading an InputStream infinitely.
023     *
024     * @author Shuyang Zhou
025     */
026    public class LimitedInputStream extends InputStream {
027    
028            public LimitedInputStream(InputStream inputStream, long offset, long length)
029                    throws IOException {
030    
031                    if (offset < 0) {
032                            throw new IllegalArgumentException("Offset is less than 0");
033                    }
034    
035                    if (length < 0) {
036                            throw new IllegalArgumentException("Length is less than 0");
037                    }
038    
039                    _inputStream = inputStream;
040                    _length = length;
041    
042                    int count = 0;
043    
044                    while (offset > 0) {
045                            long skip = _inputStream.skip(offset);
046    
047                            if (skip == 0) {
048                                    if (++count >= _SKIP_RETRY_COUNT) {
049                                            throw new IOException(
050                                                    "Most likely reached the end of the input stream");
051                                    }
052                            }
053                            else {
054                                    count = 0;
055                            }
056    
057                            offset = offset - skip;
058                    }
059            }
060    
061            @Override
062            public int available() throws IOException {
063                    int available = _inputStream.available();
064    
065                    int allowed = (int)(_length - _read);
066    
067                    if (available > allowed) {
068                            return allowed;
069                    }
070                    else {
071                            return available;
072                    }
073            }
074    
075            @Override
076            public void close() throws IOException {
077                    _inputStream.close();
078            }
079    
080            @Override
081            public void mark(int readLimit) {
082            }
083    
084            @Override
085            public boolean markSupported() {
086                    return false;
087            }
088    
089            @Override
090            public int read() throws IOException {
091                    if (_read >= _length) {
092                            return -1;
093                    }
094    
095                    int read = _inputStream.read();
096    
097                    if (read >= 0) {
098                            _read++;
099                    }
100    
101                    return read;
102            }
103    
104            @Override
105            public int read(byte[] bytes) throws IOException {
106                    if (_read >= _length) {
107                            return -1;
108                    }
109    
110                    int read = 0;
111    
112                    if ((_read + bytes.length) > _length) {
113                            read = _inputStream.read(bytes, 0, (int)(_length - _read));
114                    }
115                    else {
116                            read = _inputStream.read(bytes);
117                    }
118    
119                    _read += read;
120    
121                    return read;
122            }
123    
124            @Override
125            public int read(byte[] bytes, int offset, int length) throws IOException {
126                    if (_read >= _length) {
127                            return -1;
128                    }
129    
130                    int read = 0;
131    
132                    if ((_read + length) > _length) {
133                            read = _inputStream.read(bytes, 0, (int)(_length - _read));
134                    }
135                    else {
136                            read = _inputStream.read(bytes, offset, length);
137                    }
138    
139                    _read += read;
140    
141                    return read;
142            }
143    
144            @Override
145            public void reset() {
146            }
147    
148            @Override
149            public long skip(long skip) throws IOException {
150                    long allowed = _length - _read;
151    
152                    if (allowed < skip) {
153                            skip = allowed;
154                    }
155    
156                    skip = _inputStream.skip(skip);
157    
158                    _read += skip;
159    
160                    return skip;
161            }
162    
163            private static final int _SKIP_RETRY_COUNT = 3;
164    
165            private InputStream _inputStream;
166            private long _length;
167            private long _read;
168    
169    }