/******************************************************************************* | |
* 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 | |
} | |
} | |
} | |
} |