From ab81022bced07e8e3ac3dc93ff84ddb48cbd6a9e Mon Sep 17 00:00:00 2001 From: Walter Oggioni Date: Thu, 20 Jul 2023 11:00:48 +0800 Subject: [PATCH] backend migrated to Java 17 --- build.gradle | 99 ++++-- gradle.properties | 30 +- gradle/wrapper/gradle-wrapper.jar | Bin 59821 -> 60756 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 6 + gradlew.bat | 14 +- jpacrepo-api/build.gradle | 8 +- jpacrepo-api/src/main/java/module-info.java | 9 +- .../jpacrepo/api/model/CompressionFormat.java | 2 + .../woggioni/jpacrepo/api/model/PkgData.java | 33 +- .../woggioni/jpacrepo/api/model/PkgId.java | 2 +- .../api/service/PacmanServiceLocal.java | 11 +- .../api/service/PacmanServiceRemote.java | 38 +++ .../jpacrepo/api}/wire/PkgDataList.java | 18 +- .../woggioni/jpacrepo/api}/wire/PkgTuple.java | 4 +- .../jpacrepo/api}/wire/StringList.java | 2 +- .../src/main/resources/META-INF/beans.xml | 5 + jpacrepo-frontend/build.gradle | 11 + .../net/woggioni/jpacrepo/DocumentWalk.kt | 70 +++++ .../main/kotlin/net/woggioni/jpacrepo/Main.kt | 61 ++++ .../src/main/resources/index.html | 10 + .../jpacrepo/impl/model/PkgDataImpl.java | 6 +- nim/src/jpacrepo.nim | 6 +- settings.gradle | 9 +- src/main/java/module-info.java | 1 + .../jpacrepo/factory/BeanFactory.java | 2 + .../factory/PersistenceUnitFactory.java | 6 +- .../jpacrepo/persistence/QueryEngine.java | 92 +----- .../jpacrepo/service/PacmanServiceEJB.java | 212 +++++++++---- .../jpacrepo/service/PacmanWebService.java | 169 ++++------- .../jpacrepo/service/jpa/Queries.java | 281 ++++++++++++++++++ .../jpacrepo/service/jpa/QueryBuilder.java | 35 +++ src/main/resources/META-INF/beans.xml | 5 + src/main/resources/META-INF/persistence.xml | 10 +- src/main/resources/WEB-INF/web.xml | 2 +- src/test/java/ClientTest.java | 25 +- src/test/java/EJBTest.java | 34 +++ 37 files changed, 955 insertions(+), 375 deletions(-) rename {src/main/java/net/woggioni/jpacrepo/service => jpacrepo-api/src/main/java/net/woggioni/jpacrepo/api}/wire/PkgDataList.java (52%) rename {src/main/java/net/woggioni/jpacrepo/service => jpacrepo-api/src/main/java/net/woggioni/jpacrepo/api}/wire/PkgTuple.java (70%) rename {src/main/java/net/woggioni/jpacrepo/service => jpacrepo-api/src/main/java/net/woggioni/jpacrepo/api}/wire/StringList.java (93%) create mode 100644 jpacrepo-api/src/main/resources/META-INF/beans.xml create mode 100644 jpacrepo-frontend/build.gradle create mode 100644 jpacrepo-frontend/src/main/kotlin/net/woggioni/jpacrepo/DocumentWalk.kt create mode 100644 jpacrepo-frontend/src/main/kotlin/net/woggioni/jpacrepo/Main.kt create mode 100644 jpacrepo-frontend/src/main/resources/index.html create mode 100644 src/main/java/net/woggioni/jpacrepo/service/jpa/Queries.java create mode 100644 src/main/java/net/woggioni/jpacrepo/service/jpa/QueryBuilder.java create mode 100644 src/test/java/EJBTest.java diff --git a/build.gradle b/build.gradle index 4fdbd2b..fdfe967 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,7 @@ plugins { alias(catalog.plugins.lombok) apply false alias(catalog.plugins.wildfly) + id 'java-library' id 'war' id 'maven-publish' } @@ -8,7 +9,22 @@ plugins { import net.woggioni.gradle.wildfly.Deploy2WildflyTask allprojects { - apply plugin: 'net.woggioni.gradle.lombok' + + pluginManager.withPlugin('java-library') { + apply plugin: 'net.woggioni.gradle.lombok' + + lombok { + version = catalog.versions.lombok.get() + } + + tasks.withType(JavaCompile) { + options.release = 17 + } + + tasks.withType(Test) { + useJUnitPlatform() + } + } group = 'net.woggioni' version = getProperty('jpacrepo.version') @@ -23,24 +39,26 @@ allprojects { } mavenCentral() } +} - dependencies { - implementation platform(group: 'com.lys', name: 'lys-dependencies', version: getProperty('lys.version')) - } +configurations { + deployableArchives { + attributes { + attribute( + LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, + objects.named(LibraryElements.class, LibraryElements.JAR)) + } - lombok { - version = getProperty('lombok.version') - } - - tasks.withType(JavaCompile) { - options.release = 17 - } - - tasks.withType(Test) { - useJUnitPlatform() + transitive = false + canBeConsumed = false + visible = false + canBeResolved = true } } +java { + modularity.inferModulePath = true +} dependencies { implementation project(':jpacrepo-impl') @@ -48,19 +66,17 @@ dependencies { implementation catalog.jwo implementation catalog.slf4j.api - compileOnly catalog.hibernate.jpamodelgen - compileOnly catalog.jakarta.enterprise.cdi.api - compileOnly catalog.jakarta.servlet.api - compileOnly catalog.jakarta.ejb.api - compileOnly catalog.jakarta.ws.rs.api - compileOnly catalog.jakarta.xml.bind.api - compileOnly catalog.jakarta.enterprise.concurrent.api - + providedCompile catalog.jakarta.persistence.api + providedCompile catalog.jakarta.enterprise.cdi.api + providedCompile catalog.jakarta.servlet.api + providedCompile catalog.jakarta.ejb.api + providedCompile catalog.jakarta.ws.rs.api + providedCompile catalog.jakarta.xml.bind.api + providedCompile catalog.jakarta.enterprise.concurrent.api implementation catalog.commons.compress implementation catalog.jna - testImplementation catalog.jackson.module.jakarta.xmlbind.annotations testImplementation catalog.jboss.ejb.client testImplementation catalog.weld.se.core @@ -69,10 +85,20 @@ dependencies { testImplementation catalog.resteasy.client testImplementation catalog.resteasy.jackson2.provider testImplementation catalog.jackson.datatype.jsr310 + + testRuntimeOnly catalog.slf4j.simple + testImplementation catalog.junit.jupiter.api testImplementation catalog.junit.jupiter.params + + testRuntimeOnly catalog.jakartaee.api testRuntimeOnly catalog.junit.jupiter.engine + testRuntimeOnly group: 'org.glassfish.main.extras', name: 'glassfish-embedded-all', version: '7.0.6' + + deployableArchives project + deployableArchives project(':jpacrepo-impl') + deployableArchives project(':jpacrepo-api') } File nimDir = project.file('nim') @@ -83,7 +109,7 @@ Provider nimCompileTaskProvider = tasks.register("compileNim", Exec) { inputs.files(project.fileTree(srcDir), project.fileTree(staticDir)) File outputFile = new File(temporaryDir, "jpacrepo.js") outputs.file(outputFile) - commandLine 'nim', 'js', "-o:$outputFile", 'src/jpacrepo.nim' + commandLine 'nim', 'js', '-d:release', "-o:$outputFile", 'src/jpacrepo.nim' workingDir(nimDir) } @@ -92,7 +118,30 @@ Provider warTaskProvider = tasks.named('war', War) { from nimCompileTaskProvider } -tasks.named('deploy2Wildfly', Deploy2WildflyTask) { +tasks.named('deploy2Wildfly', Deploy2WildflyTask) { d2w -> + d2w.rpcPort = 1234 + d2w.rpcUsername = 'woggioni' +// d2w.rpcUsername = 'admin' + d2w.rpcPassword = '123456' +} + + +File warPath = tasks.named("war", War).get().archiveFile.get().getAsFile() +tasks.named("test", Test) { + jvmArgs = [ + '--add-opens', 'java.naming/javax.naming.spi=ALL-UNNAMED', + '--add-opens', 'java.base/java.lang=ALL-UNNAMED', + '--add-opens', 'java.base/java.io=ALL-UNNAMED', +// '--add-opens', 'java.base/java.util=ALL-UNNAMED', +// '--add-opens', 'java.management/javax.management.openmbean=ALL-UNNAMED', +// '--add-opens', 'java.management/javax.management=ALL-UNNAMED', + ] +// classpath = configurations.testRuntimeClasspath + sourceSets.test.output +// File warPath = tasks.named("war", War).get().archiveFile.get().getAsFile() +// classpath += warPath +// classpath += tasks.named("war", War).get().outputs.files + inputs.files(configurations.deployableArchives) + systemProperty('jpacrepo.jar.path', configurations.deployableArchives.asPath) } publishing { diff --git a/gradle.properties b/gradle.properties index 834ac3c..64ee63e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,29 +1,3 @@ -net.woggioni.gradle.lombok.version=0.1 -net.woggioni.gradle.wildfly.version=0.1 -net.woggioni.gradle.envelope.version=1.0-SNAPSHOT +jpacrepo.version=2023.07 -jpacrepo.version=2.0-SNAPSHOT - -lys.version=0.1-SNAPSHOT - -lombok.version=1.18.22 -slf4j.version=1.7.32 -xz.version=1.9 -apache.commons.compress.version=1.21 -hibernate.version=5.6.5.Final -jzstd.version=0.1-SNAPSHOT -jwo.version=1.0-SNAPSHOT -jakarta.persistence.version=3.1.0 -jakarta.inject.version=2.0.1 -jakarta.cdi.version=4.0.1 -jakarta.annotation.version=2.1.1 -jakarta.ee.version=8.0.0 -jaxb.api.version=2.3.1 - -jboss.ejb.client.version=4.0.43.Final -log4j.version=2.17.2 -weld.version=4.0.3.Final -h2.version=2.1.210 -resteasy.version=6.0.0.Final -jna.version=5.10.0 -junit.jupiter.version=5.8.2 +lys.version=2023.07.20 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 41d9927a4d4fb3f96a785543079b8df6723c946b..249e5832f090a2944b7473328c07c9755baa3196 100644 GIT binary patch delta 10197 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ae04661..84a0b92 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 1b6c787..a69d9cb 100755 --- a/gradlew +++ b/gradlew @@ -205,6 +205,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/gradlew.bat b/gradlew.bat index ac1b06f..53a6b23 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,7 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -75,13 +75,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/jpacrepo-api/build.gradle b/jpacrepo-api/build.gradle index 38a9240..ac9ed4a 100644 --- a/jpacrepo-api/build.gradle +++ b/jpacrepo-api/build.gradle @@ -4,8 +4,14 @@ plugins { dependencies { compileOnly catalog.jakarta.xml.bind.api - compileOnly catalog.jakarta.ejb.api compileOnly catalog.jakarta.persistence.api + compileOnly catalog.jakarta.inject.api + compileOnly catalog.jakarta.ejb.api + compileOnly catalog.jakarta.json.bind.api + compileOnly catalog.jakarta.annotation.api + + annotationProcessor catalog.hibernate.jpamodelgen + } tasks.named(JavaPlugin.COMPILE_JAVA_TASK_NAME, JavaCompile) { diff --git a/jpacrepo-api/src/main/java/module-info.java b/jpacrepo-api/src/main/java/module-info.java index 03f0eda..95de5bc 100644 --- a/jpacrepo-api/src/main/java/module-info.java +++ b/jpacrepo-api/src/main/java/module-info.java @@ -1,9 +1,12 @@ module net.woggioni.jpacrepo.api { requires static lombok; - requires static jakarta.xml.bind; - requires static jakarta.ejb; - requires static jakarta.persistence; + requires jakarta.xml.bind; + requires jakarta.ejb; + requires jakarta.persistence; + requires jakarta.annotation; + requires jakarta.json.bind; exports net.woggioni.jpacrepo.api.model; exports net.woggioni.jpacrepo.api.service; + exports net.woggioni.jpacrepo.api.wire; } \ No newline at end of file diff --git a/jpacrepo-api/src/main/java/net/woggioni/jpacrepo/api/model/CompressionFormat.java b/jpacrepo-api/src/main/java/net/woggioni/jpacrepo/api/model/CompressionFormat.java index aaa4325..2dd4cd8 100644 --- a/jpacrepo-api/src/main/java/net/woggioni/jpacrepo/api/model/CompressionFormat.java +++ b/jpacrepo-api/src/main/java/net/woggioni/jpacrepo/api/model/CompressionFormat.java @@ -3,6 +3,8 @@ package net.woggioni.jpacrepo.api.model; import lombok.Getter; import lombok.RequiredArgsConstructor; +import java.io.Serializable; + @RequiredArgsConstructor public enum CompressionFormat { XZ("xz"), GZIP("gz"), Z_STANDARD("zst"); diff --git a/jpacrepo-api/src/main/java/net/woggioni/jpacrepo/api/model/PkgData.java b/jpacrepo-api/src/main/java/net/woggioni/jpacrepo/api/model/PkgData.java index b7eba71..6e61692 100644 --- a/jpacrepo-api/src/main/java/net/woggioni/jpacrepo/api/model/PkgData.java +++ b/jpacrepo-api/src/main/java/net/woggioni/jpacrepo/api/model/PkgData.java @@ -1,27 +1,28 @@ package net.woggioni.jpacrepo.api.model; -import jakarta.persistence.ElementCollection; -import jakarta.persistence.Entity; +import jakarta.json.bind.annotation.JsonbTransient; +import jakarta.json.bind.annotation.JsonbVisibility; +import jakarta.json.bind.config.PropertyVisibilityStrategy; import jakarta.persistence.Access; import jakarta.persistence.AccessType; +import jakarta.persistence.ElementCollection; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; import jakarta.persistence.FetchType; +import jakarta.persistence.Index; import jakarta.persistence.NamedQueries; import jakarta.persistence.NamedQuery; import jakarta.persistence.PrePersist; import jakarta.persistence.PreUpdate; import jakarta.persistence.Table; -import jakarta.persistence.Index; -import jakarta.persistence.EmbeddedId; - -import jakarta.xml.bind.annotation.XmlRootElement; import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.XmlAccessorType; - - - +import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlTransient; import lombok.Data; -import java.time.OffsetDateTime; +import java.io.Serializable; +import java.time.Instant; import java.util.Set; @Data @@ -38,8 +39,8 @@ import java.util.Set; @Index(columnList = "fileName", unique = true) }) @XmlRootElement -@XmlAccessorType(XmlAccessType.FIELD) -public class PkgData { +@XmlAccessorType(XmlAccessType.PROPERTY) +public class PkgData implements Serializable { @EmbeddedId private PkgId id; @@ -50,7 +51,7 @@ public class PkgData { private String url; - private OffsetDateTime buildDate; + private Instant buildDate; private String packager; @@ -86,12 +87,14 @@ public class PkgData { @ElementCollection(fetch = FetchType.EAGER) private Set backup; - private OffsetDateTime updTimestamp; + @XmlTransient + @JsonbTransient + private Instant updTimestamp; @PreUpdate @PrePersist private void writeTimestamp() { - updTimestamp = OffsetDateTime.now(); + updTimestamp = Instant.now(); } } diff --git a/jpacrepo-api/src/main/java/net/woggioni/jpacrepo/api/model/PkgId.java b/jpacrepo-api/src/main/java/net/woggioni/jpacrepo/api/model/PkgId.java index ed8397a..e262e95 100644 --- a/jpacrepo-api/src/main/java/net/woggioni/jpacrepo/api/model/PkgId.java +++ b/jpacrepo-api/src/main/java/net/woggioni/jpacrepo/api/model/PkgId.java @@ -15,7 +15,7 @@ import java.io.Serializable; @Embeddable @Access(AccessType.FIELD) @XmlRootElement -@XmlAccessorType(XmlAccessType.FIELD) +@XmlAccessorType(XmlAccessType.PROPERTY) public class PkgId implements Serializable { private String name; diff --git a/jpacrepo-api/src/main/java/net/woggioni/jpacrepo/api/service/PacmanServiceLocal.java b/jpacrepo-api/src/main/java/net/woggioni/jpacrepo/api/service/PacmanServiceLocal.java index 7c0c8e6..c5d7693 100644 --- a/jpacrepo-api/src/main/java/net/woggioni/jpacrepo/api/service/PacmanServiceLocal.java +++ b/jpacrepo-api/src/main/java/net/woggioni/jpacrepo/api/service/PacmanServiceLocal.java @@ -1,8 +1,7 @@ package net.woggioni.jpacrepo.api.service; import jakarta.ejb.Local; - - +import net.woggioni.jpacrepo.api.model.CompressionFormat; import net.woggioni.jpacrepo.api.model.PkgData; import java.util.List; @@ -10,5 +9,11 @@ import java.util.List; @Local public interface PacmanServiceLocal extends PacmanServiceRemote { long countResults(String name, String version, String arch); - List searchPackage(String name, String version, String arch, int page, int pageSize, String fileName); + List searchPackage(String name, + String version, + String arch, + CompressionFormat compressionFormat, + String fileName, + int pageNumber, + int pageSize); } \ No newline at end of file diff --git a/jpacrepo-api/src/main/java/net/woggioni/jpacrepo/api/service/PacmanServiceRemote.java b/jpacrepo-api/src/main/java/net/woggioni/jpacrepo/api/service/PacmanServiceRemote.java index 95b9fa8..91aec28 100644 --- a/jpacrepo-api/src/main/java/net/woggioni/jpacrepo/api/service/PacmanServiceRemote.java +++ b/jpacrepo-api/src/main/java/net/woggioni/jpacrepo/api/service/PacmanServiceRemote.java @@ -1,9 +1,47 @@ package net.woggioni.jpacrepo.api.service; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import jakarta.ejb.Remote; +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.wire.PkgTuple; + +import java.util.Collection; +import java.util.List; +import java.util.NavigableMap; +import java.util.Set; @Remote public interface PacmanServiceRemote { void syncDB(); + void deletePackage(String filename); + + List searchName(@Nonnull String name); + + List searchByHash(@Nonnull String hash); + + List searchByFileName(@Nonnull String fileName); + + List listFiles(); + + List listHashes(); + + List searchPkgId( + String name, + String version, + String arch, + CompressionFormat compressionFormat); + + @Nullable + PkgData getPackage(PkgId pkgId); + + @Nullable + Long getFileSize(String fileName); + + Set missingFiles(Collection fileNames); + + NavigableMap getPkgMap(); } \ No newline at end of file diff --git a/src/main/java/net/woggioni/jpacrepo/service/wire/PkgDataList.java b/jpacrepo-api/src/main/java/net/woggioni/jpacrepo/api/wire/PkgDataList.java similarity index 52% rename from src/main/java/net/woggioni/jpacrepo/service/wire/PkgDataList.java rename to jpacrepo-api/src/main/java/net/woggioni/jpacrepo/api/wire/PkgDataList.java index 8634cfc..a00ce78 100644 --- a/src/main/java/net/woggioni/jpacrepo/service/wire/PkgDataList.java +++ b/jpacrepo-api/src/main/java/net/woggioni/jpacrepo/api/wire/PkgDataList.java @@ -1,18 +1,22 @@ -package net.woggioni.jpacrepo.service.wire; +package net.woggioni.jpacrepo.api.wire; +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlRootElement; import net.woggioni.jpacrepo.api.model.PkgData; import java.util.ArrayList; +import java.util.Collection; import java.util.List; -@XmlRootElement +@XmlRootElement(name = "packages") +@XmlAccessorType(XmlAccessType.PROPERTY) public class PkgDataList extends ArrayList { - - public PkgDataList(List l) { - for (PkgData el : l) add(el); + public PkgDataList() {} + public PkgDataList(Collection c) { + super(c); } public PkgDataList(PkgData... elements) { @@ -20,11 +24,11 @@ public class PkgDataList extends ArrayList { } @XmlElement(name = "pkgData") - List getItems() { + public List getItems() { return this; } - void setItems(List pkgs) { + public void setItems(List pkgs) { this.clear(); this.addAll(pkgs); } diff --git a/src/main/java/net/woggioni/jpacrepo/service/wire/PkgTuple.java b/jpacrepo-api/src/main/java/net/woggioni/jpacrepo/api/wire/PkgTuple.java similarity index 70% rename from src/main/java/net/woggioni/jpacrepo/service/wire/PkgTuple.java rename to jpacrepo-api/src/main/java/net/woggioni/jpacrepo/api/wire/PkgTuple.java index 867cae2..98cca2d 100644 --- a/src/main/java/net/woggioni/jpacrepo/service/wire/PkgTuple.java +++ b/jpacrepo-api/src/main/java/net/woggioni/jpacrepo/api/wire/PkgTuple.java @@ -1,4 +1,4 @@ -package net.woggioni.jpacrepo.service.wire; +package net.woggioni.jpacrepo.api.wire; import jakarta.xml.bind.annotation.XmlRootElement; import lombok.Data; @@ -8,7 +8,7 @@ import lombok.Data; public class PkgTuple { String md5sum; - String filename; + String fileName; long size; } \ No newline at end of file diff --git a/src/main/java/net/woggioni/jpacrepo/service/wire/StringList.java b/jpacrepo-api/src/main/java/net/woggioni/jpacrepo/api/wire/StringList.java similarity index 93% rename from src/main/java/net/woggioni/jpacrepo/service/wire/StringList.java rename to jpacrepo-api/src/main/java/net/woggioni/jpacrepo/api/wire/StringList.java index 8c1989a..867004d 100644 --- a/src/main/java/net/woggioni/jpacrepo/service/wire/StringList.java +++ b/jpacrepo-api/src/main/java/net/woggioni/jpacrepo/api/wire/StringList.java @@ -1,4 +1,4 @@ -package net.woggioni.jpacrepo.service.wire; +package net.woggioni.jpacrepo.api.wire; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlRootElement; diff --git a/jpacrepo-api/src/main/resources/META-INF/beans.xml b/jpacrepo-api/src/main/resources/META-INF/beans.xml new file mode 100644 index 0000000..028820e --- /dev/null +++ b/jpacrepo-api/src/main/resources/META-INF/beans.xml @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/jpacrepo-frontend/build.gradle b/jpacrepo-frontend/build.gradle new file mode 100644 index 0000000..840d00c --- /dev/null +++ b/jpacrepo-frontend/build.gradle @@ -0,0 +1,11 @@ +plugins { + alias(catalog.plugins.kotlin.multiplatform) +} + +kotlin { + js(IR) { + browser { + } + binaries.executable() + } +} diff --git a/jpacrepo-frontend/src/main/kotlin/net/woggioni/jpacrepo/DocumentWalk.kt b/jpacrepo-frontend/src/main/kotlin/net/woggioni/jpacrepo/DocumentWalk.kt new file mode 100644 index 0000000..6792ad8 --- /dev/null +++ b/jpacrepo-frontend/src/main/kotlin/net/woggioni/jpacrepo/DocumentWalk.kt @@ -0,0 +1,70 @@ +package net.woggioni.jpacrepo + +import org.w3c.dom.Document +import org.w3c.dom.Element + + +class HtmlBuilder private constructor(private val doc : Document, val el: Element) { + + companion object { + fun of(doc : Document, el: Element, cb : HtmlBuilder.(el : Element) -> T) : T { + return HtmlBuilder(doc, el).cb(el) + } + } + + private inline fun dfd( + name : String, + attrs : Map, + cb : HtmlBuilder.(el : Element) -> T) : T { + val child = doc.createElement(name) + for((key, value) in attrs) { + child.setAttribute(key, value) + } + el.appendChild(child) + return HtmlBuilder(doc, child).cb(child) + } + + fun html(attrs : Map = emptyMap(), + cb : HtmlBuilder.(el : Element) -> T) : T = dfd("html", attrs, cb) + fun head(attrs : Map = emptyMap(), + cb : HtmlBuilder.(el : Element) -> T) : T = dfd("head", attrs, cb) + fun body(attrs : Map = emptyMap(), + cb : HtmlBuilder.(el : Element) -> T) : T = dfd("body", attrs, cb) + + fun div(attrs : Map = emptyMap(), + cb : HtmlBuilder.(el : Element) -> T) : T = dfd("div", attrs, cb) + fun header(attrs : Map = emptyMap(), + cb : HtmlBuilder.(el : Element) -> T) : T = dfd("header", attrs, cb) + fun main(attrs : Map = emptyMap(), + cb : HtmlBuilder.(el : Element) -> T) : T = dfd("main", attrs, cb) + fun footer(attrs : Map = emptyMap(), + cb : HtmlBuilder.(el : Element) -> T) : T = dfd("footer", attrs, cb) + fun a(attrs : Map = emptyMap(), + cb : HtmlBuilder.(el : Element) -> T) : T = dfd("a", attrs, cb) + fun meta(attrs : Map = emptyMap(), + cb : HtmlBuilder.(el : Element) -> T) : T = dfd("meta", attrs, cb) + fun script(attrs : Map = emptyMap(), + cb : HtmlBuilder.(el : Element) -> T) : T = dfd("script", attrs, cb) + fun link(attrs : Map = emptyMap(), + cb : HtmlBuilder.(el : Element) -> T) : T = dfd("link", attrs, cb) + fun title(attrs : Map = emptyMap(), + cb : HtmlBuilder.(el : Element) -> T) : T = dfd("title", attrs, cb) + fun p(attrs : Map = emptyMap(), + cb : HtmlBuilder.(el : Element) -> T) : T = dfd("p", attrs, cb) + + fun h1(attrs : Map = emptyMap(), + cb : HtmlBuilder.(el : Element) -> T) : T = dfd("h1", attrs, cb) + + + fun classes(vararg classes : String) { + for(cls in classes) el.classList.add(cls) + } + + fun attr(key: String, value : String) { + el.setAttribute(key, value) + } + + fun text(txt : String) { + el.textContent = txt + } +} diff --git a/jpacrepo-frontend/src/main/kotlin/net/woggioni/jpacrepo/Main.kt b/jpacrepo-frontend/src/main/kotlin/net/woggioni/jpacrepo/Main.kt new file mode 100644 index 0000000..fa49ab5 --- /dev/null +++ b/jpacrepo-frontend/src/main/kotlin/net/woggioni/jpacrepo/Main.kt @@ -0,0 +1,61 @@ +package net.woggioni.jpacrepo + +import kotlinx.browser.document +import net.woggioni.jpacrepo.Component.container +import org.w3c.dom.HTMLElement + + +object Component { + val container = document.getElementById("container") as HTMLElement + + fun sayHelloViaDom() { + container.textContent = "Hello, DOM! Kotlin is writing…" + } +} + +fun main(vararg args: String) { +// println("JavaScript generated through Kotlin") +// +// Component.sayHelloViaDom() +// sayHelloViaJsConsole() +// sayHelloViaInlinedJavaScript() + + HtmlBuilder.of(document, document.body as HTMLElement) { + classes("d-flex", "h-100", "text-center", "text-white", "bg-dark") + div { + classes("cover-container", "d-flex", "w-100", "h-100", "p-3", "mx-auto", "flex-column") + + } + header { + classes("mb-auto") + } + main { + classes("px-3") + h1 { + text("Cover your page.") + } + p { + classes("lead") + text("Cover is a one-page template for building simple and beautiful home pages. Download, edit the text, and add your own fullscreen background photo to make it your own.") + } + p { + classes("lead") + a { + classes("btn", "btn-lg", "btn-secondary", "fw-bold", "border-white", "bg-white") + text("Learn more") + } + } + } + footer { + classes("mt-auto", "text-white-50") + } + } +} + +private fun sayHelloViaJsConsole() { + console.log("Hello from `console.log()`!") +} + +private fun sayHelloViaInlinedJavaScript() { + js("document.writeln('Hello, from inlined JavaScript in Kotlin!')") +} \ No newline at end of file diff --git a/jpacrepo-frontend/src/main/resources/index.html b/jpacrepo-frontend/src/main/resources/index.html new file mode 100644 index 0000000..f2d9c01 --- /dev/null +++ b/jpacrepo-frontend/src/main/resources/index.html @@ -0,0 +1,10 @@ + + + + + + + + + Hello World! + \ No newline at end of file diff --git a/jpacrepo-impl/src/main/java/net/woggioni/jpacrepo/impl/model/PkgDataImpl.java b/jpacrepo-impl/src/main/java/net/woggioni/jpacrepo/impl/model/PkgDataImpl.java index 5305397..14773cb 100644 --- a/jpacrepo-impl/src/main/java/net/woggioni/jpacrepo/impl/model/PkgDataImpl.java +++ b/jpacrepo-impl/src/main/java/net/woggioni/jpacrepo/impl/model/PkgDataImpl.java @@ -23,8 +23,6 @@ import java.nio.file.Files; import java.nio.file.Path; import java.text.ParseException; import java.time.Instant; -import java.time.OffsetDateTime; -import java.time.ZoneOffset; import java.util.List; import java.util.Map; import java.util.Objects; @@ -110,8 +108,8 @@ public class PkgDataImpl { data.getId().setName(value.get(0)); break; case "builddate": - data.setBuildDate(OffsetDateTime.ofInstant( - Instant.ofEpochSecond(Long.parseLong(value.get(0))), ZoneOffset.UTC)); + data.setBuildDate( + Instant.ofEpochSecond(Long.parseLong(value.get(0)))); break; case "license": data.setLicense(value.get(0)); diff --git a/nim/src/jpacrepo.nim b/nim/src/jpacrepo.nim index a3ee3b4..ab35ff5 100644 --- a/nim/src/jpacrepo.nim +++ b/nim/src/jpacrepo.nim @@ -129,7 +129,7 @@ proc readTableRow(arch : string, row : Element) : JsonNode = let version = $row.querySelector("td:nth-child(3) button").textContent let filename = $row.querySelector("td:nth-child(4) button").textContent for candidate in pkgMap[arch][pkgname][version]: - if filename == candidate["filename"].getStr: + if filename == candidate["fileName"].getStr: return candidate proc createDropdown(parent : Element, data :seq[string], onchange : proc(value : string)) = @@ -241,7 +241,7 @@ proc newPkgTable(parent: Element, arch: string, searchString : string, addButton files = f break for file in files: - data.add(file["filename"].getStr) + data.add(file["fileName"].getStr) createDropdown(elem, data, size_change_callback) "td": cb: @@ -309,7 +309,7 @@ htmlTreeappend document.body: for row in rows: let cbox = row.querySelector("td:first-child input") if cbox.checked: - dp.addPkg(readTableRow(selectedArch, row)["filename"].getStr) + dp.addPkg(readTableRow(selectedArch, row)["fileName"].getStr) oninput: searchString = $elem.value updateTable(event) diff --git a/settings.gradle b/settings.gradle index 004529f..9f24758 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,5 +1,11 @@ pluginManagement { repositories { + mavenLocal { + content { + includeGroup 'net.woggioni.gradle' + includeGroup 'net.woggioni.gradle.lombok' + } + } maven { url = 'https://woggioni.net/mvn/' content { @@ -34,4 +40,5 @@ rootProject.name = 'jpacrepo' include 'jpacrepo-api' include 'jpacrepo-impl' -include 'jpacrepo-client' \ No newline at end of file +include 'jpacrepo-client' +include 'jpacrepo-frontend' \ No newline at end of file diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index f3e0935..c37bd53 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -14,4 +14,5 @@ open module net.woggioni.jpacrepo { requires org.slf4j; requires net.woggioni.jpacrepo.api; + requires org.apache.commons.compress; } \ No newline at end of file diff --git a/src/main/java/net/woggioni/jpacrepo/factory/BeanFactory.java b/src/main/java/net/woggioni/jpacrepo/factory/BeanFactory.java index 96c6217..1fe276e 100644 --- a/src/main/java/net/woggioni/jpacrepo/factory/BeanFactory.java +++ b/src/main/java/net/woggioni/jpacrepo/factory/BeanFactory.java @@ -1,5 +1,6 @@ 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.config.AppConfig; @@ -7,6 +8,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +@ApplicationScoped public class BeanFactory { @Produces diff --git a/src/main/java/net/woggioni/jpacrepo/factory/PersistenceUnitFactory.java b/src/main/java/net/woggioni/jpacrepo/factory/PersistenceUnitFactory.java index 60de7a2..c9343bb 100644 --- a/src/main/java/net/woggioni/jpacrepo/factory/PersistenceUnitFactory.java +++ b/src/main/java/net/woggioni/jpacrepo/factory/PersistenceUnitFactory.java @@ -8,15 +8,15 @@ import net.woggioni.jpacrepo.config.AppConfig; import java.util.Properties; +@ApplicationScoped public class PersistenceUnitFactory { @Produces - @ApplicationScoped private EntityManagerFactory createEntityManagerFactory(AppConfig appConfig) { Properties properties = new Properties(); - properties.put("javax.persistence.schema-generation.database.action", + properties.put("jakarta.persistence.schema-generation.database.action", appConfig.getInitialSchemaAction().getValue()); - properties.put("javax.persistence.jtaDataSource", appConfig.getDataSourceJndi()); + properties.put("jakarta.persistence.jtaDataSource", appConfig.getDataSourceJndi()); return Persistence.createEntityManagerFactory("jpacrepo_pu", properties); } } diff --git a/src/main/java/net/woggioni/jpacrepo/persistence/QueryEngine.java b/src/main/java/net/woggioni/jpacrepo/persistence/QueryEngine.java index f7d1e06..1babb1c 100644 --- a/src/main/java/net/woggioni/jpacrepo/persistence/QueryEngine.java +++ b/src/main/java/net/woggioni/jpacrepo/persistence/QueryEngine.java @@ -5,99 +5,23 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.TypedQuery; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Path; import jakarta.persistence.criteria.Predicate; import jakarta.persistence.criteria.Root; +import net.woggioni.jpacrepo.api.model.CompressionFormat; import net.woggioni.jpacrepo.api.model.PkgData; +import net.woggioni.jpacrepo.api.model.PkgData_; +import net.woggioni.jpacrepo.api.model.PkgId; +import net.woggioni.jpacrepo.api.model.PkgId_; +import net.woggioni.jwo.JWO; import java.util.ArrayList; import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; public class QueryEngine { - private String entityName; - private String query; - private List where; - - public QueryEngine(Class cls) { - query = String.format("SELECT e FROM %s e", cls.getSimpleName()); - this.entityName = cls.getSimpleName(); - where = new ArrayList<>(); - } - - public QueryEngine(String entityName) { - query = String.format("SELECT e FROM %s e", entityName); - where = new ArrayList<>(); - } - - public QueryEngine select(String... fields) { - String[] strarr = new String[fields.length]; - for (int i = 0; i < fields.length; i++) { - strarr[i] = "e." + fields[i]; - } - query = "SELECT " + String.join(",", strarr) + " FROM " + entityName + " e"; - return this; - } - - public QueryEngine select() { - query = String.format("SELECT e FROM %s e", entityName); - return this; - } - - public QueryEngine where(String field, String operator, String value) { - where.add(String.format("e.%s %s '%s'", field, operator, value)); - return this; - } - - public String build() { - if (where.isEmpty()) { - return query; - } else { - return query + " WHERE " + String.join(" AND ", where); - } - } - - public static List searchPackage( - EntityManager em, String name, String version, String arch, int pageNumber, int pageSize, String fileName) { - CriteriaBuilder builder; - CriteriaQuery criteriaQuery; - Root entity; - - builder = em.getCriteriaBuilder(); - criteriaQuery = builder.createQuery(PkgData.class); - entity = criteriaQuery.from(PkgData.class); - Predicate finalPredicate = null, p; - - if (name != null && !name.isEmpty()) { - p = builder.equal(entity.get("name").get("id"), name); - finalPredicate = p; - } - if (version != null && !version.isEmpty()) { - p = builder.equal(entity.get("version"), version); - finalPredicate = finalPredicate != null ? builder.and(finalPredicate, p) : p; - } - if (arch != null && !arch.isEmpty()) { - p = builder.equal(entity.get("arch"), arch); - finalPredicate = finalPredicate != null ? builder.and(finalPredicate, p) : p; - } - if (fileName != null && !fileName.isEmpty()) { - p = builder.equal(entity.get("fileName"), fileName); - finalPredicate = finalPredicate != null ? builder.and(finalPredicate, p) : p; - } - - if (finalPredicate != null) { - criteriaQuery.select(entity).where(finalPredicate).orderBy(builder.asc(entity.get("fileName"))); - } else { - criteriaQuery.select(entity).orderBy(builder.asc(entity.get("fileName"))); - } - TypedQuery query = em.createQuery(criteriaQuery); - if (pageNumber >= 0) { - query.setFirstResult(pageNumber * pageSize); - } - if (pageSize > 0) { - query.setMaxResults(pageSize); - } - return query.getResultList(); - } public static long countResults(EntityManager em, String name, String version, String arch) { CriteriaBuilder builder; diff --git a/src/main/java/net/woggioni/jpacrepo/service/PacmanServiceEJB.java b/src/main/java/net/woggioni/jpacrepo/service/PacmanServiceEJB.java index 977ab62..a43e859 100644 --- a/src/main/java/net/woggioni/jpacrepo/service/PacmanServiceEJB.java +++ b/src/main/java/net/woggioni/jpacrepo/service/PacmanServiceEJB.java @@ -1,5 +1,7 @@ package net.woggioni.jpacrepo.service; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import jakarta.annotation.Resource; import jakarta.ejb.Asynchronous; import jakarta.ejb.ConcurrencyManagement; @@ -9,38 +11,45 @@ import jakarta.ejb.Lock; import jakarta.ejb.LockType; import jakarta.ejb.Remote; import jakarta.ejb.Schedule; -import jakarta.ejb.Startup; +import jakarta.ejb.Singleton; import jakarta.ejb.TransactionAttribute; import jakarta.ejb.TransactionAttributeType; import jakarta.ejb.TransactionManagement; import jakarta.ejb.TransactionManagementType; import jakarta.enterprise.concurrent.ManagedExecutorService; import jakarta.inject.Inject; -import jakarta.inject.Singleton; import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.TypedQuery; 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.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; import net.woggioni.jwo.JWO; import net.woggioni.jwo.Sup; +import net.woggioni.jwo.Tuple2; import org.slf4j.Logger; - import java.nio.file.Files; import java.nio.file.Path; -import java.time.OffsetDateTime; -import java.util.Calendar; -import java.util.Date; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Collection; import java.util.HashSet; import java.util.List; +import java.util.NavigableMap; +import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletionService; import java.util.concurrent.ExecutionException; @@ -48,8 +57,8 @@ import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; +import java.util.stream.Stream; -@Startup @Singleton @Lock(LockType.READ) @TransactionManagement(TransactionManagementType.CONTAINER) @@ -69,10 +78,6 @@ public class PacmanServiceEJB implements PacmanServiceLocal { @Resource(name = "DefaultManagedExecutorService") private ManagedExecutorService executor; - private final static String nameQuery = "SELECT pname FROM PkgName pname WHERE id = :name"; - private final static String hashQuery = "SELECT pdata FROM PkgData pdata WHERE md5sum = :md5sum"; - private final static String hashQueryCount = "SELECT count(pdata) FROM PkgData pdata WHERE md5sum = :md5sum"; - private void deletePkgData(EntityManager em, PkgData pkgData) { em.remove(pkgData); } @@ -88,7 +93,7 @@ public class PacmanServiceEJB implements PacmanServiceLocal { logger.info("Starting repository cleanup"); //Removes from DB the packages that have been deleted from filesystem logger.info("Searching for packages that are no more in the filesystem"); - List resultList = em.createQuery("SELECT p.fileName FROM PkgData p", String.class) + List resultList = Queries.listAllPackageFiles(em) .getResultList(); logger.info("Got list of filenames from db"); Set knownPkg = resultList.stream().filter(fileName -> { @@ -96,9 +101,9 @@ public class PacmanServiceEJB implements PacmanServiceLocal { boolean result = Files.exists(file); if (!result) { logger.info("Removing package {} which was not found in filesystem", file.getFileName()); - em.createQuery("SELECT p FROM PkgData p WHERE p.fileName = :fileName", PkgData.class) - .setParameter("fileName", file.getFileName().toString()) - .getResultList().forEach(pkgData -> deletePkgData(em, pkgData)); + Queries.getPackageByFileName(em, file.getFileName().toString()) + .getResultList() + .forEach(pkgData -> deletePkgData(em, pkgData)); } return result; }).collect(Collectors.toUnmodifiableSet()); @@ -106,28 +111,32 @@ public class PacmanServiceEJB implements PacmanServiceLocal { CompletionService completionService = new ExecutorCompletionService<>(executor); final Set> inProgress = new HashSet<>(); final int maxInProgress = Runtime.getRuntime().availableProcessors() * 5; - Con persistPackages = (Boolean drain) -> { - while ((drain && inProgress.size() > 0) || inProgress.size() > maxInProgress) { - Future future = completionService.poll(1, TimeUnit.SECONDS); - inProgress.remove(future); - PkgData pkgData; - try { - pkgData = future.get(); - } catch (ExecutionException ee) { - throw ee.getCause(); - } - persistPackage(em, pkgData); - } - }; - Files.list(ctx.getRepoFolder()).filter((Path file) -> { + Sup> fileListStreamSupplier = () -> Files.list(ctx.getRepoFolder()).filter((Path file) -> { String name = file.getFileName().toString(); return name.endsWith(".pkg.tar.xz") || name.endsWith(".pkg.tar.zst") || name.endsWith(".pkg.tar.gz"); - }).forEach((Con) file -> { + }); + long[] count = new long[] {0}; + long totalPackages = fileListStreamSupplier.get().count(); + + Con persistPackages = (Boolean drain) -> { + while ((drain && inProgress.size() > 0) || inProgress.size() > maxInProgress) { + Optional.ofNullable(completionService.poll(1, TimeUnit.SECONDS)) + .ifPresent((Con>) future -> { + inProgress.remove(future); + PkgData pkgData; + try { + pkgData = future.get(); + } catch (ExecutionException ee) { + throw ee.getCause(); + } + persistPackage(em, pkgData, ++count[0], totalPackages); + }); + } + }; + fileListStreamSupplier.get().forEach((Con) file -> { if (!knownPkg.contains(file.getFileName().toString()) || ((Sup) () -> { - TypedQuery query = em.createQuery("SELECT p.updTimestamp FROM PkgData p WHERE filename = :filename", Date.class); - query.setParameter("filename", file.getFileName().toString()); - Date result = query.getSingleResult(); - return Files.getLastModifiedTime(file).toMillis() > result.getTime(); + Instant result = Queries.getUpdateTimestampByFileName(em, file.getFileName().toString()).getSingleResult(); + return Files.getLastModifiedTime(file).toMillis() > result.toEpochMilli(); }).get()) { inProgress.add(completionService.submit(() -> { try { @@ -150,16 +159,13 @@ public class PacmanServiceEJB implements PacmanServiceLocal { } } - private void persistPackage(EntityManager em, PkgData pkgData) { - TypedQuery hquery = em.createQuery(hashQueryCount, Long.class); - hquery.setParameter("md5sum", pkgData.getMd5sum()); - if (hquery.getSingleResult() == 0) { - TypedQuery fquery = - em.createQuery("SELECT p FROM PkgData p WHERE p.fileName = :fileName", PkgData.class); - fquery.setParameter("fileName", pkgData.getFileName()); - fquery.getResultList().forEach(p -> deletePkgData(em, p)); + private void persistPackage(EntityManager em, PkgData pkgData, long count, long totalPackages) { + if (Queries.countPackagesByHash(em, pkgData.getMd5sum()).getSingleResult() == 0) { + Queries.getPackageByFileName(em, pkgData.getFileName()) + .getResultList() + .forEach(p -> deletePkgData(em, p)); em.persist(pkgData); - logger.info("Persisting package {}", pkgData.getFileName()); + logger.info("({}/{}) Persisting package {}", count, totalPackages, pkgData.getFileName()); } } @@ -174,28 +180,19 @@ public class PacmanServiceEJB implements PacmanServiceLocal { @SneakyThrows private void deletePackage(EntityManager em, String filename) { - TypedQuery fquery = em.createQuery("SELECT p FROM PkgData p WHERE fileName = :fileName", PkgData.class); - fquery.setParameter("fileName", filename); - List savedFiles = fquery.getResultList(); + List savedFiles = Queries.getPackageByFileName(em, filename).getResultList(); if (savedFiles.size() == 0) { throw JWO.newThrowable(IllegalArgumentException.class, "Package with name %s not found", filename); } - PkgData pkg = fquery.getResultList().get(0); - Files.delete(ctx.getFile(pkg)); + PkgData pkg = savedFiles.get(0); em.remove(pkg); + Files.delete(ctx.getFile(pkg)); } - - static private final String deleteQuery = - "SELECT p.fileName FROM PkgData p WHERE p.buildDate < :cutoff and p.id.name in \n" + "(SELECT p2.id.name FROM PkgData p2 GROUP BY p2.id.name HAVING count(p2.id.name) > :minVersions\n)"; - private void deleteOld(EntityManager em) { - TypedQuery query = em.createQuery(deleteQuery, String.class); - Calendar cutoff = Calendar.getInstance(); - cutoff.add(Calendar.YEAR, -2); - query.setParameter("cutoff", OffsetDateTime.now()); - query.setParameter("minVersions", 2L); - List list = query.getResultList(); - list.forEach(this::deletePackage); + Instant cutoff = Instant.now().minus(365 * 3, ChronoUnit.DAYS); + Queries.getOldPackages2Delete(em, cutoff, 3L) + .getResultStream() + .forEach(this::deletePackage); } @Override @@ -207,8 +204,101 @@ public class PacmanServiceEJB implements PacmanServiceLocal { @Override @TransactionAttribute(TransactionAttributeType.SUPPORTS) - public List searchPackage(String name, String version, String arch, int pageNumber, int pageSize, String fileName) { + public List searchPackage(String name, + String version, + String arch, + CompressionFormat compressionFormat, + String fileName, + int pageNumber, + int pageSize) { EntityManager em = emf.createEntityManager(); - return QueryEngine.searchPackage(em, name, version, arch, pageNumber, pageSize, null); + return Queries.searchPackage(em, name, version, arch, compressionFormat, fileName) + .setMaxResults(pageSize) + .setFirstResult(pageNumber * pageSize) + .getResultList(); + } + + @Override + public List searchName(@Nonnull String name) { + EntityManager em = emf.createEntityManager(); + return Queries.searchPackageName(em, name).getResultList(); + } + + @Override + public List searchByFileName(@Nonnull String fileName) { + EntityManager em = emf.createEntityManager(); + return Queries.searchPackagesByFileName(em, fileName).getResultList(); + } + + @Override + public List searchByHash(@Nonnull String hash) { + EntityManager em = emf.createEntityManager(); + return Queries.searchPackagesByHash(em, hash).getResultList(); + } + + @Override + public List listFiles() { + EntityManager em = emf.createEntityManager(); + return Queries.listFiles(em).getResultList(); + } + + @Override + public List listHashes() { + EntityManager em = emf.createEntityManager(); + return Queries.listHashes(em).getResultList(); + } + + @Override + @TransactionAttribute(TransactionAttributeType.SUPPORTS) + public List searchPkgId( + String name, String version, String arch, CompressionFormat compressionFormat) { + EntityManager em = emf.createEntityManager(); + return Queries.searchPackages(em, name, version, arch, compressionFormat).getResultList(); + } + + @Override + @SneakyThrows + @Nullable + public Long getFileSize(String fileName) { + java.nio.file.Path res = ctx.getFile(fileName); + if (!Files.exists(res)) return null; + return Files.size(res); + } + + public Set missingFiles(Collection fileNames) { + EntityManager em = emf.createEntityManager(); + Stream result = fileNames.stream(); + Set existing = Queries.getExistingFiles(em, fileNames) + .getResultStream().collect(CollectionUtils.toUnmodifiableTreeSet()); + return result.filter(JWO.not(existing::contains)) + .collect(CollectionUtils.toUnmodifiableTreeSet()); + } + + public NavigableMap getPkgMap() { + EntityManager em = emf.createEntityManager(); + return Queries.getPkgMap(em).getResultStream().map(tuple -> { + PkgId pkgId = tuple.get(0, PkgId.class); + String filename = tuple.get(1, String.class); + long size = tuple.get(2, Long.class); + String md5sum = tuple.get(3, String.class);; + PkgTuple pkgTuple = new PkgTuple(); + pkgTuple.setFileName(filename); + pkgTuple.setSize(size); + pkgTuple.setMd5sum(md5sum); + return Tuple2.newInstance(pkgId, pkgTuple); + }).collect( + CollectionUtils.toUnmodifiableTreeMap( + Tuple2::get_1, + Tuple2::get_2, + PkgIdComparator.getComparator() + ) + ); + } + + @Nullable + @Override + public PkgData getPackage(PkgId pkgId) { + EntityManager em = emf.createEntityManager(); + return em.find(PkgData.class, pkgId); } } \ No newline at end of file diff --git a/src/main/java/net/woggioni/jpacrepo/service/PacmanWebService.java b/src/main/java/net/woggioni/jpacrepo/service/PacmanWebService.java index d6930fd..e680bc9 100644 --- a/src/main/java/net/woggioni/jpacrepo/service/PacmanWebService.java +++ b/src/main/java/net/woggioni/jpacrepo/service/PacmanWebService.java @@ -2,7 +2,6 @@ package net.woggioni.jpacrepo.service; import jakarta.ejb.ConcurrencyManagement; import jakarta.ejb.ConcurrencyManagementType; -import jakarta.ejb.Singleton; import jakarta.ejb.TransactionAttribute; import jakarta.ejb.TransactionAttributeType; import jakarta.ejb.TransactionManagement; @@ -35,7 +34,6 @@ import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.StreamingOutput; import jakarta.ws.rs.core.UriInfo; import lombok.SneakyThrows; -import lombok.val; import net.woggioni.jpacrepo.api.model.CompressionFormat; import net.woggioni.jpacrepo.api.model.PkgData; import net.woggioni.jpacrepo.api.model.PkgId; @@ -43,9 +41,9 @@ import net.woggioni.jpacrepo.api.service.PacmanServiceLocal; import net.woggioni.jpacrepo.config.AppConfig; import net.woggioni.jpacrepo.impl.model.CompressionFormatImpl; import net.woggioni.jpacrepo.impl.model.PkgDataImpl; -import net.woggioni.jpacrepo.service.wire.PkgDataList; -import net.woggioni.jpacrepo.service.wire.PkgTuple; -import net.woggioni.jpacrepo.service.wire.StringList; +import net.woggioni.jpacrepo.api.wire.PkgDataList; +import net.woggioni.jpacrepo.api.wire.PkgTuple; +import net.woggioni.jpacrepo.api.wire.StringList; import net.woggioni.jpacrepo.version.PkgIdComparator; import net.woggioni.jpacrepo.version.VersionComparator; import net.woggioni.jwo.CollectionUtils; @@ -56,7 +54,6 @@ import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; import org.slf4j.Logger; - import java.io.InputStream; import java.io.OutputStream; import java.net.URI; @@ -72,11 +69,9 @@ import java.util.Map; import java.util.NavigableMap; import java.util.NavigableSet; import java.util.Optional; -import java.util.Set; import java.util.TreeMap; import java.util.stream.Collectors; -@Singleton @Path("/pkg") @ConcurrencyManagement(ConcurrencyManagementType.BEAN) @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) @@ -107,36 +102,7 @@ public class PacmanWebService { synchronized(this) { result = cachedMap; if (result == null) { - EntityManager em = emf.createEntityManager(); - TypedQuery query = em.createQuery( - "SELECT pkg.id.name, pkg.id.version, pkg.id.arch, pkg.id.compressionFormat, pkg.fileName, pkg.size, pkg.md5sum " + - "FROM PkgData pkg ORDER BY pkg.id.name, pkg.id.version, pkg.id.arch", - Object[].class); - cachedMap = query.getResultStream() - .map((Object[] pkg) -> { - String name = (String) pkg[0]; - String version = (String) pkg[1]; - String arch = (String) pkg[2]; - CompressionFormat compressionFormat = (CompressionFormat) pkg[3]; - String filename = (String) pkg[4]; - long size = (long) pkg[5]; - String md5sum = (String) pkg[6]; - PkgTuple tuple = new PkgTuple(); - tuple.setFilename(filename); - tuple.setSize(size); - tuple.setMd5sum(md5sum); - PkgId id = new PkgId(); - id.setName(name); - id.setVersion(version); - id.setArch(arch); - id.setCompressionFormat(compressionFormat); - return Tuple2.newInstance(id, tuple); - }).collect( - CollectionUtils.toUnmodifiableTreeMap( - Tuple2::get_1, - Tuple2::get_2, - PkgIdComparator.getComparator()) - ); + cachedMap = service.getPkgMap(); ctx.getInvalidateCache().set(false); result = cachedMap; } @@ -162,46 +128,26 @@ public class PacmanWebService { @GET @Path("searchByName/{name}") public Response searchByName(@PathParam("name") String name) { - val em = emf.createEntityManager(); if (name == null) throw new WebApplicationException(Response.Status.BAD_REQUEST); - String query = String.format("SELECT pkgId.name FROM PkgId pkgId WHERE LOWER(pkgId.name) LIKE '%%%s%%' ORDER BY pkgId.name", name); - return Response.ok(em.createQuery(query, String.class).getResultList()).build(); + return Response.ok(service.searchName(name)).build(); } @GET @Path("searchByHash/{md5sum}") public Response searchByHash(@PathParam("md5sum") String md5sum) { + if (md5sum == null) throw new WebApplicationException(Response.Status.BAD_REQUEST); return getPackageByHash(md5sum); } @GET - @Path("list/{name}") - public Response getPackage(@PathParam("name") String name) { - EntityManager em = emf.createEntityManager(); - TypedQuery query = em.createQuery("SELECT pkg.id.version FROM PkgData pkg WHERE pkg.id.name = :name ORDER BY pkg.id.version", String.class); - query.setParameter("name", name); - return Response.ok(new StringList(query.getResultList())).build(); - } - - @GET - @Path("list/{name}/{version}") - public Response getPackage(@PathParam("name") String name, @PathParam("version") String version) { - EntityManager em = emf.createEntityManager(); - TypedQuery query = em.createQuery("SELECT pkg.arch FROM PkgData pkg WHERE pkg.id.name = :name AND pkg.id.version = :version ORDER BY pkg.id.arch", String.class); - query.setParameter("name", name); - query.setParameter("version", version); - return Response.ok(new StringList(query.getResultList())).build(); - } - - @GET - @Path("list/{name}/{version}/{arch}") - public Response getPackage(@PathParam("name") String name, @PathParam("version") String version, @PathParam("arch") String arch) { - EntityManager em = emf.createEntityManager(); - TypedQuery query = em.createQuery("SELECT pkg FROM PkgData pkg WHERE " + "pkg.id.name = :name AND " + "pkg.id.version = :version AND " + "pkg.id.arch = :arch " + "ORDER BY pkg.arch", PkgData.class); - query.setParameter("name", name); - query.setParameter("version", version); - query.setParameter("arch", arch); - return Response.ok(query.getSingleResult()).build(); + @Path("id/search") + public Response searchPackages( + @QueryParam("name") String name, + @QueryParam("version") String version, + @QueryParam("arch") String arch, + @QueryParam("compressionFormat") CompressionFormat compressionFormat + ) { + return Response.ok(service.searchPkgId(name, version, arch, compressionFormat)).build(); } @GET @@ -213,7 +159,7 @@ public class PacmanWebService { cc.setMustRevalidate(true); cc.setNoCache(true); - NavigableMap cachedMap = getCachedMap(); + NavigableMap cachedMap = service.getPkgMap(); EntityTag etag = new EntityTag(Integer.toString(cachedMap.hashCode()), false); Response.ResponseBuilder builder = request.evaluatePreconditions(etag); if (builder == null) { @@ -227,7 +173,7 @@ public class PacmanWebService { Collectors.mapping( Map.Entry::getValue, CollectionUtils.toUnmodifiableTreeSet( - Comparator.comparing(PkgTuple::getFilename) + Comparator.comparing(PkgTuple::getFileName) ) ) ) @@ -244,31 +190,27 @@ public class PacmanWebService { @GET @Path("hashes") public Response getHashes() { - EntityManager em = emf.createEntityManager(); - TypedQuery query = em.createQuery("SELECT p.md5sum FROM PkgData p", String.class); - return Response.ok(new StringList(query.getResultList())).build(); + return Response.ok(new StringList(service.listHashes())).build(); } @GET @Path("files") public Response getFiles() { - EntityManager em = emf.createEntityManager(); - TypedQuery query = em.createQuery("SELECT p.fileName FROM PkgData p", String.class); - return Response.ok(new StringList(query.getResultList())).build(); + return Response.ok(new StringList(service.listFiles())).build(); } private Response getPackageByHash(String md5sum) { - EntityManager em = emf.createEntityManager(); - TypedQuery hquery = em.createNamedQuery("searchByHash", PkgData.class); - if (md5sum != null) hquery.setParameter("md5sum", md5sum); - return manageQueryResult(hquery.getResultList(), true); + return manageQueryResult(service.searchByHash(md5sum), true); } - private Response getPackageByFileName(String file) { - EntityManager em = emf.createEntityManager(); - TypedQuery fnquery = em.createNamedQuery("searchByFileName", PkgData.class); - fnquery.setParameter("fileName", file); - return manageQueryResult(fnquery.getResultList(), true); + @GET + @Path("file/{fileName}") + private Response getPackageByFileName(String fileName) { + try { + return Response.ok(service.searchByFileName(fileName)).build(); + } catch (NoResultException nre) { + return Response.status(Response.Status.NOT_FOUND).build(); + } } @GET @@ -282,9 +224,9 @@ public class PacmanWebService { EntityTag etag = new EntityTag(Integer.toString(getCachedMap().hashCode())); Response.ResponseBuilder builder = request.evaluatePreconditions(etag); if (builder == null) { - java.nio.file.Path res = ctx.getFile(fileName); - if (!Files.exists(res)) throw new NotFoundException(String.format("File '%s' was not found", fileName)); - builder = Response.ok(Files.size(res)); + Long size = service.getFileSize(fileName); + if (size == null) throw new NotFoundException(String.format("File '%s' was not found", fileName)); + builder = Response.ok(size); builder.tag(etag); } builder.cacheControl(cc); @@ -296,37 +238,31 @@ public class PacmanWebService { @Path("download/{filename}") @Produces(MediaType.APPLICATION_OCTET_STREAM) public Response downloadPackage(@PathParam("filename") String fileName) { - EntityManager em = emf.createEntityManager(); - TypedQuery fnquery = em.createNamedQuery("searchByFileName", PkgData.class); - fnquery.setParameter("fileName", fileName); - try { - PkgData pkg = fnquery.getSingleResult(); + List pkgs = service.searchByFileName(fileName); + if(pkgs.isEmpty()) { + return Response.status(Response.Status.NOT_FOUND).build(); + } else if(pkgs.size() == 1) { + PkgData pkg = pkgs.get(0); StreamingOutput stream = (OutputStream output) -> { - try(InputStream is = Files.newInputStream(ctx.getFile(pkg))) { + try (InputStream is = Files.newInputStream(ctx.getFile(pkg))) { JWO.copy(is, output, 0x10000); } }; return Response.ok(stream).header("Content-Length", Files.size(ctx.getFile(pkg))).build(); - } catch(NoResultException nre) { - throw new NotFoundException(); + } else { + throw new RuntimeException("This should never happen"); } } @POST @Path("/doYouWantAny") @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) - public Response doYouWantAny(List filenames) { - EntityManager em = emf.createEntityManager(); - Set result = filenames.stream().collect(CollectionUtils.toTreeSet()); - if (!result.isEmpty()) { - TypedQuery query = em.createQuery("SELECT pkg.fileName from PkgData pkg WHERE pkg.fileName in :filenames", String.class); - query.setParameter("filenames", filenames); - Set toBeRemoved = query.getResultStream().collect(CollectionUtils.toTreeSet()); - result.removeAll(toBeRemoved); - return Response.ok(new StringList(result)).build(); + public Response doYouWantAny(List fileNames) { + if (fileNames.isEmpty()) { + return Response.ok(fileNames).build(); } else { - return Response.ok(result.toArray()).build(); + return Response.ok(new StringList(service.missingFiles(fileNames))).build(); } } @@ -342,9 +278,7 @@ public class PacmanWebService { EntityManager em = emf.createEntityManager(); if (filename == null) throw new BadRequestException(); java.nio.file.Path file = Files.createTempFile(ctx.getRepoFolder(), filename, null); - TypedQuery fquery = em.createNamedQuery("searchByFileName", PkgData.class); - fquery.setParameter("fileName", filename); - List savedFiles = fquery.getResultList(); + List savedFiles = service.searchByFileName(filename); Response result; if (savedFiles.size() > 0) result = Response.notModified().build(); else { @@ -374,14 +308,23 @@ public class PacmanWebService { @GET @Path("/search") - public List searchPackage( + public PkgDataList searchPackage( @QueryParam("name") String name, @QueryParam("version") String version, @QueryParam("arch") String arch, + @QueryParam("compressionFormat") CompressionFormat compressionFormat, + @QueryParam("fileName") String fileName, @QueryParam("page") int pageNumber, - @QueryParam("pageSize") int pageSize, - @QueryParam("fileName") String fileName) { - return service.searchPackage(name, version, arch, pageNumber, pageSize, fileName); + @QueryParam("pageSize") int pageSize) { + return new PkgDataList(service.searchPackage( + name, + version, + arch, + compressionFormat, + fileName, + pageNumber, + Optional.of(pageSize).filter(it -> it > 0).orElse(10) + )); } @OPTIONS diff --git a/src/main/java/net/woggioni/jpacrepo/service/jpa/Queries.java b/src/main/java/net/woggioni/jpacrepo/service/jpa/Queries.java new file mode 100644 index 0000000..dd51248 --- /dev/null +++ b/src/main/java/net/woggioni/jpacrepo/service/jpa/Queries.java @@ -0,0 +1,281 @@ +package net.woggioni.jpacrepo.service.jpa; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.Tuple; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Path; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; +import jakarta.persistence.criteria.Subquery; +import jakarta.persistence.metamodel.EntityType; +import jakarta.persistence.metamodel.Metamodel; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import net.woggioni.jpacrepo.api.model.CompressionFormat; +import net.woggioni.jpacrepo.api.model.PkgData; +import net.woggioni.jpacrepo.api.model.PkgData_; +import net.woggioni.jpacrepo.api.model.PkgId; +import net.woggioni.jpacrepo.api.model.PkgId_; +import net.woggioni.jwo.JWO; + +import java.time.Instant; +import java.util.Collection; +import java.util.Optional; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class Queries { + private interface PredicateSupplier { + Predicate get(CriteriaBuilder cb, Root root); + } + + public static TypedQuery listAllPackageFiles(EntityManager em) { + CriteriaBuilder cb = em.getCriteriaBuilder(); + Metamodel metamodel = em.getMetamodel(); + CriteriaQuery criteriaQuery = cb.createQuery(String.class); + Root root = criteriaQuery.from(metamodel.entity(PkgData.class)); + criteriaQuery.select(root.get(PkgData_.fileName)); + return em.createQuery(criteriaQuery); + } + + public static TypedQuery getPackageByFileName(EntityManager em, String fileName) { + CriteriaBuilder cb = em.getCriteriaBuilder(); + Metamodel metamodel = em.getMetamodel(); + EntityType entity = metamodel.entity(PkgData.class); + CriteriaQuery criteriaQuery = cb.createQuery(PkgData.class); + Root root = criteriaQuery.from(entity); + Predicate predicate = cb.equal(root.get(PkgData_.fileName), fileName); + criteriaQuery.select(root).where(predicate); + return em.createQuery(criteriaQuery); + } + + public static TypedQuery getUpdateTimestampByFileName(EntityManager em, String fileName) { + CriteriaBuilder cb = em.getCriteriaBuilder(); + Metamodel metamodel = em.getMetamodel(); + EntityType entity = metamodel.entity(PkgData.class); + CriteriaQuery criteriaQuery = cb.createQuery(Instant.class); + Root root = criteriaQuery.from(entity); + Predicate predicate = cb.equal(root.get(PkgData_.fileName), fileName); + criteriaQuery.select(root.get(PkgData_.updTimestamp)).where(predicate); + return em.createQuery(criteriaQuery); + } + + public static TypedQuery getOldPackages2Delete( + EntityManager em, + Instant cutoff, + long minNumberOfDifferentVersions) { + CriteriaBuilder cb = em.getCriteriaBuilder(); + CriteriaQuery criteriaQuery = cb.createQuery(String.class); + Metamodel metamodel = em.getMetamodel(); + EntityType entity = metamodel.entity(PkgData.class); + Root pkgDataRoot = criteriaQuery.from(entity); + Subquery subQuery = criteriaQuery.subquery(String.class); + Root pkgDataRootSub = subQuery.from(entity); + Path pkgIdPathSub = pkgDataRootSub.get(PkgData_.id); + Predicate havingPredicate = cb.greaterThan(cb.count( + pkgIdPathSub.get(PkgId_.version)), minNumberOfDifferentVersions); + subQuery.select(pkgIdPathSub.get(PkgId_.name)) + .groupBy( + pkgIdPathSub.get(PkgId_.name), + pkgIdPathSub.get(PkgId_.arch) + ).having(havingPredicate); + Predicate predicate = cb.and( + cb.lessThan(pkgDataRoot.get(PkgData_.buildDate), cutoff), + pkgDataRoot.get(PkgData_.id).get(PkgId_.name).in(subQuery.getSelection()) + ); + criteriaQuery.select(pkgDataRoot.get(PkgData_.fileName)) + .where(predicate) + .orderBy(cb.asc(pkgDataRoot.get(PkgData_.fileName))); + return em.createQuery(criteriaQuery); + } + + public static TypedQuery countPackagesByHash(EntityManager em, String hash) { + CriteriaBuilder cb = em.getCriteriaBuilder(); + CriteriaQuery criteriaQuery = cb.createQuery(Long.class); + Metamodel metamodel = em.getMetamodel(); + EntityType entity = metamodel.entity(PkgData.class); + Root pkgDataRoot = criteriaQuery.from(entity); + Predicate predicate = cb.equal(pkgDataRoot.get(PkgData_.md5sum), hash); + criteriaQuery.select(cb.count(pkgDataRoot)).where(predicate); + return em.createQuery(criteriaQuery); + } + + private static TypedQuery searchPackagesByPredicate(EntityManager em, + PredicateSupplier predicateSupplier) { + CriteriaBuilder cb = em.getCriteriaBuilder(); + CriteriaQuery criteriaQuery = cb.createQuery(PkgData.class); + Metamodel metamodel = em.getMetamodel(); + EntityType entity = metamodel.entity(PkgData.class); + Root pkgDataRoot = criteriaQuery.from(entity); + criteriaQuery.select(pkgDataRoot).where(predicateSupplier.get(cb, pkgDataRoot)); + return em.createQuery(criteriaQuery); + } + + public static TypedQuery searchPackagesByHash(EntityManager em, String hash) { + return searchPackagesByPredicate(em, (cb, root) -> cb.equal(root.get(PkgData_.md5sum), hash)); + } + + public static TypedQuery searchPackagesByFileName(EntityManager em, String fileName) { + return searchPackagesByPredicate(em, (cb, root) -> cb.equal(root.get(PkgData_.fileName), fileName)); + } + + public static TypedQuery getSize(EntityManager em, String fileName) { + CriteriaBuilder cb = em.getCriteriaBuilder(); + CriteriaQuery criteriaQuery = cb.createQuery(Long.class); + Metamodel metamodel = em.getMetamodel(); + EntityType entity = metamodel.entity(PkgData.class); + Root pkgDataRoot = criteriaQuery.from(entity); + Predicate predicate = cb.equal(pkgDataRoot.get(PkgData_.fileName), fileName); + criteriaQuery.select(pkgDataRoot.get(PkgData_.size)).where(predicate); + return em.createQuery(criteriaQuery); + } + + public static TypedQuery searchPackageName(EntityManager em, String needle) { + CriteriaBuilder cb = em.getCriteriaBuilder(); + CriteriaQuery criteriaQuery = cb.createQuery(String.class); + Metamodel metamodel = em.getMetamodel(); + EntityType entity = metamodel.entity(PkgData.class); + Root root = criteriaQuery.from(entity); + Path pkgIdPath = root.get(PkgData_.id); + Predicate predicate = cb.like( + cb.lower(pkgIdPath.get(PkgId_.name)), + "%%" + needle + "%%" + ); + criteriaQuery.select(pkgIdPath.get(PkgId_.name)) + .where(predicate) + .orderBy(cb.asc(pkgIdPath.get(PkgId_.name))); + return em.createQuery(criteriaQuery); + } + + public static TypedQuery listFiles(EntityManager em) { + CriteriaBuilder cb = em.getCriteriaBuilder(); + CriteriaQuery criteriaQuery = cb.createQuery(String.class); + Metamodel metamodel = em.getMetamodel(); + EntityType entity = metamodel.entity(PkgData.class); + Root root = criteriaQuery.from(entity); + criteriaQuery.select(root.get(PkgData_.fileName)) + .orderBy(cb.asc(root.get(PkgData_.fileName))); + return em.createQuery(criteriaQuery); + } + + public static TypedQuery listHashes(EntityManager em) { + CriteriaBuilder cb = em.getCriteriaBuilder(); + CriteriaQuery criteriaQuery = cb.createQuery(String.class); + Metamodel metamodel = em.getMetamodel(); + EntityType entity = metamodel.entity(PkgData.class); + Root root = criteriaQuery.from(entity); + criteriaQuery.select(root.get(PkgData_.md5sum)) + .orderBy(cb.asc(root.get(PkgData_.md5sum))); + return em.createQuery(criteriaQuery); + } + + public static TypedQuery searchPackages(EntityManager em, + String name, + String version, + String arch, + CompressionFormat compressionFormat) { + CriteriaBuilder cb = em.getCriteriaBuilder(); + CriteriaQuery criteriaQuery = cb.createQuery(PkgId.class); + Metamodel metamodel = em.getMetamodel(); + EntityType entity = metamodel.entity(PkgData.class); + Root root = criteriaQuery.from(entity); + Path pkgIdRoot = root.get(PkgData_.id); + Predicate[] predicates = JWO.streamCat( + Optional.ofNullable(name) + .map(it -> cb.equal(pkgIdRoot.get(PkgId_.name), it)).stream(), + Optional.ofNullable(version) + .map(it -> cb.equal(pkgIdRoot.get(PkgId_.version), it)).stream(), + Optional.ofNullable(arch) + .map(it -> cb.equal(pkgIdRoot.get(PkgId_.name), it)).stream(), + Optional.ofNullable(compressionFormat) + .map(it -> cb.equal(pkgIdRoot.get(PkgId_.compressionFormat), it)).stream() + ).toArray(Predicate[]::new); + criteriaQuery.select(pkgIdRoot) + .where(cb.and(predicates)) + .orderBy( + cb.asc(pkgIdRoot.get(PkgId_.name)), + cb.asc(pkgIdRoot.get(PkgId_.version)), + cb.asc(pkgIdRoot.get(PkgId_.arch)), + cb.asc(pkgIdRoot.get(PkgId_.compressionFormat)) + ); + return em.createQuery(criteriaQuery); + } + + public static TypedQuery getExistingFiles(EntityManager em, Collection fileNames) { + CriteriaBuilder cb = em.getCriteriaBuilder(); + CriteriaQuery criteriaQuery = cb.createQuery(String.class); + Metamodel metamodel = em.getMetamodel(); + EntityType entity = metamodel.entity(PkgData.class); + Root root = criteriaQuery.from(entity); + Predicate predicate = root.get(PkgData_.fileName).in(fileNames); + criteriaQuery.select(root.get(PkgData_.fileName)).where(predicate); + return em.createQuery(criteriaQuery); + } + + public static TypedQuery getPkgMap(EntityManager em) { + CriteriaBuilder cb = em.getCriteriaBuilder(); + CriteriaQuery criteriaQuery = cb.createTupleQuery(); + Metamodel metamodel = em.getMetamodel(); + EntityType entity = metamodel.entity(PkgData.class); + Root root = criteriaQuery.from(entity); + Path idPath = root.get(PkgData_.id); + criteriaQuery.multiselect( + idPath, + root.get(PkgData_.fileName), + root.get(PkgData_.size), + root.get(PkgData_.md5sum) + ).orderBy( + cb.asc(idPath.get(PkgId_.arch)), + cb.asc(idPath.get(PkgId_.name)), + cb.asc(idPath.get(PkgId_.version)), + cb.asc(idPath.get(PkgId_.compressionFormat)) + ); + return em.createQuery(criteriaQuery); + } + + public static TypedQuery searchPackage( + EntityManager em, String name, + String version, + String arch, + CompressionFormat compressionFormat, + String fileName) { + CriteriaQuery criteriaQuery; + CriteriaBuilder builder = em.getCriteriaBuilder(); + criteriaQuery = builder.createQuery(PkgData.class); + Root entity = criteriaQuery.from(PkgData.class); + Path pkgIdPath = entity.get(PkgData_.id); + Predicate predicate = builder.and(JWO.streamCat( + JWO.optional2Stream( + Optional.ofNullable(name) + .filter(JWO.not(String::isEmpty)) + .map(it -> builder.equal(pkgIdPath.get(PkgId_.name), it))), + JWO.optional2Stream( + Optional.ofNullable(version) + .filter(JWO.not(String::isEmpty)) + .map(it -> builder.equal(pkgIdPath.get(PkgId_.version), it))), + JWO.optional2Stream( + Optional.ofNullable(arch) + .filter(JWO.not(String::isEmpty)) + .map(it -> builder.equal(pkgIdPath.get(PkgId_.arch), it))), + JWO.optional2Stream( + Optional.ofNullable(compressionFormat) + .map(it -> builder.equal(pkgIdPath.get(PkgId_.compressionFormat), it))), + JWO.optional2Stream( + Optional.ofNullable(fileName) + .filter(JWO.not(String::isEmpty)) + .map(it -> builder.equal(entity.get(PkgData_.fileName), it))) + ).toArray(Predicate[]::new)); + criteriaQuery.select(entity) + .where(predicate) + .orderBy( + builder.asc(pkgIdPath.get(PkgId_.name)), + builder.asc(pkgIdPath.get(PkgId_.version)), + builder.asc(pkgIdPath.get(PkgId_.arch)), + builder.asc(pkgIdPath.get(PkgId_.compressionFormat)), + builder.asc(entity.get(PkgData_.fileName)) + ); + return em.createQuery(criteriaQuery); + } + +} diff --git a/src/main/java/net/woggioni/jpacrepo/service/jpa/QueryBuilder.java b/src/main/java/net/woggioni/jpacrepo/service/jpa/QueryBuilder.java new file mode 100644 index 0000000..93cf729 --- /dev/null +++ b/src/main/java/net/woggioni/jpacrepo/service/jpa/QueryBuilder.java @@ -0,0 +1,35 @@ +package net.woggioni.jpacrepo.service.jpa; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Root; +import jakarta.persistence.criteria.Selection; +import jakarta.persistence.metamodel.Metamodel; + +public class QueryBuilder { + private final EntityManager em; + private final CriteriaBuilder cb; + private final CriteriaQuery criteriaQuery; + private final Metamodel metamodel; + public QueryBuilder(EntityManager em, Class cls) { + this.em = em; + this.cb = em.getCriteriaBuilder(); + this.criteriaQuery = cb.createQuery(cls); + this.metamodel = em.getMetamodel(); + } + + public QueryBuilder select(Selection a) { + criteriaQuery.select(a); + return this; + } + + public Root from(Class cls) { + return criteriaQuery.from(metamodel.entity(cls)); + } + + public TypedQuery build() { + return em.createQuery(criteriaQuery); + } +} diff --git a/src/main/resources/META-INF/beans.xml b/src/main/resources/META-INF/beans.xml index e69de29..028820e 100644 --- a/src/main/resources/META-INF/beans.xml +++ b/src/main/resources/META-INF/beans.xml @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/src/main/resources/META-INF/persistence.xml b/src/main/resources/META-INF/persistence.xml index 87f18df..6da4456 100644 --- a/src/main/resources/META-INF/persistence.xml +++ b/src/main/resources/META-INF/persistence.xml @@ -11,14 +11,14 @@ - - + - - + - + diff --git a/src/main/resources/WEB-INF/web.xml b/src/main/resources/WEB-INF/web.xml index 682c72d..650895e 100644 --- a/src/main/resources/WEB-INF/web.xml +++ b/src/main/resources/WEB-INF/web.xml @@ -12,7 +12,7 @@ 404 - 404.xhtml + /404.xhtml diff --git a/src/test/java/ClientTest.java b/src/test/java/ClientTest.java index 6c82359..0f40962 100644 --- a/src/test/java/ClientTest.java +++ b/src/test/java/ClientTest.java @@ -116,6 +116,7 @@ public class ClientTest { } } + @Test public void invokeStatelessBean() throws Exception { Properties prop = new Properties(); InputStream in = getClass().getClassLoader().getResourceAsStream("jboss-ejb-client.properties"); @@ -123,13 +124,13 @@ public class ClientTest { prop.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming"); prop.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory"); - prop.put(Context.PROVIDER_URL, "http-remoting://localhost:8080"); -// prop.put(Context.PROVIDER_URL, "http-remoting://nuc:8080"); + prop.put(Context.PROVIDER_URL, "http-remoting://localhost:7080"); +// prop.put(Context.PROVIDER_URL, "http-remoting://localhost:8080"); // prop.put(Context.PROVIDER_URL, "remote://odroid-u3:4447"); -// prop.put(Context.SECURITY_PRINCIPAL, "walter"); -// prop.put(Context.SECURITY_CREDENTIALS, "27ff5990757d1d"); - prop.put(Context.SECURITY_PRINCIPAL, "luser"); - prop.put(Context.SECURITY_CREDENTIALS, "123456"); + prop.put(Context.SECURITY_PRINCIPAL, "walter"); + prop.put(Context.SECURITY_CREDENTIALS, "27ff5990757d1d"); +// prop.put(Context.SECURITY_PRINCIPAL, "luser"); +// prop.put(Context.SECURITY_CREDENTIALS, "123456"); prop.put("jboss.naming.client.ejb.context", true); Context context = new InitialContext(prop); @@ -137,11 +138,21 @@ public class ClientTest { traverseJndiNode("/", context); // final PacmanService stateService = (PacmanService) ctx.lookup("/jpacrepo-1.0/remote/PacmanServiceEJB!service.PacmanService"); final PacmanServiceRemote service = (PacmanServiceRemote) ctx.lookup( - "/jpacrepo-2.0-SNAPSHOT/PacmanServiceEJB!net.woggioni.jpacrepo.service.PacmanServiceRemote" + "/jpacrepo-2023.07/PacmanServiceEJB!net.woggioni.jpacrepo.api.service.PacmanServiceRemote" ); // List pkgs = service.searchPackage("google-earth", null, null, 1, 10); // System.out.println(new XStream().toXML(pkgs)); service.syncDB(); +// service.searchPkgId("jre8-openjdk", null, null, null) +// .stream() +// .map(service::getPackage) +// .filter(Objects::nonNull) +// .map(PkgData::getFileName) +// .forEach(System.out::println); +// final TestEJB service = (TestEJB) ctx.lookup( +// "/jpacrepo-2023.07TestEJBImpl!net.woggioni.jpacrepo.api.service.TestEJB" +// ); +// System.out.println(service.hello()); } } \ No newline at end of file diff --git a/src/test/java/EJBTest.java b/src/test/java/EJBTest.java new file mode 100644 index 0000000..71ebc5a --- /dev/null +++ b/src/test/java/EJBTest.java @@ -0,0 +1,34 @@ +import jakarta.ejb.embeddable.EJBContainer; +import org.junit.jupiter.api.BeforeEach; + +import javax.naming.Context; +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Locale; +import java.util.Properties; + +public class EJBTest { + + private EJBContainer ejbContainer = null; + + private Context context = null; + + @BeforeEach + public void init() { + Arrays.stream(System.getProperty("java.class.path").split(System.getProperty("path.separator"))) + .filter(it -> it.toLowerCase(Locale.ROOT).endsWith(".war")) + .forEach(System.out::println); + Properties properties = new Properties(); + File[] files = Arrays.stream(System.getProperty("jpacrepo.jar.path").split(System.getProperty("path.separator"))) + .map(Path::of) + .filter(Files::exists) + .map(Path::toFile) + .toArray(File[]::new); + properties.put(EJBContainer.MODULES, files); + ejbContainer = EJBContainer.createEJBContainer(properties); + context = ejbContainer.getContext(); + } + +}