bugfix
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
jpacrepo.version=2023.07
|
||||
|
||||
lys.version=2023.07.20
|
||||
lys.version=2023.07.28
|
||||
|
@@ -8,6 +8,7 @@ import net.woggioni.jpacrepo.api.model.PkgData;
|
||||
import net.woggioni.jpacrepo.api.model.PkgId;
|
||||
import net.woggioni.jpacrepo.api.wire.PkgTuple;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.NavigableMap;
|
||||
@@ -44,4 +45,6 @@ public interface PacmanServiceRemote {
|
||||
Set<String> missingFiles(Collection<String> fileNames);
|
||||
|
||||
NavigableMap<PkgId, PkgTuple> getPkgMap();
|
||||
|
||||
boolean addPackage(String fileName, InputStream inputStream);
|
||||
}
|
@@ -1,11 +1,5 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
mavenLocal {
|
||||
content {
|
||||
includeGroup 'net.woggioni.gradle'
|
||||
includeGroup 'net.woggioni.gradle.lombok'
|
||||
}
|
||||
}
|
||||
maven {
|
||||
url = 'https://woggioni.net/mvn/'
|
||||
content {
|
||||
@@ -41,4 +35,4 @@ rootProject.name = 'jpacrepo'
|
||||
include 'jpacrepo-api'
|
||||
include 'jpacrepo-impl'
|
||||
include 'jpacrepo-client'
|
||||
include 'jpacrepo-frontend'
|
||||
include 'jpacrepo-frontend'
|
||||
|
37
src/main/java/net/woggioni/jpacrepo/cache/PackageCache.java
vendored
Normal file
37
src/main/java/net/woggioni/jpacrepo/cache/PackageCache.java
vendored
Normal 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);
|
||||
}
|
||||
}
|
@@ -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());
|
||||
}
|
||||
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user