Source code: juju/reattore/io/impl/ChannelFileSource.java
1 /* Reattore HTTP Server
2
3 Copyright (C) 2002 Michael Hope <michaelh@juju.net.nz>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19 $Id: ChannelFileSource.java,v 1.13 2003/02/22 04:29:52 michaelh Exp $
20 */
21
22 package juju.reattore.io.impl;
23
24 import java.io.*;
25 import java.nio.ByteBuffer;
26 import java.nio.channels.*;
27
28 import juju.reattore.io.*;
29
30 /** A byte source that serves from a file using channels. Implemented
31 by reading the whole file into a byte buffer.
32
33 @todo Minor: Cannot handle > 4GB files
34 @todo Doesn't implement get() for > readSize files
35 */
36 public class ChannelFileSource
37 implements FileSource, CachableSource, BulkByteSource {
38
39 private Cache cache;
40
41 private File file;
42 private FileChannel fch;
43 private int size;
44 private int readSize = 64 * 1024;
45
46 private ByteBuffer bb;
47 private boolean oversized;
48 private boolean firstRead = true;
49
50 private static final int CHECK_INTERVAL = 5;
51
52 private long stamp;
53 private int checkIn = CHECK_INTERVAL;
54
55 /** Create a new source around the given file.
56
57 @param file The file to wrap around.
58 @throws IOException if the file cannot be opened.
59 */
60 public ChannelFileSource(File file)
61 throws IOException {
62
63 this.file = file;
64
65 stamp = file.lastModified();
66
67 FileInputStream fin = new FileInputStream(file);
68 fch = fin.getChannel();
69
70 size = (int)fch.size();
71 }
72
73 private void setup() {
74 if (bb == null) {
75 oversized = size > readSize;
76 bb = ByteBufferPool.allocate(oversized ? readSize : size);
77 }
78 }
79
80 private void read()
81 throws IOException {
82
83 setup();
84
85 if (firstRead || oversized) {
86 bb.clear();
87 fch.read(bb);
88
89 bb.flip();
90 }
91
92 firstRead = false;
93 }
94
95 /** Test method that changes the maximum size a file may be before
96 it is chunked into parts.
97
98 @param to The maximum size in bytes.
99 */
100 public void setReadSize(int to) {
101 this.readSize = to;
102 }
103
104 /** @see BulkByteSource */
105 public ByteBuffer getBulk()
106 throws IOException {
107
108 read();
109 return bb;
110 }
111
112 /** @see ByteSource */
113 public int get()
114 throws IOException {
115
116 read();
117 assert oversized == false;
118
119 return bb.get();
120 }
121
122 /** @see ByteSource */
123 public int get(byte[] into, int offset, int length)
124 throws IOException {
125
126 read();
127 assert oversized == false;
128
129 if (length > bb.remaining()) {
130 length = bb.remaining();
131 }
132
133 bb.get(into, offset, length);
134
135 return length;
136 }
137
138 /** @see ByteSource */
139 public int remaining()
140 throws IOException {
141
142 setup();
143
144 if (oversized) {
145 return (int)(size - fch.position());
146 }
147 else {
148 read();
149 return bb.remaining();
150 }
151 }
152
153 /** @see CachableSource */
154 public void release() {
155 if (fch != null) {
156 try {
157 fch.close();
158 }
159 catch (IOException ex) {
160 /* Drop */
161 }
162 finally {
163 fch = null;
164 }
165 }
166 if (bb != null) {
167 ByteBufferPool.release(bb);
168 bb = null;
169 }
170 }
171
172 /** @see Source */
173 public void dispose() {
174 if (cache != null && cache.onClose(this) == false) {
175 /* The cache took us back */
176 }
177 else {
178 release();
179 }
180 }
181
182 /** @see CachableSource */
183 public void rewind() {
184 setup();
185
186 if (oversized) {
187 }
188 else {
189 bb.rewind();
190 }
191 }
192
193 /** @see CachableSource
194 */
195 public boolean isExpired() {
196 if (oversized) {
197 return true;
198 }
199 else if ((--checkIn) == 0) {
200 checkIn = CHECK_INTERVAL;
201 return file.lastModified() != stamp;
202 }
203 else {
204 return false;
205 }
206 }
207
208 /** @see CachableSource */
209 public void setCacheCallback(Cache ch) {
210 this.cache = ch;
211 }
212 }