1f4878df180c8dc5f046d14d566a96b2f72720f0
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.core / src / org / eclipse / tracecompass / internal / tmf / core / trace / indexer / AbstractFileCheckpointCollection.java
1 /*******************************************************************************
2 * Copyright (c) 2013, 2014 Ericsson
3 *
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *
9 * Contributors:
10 * Marc-Andre Laperle - Initial API and implementation
11 *******************************************************************************/
12
13 package org.eclipse.tracecompass.internal.tmf.core.trace.indexer;
14
15 import java.io.File;
16 import java.io.FileNotFoundException;
17 import java.io.IOException;
18 import java.io.RandomAccessFile;
19 import java.nio.ByteBuffer;
20 import java.nio.channels.FileChannel;
21 import java.text.MessageFormat;
22
23 import org.eclipse.tracecompass.internal.tmf.core.Activator;
24 import org.eclipse.tracecompass.internal.tmf.core.TmfCoreTracer;
25 import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
26 import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
27 import org.eclipse.tracecompass.tmf.core.trace.indexer.ITmfPersistentlyIndexable;
28
29 /**
30 * Common implementation of file-based checkpoint collection
31 *
32 * @author Marc-Andre Laperle
33 */
34 public abstract class AbstractFileCheckpointCollection implements ICheckpointCollection {
35
36 private static final int INVALID_VERSION = -1;
37 private static final int VERSION = 3;
38 private static final int SUB_VERSION_NONE = -1;
39
40 /**
41 * The base file header, can be extended
42 */
43 protected class CheckpointCollectionFileHeader {
44 private static final int SIZE = INT_SIZE +
45 INT_SIZE +
46 LONG_SIZE +
47 LONG_SIZE +
48 MAX_TIME_RANGE_SERIALIZE_SIZE;
49
50 /**
51 * Get the size of the header in bytes. This should be overridden if the
52 * header is augmented with more data
53 *
54 * @return the size of the header in bytes
55 */
56 public int getSize() {
57 return SIZE;
58 }
59
60 /**
61 * Get the sub version of this header
62 *
63 * @return the sub version
64 */
65 public int getSubVersion() {
66 return SUB_VERSION_NONE;
67 }
68
69 /**
70 * Constructs a new file header for an existing file
71 *
72 * @param randomAccessFile
73 * the existing file
74 * @throws IOException
75 * if an I/O error occurs reading from the file
76 */
77 public CheckpointCollectionFileHeader(RandomAccessFile randomAccessFile) throws IOException {
78 fVersion = randomAccessFile.readInt();
79 fSize = randomAccessFile.readInt();
80 fNbEvents = randomAccessFile.readLong();
81 ByteBuffer b = ByteBuffer.allocate(MAX_TIME_RANGE_SERIALIZE_SIZE);
82 b.clear();
83 fFileChannel.read(b);
84 b.flip();
85 fTimeRange = new TmfTimeRange(new TmfTimestamp(b), new TmfTimestamp(b));
86 }
87
88 /**
89 * Constructs a new file header for the given version
90 *
91 * @param version
92 * the version
93 */
94 public CheckpointCollectionFileHeader(int version) {
95 fVersion = version;
96 }
97
98 /**
99 * Serialize the header to a file
100 *
101 * @param randomAccessFile
102 * the existing file
103 * @throws IOException
104 * if an I/O error occurs writing to the file
105 */
106 public void serialize(RandomAccessFile randomAccessFile) throws IOException {
107 randomAccessFile.seek(0);
108 // Version is written at the very last on dispose
109 randomAccessFile.writeInt(INVALID_VERSION);
110 randomAccessFile.writeInt(fSize);
111 randomAccessFile.writeLong(fNbEvents);
112
113 ByteBuffer b = ByteBuffer.allocate(MAX_TIME_RANGE_SERIALIZE_SIZE);
114 b.clear();
115 new TmfTimestamp(fTimeRange.getStartTime()).serialize(b);
116 new TmfTimestamp(fTimeRange.getEndTime()).serialize(b);
117 b.rewind();
118 fFileChannel.write(b);
119 }
120
121 /**
122 * The version of the collection. Should be incremented if a binary
123 * incompatible change occurs.
124 */
125 protected final int fVersion;
126 /**
127 * The size of the collection expressed in a number of checkpoints.
128 */
129 protected int fSize = 0;
130 /**
131 * The total number of events in the trace
132 */
133 protected long fNbEvents;
134
135 /**
136 * The time range of the trace.
137 */
138 protected TmfTimeRange fTimeRange = new TmfTimeRange(TmfTimestamp.ZERO, TmfTimestamp.ZERO);
139 }
140
141 /**
142 * The size of an int in bytes
143 */
144 protected static final int INT_SIZE = 4;
145 /**
146 * The size of a long in bytes
147 */
148 protected static final int LONG_SIZE = 8;
149
150 /**
151 * The maximum size of the serialize buffer when writing the time range
152 */
153 protected static final int MAX_TIME_RANGE_SERIALIZE_SIZE = 128;
154
155 /**
156 * The originating trace
157 */
158 private ITmfPersistentlyIndexable fTrace;
159
160 private long fCacheMisses = 0;
161 private boolean fCreatedFromScratch;
162
163 /**
164 * File handle for the file being read/written
165 */
166 private RandomAccessFile fRandomAccessFile;
167 /**
168 * File handle for the file being read/written
169 */
170 private File fFile;
171
172 /**
173 * The base file header
174 */
175 private final CheckpointCollectionFileHeader fHeader;
176
177 // Cached values
178 private FileChannel fFileChannel;
179
180 /**
181 * Constructs a checkpoint collection for a given trace from scratch or from
182 * an existing file. When the checkpoint collection is created from scratch,
183 * it is populated by subsequent calls to {@link #insert}.
184 *
185 * @param file
186 * the file to use as the persistent storage
187 * @param trace
188 * the trace
189 */
190 public AbstractFileCheckpointCollection(File file, ITmfPersistentlyIndexable trace) {
191 fTrace = trace;
192 fFile = file;
193 setCreatedFromScratch(!fFile.exists());
194
195 CheckpointCollectionFileHeader header = null;
196
197 if (!isCreatedFromScratch()) {
198 header = tryRestore();
199 if (header == null) {
200 delete();
201 }
202 }
203
204 if (isCreatedFromScratch()) {
205 header = initialize();
206 }
207
208 fHeader = header;
209 }
210
211 /**
212 * Creates a new basic file header with the version field initialized. This
213 * should be overridden if the file header is extended
214 *
215 * @return the created file header
216 */
217 protected CheckpointCollectionFileHeader createHeader() {
218 return new CheckpointCollectionFileHeader(VERSION);
219 }
220
221 /**
222 * Creates a new basic file header for an existing file. This should be
223 * overridden if the file header is extended
224 *
225 * @param randomAccessFile
226 * the existing file
227 * @return the created file header
228 * @throws IOException
229 * if an I/O error occurs reading from the file
230 */
231 protected CheckpointCollectionFileHeader createHeader(RandomAccessFile randomAccessFile) throws IOException {
232 return new CheckpointCollectionFileHeader(randomAccessFile);
233 }
234
235 /**
236 * Get the version of the collection.
237 *
238 * @return the version of the collection.
239 */
240 protected int getVersion() {
241 return VERSION;
242 }
243
244 /**
245 * Get the sub version of the collection.
246 *
247 * @return the sub version of the collection.
248 */
249 protected int getSubVersion() {
250 return SUB_VERSION_NONE;
251 }
252
253 private CheckpointCollectionFileHeader initialize() {
254 CheckpointCollectionFileHeader header = null;
255 try {
256 fRandomAccessFile = new RandomAccessFile(fFile, "rw"); //$NON-NLS-1$
257 fFileChannel = fRandomAccessFile.getChannel();
258 header = createHeader();
259
260 // Reserve space for header
261 fRandomAccessFile.setLength(header.getSize());
262 TmfCoreTracer.traceIndexer(CheckpointCollectionFileHeader.class.getSimpleName() + " initialize " + "nbEvents: " + header.fNbEvents + " fTimeRange: " + header.fTimeRange); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
263 } catch (IOException e) {
264 Activator.logError(MessageFormat.format(Messages.ErrorOpeningIndex, fFile), e);
265 return null;
266 }
267
268 return header;
269 }
270
271 /**
272 * Try to restore the index from disk. Try to open the file and check the
273 * version. Returns the loaded header or null if it could not be loaded.
274 *
275 * @return the loaded header or null if it could not be loaded.
276 */
277 private CheckpointCollectionFileHeader tryRestore() {
278 CheckpointCollectionFileHeader header = null;
279
280 try {
281 fRandomAccessFile = new RandomAccessFile(fFile, "rw"); //$NON-NLS-1$
282 fFileChannel = fRandomAccessFile.getChannel();
283 } catch (FileNotFoundException e) {
284 Activator.logError(MessageFormat.format(Messages.ErrorOpeningIndex, fFile), e);
285 return null;
286 }
287
288 try {
289 header = createHeader(fRandomAccessFile);
290 if (header.fVersion != VERSION || header.getSubVersion() != getSubVersion()) {
291 return null;
292 }
293 TmfCoreTracer.traceIndexer(CheckpointCollectionFileHeader.class.getSimpleName() + " read " + fFile + " nbEvents: " + header.fNbEvents + " fTimeRange: " + header.fTimeRange); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
294
295 // Write an invalid version until the very last moment when dispose
296 // the index. This is how we know if it's corrupted or not.
297 fRandomAccessFile.seek(0);
298 fRandomAccessFile.writeInt(INVALID_VERSION);
299 } catch (IOException e) {
300 Activator.logError(MessageFormat.format(Messages.IOErrorReadingHeader, fFile), e);
301 return null;
302 }
303
304 return header;
305 }
306
307 /**
308 *
309 * @return true if the checkpoint collection was created from scratch, false
310 * otherwise
311 */
312 @Override
313 public boolean isCreatedFromScratch() {
314 return fCreatedFromScratch;
315 }
316
317 /**
318 * Set whether or not the collection is created from scratch
319 *
320 * @param isCreatedFromScratch
321 * whether or not the collection is created from scratch
322 */
323 protected void setCreatedFromScratch(boolean isCreatedFromScratch) {
324 fCreatedFromScratch = isCreatedFromScratch;
325 }
326
327 /**
328 * @return the number of cache misses.
329 */
330 public long getCacheMisses() {
331 return fCacheMisses;
332 }
333
334 /**
335 * Increment the number of cache misses.
336 */
337 protected void incCacheMisses() {
338 ++fCacheMisses;
339 }
340
341 /**
342 * Returns the size of the checkpoint collection expressed as a number of
343 * checkpoints.
344 *
345 * @return the size of the checkpoint collection
346 */
347 @Override
348 public int size() {
349 return fHeader.fSize;
350 }
351
352 /**
353 * Set the trace time range
354 *
355 * @param timeRange
356 * the trace time range
357 */
358 @Override
359 public void setTimeRange(TmfTimeRange timeRange) {
360 fHeader.fTimeRange = timeRange;
361 }
362
363 /**
364 * Get the trace time range
365 *
366 * @return the trace time range
367 */
368 @Override
369 public TmfTimeRange getTimeRange() {
370 return fHeader.fTimeRange;
371 }
372
373 /**
374 * Set the number of events in the trace
375 *
376 * @param nbEvents
377 * the number of events in the trace
378 */
379 @Override
380 public void setNbEvents(long nbEvents) {
381 fHeader.fNbEvents = nbEvents;
382 }
383
384 /**
385 * Get the number of events in the trace
386 *
387 * @return the number of events in the trace
388 */
389 @Override
390 public long getNbEvents() {
391 return fHeader.fNbEvents;
392 }
393
394 /**
395 * Get the trace
396 *
397 * @return the trace
398 */
399 protected ITmfPersistentlyIndexable getTrace() {
400 return fTrace;
401 }
402
403 /**
404 * Get the random access file currently opened
405 *
406 * @return the file
407 */
408 protected RandomAccessFile getRandomAccessFile() {
409 return fRandomAccessFile;
410 }
411
412 /**
413 * Get the file channel currently used for the index
414 *
415 * @return the file channel
416 */
417 protected FileChannel getFileChannel() {
418 return fRandomAccessFile.getChannel();
419 }
420
421 /**
422 * Get the file handle for the index
423 *
424 * @return the file
425 */
426 protected File getFile() {
427 return fFile;
428 }
429
430 /**
431 * Get the header for this collection
432 *
433 * @return the header
434 */
435 public CheckpointCollectionFileHeader getHeader() {
436 return fHeader;
437 }
438
439 /**
440 * Dispose and delete the checkpoint collection
441 */
442 @Override
443 public void delete() {
444 dispose(true);
445 if (fFile.exists()) {
446 fFile.delete();
447 }
448 }
449
450 /**
451 * Dispose the collection and its resources
452 */
453 @Override
454 public void dispose() {
455 dispose(false);
456 }
457
458 private void dispose(boolean deleting) {
459 try {
460 if (fRandomAccessFile != null) {
461 if (!deleting) {
462 if (fHeader != null) {
463 fHeader.serialize(fRandomAccessFile);
464 }
465
466 fRandomAccessFile.seek(0);
467 fRandomAccessFile.writeInt(getVersion());
468 }
469
470 fRandomAccessFile.close();
471 }
472 setCreatedFromScratch(true);
473 fRandomAccessFile = null;
474 String headerTrace = fHeader == null ? "No header" : "nbEvents: " + fHeader.fNbEvents + " timerange:" + fHeader.fTimeRange; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
475 TmfCoreTracer.traceIndexer(this.getClass().getSimpleName() + " disposed. " + headerTrace); //$NON-NLS-1$
476 } catch (IOException e) {
477 Activator.logError(MessageFormat.format(Messages.IOErrorClosingIndex, fFile), e);
478 }
479 }
480 }
This page took 0.04185 seconds and 4 git commands to generate.