1 package org.apache.commons.jcs.auxiliary.disk.indexed;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.File;
23 import java.io.FileNotFoundException;
24 import java.io.IOException;
25 import java.io.RandomAccessFile;
26 import java.io.Serializable;
27 import java.nio.ByteBuffer;
28 import java.nio.channels.FileChannel;
29
30 import org.apache.commons.jcs.engine.behavior.IElementSerializer;
31 import org.apache.commons.logging.Log;
32 import org.apache.commons.logging.LogFactory;
33
34
35 class IndexedDisk
36 {
37
38 public static final byte HEADER_SIZE_BYTES = 4;
39
40
41 private final IElementSerializer elementSerializer;
42
43
44 private static final Log log = LogFactory.getLog( IndexedDisk.class );
45
46
47 private final String filepath;
48
49
50 private final FileChannel fc;
51
52
53
54
55
56
57
58
59 public IndexedDisk( File file, IElementSerializer elementSerializer )
60 throws FileNotFoundException
61 {
62 this.filepath = file.getAbsolutePath();
63 this.elementSerializer = elementSerializer;
64 RandomAccessFile raf = new RandomAccessFile( filepath, "rw" );
65 this.fc = raf.getChannel();
66 }
67
68
69
70
71
72
73
74
75
76
77
78
79 protected <T extends Serializable> T readObject( IndexedDiskElementDescriptor ded )
80 throws IOException, ClassNotFoundException
81 {
82 String message = null;
83 boolean corrupted = false;
84 long fileLength = fc.size();
85 if ( ded.pos > fileLength )
86 {
87 corrupted = true;
88 message = "Record " + ded + " starts past EOF.";
89 }
90 else
91 {
92 ByteBuffer datalength = ByteBuffer.allocate(HEADER_SIZE_BYTES);
93 fc.read(datalength, ded.pos);
94 datalength.flip();
95 int datalen = datalength.getInt();
96 if ( ded.len != datalen )
97 {
98 corrupted = true;
99 message = "Record " + ded + " does not match data length on disk (" + datalen + ")";
100 }
101 else if ( ded.pos + ded.len > fileLength )
102 {
103 corrupted = true;
104 message = "Record " + ded + " exceeds file length.";
105 }
106 }
107
108 if ( corrupted )
109 {
110 log.warn( "\n The file is corrupt: " + "\n " + message );
111 throw new IOException( "The File Is Corrupt, need to reset" );
112 }
113
114 ByteBuffer data = ByteBuffer.allocate(ded.len);
115 fc.read(data, ded.pos + HEADER_SIZE_BYTES);
116 data.flip();
117
118 return elementSerializer.deSerialize( data.array(), null );
119 }
120
121
122
123
124
125
126
127
128 protected void move( final IndexedDiskElementDescriptor ded, final long newPosition )
129 throws IOException
130 {
131 ByteBuffer datalength = ByteBuffer.allocate(HEADER_SIZE_BYTES);
132 fc.read(datalength, ded.pos);
133 datalength.flip();
134 int length = datalength.getInt();
135
136 if ( length != ded.len )
137 {
138 throw new IOException( "Mismatched memory and disk length (" + length + ") for " + ded );
139 }
140
141
142
143 long readPos = ded.pos;
144 long writePos = newPosition;
145
146
147 int remaining = HEADER_SIZE_BYTES + length;
148 ByteBuffer buffer = ByteBuffer.allocate(16384);
149
150 while ( remaining > 0 )
151 {
152
153 int chunkSize = Math.min( remaining, buffer.capacity() );
154 buffer.limit(chunkSize);
155 fc.read(buffer, readPos);
156 buffer.flip();
157 fc.write(buffer, writePos);
158 buffer.clear();
159
160 writePos += chunkSize;
161 readPos += chunkSize;
162 remaining -= chunkSize;
163 }
164
165 ded.pos = newPosition;
166 }
167
168
169
170
171
172
173
174
175
176 protected boolean write( IndexedDiskElementDescriptor ded, byte[] data )
177 throws IOException
178 {
179 long pos = ded.pos;
180 if ( log.isTraceEnabled() )
181 {
182 log.trace( "write> pos=" + pos );
183 log.trace( fc + " -- data.length = " + data.length );
184 }
185
186 if ( data.length != ded.len )
187 {
188 throw new IOException( "Mismatched descriptor and data lengths" );
189 }
190
191 ByteBuffer buffer = ByteBuffer.allocate(HEADER_SIZE_BYTES + data.length);
192 buffer.putInt(data.length);
193 buffer.put(data);
194 buffer.flip();
195 int written = fc.write(buffer, pos);
196
197
198 return written == data.length;
199 }
200
201
202
203
204
205
206
207
208
209
210 protected boolean writeObject( Serializable obj, long pos )
211 throws IOException
212 {
213 byte[] data = elementSerializer.serialize( obj );
214 write( new IndexedDiskElementDescriptor( pos, data.length ), data );
215 return true;
216 }
217
218
219
220
221
222
223
224 protected long length()
225 throws IOException
226 {
227 return fc.size();
228 }
229
230
231
232
233
234
235 protected void close()
236 throws IOException
237 {
238 fc.close();
239 }
240
241
242
243
244
245
246 protected synchronized void reset()
247 throws IOException
248 {
249 if ( log.isDebugEnabled() )
250 {
251 log.debug( "Resetting Indexed File [" + filepath + "]" );
252 }
253 fc.truncate(0);
254 fc.force(true);
255 }
256
257
258
259
260
261
262
263 protected void truncate( long length )
264 throws IOException
265 {
266 if ( log.isInfoEnabled() )
267 {
268 log.info( "Truncating file [" + filepath + "] to " + length );
269 }
270 fc.truncate( length );
271 }
272
273
274
275
276
277
278 protected String getFilePath()
279 {
280 return filepath;
281 }
282 }