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
.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
;
30 * Common implementation of file-based checkpoint collection
32 * @author Marc-Andre Laperle
34 public abstract class AbstractFileCheckpointCollection
implements ICheckpointCollection
{
36 private static final int INVALID_VERSION
= -1;
37 private static final int VERSION
= 3;
38 private static final int SUB_VERSION_NONE
= -1;
41 * The base file header, can be extended
43 protected class CheckpointCollectionFileHeader
{
44 private static final int SIZE
= INT_SIZE
+
48 MAX_TIME_RANGE_SERIALIZE_SIZE
;
51 * Get the size of the header in bytes. This should be overridden if the
52 * header is augmented with more data
54 * @return the size of the header in bytes
56 public int getSize() {
61 * Get the sub version of this header
63 * @return the sub version
65 public int getSubVersion() {
66 return SUB_VERSION_NONE
;
70 * Constructs a new file header for an existing file
72 * @param randomAccessFile
75 * if an I/O error occurs reading from the file
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
);
85 fTimeRange
= new TmfTimeRange(new TmfTimestamp(b
), new TmfTimestamp(b
));
89 * Constructs a new file header for the given version
94 public CheckpointCollectionFileHeader(int version
) {
99 * Serialize the header to a file
101 * @param randomAccessFile
103 * @throws IOException
104 * if an I/O error occurs writing to the file
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
);
113 ByteBuffer b
= ByteBuffer
.allocate(MAX_TIME_RANGE_SERIALIZE_SIZE
);
115 new TmfTimestamp(fTimeRange
.getStartTime()).serialize(b
);
116 new TmfTimestamp(fTimeRange
.getEndTime()).serialize(b
);
118 fFileChannel
.write(b
);
122 * The version of the collection. Should be incremented if a binary
123 * incompatible change occurs.
125 protected final int fVersion
;
127 * The size of the collection expressed in a number of checkpoints.
129 protected int fSize
= 0;
131 * The total number of events in the trace
133 protected long fNbEvents
;
136 * The time range of the trace.
138 protected TmfTimeRange fTimeRange
= new TmfTimeRange(TmfTimestamp
.ZERO
, TmfTimestamp
.ZERO
);
142 * The size of an int in bytes
144 protected static final int INT_SIZE
= 4;
146 * The size of a long in bytes
148 protected static final int LONG_SIZE
= 8;
151 * The maximum size of the serialize buffer when writing the time range
153 protected static final int MAX_TIME_RANGE_SERIALIZE_SIZE
= 128;
156 * The originating trace
158 private ITmfPersistentlyIndexable fTrace
;
160 private long fCacheMisses
= 0;
161 private boolean fCreatedFromScratch
;
164 * File handle for the file being read/written
166 private RandomAccessFile fRandomAccessFile
;
168 * File handle for the file being read/written
173 * The base file header
175 private final CheckpointCollectionFileHeader fHeader
;
178 private FileChannel fFileChannel
;
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}.
186 * the file to use as the persistent storage
190 public AbstractFileCheckpointCollection(File file
, ITmfPersistentlyIndexable trace
) {
193 setCreatedFromScratch(!fFile
.exists());
195 CheckpointCollectionFileHeader header
= null;
197 if (!isCreatedFromScratch()) {
198 header
= tryRestore();
199 if (header
== null) {
204 if (isCreatedFromScratch()) {
205 header
= initialize();
212 * Creates a new basic file header with the version field initialized. This
213 * should be overridden if the file header is extended
215 * @return the created file header
217 protected CheckpointCollectionFileHeader
createHeader() {
218 return new CheckpointCollectionFileHeader(VERSION
);
222 * Creates a new basic file header for an existing file. This should be
223 * overridden if the file header is extended
225 * @param randomAccessFile
227 * @return the created file header
228 * @throws IOException
229 * if an I/O error occurs reading from the file
231 protected CheckpointCollectionFileHeader
createHeader(RandomAccessFile randomAccessFile
) throws IOException
{
232 return new CheckpointCollectionFileHeader(randomAccessFile
);
236 * Get the version of the collection.
238 * @return the version of the collection.
240 protected int getVersion() {
245 * Get the sub version of the collection.
247 * @return the sub version of the collection.
249 protected int getSubVersion() {
250 return SUB_VERSION_NONE
;
253 private CheckpointCollectionFileHeader
initialize() {
254 CheckpointCollectionFileHeader header
= null;
256 fRandomAccessFile
= new RandomAccessFile(fFile
, "rw"); //$NON-NLS-1$
257 fFileChannel
= fRandomAccessFile
.getChannel();
258 header
= createHeader();
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
);
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.
275 * @return the loaded header or null if it could not be loaded.
277 private CheckpointCollectionFileHeader
tryRestore() {
278 CheckpointCollectionFileHeader header
= null;
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
);
289 header
= createHeader(fRandomAccessFile
);
290 if (header
.fVersion
!= VERSION
|| header
.getSubVersion() != getSubVersion()) {
293 TmfCoreTracer
.traceIndexer(CheckpointCollectionFileHeader
.class.getSimpleName() + " read " + fFile
+ " nbEvents: " + header
.fNbEvents
+ " fTimeRange: " + header
.fTimeRange
); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
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
);
309 * @return true if the checkpoint collection was created from scratch, false
313 public boolean isCreatedFromScratch() {
314 return fCreatedFromScratch
;
318 * Set whether or not the collection is created from scratch
320 * @param isCreatedFromScratch
321 * whether or not the collection is created from scratch
323 protected void setCreatedFromScratch(boolean isCreatedFromScratch
) {
324 fCreatedFromScratch
= isCreatedFromScratch
;
328 * @return the number of cache misses.
330 public long getCacheMisses() {
335 * Increment the number of cache misses.
337 protected void incCacheMisses() {
342 * Returns the size of the checkpoint collection expressed as a number of
345 * @return the size of the checkpoint collection
349 return fHeader
.fSize
;
353 * Set the trace time range
356 * the trace time range
359 public void setTimeRange(TmfTimeRange timeRange
) {
360 fHeader
.fTimeRange
= timeRange
;
364 * Get the trace time range
366 * @return the trace time range
369 public TmfTimeRange
getTimeRange() {
370 return fHeader
.fTimeRange
;
374 * Set the number of events in the trace
377 * the number of events in the trace
380 public void setNbEvents(long nbEvents
) {
381 fHeader
.fNbEvents
= nbEvents
;
385 * Get the number of events in the trace
387 * @return the number of events in the trace
390 public long getNbEvents() {
391 return fHeader
.fNbEvents
;
399 protected ITmfPersistentlyIndexable
getTrace() {
404 * Get the random access file currently opened
408 protected RandomAccessFile
getRandomAccessFile() {
409 return fRandomAccessFile
;
413 * Get the file channel currently used for the index
415 * @return the file channel
417 protected FileChannel
getFileChannel() {
418 return fRandomAccessFile
.getChannel();
422 * Get the file handle for the index
426 protected File
getFile() {
431 * Get the header for this collection
435 public CheckpointCollectionFileHeader
getHeader() {
440 * Dispose and delete the checkpoint collection
443 public void delete() {
445 if (fFile
.exists()) {
451 * Dispose the collection and its resources
454 public void dispose() {
458 private void dispose(boolean deleting
) {
460 if (fRandomAccessFile
!= null) {
462 if (fHeader
!= null) {
463 fHeader
.serialize(fRandomAccessFile
);
466 fRandomAccessFile
.seek(0);
467 fRandomAccessFile
.writeInt(getVersion());
470 fRandomAccessFile
.close();
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
);