Add per-file ReadWriteLock to CacheLock
This commit is contained in:
parent
84e456c5dc
commit
09ab60405d
|
@ -0,0 +1,49 @@
|
||||||
|
package org.hl7.fhir.utilities.npm;
|
||||||
|
|
||||||
|
import org.hl7.fhir.utilities.TextFile;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.nio.channels.FileLock;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.locks.ReadWriteLock;
|
||||||
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
|
|
||||||
|
public class FilesystemPackageCacheLock {
|
||||||
|
|
||||||
|
private static final ConcurrentHashMap<File, ReadWriteLock> locks = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private final File lockFile;
|
||||||
|
|
||||||
|
public FilesystemPackageCacheLock(File cacheFolder, String name) throws IOException {
|
||||||
|
this.lockFile = new File(cacheFolder, name + ".lock");
|
||||||
|
if (!lockFile.isFile()) {
|
||||||
|
TextFile.stringToFile("", lockFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T doWriteWithLock(FilesystemPackageCacheManager.CacheLockFunction<T> f) throws IOException {
|
||||||
|
|
||||||
|
try (FileChannel channel = new RandomAccessFile(lockFile, "rw").getChannel()) {
|
||||||
|
locks.putIfAbsent(lockFile, new ReentrantReadWriteLock());
|
||||||
|
ReadWriteLock lock = locks.get(lockFile);
|
||||||
|
lock.writeLock().lock();
|
||||||
|
final FileLock fileLock = channel.lock();
|
||||||
|
T result = null;
|
||||||
|
try {
|
||||||
|
result = f.get();
|
||||||
|
} finally {
|
||||||
|
fileLock.release();
|
||||||
|
lock.writeLock().unlock();
|
||||||
|
}
|
||||||
|
if (!lockFile.delete()) {
|
||||||
|
lockFile.deleteOnExit();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,12 +3,8 @@ package org.hl7.fhir.utilities.npm;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.RandomAccessFile;
|
|
||||||
import java.nio.channels.FileChannel;
|
|
||||||
import java.nio.channels.FileLock;
|
|
||||||
|
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
@ -118,8 +114,7 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
|
||||||
private JsonArray buildInfo;
|
private JsonArray buildInfo;
|
||||||
private boolean suppressErrors;
|
private boolean suppressErrors;
|
||||||
private boolean minimalMemory;
|
private boolean minimalMemory;
|
||||||
|
|
||||||
|
|
||||||
public FilesystemPackageCacheManager(boolean userMode) throws IOException {
|
public FilesystemPackageCacheManager(boolean userMode) throws IOException {
|
||||||
init(userMode ? FilesystemPackageCacheMode.USER : FilesystemPackageCacheMode.SYSTEM);
|
init(userMode ? FilesystemPackageCacheMode.USER : FilesystemPackageCacheMode.SYSTEM);
|
||||||
}
|
}
|
||||||
|
@ -139,36 +134,35 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
|
||||||
init(FilesystemPackageCacheMode.CUSTOM);
|
init(FilesystemPackageCacheMode.CUSTOM);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
protected void init(FilesystemPackageCacheMode mode) throws IOException {
|
protected void init(FilesystemPackageCacheMode mode) throws IOException {
|
||||||
initPackageServers();
|
initPackageServers();
|
||||||
|
|
||||||
switch (mode) {
|
if (mode == FilesystemPackageCacheMode.CUSTOM) {
|
||||||
case SYSTEM:
|
if (!this.cacheFolder.exists()) {
|
||||||
if (Utilities.isWindows()) {
|
|
||||||
cacheFolder = new File(Utilities.path(System.getenv("ProgramData"), ".fhir", "packages"));
|
|
||||||
} else {
|
|
||||||
cacheFolder = new File(Utilities.path("/var", "lib", ".fhir", "packages"));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case USER:
|
|
||||||
cacheFolder = new File(Utilities.path(System.getProperty("user.home"), ".fhir", "packages"));
|
|
||||||
break;
|
|
||||||
case TESTING:
|
|
||||||
cacheFolder = new File(Utilities.path("[tmp]", ".fhir", "packages"));
|
|
||||||
break;
|
|
||||||
case CUSTOM:
|
|
||||||
if (!cacheFolder.exists()) {
|
|
||||||
throw new FHIRException("The folder ''"+cacheFolder+"' could not be found");
|
throw new FHIRException("The folder ''"+cacheFolder+"' could not be found");
|
||||||
}
|
}
|
||||||
default:
|
} else {
|
||||||
break;
|
this.cacheFolder = getCacheFolder(mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
initCacheFolder();
|
initCacheFolder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static File getCacheFolder(FilesystemPackageCacheMode mode) throws IOException {
|
||||||
|
switch (mode) {
|
||||||
|
case SYSTEM:
|
||||||
|
if (Utilities.isWindows()) {
|
||||||
|
return new File(Utilities.path(System.getenv("ProgramData"), ".fhir", "packages"));
|
||||||
|
} else {
|
||||||
|
return new File(Utilities.path("/var", "lib", ".fhir", "packages"));
|
||||||
|
}
|
||||||
|
case USER:
|
||||||
|
return new File(Utilities.path(System.getProperty("user.home"), ".fhir", "packages"));
|
||||||
|
case TESTING:
|
||||||
|
return new File(Utilities.path("[tmp]", ".fhir", "packages"));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
protected void initCacheFolder() throws IOException {
|
protected void initCacheFolder() throws IOException {
|
||||||
if (!(cacheFolder.exists()))
|
if (!(cacheFolder.exists()))
|
||||||
Utilities.createDirectory(cacheFolder.getAbsolutePath());
|
Utilities.createDirectory(cacheFolder.getAbsolutePath());
|
||||||
|
@ -254,7 +248,7 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
|
||||||
private void clearCache() throws IOException {
|
private void clearCache() throws IOException {
|
||||||
for (File f : cacheFolder.listFiles()) {
|
for (File f : cacheFolder.listFiles()) {
|
||||||
if (f.isDirectory()) {
|
if (f.isDirectory()) {
|
||||||
new CacheLock(f.getName()).doWithLock(() -> {
|
new FilesystemPackageCacheLock(cacheFolder, f.getName()).doWriteWithLock(() -> {
|
||||||
Utilities.clearDirectory(f.getAbsolutePath());
|
Utilities.clearDirectory(f.getAbsolutePath());
|
||||||
try {
|
try {
|
||||||
FileUtils.deleteDirectory(f);
|
FileUtils.deleteDirectory(f);
|
||||||
|
@ -399,7 +393,7 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public void removePackage(String id, String ver) throws IOException {
|
public void removePackage(String id, String ver) throws IOException {
|
||||||
new CacheLock(id + "#" + ver).doWithLock(() -> {
|
new FilesystemPackageCacheLock(cacheFolder, id + "#" + ver).doWriteWithLock(() -> {
|
||||||
String f = Utilities.path(cacheFolder, id + "#" + ver);
|
String f = Utilities.path(cacheFolder, id + "#" + ver);
|
||||||
File ff = new File(f);
|
File ff = new File(f);
|
||||||
if (ff.exists()) {
|
if (ff.exists()) {
|
||||||
|
@ -490,7 +484,7 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
|
||||||
}
|
}
|
||||||
|
|
||||||
String v = version;
|
String v = version;
|
||||||
return new CacheLock(id + "#" + version).doWithLock(() -> {
|
return new FilesystemPackageCacheLock(cacheFolder, id + "#" + version).doWriteWithLock(() -> {
|
||||||
NpmPackage pck = null;
|
NpmPackage pck = null;
|
||||||
String packRoot = Utilities.path(cacheFolder, id + "#" + v);
|
String packRoot = Utilities.path(cacheFolder, id + "#" + v);
|
||||||
try {
|
try {
|
||||||
|
@ -1015,34 +1009,6 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CacheLock {
|
|
||||||
|
|
||||||
private final File lockFile;
|
|
||||||
|
|
||||||
public CacheLock(String name) throws IOException {
|
|
||||||
this.lockFile = new File(cacheFolder, name + ".lock");
|
|
||||||
if (!lockFile.isFile()) {
|
|
||||||
TextFile.stringToFile("", lockFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> T doWithLock(CacheLockFunction<T> f) throws FileNotFoundException, IOException {
|
|
||||||
try (FileChannel channel = new RandomAccessFile(lockFile, "rw").getChannel()) {
|
|
||||||
final FileLock fileLock = channel.lock();
|
|
||||||
T result = null;
|
|
||||||
try {
|
|
||||||
result = f.get();
|
|
||||||
} finally {
|
|
||||||
fileLock.release();
|
|
||||||
}
|
|
||||||
if (!lockFile.delete()) {
|
|
||||||
lockFile.deleteOnExit();
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean packageExists(String id, String ver) throws IOException {
|
public boolean packageExists(String id, String ver) throws IOException {
|
||||||
if (packageInstalled(id, ver)) {
|
if (packageInstalled(id, ver)) {
|
||||||
return true;
|
return true;
|
||||||
|
|
Loading…
Reference in New Issue