blob: 78a3356798cdbc68ff223408b4c9284acb211163 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011 Nokia and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Nokia - Initial API and implementation. Aug 29, 2011
*******************************************************************************/
package org.eclipse.cdt.debug.edc.internal;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.cdt.debug.edc.internal.symbols.files.FileStatistics;
import org.eclipse.cdt.utils.ERandomAccessFile;
/**
* This class manages open, close and random access of a file. Currently only
* read operation is allowed.
*
* The most important trait of the class is the file will be auto closed after
* it's not accessed for a certain period. This is intended for cases like this:
* release an executable/symbol file after debug so that user can rebuild it.
*/
public class FileAccessor {
/**
* Global cache
*/
static private Map<File, FileAccessor> accessors = Collections.synchronizedMap(new HashMap<File, FileAccessor>());
/**
* Timeout on when to close the file (release the file handle).
*/
static final private long TIME_OUT = 3000; // ms
private final boolean DEBUG = false;
private RandomAccessFile raFile = null;
private File file = null;
private long fileAccessTime = 0;
private Boolean accessingFile = true;
/**
* Monitor thread: if a file has not been accessed for certain time, close it.
*/
class MonitorThread extends Thread {
public MonitorThread() {
super();
setDaemon(true);
setName("File Monitor");
}
@Override
public void run() {
while (true) {
synchronized (accessingFile) {
if (fileAccessTime != 0 &&
System.currentTimeMillis() > fileAccessTime + TIME_OUT)
{
close();
fileAccessTime = 0;
break; // exit the thread
}
}
try {
sleep(300);
} catch (InterruptedException e) {
break;
}
}
}
}
/**
* Get the {@link FileAccessor} object for the given file.
*
* @param osFile
* can be null.
* @return cached {@link FileAccessor} object for the file, or create a new
* one if not cached. <code>null</code> if osFile is null.
*/
static public FileAccessor getFileAccessor(File osFile) {
if (osFile == null)
return null;
FileAccessor ret = accessors.get(osFile);
if (ret == null) {
ret = new FileAccessor(osFile);
accessors.put(osFile, ret);
}
return ret;
}
private FileAccessor(File osFile) {
this.file = osFile;
}
public void dispose() {
synchronized (accessingFile) {
close();
}
accessors.remove(file);
}
/**
* Read bytes from given offset in the file.
*
* @param buffer
* caller allocated buffer to hold data.
* @param sourceOffset
* offset in the file (in bytes)
* @param count
* number of bytes to read
* @throws IOException file access failure.
*/
public void read(byte[] buffer, long sourceOffset, int count) throws IOException {
synchronized (accessingFile) {
ensureOpen();
raFile.seek(sourceOffset);
raFile.read(buffer, 0, count);
fileAccessTime = System.currentTimeMillis();
}
}
private void ensureOpen() throws IOException {
if (raFile == null && file != null) {
if (DEBUG) System.out.println("Thread " + Thread.currentThread().getName() + ": Opening " + file);
FileStatistics.executablesOpened++;
FileStatistics.executablesOpen++;
raFile = new ERandomAccessFile(file, "r");
new MonitorThread().start(); // start the monitor thread
}
}
private void close() {
if (raFile != null && file != null) {
try {
if (DEBUG) System.out.println("File Monitor Thread " + Thread.currentThread().getId() + ": closing " + file);
FileStatistics.executablesOpen--;
raFile.close();
raFile = null;
} catch (IOException e) {
// ignore
}
}
}
}