/
FileResourceCache.java
142 lines (126 loc) · 3.94 KB
/
FileResourceCache.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package aQute.bnd.osgi.resource;
import static aQute.bnd.exceptions.SupplierWithException.asSupplierOrElse;
import static aQute.bnd.osgi.Constants.MIME_TYPE_BUNDLE;
import static aQute.bnd.osgi.Constants.MIME_TYPE_JAR;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import org.osgi.resource.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import aQute.bnd.exceptions.Exceptions;
import aQute.bnd.osgi.Domain;
import aQute.libg.cryptography.SHA256;
class FileResourceCache {
private final static Logger logger = LoggerFactory.getLogger(FileResourceCache.class);
private final static long EXPIRED_DURATION_NANOS = TimeUnit.NANOSECONDS.convert(30L,
TimeUnit.MINUTES);
private static final FileResourceCache INSTANCE = new FileResourceCache();
private final Map<CacheKey, Resource> cache;
private long time;
private FileResourceCache() {
cache = new ConcurrentHashMap<>();
time = System.nanoTime();
}
static FileResourceCache getInstance() {
return INSTANCE;
}
Resource getResource(File file, URI uri) {
if (!file.isFile()) {
return null;
}
// Make sure we don't grow infinitely
final long now = System.nanoTime();
if ((now - time) > EXPIRED_DURATION_NANOS) {
cache.keySet()
.removeIf(key -> (now - key.time) > EXPIRED_DURATION_NANOS);
time = now;
}
CacheKey cacheKey = new CacheKey(file);
Resource resource = cache.computeIfAbsent(cacheKey, key -> {
logger.debug("parsing {}", file);
ResourceBuilder rb = new ResourceBuilder();
try {
Domain manifest = Domain.domain(file);
boolean hasIdentity = false;
if (manifest != null) {
hasIdentity = rb.addManifest(manifest);
}
String mime = hasIdentity ? MIME_TYPE_BUNDLE : MIME_TYPE_JAR;
int deferredHashCode = hashCode(file);
DeferredValue<String> sha256 = new DeferredComparableValue<>(String.class,
asSupplierOrElse(() -> SHA256.digest(file)
.asHex(), null),
deferredHashCode);
rb.addContentCapability(uri, sha256, file.length(), mime);
if (hasIdentity) {
rb.addHashes(file);
}
} catch (Exception e) {
throw Exceptions.duck(e);
}
return rb.build();
});
return resource;
}
private static int hashCode(File file) {
return file.getAbsoluteFile()
.hashCode();
}
static final class CacheKey {
private final Object fileKey;
private final long lastModifiedTime;
private final long size;
final long time;
CacheKey(File file) {
this(file.toPath());
}
CacheKey(Path path) {
BasicFileAttributes attributes;
try {
attributes = Files.getFileAttributeView(path, BasicFileAttributeView.class)
.readAttributes();
} catch (IOException e) {
throw Exceptions.duck(e);
}
if (!attributes.isRegularFile()) {
throw new IllegalArgumentException("File must be a regular file: " + path);
}
Object fileKey = attributes.fileKey();
this.fileKey = (fileKey != null) ? fileKey //
: path.toAbsolutePath(); // Windows FS does not have fileKey
this.lastModifiedTime = attributes.lastModifiedTime()
.toMillis();
this.size = attributes.size();
this.time = System.nanoTime();
}
@Override
public int hashCode() {
return (Objects.hashCode(fileKey) * 31 + Long.hashCode(lastModifiedTime)) * 31 + Long.hashCode(size);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof CacheKey)) {
return false;
}
CacheKey other = (CacheKey) obj;
return Objects.equals(fileKey, other.fileKey) && (lastModifiedTime == other.lastModifiedTime)
&& (size == other.size);
}
@Override
public String toString() {
return Objects.toString(fileKey);
}
}
}