This commit is contained in:
2023-07-28 09:15:24 +08:00
parent ab81022bce
commit b9851eecaf
8 changed files with 104 additions and 79 deletions

View File

@@ -0,0 +1,37 @@
package net.woggioni.jpacrepo.cache;
import net.woggioni.jpacrepo.api.model.PkgId;
import net.woggioni.jpacrepo.api.wire.PkgTuple;
import java.util.NavigableMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
public class PackageCache {
private NavigableMap<PkgId, PkgTuple> cachedMap;
private final AtomicBoolean invalidateCache = new AtomicBoolean(true);
public NavigableMap<PkgId, PkgTuple> getCachedMap(Supplier<NavigableMap<PkgId, PkgTuple>> mapSupplier) {
NavigableMap<PkgId, PkgTuple> result = null;
if (!invalidateCache.get()) {
result = cachedMap;
}
if (result == null) {
synchronized(this) {
if (!invalidateCache.get()) {
result = cachedMap;
}
if (result == null) {
cachedMap = mapSupplier.get();
invalidateCache.set(false);
result = cachedMap;
}
}
}
return result;
}
public void invalidateCache() {
invalidateCache.set(true);
}
}

View File

@@ -12,7 +12,6 @@ import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicBoolean;
@RequiredArgsConstructor(access = AccessLevel.PUBLIC)
public class AppConfig {
@@ -26,9 +25,6 @@ public class AppConfig {
@Getter
private final String dataSourceJndi;
@Getter
private final AtomicBoolean invalidateCache = new AtomicBoolean(true);
public Path getFile(PkgData pkg) {
return repoFolder.resolve(pkg.getFileName());
}

View File

@@ -3,6 +3,7 @@ package net.woggioni.jpacrepo.factory;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces;
import jakarta.enterprise.inject.spi.InjectionPoint;
import net.woggioni.jpacrepo.cache.PackageCache;
import net.woggioni.jpacrepo.config.AppConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -22,4 +23,9 @@ public class BeanFactory {
public Logger createLogger(InjectionPoint injectionPoint) {
return LoggerFactory.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
}
@Produces
@ApplicationScoped
public PackageCache createCache() {
return new PackageCache();
}
}

View File

@@ -11,7 +11,7 @@ import jakarta.ejb.Lock;
import jakarta.ejb.LockType;
import jakarta.ejb.Remote;
import jakarta.ejb.Schedule;
import jakarta.ejb.Singleton;
import jakarta.ejb.Stateless;
import jakarta.ejb.TransactionAttribute;
import jakarta.ejb.TransactionAttributeType;
import jakarta.ejb.TransactionManagement;
@@ -19,20 +19,20 @@ import jakarta.ejb.TransactionManagementType;
import jakarta.enterprise.concurrent.ManagedExecutorService;
import jakarta.inject.Inject;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.TypedQuery;
import jakarta.persistence.PersistenceContext;
import lombok.SneakyThrows;
import net.woggioni.jpacrepo.api.model.CompressionFormat;
import net.woggioni.jpacrepo.api.model.PkgData;
import net.woggioni.jpacrepo.api.model.PkgId;
import net.woggioni.jpacrepo.api.service.PacmanServiceLocal;
import net.woggioni.jpacrepo.api.service.PacmanServiceRemote;
import net.woggioni.jpacrepo.api.wire.PkgTuple;
import net.woggioni.jpacrepo.cache.PackageCache;
import net.woggioni.jpacrepo.config.AppConfig;
import net.woggioni.jpacrepo.impl.model.CompressionFormatImpl;
import net.woggioni.jpacrepo.impl.model.PkgDataImpl;
import net.woggioni.jpacrepo.persistence.QueryEngine;
import net.woggioni.jpacrepo.service.jpa.Queries;
import net.woggioni.jpacrepo.api.wire.PkgTuple;
import net.woggioni.jpacrepo.version.PkgIdComparator;
import net.woggioni.jwo.CollectionUtils;
import net.woggioni.jwo.Con;
@@ -41,8 +41,12 @@ import net.woggioni.jwo.Sup;
import net.woggioni.jwo.Tuple2;
import org.slf4j.Logger;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Collection;
@@ -59,15 +63,15 @@ import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Singleton
@Stateless
@Lock(LockType.READ)
@TransactionManagement(TransactionManagementType.CONTAINER)
@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
@Local({PacmanServiceLocal.class})
@Remote({PacmanServiceRemote.class})
public class PacmanServiceEJB implements PacmanServiceLocal {
@Inject
private EntityManagerFactory emf;
@PersistenceContext
private EntityManager em;
@Inject
private AppConfig ctx;
@@ -78,6 +82,9 @@ public class PacmanServiceEJB implements PacmanServiceLocal {
@Resource(name = "DefaultManagedExecutorService")
private ManagedExecutorService executor;
@Inject
private PackageCache packageCache;
private void deletePkgData(EntityManager em, PkgData pkgData) {
em.remove(pkgData);
}
@@ -88,7 +95,6 @@ public class PacmanServiceEJB implements PacmanServiceLocal {
@TransactionAttribute(TransactionAttributeType.REQUIRED)
@Schedule(hour = "4", minute = "00", persistent = false)
public void syncDB() {
EntityManager em = emf.createEntityManager();
try {
logger.info("Starting repository cleanup");
//Removes from DB the packages that have been deleted from filesystem
@@ -153,7 +159,7 @@ public class PacmanServiceEJB implements PacmanServiceLocal {
logger.info("Removing obsolete packages");
deleteOld(em);
logger.info("Repository cleanup completed successfully");
ctx.getInvalidateCache().set(true);
packageCache.invalidateCache();
} finally {
em.close();
}
@@ -173,7 +179,6 @@ public class PacmanServiceEJB implements PacmanServiceLocal {
@Override
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void deletePackage(String filename) {
EntityManager em = emf.createEntityManager();
deletePackage(em, filename);
logger.info("Package {} has been deleted", filename);
}
@@ -198,7 +203,6 @@ public class PacmanServiceEJB implements PacmanServiceLocal {
@Override
@TransactionAttribute(TransactionAttributeType.SUPPORTS)
public long countResults(String name, String version, String arch) {
EntityManager em = emf.createEntityManager();
return QueryEngine.countResults(em, name, version, arch);
}
@@ -211,7 +215,6 @@ public class PacmanServiceEJB implements PacmanServiceLocal {
String fileName,
int pageNumber,
int pageSize) {
EntityManager em = emf.createEntityManager();
return Queries.searchPackage(em, name, version, arch, compressionFormat, fileName)
.setMaxResults(pageSize)
.setFirstResult(pageNumber * pageSize)
@@ -220,31 +223,26 @@ public class PacmanServiceEJB implements PacmanServiceLocal {
@Override
public List<String> searchName(@Nonnull String name) {
EntityManager em = emf.createEntityManager();
return Queries.searchPackageName(em, name).getResultList();
}
@Override
public List<PkgData> searchByFileName(@Nonnull String fileName) {
EntityManager em = emf.createEntityManager();
return Queries.searchPackagesByFileName(em, fileName).getResultList();
}
@Override
public List<PkgData> searchByHash(@Nonnull String hash) {
EntityManager em = emf.createEntityManager();
return Queries.searchPackagesByHash(em, hash).getResultList();
}
@Override
public List<String> listFiles() {
EntityManager em = emf.createEntityManager();
return Queries.listFiles(em).getResultList();
}
@Override
public List<String> listHashes() {
EntityManager em = emf.createEntityManager();
return Queries.listHashes(em).getResultList();
}
@@ -252,7 +250,6 @@ public class PacmanServiceEJB implements PacmanServiceLocal {
@TransactionAttribute(TransactionAttributeType.SUPPORTS)
public List<PkgId> searchPkgId(
String name, String version, String arch, CompressionFormat compressionFormat) {
EntityManager em = emf.createEntityManager();
return Queries.searchPackages(em, name, version, arch, compressionFormat).getResultList();
}
@@ -266,7 +263,6 @@ public class PacmanServiceEJB implements PacmanServiceLocal {
}
public Set<String> missingFiles(Collection<String> fileNames) {
EntityManager em = emf.createEntityManager();
Stream<String> result = fileNames.stream();
Set<String> existing = Queries.getExistingFiles(em, fileNames)
.getResultStream().collect(CollectionUtils.toUnmodifiableTreeSet());
@@ -274,8 +270,12 @@ public class PacmanServiceEJB implements PacmanServiceLocal {
.collect(CollectionUtils.toUnmodifiableTreeSet());
}
@Override
public NavigableMap<PkgId, PkgTuple> getPkgMap() {
EntityManager em = emf.createEntityManager();
return packageCache.getCachedMap(this::computePackageMap);
}
private NavigableMap<PkgId, PkgTuple> computePackageMap() {
return Queries.getPkgMap(em).getResultStream().map(tuple -> {
PkgId pkgId = tuple.get(0, PkgId.class);
String filename = tuple.get(1, String.class);
@@ -298,7 +298,33 @@ public class PacmanServiceEJB implements PacmanServiceLocal {
@Nullable
@Override
public PkgData getPackage(PkgId pkgId) {
EntityManager em = emf.createEntityManager();
return em.find(PkgData.class, pkgId);
}
@SneakyThrows
public boolean addPackage(String fileName, InputStream input) {
java.nio.file.Path file = Files.createTempFile(ctx.getRepoFolder(), fileName, null);
List<PkgData> savedFiles = searchByFileName(fileName);
if (savedFiles.size() > 0) return false;
else {
try(OutputStream output = Files.newOutputStream(file)) {
JWO.copy(input, output, 0x10000);
PkgData pkg = PkgDataImpl.parseFile(file,
CompressionFormatImpl.guess(Paths.get(fileName)));
pkg.setFileName(fileName);
Optional.ofNullable(em.find(PkgData.class, pkg.getId())).ifPresent((Con<PkgData>) (pkgData -> {
em.remove(pkgData);
Files.delete(ctx.getRepoFolder().resolve(pkgData.getFileName()));
}));
logger.info("Persisting package {}", pkg.getFileName());
em.persist(pkg);
Files.move(file, ctx.getRepoFolder().resolve(fileName), StandardCopyOption.ATOMIC_MOVE);
packageCache.invalidateCache();
return true;
} catch (Throwable t) {
Files.delete(file);
throw t;
}
}
}
}

View File

@@ -78,9 +78,6 @@ import java.util.stream.Collectors;
@Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
@TransactionManagement(TransactionManagementType.CONTAINER)
public class PacmanWebService {
private NavigableMap<PkgId, PkgTuple> cachedMap;
@Inject
private EntityManagerFactory emf;
@@ -93,24 +90,6 @@ public class PacmanWebService {
@Inject
private AppConfig ctx;
private NavigableMap<PkgId, PkgTuple> getCachedMap() {
NavigableMap<PkgId, PkgTuple> result = null;
if (!ctx.getInvalidateCache().get()) {
result = cachedMap;
}
if (result == null) {
synchronized(this) {
result = cachedMap;
if (result == null) {
cachedMap = service.getPkgMap();
ctx.getInvalidateCache().set(false);
result = cachedMap;
}
}
}
return result;
}
private Response manageQueryResult(List<PkgData> list) {
return manageQueryResult(list, false);
}
@@ -163,6 +142,7 @@ public class PacmanWebService {
EntityTag etag = new EntityTag(Integer.toString(cachedMap.hashCode()), false);
Response.ResponseBuilder builder = request.evaluatePreconditions(etag);
if (builder == null) {
TreeMap<String, TreeMap<String, Map<String, NavigableSet<PkgTuple>>>> result = cachedMap.entrySet().stream().collect(
Collectors.groupingBy((Map.Entry<PkgId, PkgTuple> entry) -> entry.getKey().getArch(),
TreeMap::new,
@@ -221,7 +201,8 @@ public class PacmanWebService {
cc.setMaxAge(86400);
cc.setMustRevalidate(true);
cc.setNoCache(true);
EntityTag etag = new EntityTag(Integer.toString(getCachedMap().hashCode()));
NavigableMap<PkgId, PkgTuple> cachedMap = service.getPkgMap();
EntityTag etag = new EntityTag(Integer.toString(cachedMap.hashCode()));
Response.ResponseBuilder builder = request.evaluatePreconditions(etag);
if (builder == null) {
Long size = service.getFileSize(fileName);
@@ -277,31 +258,13 @@ public class PacmanWebService {
@Context UriInfo uriInfo) {
EntityManager em = emf.createEntityManager();
if (filename == null) throw new BadRequestException();
java.nio.file.Path file = Files.createTempFile(ctx.getRepoFolder(), filename, null);
List<PkgData> savedFiles = service.searchByFileName(filename);
Response result;
if (savedFiles.size() > 0) result = Response.notModified().build();
else {
try(OutputStream output = Files.newOutputStream(file)) {
JWO.copy(input, output, 0x10000);
PkgData pkg = PkgDataImpl.parseFile(file,
CompressionFormatImpl.guess(Paths.get(filename)));
pkg.setFileName(filename);
Optional.ofNullable(em.find(PkgData.class, pkg.getId())).ifPresent((Con<PkgData>) (pkgData -> {
em.remove(pkgData);
Files.delete(ctx.getRepoFolder().resolve(pkgData.getFileName()));
}));
log.info("Persisting package {}", pkg.getFileName());
em.persist(pkg);
URI pkgUri = uriInfo.getAbsolutePathBuilder().path(pkg.getFileName()).build();
Files.move(file, ctx.getRepoFolder().resolve(filename), StandardCopyOption.ATOMIC_MOVE);
ctx.getInvalidateCache().set(true);
cachedMap = null;
result = Response.created(pkgUri).build();
} catch (Throwable t) {
Files.delete(file);
throw t;
}
boolean added = service.addPackage(filename, input);
if(!added) {
result = Response.notModified().build();
} else {
URI pkgUri = uriInfo.getAbsolutePathBuilder().path(filename).build();
result = Response.created(pkgUri).build();
}
return result;
}