forked from bndtools/bnd
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
repository: Add a Resource cache to FileSetRepository
The cache reduces the need to create new Resource objects, including SHA-256 computation, for unchanged files. Fixes bndtools#5367 Signed-off-by: BJ Hargrave <bj@hargrave.dev>
- Loading branch information
1 parent
e1eefb7
commit 133e3ea
Showing
3 changed files
with
163 additions
and
54 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
93 changes: 93 additions & 0 deletions
93
biz.aQute.repository/src/aQute/bnd/repository/fileset/ResourceCache.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
package aQute.bnd.repository.fileset; | ||
|
||
import static aQute.bnd.exceptions.FunctionWithException.asFunctionOrElse; | ||
|
||
import java.io.File; | ||
import java.util.Map; | ||
import java.util.Optional; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
import java.util.concurrent.TimeUnit; | ||
|
||
import org.osgi.framework.namespace.IdentityNamespace; | ||
import org.osgi.resource.Resource; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import aQute.bnd.osgi.Jar; | ||
import aQute.bnd.osgi.resource.CapReqBuilder; | ||
import aQute.bnd.osgi.resource.ResourceBuilder; | ||
import aQute.bnd.service.RepositoryPlugin; | ||
import aQute.bnd.version.MavenVersion; | ||
import aQute.bnd.version.Version; | ||
import aQute.maven.api.Revision; | ||
import aQute.maven.provider.POM; | ||
|
||
class ResourceCache { | ||
private final static Logger logger = LoggerFactory | ||
.getLogger(ResourceCache.class); | ||
private final static long EXPIRED_DURATION_NANOS = TimeUnit.NANOSECONDS.convert(30L, | ||
TimeUnit.MINUTES); | ||
private final Map<ResourceCacheKey, Resource> cache; | ||
private long time; | ||
|
||
ResourceCache() { | ||
cache = new ConcurrentHashMap<>(); | ||
time = System.nanoTime(); | ||
} | ||
|
||
Resource getResource(RepositoryPlugin repo, File file) { | ||
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; | ||
} | ||
ResourceCacheKey cacheKey = new ResourceCacheKey(file); | ||
Resource resource = cache.computeIfAbsent(cacheKey, key -> { | ||
ResourceBuilder rb = new ResourceBuilder(); | ||
try { | ||
boolean hasIdentity = rb.addFile(file, null); | ||
if (!hasIdentity) { | ||
try (Jar jar = new Jar(file)) { | ||
Optional<Revision> revision = jar.getPomXmlResources() | ||
.findFirst() | ||
.map(asFunctionOrElse(pomResource -> new POM(null, pomResource.openInputStream(), true), | ||
null)) | ||
.map(POM::getRevision); | ||
|
||
String name = jar.getModuleName(); | ||
if (name == null) { | ||
name = revision.map(r -> r.program.toString()) | ||
.orElse(null); | ||
if (name == null) { | ||
return null; | ||
} | ||
} | ||
|
||
Version version = revision.map(r -> r.version.getOSGiVersion()) | ||
.orElse(null); | ||
if (version == null) { | ||
version = new MavenVersion(jar.getModuleVersion()).getOSGiVersion(); | ||
} | ||
|
||
CapReqBuilder identity = new CapReqBuilder(IdentityNamespace.IDENTITY_NAMESPACE) | ||
.addAttribute(IdentityNamespace.IDENTITY_NAMESPACE, name) | ||
.addAttribute(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE, version) | ||
.addAttribute(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE, IdentityNamespace.TYPE_UNKNOWN); | ||
rb.addCapability(identity); | ||
} | ||
} | ||
} catch (Exception f) { | ||
return null; | ||
} | ||
logger.debug("{}: parsing {}", repo.getName(), file); | ||
return rb.build(); | ||
}); | ||
|
||
return resource; | ||
} | ||
} |
64 changes: 64 additions & 0 deletions
64
biz.aQute.repository/src/aQute/bnd/repository/fileset/ResourceCacheKey.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package aQute.bnd.repository.fileset; | ||
|
||
import java.io.File; | ||
import java.io.IOException; | ||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.nio.file.attribute.BasicFileAttributeView; | ||
import java.nio.file.attribute.BasicFileAttributes; | ||
import java.nio.file.attribute.FileTime; | ||
import java.util.Objects; | ||
|
||
import aQute.bnd.exceptions.Exceptions; | ||
|
||
final class ResourceCacheKey { | ||
private final Object fileKey; | ||
private final FileTime lastAccessTime; | ||
private final long size; | ||
final long time; | ||
|
||
|
||
ResourceCacheKey(File file) { | ||
this(file.toPath()); | ||
} | ||
|
||
ResourceCacheKey(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); | ||
} | ||
fileKey = attributes.fileKey(); | ||
lastAccessTime = attributes.lastAccessTime(); | ||
size = attributes.size(); | ||
time = System.nanoTime(); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return (Objects.hashCode(fileKey) * 31 + Objects.hashCode(lastAccessTime)) * 31 + Long.hashCode(size); | ||
} | ||
|
||
@Override | ||
public boolean equals(Object obj) { | ||
if (this == obj) { | ||
return true; | ||
} | ||
if (!(obj instanceof ResourceCacheKey)) { | ||
return false; | ||
} | ||
ResourceCacheKey other = (ResourceCacheKey) obj; | ||
return Objects.equals(fileKey, other.fileKey) && Objects.equals(lastAccessTime, other.lastAccessTime) | ||
&& (size == other.size); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return Objects.toString(fileKey); | ||
} | ||
} |