blob: 463f2898b466e132e224da2df848c48158b16edb [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* @author max
*/
package com.intellij.util.io;
import org.jetbrains.annotations.NonNls;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class OpenChannelsCache { // TODO: Will it make sense to have a background thread, that flushes the cache by timeout?
private final int myCacheSizeLimit;
private final String myAccessMode;
private final Map<File, ChannelDescriptor> myCache;
public OpenChannelsCache(final int cacheSizeLimit, @NonNls String accessMode) {
myCacheSizeLimit = cacheSizeLimit;
myAccessMode = accessMode;
myCache = new LinkedHashMap<File, ChannelDescriptor>(cacheSizeLimit, 0.5f, true);
}
public synchronized RandomAccessFile getChannel(File ioFile) throws FileNotFoundException {
ChannelDescriptor descriptor = myCache.get(ioFile);
if (descriptor == null) {
dropOvercache();
descriptor = new ChannelDescriptor(ioFile, myAccessMode);
myCache.put(ioFile, descriptor);
}
descriptor.lock();
return descriptor.getChannel();
}
private void dropOvercache() {
int dropCount = myCache.size() - myCacheSizeLimit;
if (dropCount >= 0) {
List<File> keysToDrop = new ArrayList<File>();
for (Map.Entry<File, ChannelDescriptor> entry : myCache.entrySet()) {
if (dropCount < 0) break;
if (!entry.getValue().isLocked()) {
dropCount--;
keysToDrop.add(entry.getKey());
}
}
for (File file : keysToDrop) {
closeChannel(file);
}
}
}
public synchronized void releaseChannel(File ioFile) {
ChannelDescriptor descriptor = myCache.get(ioFile);
assert descriptor != null;
descriptor.unlock();
}
public synchronized void closeChannel(File ioFile) {
final ChannelDescriptor descriptor = myCache.remove(ioFile);
if (descriptor != null) {
assert !descriptor.isLocked();
AntivirusDetector.getInstance().execute(new Runnable() {
@Override
public void run() {
try {
descriptor.getChannel().close();
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
});
}
}
private static class ChannelDescriptor {
private int lockCount = 0;
private final RandomAccessFile myChannel;
private final File myFile;
@SuppressWarnings({"IOResourceOpenedButNotSafelyClosed"})
public ChannelDescriptor(File file, String accessMode) throws FileNotFoundException {
myFile = file;
myChannel = new RandomAccessFile(file, accessMode);
}
public void lock() {
lockCount++;
}
public void unlock() {
lockCount--;
}
public boolean isLocked() {
return lockCount != 0;
}
public RandomAccessFile getChannel() {
return myChannel;
}
}
}