/*******************************************************************************
* Copyright (c) 2004, 2006
* Thomas Hallgren, Kenneth Olwing, Mitch Sonies
* Pontus Rydin, Nils Unden, Peer Torngren
* The code, documentation and other materials contained herein have been
* licensed under the Eclipse Public License - v 1.0 by the individual
* copyright holders listed above, as Initial Contributors under such license.
* The text of such license is available at www.eclipse.org.
*******************************************************************************/
package org.eclipse.buckminster.core.helpers;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import org.eclipse.buckminster.core.Messages;
import org.eclipse.buckminster.runtime.FileInfoBuilder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
/**
* @author Thomas Hallgren
*/
public class ShortDurationFileCache extends TimedHashMap<String, CacheEntry> {
public interface Materializer {
String getKey();
FileHandle materialize(IProgressMonitor monitor, FileInfoBuilder fileInfo) throws IOException, CoreException;
}
public ShortDurationFileCache(long keepAlive, String prefix, String suffix, File tempDir) {
super(keepAlive, new TimedHashMap.EvictionPolicy<String, CacheEntry>() {
@Override
public void evict(Entry<String, CacheEntry> entry) {
CacheEntry ce = entry.getValue();
if (ce != null)
ce.remove();
}
});
}
public InputStream open(Materializer materializer, IProgressMonitor monitor) throws IOException, CoreException {
return open(materializer, monitor, null);
}
public InputStream open(Materializer materializer, IProgressMonitor monitor, FileInfoBuilder fileInfo) throws IOException, CoreException {
String key = materializer.getKey();
CacheEntry ce;
// Synchronize the actual cache access. Not the whole method. We do not
// want everyone to wait for every file.
//
synchronized (this) {
ce = get(key);
if (ce == null) {
ce = new CacheEntry();
put(key, ce);
}
}
// This call is synchronized and will only do something for the first
// caller.
//
synchronized (ce) {
ce.initialize(this, materializer, monitor, fileInfo);
return ce.open();
}
}
/**
* This method will always return false since we want to defer scheduling
* until the completion of a materialization.
*
* @return false
*/
@Override
public boolean scheduleOnPut() {
return false;
}
}
class CacheEntry {
class DeletingInputStream extends FileInputStream {
DeletingInputStream(File file) throws FileNotFoundException {
super(file);
}
@Override
public void close() throws IOException {
try {
super.close();
} finally {
synchronized (CacheEntry.this) {
if (--openFileCounter < 1 && removePending)
tempFile.getFile().delete();
}
}
}
}
private int openFileCounter = 0;
private boolean removePending = false;
private FileHandle tempFile;
private FileInfoBuilder fileInfo = null;
public synchronized void initialize(ShortDurationFileCache cache, ShortDurationFileCache.Materializer materializer, IProgressMonitor monitor,
FileInfoBuilder info) throws CoreException, IOException {
boolean success = false;
try {
if (tempFile == null) {
tempFile = materializer.materialize(monitor, info);
if (tempFile.isTemporary())
tempFile.getFile().deleteOnExit();
if (info != null)
fileInfo = new FileInfoBuilder(info);
cache.schedule(materializer.getKey());
} else if (fileInfo != null && info != null)
fileInfo.initFrom(info);
// All is well. No exceptions will bring us here.
//
success = true;
} finally {
if (!success)
//
// We're leaving because of some exception. Remove the
// entry immediately.
//
cache.remove(materializer.getKey());
}
}
public synchronized InputStream open() throws FileNotFoundException {
if (removePending)
throw new FileNotFoundException(Messages.File_is_closed);
if (tempFile.isTemporary()) {
++openFileCounter;
return new DeletingInputStream(tempFile.getFile());
}
return new FileInputStream(tempFile.getFile());
}
public final synchronized void remove() {
if (openFileCounter > 0)
//
// Streams are currently open on this file
//
removePending = true;
else if (tempFile != null && tempFile.isTemporary() && !removePending) {
removePending = true;
tempFile.getFile().delete();
}
}
}