1 /*******************************************************************************
2 * Copyright (c) 2013, 2014 Ericsson
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
10 * Marc-Andre Laperle - Initial API and implementation
11 *******************************************************************************/
13 package org
.eclipse
.tracecompass
.internal
.tmf
.core
.trace
.indexer
;
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
;
23 import org
.eclipse
.tracecompass
.internal
.tmf
.core
.Activator
;
24 import org
.eclipse
.tracecompass
.tmf
.core
.timestamp
.TmfTimeRange
;
25 import org
.eclipse
.tracecompass
.tmf
.core
.timestamp
.TmfTimestamp
;
26 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.indexer
.ITmfPersistentlyIndexable
;
29 * Common implementation of file-based checkpoint collection
31 * @author Marc-Andre Laperle
33 public abstract class AbstractFileCheckpointCollection
implements ICheckpointCollection
{
35 private static final int VERSION
= 2;
36 private static final int SUB_VERSION_NONE
= -1;
39 * The base file header, can be extended
41 protected class CheckpointCollectionFileHeader
{
42 private final static int SIZE
= INT_SIZE
+
48 * Get the size of the header in bytes. This should be overridden if the
49 * header is augmented with more data
51 * @return the size of the header in bytes
53 public int getSize() {
58 * Get the sub version of this header
60 * @return the sub version
62 public int getSubVersion() {
63 return SUB_VERSION_NONE
;
67 * Constructs a new file header for an existing file
69 * @param randomAccessFile
72 * if an I/O error occurs reading from the file
74 public CheckpointCollectionFileHeader(RandomAccessFile randomAccessFile
) throws IOException
{
75 fVersion
= randomAccessFile
.readInt();
76 fSize
= randomAccessFile
.readInt();
77 fNbEvents
= randomAccessFile
.readLong();
78 fTimeRangeOffset
= randomAccessFile
.readLong();
82 * Constructs a new file header for the given version
87 public CheckpointCollectionFileHeader(int version
) {
92 * Serialize the header to a file
94 * @param randomAccessFile
97 * if an I/O error occurs writing to the file
99 public void serialize(RandomAccessFile randomAccessFile
) throws IOException
{
100 randomAccessFile
.seek(0);
101 randomAccessFile
.writeInt(getVersion());
102 randomAccessFile
.writeInt(fSize
);
103 randomAccessFile
.writeLong(fNbEvents
);
104 randomAccessFile
.writeLong(fTimeRangeOffset
);
108 * The version of the collection. Should be incremented if a binary
109 * incompatible change occurs.
111 protected final int fVersion
;
113 * The size of the collection expressed in a number of checkpoints.
115 protected int fSize
= 0;
117 * Offset in bytes where the time range is store
119 protected long fTimeRangeOffset
;
121 * The total number of events in the trace
123 protected long fNbEvents
;
127 * The size of an int in bytes
129 protected static final int INT_SIZE
= 4;
131 * The size of a long in bytes
133 protected static final int LONG_SIZE
= 8;
136 * The maximum size of the serialize buffer when writing the time range
138 protected static final int MAX_TIME_RANGE_SERIALIZE_SIZE
= 1024;
141 * The originating trace
143 private ITmfPersistentlyIndexable fTrace
;
145 private long fCacheMisses
= 0;
146 private boolean fCreatedFromScratch
;
149 * File handle for the file being read/written
151 private RandomAccessFile fRandomAccessFile
;
153 * File handle for the file being read/written
158 * The base file header
160 private final CheckpointCollectionFileHeader fHeader
;
163 private FileChannel fFileChannel
;
164 private TmfTimeRange fTimeRange
;
167 * Constructs a checkpoint collection for a given trace from scratch or from
168 * an existing file. When the checkpoint collection is created from scratch,
169 * it is populated by subsequent calls to {@link #insert}.
172 * the file to use as the persistent storage
176 public AbstractFileCheckpointCollection(File file
, ITmfPersistentlyIndexable trace
) {
179 setCreatedFromScratch(!fFile
.exists());
181 CheckpointCollectionFileHeader header
= null;
183 if (!isCreatedFromScratch()) {
184 header
= tryRestore();
185 if (header
== null) {
191 if (isCreatedFromScratch()) {
192 header
= initialize();
199 * Creates a new basic file header with the version field initialized. This
200 * should be overridden if the file header is extended
202 * @return the created file header
204 protected CheckpointCollectionFileHeader
createHeader() {
205 return new CheckpointCollectionFileHeader(VERSION
);
209 * Creates a new basic file header for an existing file. This should be
210 * overridden if the file header is extended
212 * @param randomAccessFile
214 * @return the created file header
215 * @throws IOException
216 * if an I/O error occurs reading from the file
218 protected CheckpointCollectionFileHeader
createHeader(RandomAccessFile randomAccessFile
) throws IOException
{
219 return new CheckpointCollectionFileHeader(randomAccessFile
);
223 * Get the version of the collection.
225 * @return the version of the collection.
227 protected int getVersion() {
232 * Get the sub version of the collection.
234 * @return the sub version of the collection.
236 protected int getSubVersion() {
237 return SUB_VERSION_NONE
;
240 private CheckpointCollectionFileHeader
initialize() {
241 CheckpointCollectionFileHeader header
= null;
243 fRandomAccessFile
= new RandomAccessFile(fFile
, "rw"); //$NON-NLS-1$
244 fFileChannel
= fRandomAccessFile
.getChannel();
245 header
= createHeader();
247 // Reserve space for header
248 fRandomAccessFile
.setLength(header
.getSize());
250 fTimeRange
= new TmfTimeRange(new TmfTimestamp(0), new TmfTimestamp(0));
251 } catch (IOException e
) {
252 Activator
.logError(MessageFormat
.format(Messages
.ErrorOpeningIndex
, fFile
), e
);
260 * Try to restore the index from disk. Try to open the file and check the
261 * version. Returns the loaded header or null if it could not be loaded.
263 * @return the loaded header or null if it could not be loaded.
265 private CheckpointCollectionFileHeader
tryRestore() {
266 CheckpointCollectionFileHeader header
= null;
269 fRandomAccessFile
= new RandomAccessFile(fFile
, "r"); //$NON-NLS-1$
270 fFileChannel
= fRandomAccessFile
.getChannel();
271 } catch (FileNotFoundException e
) {
272 Activator
.logError(MessageFormat
.format(Messages
.ErrorOpeningIndex
, fFile
), e
);
277 header
= createHeader(fRandomAccessFile
);
278 if (header
.fVersion
!= VERSION
|| header
.getSubVersion() != getSubVersion()) {
281 serializeInTimeRange(header
);
282 } catch (IOException e
) {
283 Activator
.logError(MessageFormat
.format(Messages
.IOErrorReadingHeader
, fFile
), e
);
290 private void serializeInTimeRange(CheckpointCollectionFileHeader header
) throws IOException
{
291 ByteBuffer b
= ByteBuffer
.allocate(MAX_TIME_RANGE_SERIALIZE_SIZE
);
293 fFileChannel
.read(b
, header
.fTimeRangeOffset
);
295 fTimeRange
= new TmfTimeRange(new TmfTimestamp(b
), new TmfTimestamp(b
));
298 private void serializeOutTimeRange() throws IOException
{
299 fHeader
.fTimeRangeOffset
= fRandomAccessFile
.length();
300 ByteBuffer b
= ByteBuffer
.allocate(MAX_TIME_RANGE_SERIALIZE_SIZE
);
302 new TmfTimestamp(fTimeRange
.getStartTime()).serialize(b
);
303 new TmfTimestamp(fTimeRange
.getEndTime()).serialize(b
);
305 fFileChannel
.write(b
, fHeader
.fTimeRangeOffset
);
309 * Set the index as complete. No more checkpoints will be inserted.
312 public void setIndexComplete() {
314 serializeOutTimeRange();
316 fHeader
.serialize(fRandomAccessFile
);
317 } catch (IOException e
) {
318 Activator
.logError(MessageFormat
.format(Messages
.IOErrorWritingHeader
, fFile
), e
);
324 * @return true if the checkpoint collection was created from scratch, false
328 public boolean isCreatedFromScratch() {
329 return fCreatedFromScratch
;
333 * Set whether or not the collection is created from scratch
335 * @param isCreatedFromScratch
336 * whether or not the collection is created from scratch
338 protected void setCreatedFromScratch(boolean isCreatedFromScratch
) {
339 fCreatedFromScratch
= isCreatedFromScratch
;
343 * @return the number of cache misses.
345 public long getCacheMisses() {
350 * Increment the number of cache misses.
352 protected void incCacheMisses() {
357 * Returns the size of the checkpoint collection expressed as a number of
360 * @return the size of the checkpoint collection
364 return fHeader
.fSize
;
368 * Set the trace time range
371 * the trace time range
374 public void setTimeRange(TmfTimeRange timeRange
) {
375 fTimeRange
= timeRange
;
379 * Get the trace time range
381 * @return the trace time range
384 public TmfTimeRange
getTimeRange() {
389 * Set the number of events in the trace
392 * the number of events in the trace
395 public void setNbEvents(long nbEvents
) {
396 fHeader
.fNbEvents
= nbEvents
;
400 * Get the number of events in the trace
402 * @return the number of events in the trace
405 public long getNbEvents() {
406 return fHeader
.fNbEvents
;
414 protected ITmfPersistentlyIndexable
getTrace() {
419 * Get the random access file currently opened
423 protected RandomAccessFile
getRandomAccessFile() {
424 return fRandomAccessFile
;
428 * Get the file channel currently used for the index
430 * @return the file channel
432 protected FileChannel
getFileChannel() {
433 return fRandomAccessFile
.getChannel();
437 * Get the file handle for the index
441 protected File
getFile() {
446 * Get the header for this collection
450 public CheckpointCollectionFileHeader
getHeader() {
453 * Dispose and delete the checkpoint collection
456 public void delete() {
458 if (fFile
.exists()) {
464 * Dispose the collection and its resources
467 public void dispose() {
469 if (fRandomAccessFile
!= null) {
470 fRandomAccessFile
.close();
472 setCreatedFromScratch(true);
473 fRandomAccessFile
= null;
474 } catch (IOException e
) {
475 Activator
.logError(MessageFormat
.format(Messages
.IOErrorClosingIndex
, fFile
), e
);