1 package org.apache.commons.jcs3.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.IOException;
24 import java.nio.ByteBuffer;
25 import java.nio.channels.FileChannel;
26 import java.nio.file.StandardOpenOption;
27
28 import org.apache.commons.jcs3.engine.behavior.IElementSerializer;
29 import org.apache.commons.jcs3.log.Log;
30 import org.apache.commons.jcs3.log.LogManager;
31
32
33 public class IndexedDisk implements AutoCloseable
34 {
35
36 public static final byte HEADER_SIZE_BYTES = 4;
37
38
39 private final IElementSerializer elementSerializer;
40
41
42 private static final Log log = LogManager.getLog(IndexedDisk.class);
43
44
45 private final String filepath;
46
47
48 private final FileChannel fc;
49
50
51
52
53
54
55
56
57 public IndexedDisk(final File file, final IElementSerializer elementSerializer)
58 throws IOException
59 {
60 this.filepath = file.getAbsolutePath();
61 this.elementSerializer = elementSerializer;
62 this.fc = FileChannel.open(file.toPath(),
63 StandardOpenOption.CREATE,
64 StandardOpenOption.READ,
65 StandardOpenOption.WRITE);
66 }
67
68
69
70
71
72
73
74
75
76
77
78
79 protected <T> T readObject(final IndexedDiskElementDescriptor ded)
80 throws IOException, ClassNotFoundException
81 {
82 String message = null;
83 boolean corrupted = false;
84 final 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 final ByteBuffer datalength = ByteBuffer.allocate(HEADER_SIZE_BYTES);
93 fc.read(datalength, ded.pos);
94 datalength.flip();
95 final 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 {0}", message);
111 throw new IOException("The File Is Corrupt, need to reset");
112 }
113
114 final 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 final ByteBuffer datalength = ByteBuffer.allocate(HEADER_SIZE_BYTES);
132 fc.read(datalength, ded.pos);
133 datalength.flip();
134 final 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 final ByteBuffer buffer = ByteBuffer.allocate(16384);
149
150 while (remaining > 0)
151 {
152
153 final 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(final IndexedDiskElementDescriptor ded, final byte[] data)
177 throws IOException
178 {
179 final long pos = ded.pos;
180 if (log.isTraceEnabled())
181 {
182 log.trace("write> pos={0}", pos);
183 log.trace("{0} -- data.length = {1}", fc, data.length);
184 }
185
186 if (data.length != ded.len)
187 {
188 throw new IOException("Mismatched descriptor and data lengths");
189 }
190
191 final ByteBuffer headerBuffer = ByteBuffer.allocate(HEADER_SIZE_BYTES);
192 headerBuffer.putInt(data.length);
193
194 headerBuffer.flip();
195 int written = fc.write(headerBuffer, pos);
196 assert written == HEADER_SIZE_BYTES;
197
198
199 final ByteBuffer dataBuffer = ByteBuffer.wrap(data);
200 written = fc.write(dataBuffer, pos + HEADER_SIZE_BYTES);
201
202 return written == data.length;
203 }
204
205
206
207
208
209
210
211
212
213 protected <T> void writeObject(final T obj, final long pos)
214 throws IOException
215 {
216 final byte[] data = elementSerializer.serialize(obj);
217 write(new IndexedDiskElementDescriptor(pos, data.length), data);
218 }
219
220
221
222
223
224
225
226 protected long length()
227 throws IOException
228 {
229 return fc.size();
230 }
231
232
233
234
235
236
237 @Override
238 public void close()
239 throws IOException
240 {
241 fc.close();
242 }
243
244
245
246
247
248
249 protected synchronized void reset()
250 throws IOException
251 {
252 log.debug("Resetting Indexed File [{0}]", filepath);
253 fc.truncate(0);
254 fc.force(true);
255 }
256
257
258
259
260
261
262
263 protected void truncate(final long length)
264 throws IOException
265 {
266 log.info("Truncating file [{0}] to {1}", filepath, length);
267 fc.truncate(length);
268 }
269
270
271
272
273
274
275 protected String getFilePath()
276 {
277 return filepath;
278 }
279
280
281
282
283
284
285
286 protected boolean isEmpty() throws IOException
287 {
288 return length() == 0;
289 }
290 }