From 8412c99caa064398ae47d7121311d1f4eddf0a58 Mon Sep 17 00:00:00 2001 From: Sven F <9976560+sven1103@users.noreply.github.com> Date: Tue, 16 Apr 2024 14:07:33 +0200 Subject: [PATCH 1/5] Implement first data scanner logic (#1) First basic core application logic --- .github/workflows/build_package.yml | 11 +- .github/workflows/codeql-analysis.yml | 15 +- .github/workflows/create-release.yml | 9 +- .github/workflows/nexus-publish-snapshots.yml | 9 +- .github/workflows/run_tests.yml | 9 +- LICENSE | 2 +- pom.xml | 211 +++++++++------ src/main/groovy/.DS_Store | Bin 6148 -> 0 bytes src/main/groovy/life/.DS_Store | Bin 6148 -> 0 bytes src/main/groovy/life/qbic/.DS_Store | Bin 6148 -> 0 bytes .../springminimaltemplate/AppConfig.groovy | 32 --- .../CodingPrayersMessageService.groovy | 29 -- .../DeveloperNews.groovy | 20 -- .../MessageService.groovy | 18 -- .../springminimaltemplate/NewsMedia.groovy | 17 -- .../SpringMinimalTemplateApplication.groovy | 23 -- .../life/qbic/data/processing/AppConfig.java | 88 ++++++ .../qbic/data/processing/Application.java | 81 ++++++ .../ConcurrentRegistrationQueue.java | 87 ++++++ .../qbic/data/processing/ErrorSummary.java | 40 +++ .../qbic/data/processing/GlobalConfig.java | 21 ++ .../life/qbic/data/processing/Provenance.java | 117 ++++++++ .../config/EvaluationWorkersConfig.java | 48 ++++ .../config/ProcessingWorkersConfig.java | 41 +++ .../config/RegistrationWorkersConfig.java | 42 +++ .../evaluation/EvaluationConfiguration.java | 56 ++++ .../evaluation/EvaluationRequest.java | 250 ++++++++++++++++++ .../processing/ProcessingConfiguration.java | 38 +++ .../processing/ProcessingRequest.java | 234 ++++++++++++++++ .../ProcessRegistrationRequest.java | 115 ++++++++ .../RegistrationConfiguration.java | 37 +++ .../registration/RegistrationRequest.java | 26 ++ .../qbic/data/processing/scanner/Scanner.java | 141 ++++++++++ .../scanner/ScannerConfiguration.java | 23 ++ src/main/resources/application.properties | 56 +++- ...ringMinimalTemplateApplicationTests.groovy | 26 -- 36 files changed, 1695 insertions(+), 277 deletions(-) delete mode 100644 src/main/groovy/.DS_Store delete mode 100644 src/main/groovy/life/.DS_Store delete mode 100644 src/main/groovy/life/qbic/.DS_Store delete mode 100644 src/main/groovy/life/qbic/springminimaltemplate/AppConfig.groovy delete mode 100644 src/main/groovy/life/qbic/springminimaltemplate/CodingPrayersMessageService.groovy delete mode 100644 src/main/groovy/life/qbic/springminimaltemplate/DeveloperNews.groovy delete mode 100644 src/main/groovy/life/qbic/springminimaltemplate/MessageService.groovy delete mode 100644 src/main/groovy/life/qbic/springminimaltemplate/NewsMedia.groovy delete mode 100644 src/main/groovy/life/qbic/springminimaltemplate/SpringMinimalTemplateApplication.groovy create mode 100644 src/main/java/life/qbic/data/processing/AppConfig.java create mode 100644 src/main/java/life/qbic/data/processing/Application.java create mode 100644 src/main/java/life/qbic/data/processing/ConcurrentRegistrationQueue.java create mode 100644 src/main/java/life/qbic/data/processing/ErrorSummary.java create mode 100644 src/main/java/life/qbic/data/processing/GlobalConfig.java create mode 100644 src/main/java/life/qbic/data/processing/Provenance.java create mode 100644 src/main/java/life/qbic/data/processing/config/EvaluationWorkersConfig.java create mode 100644 src/main/java/life/qbic/data/processing/config/ProcessingWorkersConfig.java create mode 100644 src/main/java/life/qbic/data/processing/config/RegistrationWorkersConfig.java create mode 100644 src/main/java/life/qbic/data/processing/evaluation/EvaluationConfiguration.java create mode 100644 src/main/java/life/qbic/data/processing/evaluation/EvaluationRequest.java create mode 100644 src/main/java/life/qbic/data/processing/processing/ProcessingConfiguration.java create mode 100644 src/main/java/life/qbic/data/processing/processing/ProcessingRequest.java create mode 100644 src/main/java/life/qbic/data/processing/registration/ProcessRegistrationRequest.java create mode 100644 src/main/java/life/qbic/data/processing/registration/RegistrationConfiguration.java create mode 100644 src/main/java/life/qbic/data/processing/registration/RegistrationRequest.java create mode 100644 src/main/java/life/qbic/data/processing/scanner/Scanner.java create mode 100644 src/main/java/life/qbic/data/processing/scanner/ScannerConfiguration.java delete mode 100644 src/test/groovy/life/qbic/springminimaltemplate/SpringMinimalTemplateApplicationTests.groovy diff --git a/.github/workflows/build_package.yml b/.github/workflows/build_package.yml index 02a177d..0a9a0b9 100644 --- a/.github/workflows/build_package.yml +++ b/.github/workflows/build_package.yml @@ -12,11 +12,12 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up JDK 1.17 - uses: actions/setup-java@v1 + uses: actions/setup-java@v4 with: - java-version: 1.17 + distribution: 'zulu' + java-version: '17' - name: Load local Maven repository cache uses: actions/cache@v2 @@ -27,4 +28,6 @@ jobs: ${{ runner.os }}-maven- - name: Run mvn package - run: mvn -B package --file pom.xml + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=qbicsoftware_data-processing diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index d030d5a..46ff394 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -38,15 +38,16 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up JDK 1.17 - uses: actions/setup-java@v1 + uses: actions/setup-java@v4 with: - java-version: 1.17 + distribution: 'zulu' + java-version: '17' settings-path: ${{ github.workspace }} - name: Load local Maven repository cache - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} @@ -55,7 +56,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -66,7 +67,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v1 + uses: github/codeql-action/autobuild@v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -80,4 +81,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index 38ce57d..4e8e459 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -11,15 +11,16 @@ jobs: release: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up JDK 1.17 - uses: actions/setup-java@v1 + uses: actions/setup-java@v4 with: - java-version: 1.17 + distribution: 'zulu' + java-version: '17' settings-path: ${{ github.workspace }} - name: Load local Maven repository cache - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} diff --git a/.github/workflows/nexus-publish-snapshots.yml b/.github/workflows/nexus-publish-snapshots.yml index fc25f08..01dddb9 100644 --- a/.github/workflows/nexus-publish-snapshots.yml +++ b/.github/workflows/nexus-publish-snapshots.yml @@ -15,15 +15,16 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up JDK 1.17 - uses: actions/setup-java@v1 + uses: actions/setup-java@v4 with: - java-version: 1.17 + distribution: 'zulu' + java-version: '17' settings-path: ${{ github.workspace }} - name: Load local Maven repository cache - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 1dde144..4f3ce07 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -13,14 +13,15 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up JDK 1.17 - uses: actions/setup-java@v1 + uses: actions/setup-java@v4 with: - java-version: 1.17 + distribution: 'zulu' + java-version: '17' - name: Load local Maven repository cache - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} diff --git a/LICENSE b/LICENSE index 9ac8ded..f43eaba 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 QBiC +Copyright (c) 2023 University of Tübingen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pom.xml b/pom.xml index 7f819b9..d3173ae 100644 --- a/pom.xml +++ b/pom.xml @@ -1,97 +1,134 @@ - - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 2.5.6 - - - life.qbic - spring-minimal-template - 1.0.2 - spring-minimal-template - Demo project for Spring Boot - - 17 - 3.0.8 - - - - org.springframework.boot - spring-boot-starter - - - org.codehaus.groovy - groovy - + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.2.4 + + + life.qbic + data-processing + 1.0.0 + data processing + A Java tool that scans file move events and triggers a cascade of dataset + pre-processing + steps, before it moves the dataset finally to an ETL routine. + + + 17 + qbicsoftware + https://sonarcloud.io + + + + org.springframework.boot + spring-boot-starter + - - org.springframework.boot - spring-boot-starter-test - test - + + org.springframework.boot + spring-boot-starter-test + - - org.spockframework - spock-core - 2.0-groovy-3.0 - test - + + org.spockframework + spock-core + - - org.spockframework - spock-spring - 2.0-groovy-3.0 - test - - + + org.spockframework + spock-spring + - - - - true - nexus-releases - QBiC Releases - https://qbic-repo.qbic.uni-tuebingen.de/repository/maven-releases - - - false - nexus-snapshots - QBiC Snapshots - https://qbic-repo.qbic.uni-tuebingen.de/repository/maven-snapshots - - + + + true + nexus-releases + QBiC Releases + https://qbic-repo.qbic.uni-tuebingen.de/repository/maven-releases + + + false + nexus-snapshots + QBiC Snapshots + https://qbic-repo.qbic.uni-tuebingen.de/repository/maven-snapshots + + - - - - org.springframework.boot - spring-boot-maven-plugin - - - org.codehaus.gmavenplus - gmavenplus-plugin - 1.13.0 - - - - addSources - addTestSources - generateStubs - compile - generateTestStubs - compileTests - removeStubs - removeTestStubs - - - - - - + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.codehaus.gmavenplus + gmavenplus-plugin + 1.13.0 + + + + addSources + addTestSources + generateStubs + compile + generateTestStubs + compileTests + removeStubs + removeTestStubs + + + + + + diff --git a/src/main/groovy/.DS_Store b/src/main/groovy/.DS_Store deleted file mode 100644 index f94f0281fb9b43b076bd9f9eb9a68ba8543a379c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~O=`nH427Q>F9O|ondNMHfZkvT=?Qv)ke@=pkVVOUj-DrtHgUTSCOm=kjWiav z-@;=7uNbSFMM%*>b%m~g=l&Zpnwbhut8FVfyR;3<8?Y(KXJDIf);fE17d zQeZ&}+P_@gl09MAO)nrOo3I;M?e3+=>N_CvlgXN zKnlE=0ygZvcU!(xo~?gg&+GfF`nu7{xSZkPCxD3`#T$AU_lqycnrxk{(DWk^GAKxa Hrz&s<%q|k8 diff --git a/src/main/groovy/life/.DS_Store b/src/main/groovy/life/.DS_Store deleted file mode 100644 index e42608840c701fcc71accfc3a01d53e3f0b86eca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~F^4$))}7;h8O|Jf#oo+W0oL`7s#4yovhF-rw7Yc zi!sFO(N31Ut|nV&Z-?dZVR>isDTZdf9afmotOgXMfE1W0@YVCt&;K3$-TXgkQ7Q$b zz=tVd!*;*j@TKx>eR(~v|7O+KjZVhp3{O7+O#CSRriXFA_=2p-*2xM@KLR0xf)w~y F1s=3%5~KhC diff --git a/src/main/groovy/life/qbic/.DS_Store b/src/main/groovy/life/qbic/.DS_Store deleted file mode 100644 index 73c441a7f0ca095ed89d5aad8f063030dfe7352a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKOG*P#5UkcL0&cPqT)r!KgCWEdcmbmk2~12dx}R0fm7`hx0STjS`(O43dWNP&w|z$S~w#hkBHy>;|*-fJ8EiS9K= rx*Ox5aEW$IjCRb8x8u7g%DUz;&%44QG3d+(ov5Dy*F`1;{#$`>9%mX( diff --git a/src/main/groovy/life/qbic/springminimaltemplate/AppConfig.groovy b/src/main/groovy/life/qbic/springminimaltemplate/AppConfig.groovy deleted file mode 100644 index 70c3187..0000000 --- a/src/main/groovy/life/qbic/springminimaltemplate/AppConfig.groovy +++ /dev/null @@ -1,32 +0,0 @@ -package life.qbic.springminimaltemplate - -import org.springframework.beans.factory.annotation.Value -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import org.springframework.context.annotation.PropertySource - -/** - * Spring configuration class - * - *

Reads properties from a properties file and creates beans for the application.

- * - * @since 0.1.0 - */ -@Configuration -@PropertySource("application.properties") -class AppConfig { - - @Value('${messages.file}') - public String messagesFile - - @Bean - MessageService messageService() { - return new CodingPrayersMessageService(messagesFile) - } - - @Bean - NewsMedia newsMedia() { - return new DeveloperNews(messageService()) - } - -} diff --git a/src/main/groovy/life/qbic/springminimaltemplate/CodingPrayersMessageService.groovy b/src/main/groovy/life/qbic/springminimaltemplate/CodingPrayersMessageService.groovy deleted file mode 100644 index 54c29f1..0000000 --- a/src/main/groovy/life/qbic/springminimaltemplate/CodingPrayersMessageService.groovy +++ /dev/null @@ -1,29 +0,0 @@ -package life.qbic.springminimaltemplate - -/** - * Example implementation of a {@link MessageService} - * - * @since 0.1.0 - */ -class CodingPrayersMessageService implements MessageService { - - private List messages - - CodingPrayersMessageService() { - this.messages = new ArrayList<>() - } - - CodingPrayersMessageService(String filePath) { - this.messages = readMessagesFromClassPath(filePath) - } - - @Override - String collectMessage() { - return messages.get(new Random().nextInt(messages.size())) - } - - private List readMessagesFromClassPath(String path) { - URL url = getClass().getClassLoader().getResource(path) - return url.readLines().each { it.trim() }.collect() - } -} diff --git a/src/main/groovy/life/qbic/springminimaltemplate/DeveloperNews.groovy b/src/main/groovy/life/qbic/springminimaltemplate/DeveloperNews.groovy deleted file mode 100644 index 04ed5d1..0000000 --- a/src/main/groovy/life/qbic/springminimaltemplate/DeveloperNews.groovy +++ /dev/null @@ -1,20 +0,0 @@ -package life.qbic.springminimaltemplate - -/** - * An example {@link NewsMedia} implementation for developer news. - * - * @since 0.1.0 - */ -class DeveloperNews implements NewsMedia { - - private MessageService service - - DeveloperNews(MessageService service) { - this.service = service - } - - @Override - String getNews() { - return service.collectMessage() - } -} diff --git a/src/main/groovy/life/qbic/springminimaltemplate/MessageService.groovy b/src/main/groovy/life/qbic/springminimaltemplate/MessageService.groovy deleted file mode 100644 index eaadf3e..0000000 --- a/src/main/groovy/life/qbic/springminimaltemplate/MessageService.groovy +++ /dev/null @@ -1,18 +0,0 @@ -package life.qbic.springminimaltemplate - -/** - * Small toy interface that represents message services - * - *

Message services shall provide access to received messages.

- * - * @since 0.1.0 - */ -interface MessageService { - - /** - * Collects the latest message - * @return the latest message - * @since 0.1.0 - */ - String collectMessage() -} diff --git a/src/main/groovy/life/qbic/springminimaltemplate/NewsMedia.groovy b/src/main/groovy/life/qbic/springminimaltemplate/NewsMedia.groovy deleted file mode 100644 index d335196..0000000 --- a/src/main/groovy/life/qbic/springminimaltemplate/NewsMedia.groovy +++ /dev/null @@ -1,17 +0,0 @@ -package life.qbic.springminimaltemplate - -/** - * Example interface for a news media - * - * @since 0.1.0 - */ -interface NewsMedia { - - /** - * Returns latest news - * @return news stuff! - * @since 0.1.0 - */ - String getNews() - -} \ No newline at end of file diff --git a/src/main/groovy/life/qbic/springminimaltemplate/SpringMinimalTemplateApplication.groovy b/src/main/groovy/life/qbic/springminimaltemplate/SpringMinimalTemplateApplication.groovy deleted file mode 100644 index e9ba274..0000000 --- a/src/main/groovy/life/qbic/springminimaltemplate/SpringMinimalTemplateApplication.groovy +++ /dev/null @@ -1,23 +0,0 @@ -package life.qbic.springminimaltemplate - -import org.springframework.boot.SpringApplication -import org.springframework.boot.autoconfigure.SpringBootApplication -import org.springframework.context.annotation.AnnotationConfigApplicationContext - -@SpringBootApplication -class SpringMinimalTemplateApplication { - - static void main(String[] args) { - SpringApplication.run(SpringMinimalTemplateApplication, args) - - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class) - - NewsMedia media = context.getBean("newsMedia", NewsMedia.class) - println "####################### Message of the day ##################" - println media.getNews() - println "##############################################################" - - context.close() - } - -} diff --git a/src/main/java/life/qbic/data/processing/AppConfig.java b/src/main/java/life/qbic/data/processing/AppConfig.java new file mode 100644 index 0000000..8547e01 --- /dev/null +++ b/src/main/java/life/qbic/data/processing/AppConfig.java @@ -0,0 +1,88 @@ +package life.qbic.data.processing; + +import java.nio.file.Path; +import life.qbic.data.processing.config.EvaluationWorkersConfig; +import life.qbic.data.processing.config.ProcessingWorkersConfig; +import life.qbic.data.processing.config.RegistrationWorkersConfig; +import life.qbic.data.processing.evaluation.EvaluationConfiguration; +import life.qbic.data.processing.processing.ProcessingConfiguration; +import life.qbic.data.processing.registration.RegistrationConfiguration; +import life.qbic.data.processing.scanner.ScannerConfiguration; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; + +/** + * Spring configuration class + * + *

Reads properties from a properties file and creates beans for the application.

+ * + * @since 0.1.0 + */ +@Configuration +@PropertySource("application.properties") +class AppConfig { + + @Bean + ScannerConfiguration scannerConfiguration( + @Value("${scanner.directory}") String scannerDirectory, + @Value("${scanner.interval}") int interval) { + return new ScannerConfiguration(scannerDirectory, interval); + } + + @Bean + RegistrationWorkersConfig registrationWorkersConfig( + @Value("${registration.threads}") int amountOfWorkers, + @Value("${registration.working.dir}") String workingDirectory, + @Value("${registration.target.dir}") String targetDirectory) { + return new RegistrationWorkersConfig(amountOfWorkers, workingDirectory, targetDirectory); + } + + @Bean + RegistrationConfiguration registrationConfiguration( + RegistrationWorkersConfig registrationWorkersConfig) { + return new RegistrationConfiguration(registrationWorkersConfig.workingDirectory().toString(), + registrationWorkersConfig.targetDirectory().toString()); + } + + @Bean + EvaluationWorkersConfig evaluationWorkersConfig( + @Value("${evaluations.threads}") int amountOfWorkers, + @Value("${evaluation.working.dir}") String workingDirectory, + @Value("${evaluation.target.dir}") String targetDirectory, + @Value("${evaluation.measurement-id.pattern}") String measurementIdPattern) { + return new EvaluationWorkersConfig(amountOfWorkers, workingDirectory, targetDirectory, + measurementIdPattern); + } + + @Bean + EvaluationConfiguration evaluationConfiguration(EvaluationWorkersConfig evaluationWorkersConfig, + GlobalConfig globalConfig) { + return new EvaluationConfiguration(evaluationWorkersConfig.workingDirectory().toString(), + evaluationWorkersConfig.targetDirectory().toString(), + evaluationWorkersConfig.measurementIdPattern().toString(), globalConfig); + } + + @Bean + ProcessingWorkersConfig processingWorkersConfig( + @Value("${processing.threads}") int amountOfWorkers, + @Value("${processing.working.dir}") String workingDirectory, + @Value("${processing.target.dir}") String targetDirectory) { + return new ProcessingWorkersConfig(amountOfWorkers, Path.of(workingDirectory), + Path.of(targetDirectory)); + } + + @Bean + ProcessingConfiguration processingConfiguration(ProcessingWorkersConfig processingWorkersConfig) { + return new ProcessingConfiguration(processingWorkersConfig.workingDirectory(), + processingWorkersConfig.targetDirectory()); + } + + @Bean + GlobalConfig globalConfig( + @Value("${users.error.directory.name}") String usersErrorDirectoryName) { + return new GlobalConfig(usersErrorDirectoryName); + } + +} diff --git a/src/main/java/life/qbic/data/processing/Application.java b/src/main/java/life/qbic/data/processing/Application.java new file mode 100644 index 0000000..268da61 --- /dev/null +++ b/src/main/java/life/qbic/data/processing/Application.java @@ -0,0 +1,81 @@ +package life.qbic.data.processing; + +import java.util.LinkedList; +import java.util.List; +import life.qbic.data.processing.config.EvaluationWorkersConfig; +import life.qbic.data.processing.config.ProcessingWorkersConfig; +import life.qbic.data.processing.config.RegistrationWorkersConfig; +import life.qbic.data.processing.evaluation.EvaluationConfiguration; +import life.qbic.data.processing.evaluation.EvaluationRequest; +import life.qbic.data.processing.processing.ProcessingConfiguration; +import life.qbic.data.processing.processing.ProcessingRequest; +import life.qbic.data.processing.registration.ProcessRegistrationRequest; +import life.qbic.data.processing.registration.RegistrationConfiguration; +import life.qbic.data.processing.scanner.Scanner; +import life.qbic.data.processing.scanner.ScannerConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +@SpringBootApplication +public class Application { + + private static final Logger log = LoggerFactory.getLogger(Application.class); + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( + AppConfig.class); + + ScannerConfiguration scannerConfiguration = context.getBean(ScannerConfiguration.class); + RegistrationWorkersConfig registrationWorkersConfig = context.getBean(RegistrationWorkersConfig.class); + RegistrationConfiguration registrationConfiguration = context.getBean(RegistrationConfiguration.class); + ProcessingWorkersConfig processingWorkersConfig = context.getBean(ProcessingWorkersConfig.class); + ProcessingConfiguration processingConfiguration = context.getBean(ProcessingConfiguration.class); + EvaluationWorkersConfig evaluationWorkersConfig = context.getBean(EvaluationWorkersConfig.class); + EvaluationConfiguration evaluationConfiguration = context.getBean(EvaluationConfiguration.class); + GlobalConfig globalConfig = context.getBean(GlobalConfig.class); + + var requestQueue = new ConcurrentRegistrationQueue(); + var scannerThread = new Scanner(scannerConfiguration, requestQueue, globalConfig); + + log.info("Registering {} registration workers...", registrationWorkersConfig.amountOfWorkers()); + + List registrationWorkers = new LinkedList<>(); + for (int i=0; i processingWorkers = new LinkedList<>(); + for (int i=0; i evaluationWorkers = new LinkedList<>(); + for (int i=0; i + { + log.info("Shutting sequence initiated..."); + scannerThread.interrupt(); + registrationWorkers.forEach(Thread::interrupt); + processingWorkers.forEach(Thread::interrupt); + evaluationWorkers.forEach(Thread::interrupt); + }, "Shutdown-thread")); + + } +} diff --git a/src/main/java/life/qbic/data/processing/ConcurrentRegistrationQueue.java b/src/main/java/life/qbic/data/processing/ConcurrentRegistrationQueue.java new file mode 100644 index 0000000..8e9ba3b --- /dev/null +++ b/src/main/java/life/qbic/data/processing/ConcurrentRegistrationQueue.java @@ -0,0 +1,87 @@ +package life.qbic.data.processing; + +import static org.apache.logging.log4j.LogManager.getLogger; + +import java.util.Queue; +import java.util.concurrent.LinkedBlockingQueue; +import life.qbic.data.processing.registration.RegistrationRequest; +import org.apache.logging.log4j.Logger; + +/** + * Concurrent Registration Queue + *

+ * Simple FIFO queue, that allows for backpressure. + * + * @since 1.0.0 + */ +public class ConcurrentRegistrationQueue { + + private static final int DEFAULT_CAPACITY = 10; + private final Queue queue = new LinkedBlockingQueue<>(); + private final int capacity; + private static final Logger log = getLogger(ConcurrentRegistrationQueue.class); + + + public ConcurrentRegistrationQueue() { + this(DEFAULT_CAPACITY); + } + + public ConcurrentRegistrationQueue(int capacity) { + this.capacity = capacity; + } + + /** + * Adds a new {@link RegistrationRequest} to the registration queue. + *

+ * If the queue has reached its maximal capacity, the calling thread is put into the wait state, + * until the queue's load is reduced below its configured maximal capacity. + * + * @param request the request to add to the registration queue + * @since 1.0.0 + */ + public synchronized void add(RegistrationRequest request) { + while (queue.size() >= capacity) { + try { + wait(); + } catch (InterruptedException e) { + log.error("Interrupted while waiting for registration request", e); + Thread.currentThread().interrupt(); + } + } + queue.add(request); + notifyAll(); + } + + /** + * Requests the next {@link RegistrationRequest} in the queue. + *

+ * If the queue is empty, the calling thread is put into the wait state (via + * {@link Object#wait()}) until it gets notified again when a new task is available in the queue. + * + * @return the next registration request available. + * @since 1.0.0 + */ + public synchronized RegistrationRequest poll() { + while (queue.isEmpty()) { + try { + wait(); + } catch (InterruptedException e) { + log.error("Interrupted while waiting for registration request", e); + Thread.currentThread().interrupt(); + } + } + var request = queue.poll(); + if (queue.size() == capacity - 1) { + notifyAll(); + } + return request; + } + + public synchronized boolean hasItems() { + return !queue.isEmpty(); + } + + public int items() { + return queue.size(); + } +} diff --git a/src/main/java/life/qbic/data/processing/ErrorSummary.java b/src/main/java/life/qbic/data/processing/ErrorSummary.java new file mode 100644 index 0000000..f24c609 --- /dev/null +++ b/src/main/java/life/qbic/data/processing/ErrorSummary.java @@ -0,0 +1,40 @@ +package life.qbic.data.processing; + +import java.util.HashMap; +import java.util.Map; + +/** + * Error Summary + * + *

Provides the data submitter with some helpful contextual information about a + * registration failure

+ * + * @since 1.0.0 + */ +public record ErrorSummary(String taskId, String affectedDataset, String reason, String description, + Map contextProperties) { + + public static ErrorSummary create(String taskId, String affectedDataset, String reason, + String description, Map contextProperties) { + return new ErrorSummary(taskId, affectedDataset, reason, description, + new HashMap<>(contextProperties)); + } + + public static ErrorSummary createSimple(String taskId, String affectedDataset, String reason, + String description) { + return new ErrorSummary(taskId, affectedDataset, reason, description, new HashMap<>()); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("Task ID: ").append(taskId).append("\n"); + sb.append("Affected Dataset: ").append(affectedDataset).append("\n"); + sb.append("Reason: ").append(reason).append("\n"); + sb.append("Description: ").append(description).append("\n"); + for (Map.Entry entry : contextProperties.entrySet()) { + sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); + } + return sb.toString(); + } +} diff --git a/src/main/java/life/qbic/data/processing/GlobalConfig.java b/src/main/java/life/qbic/data/processing/GlobalConfig.java new file mode 100644 index 0000000..0f5610f --- /dev/null +++ b/src/main/java/life/qbic/data/processing/GlobalConfig.java @@ -0,0 +1,21 @@ +package life.qbic.data.processing; + +import java.nio.file.Path; +import java.nio.file.Paths; + +public class GlobalConfig { + + private final Path usersErrorDirectoryName; + + public GlobalConfig(String usersErrorDirectoryName) { + if (usersErrorDirectoryName == null || usersErrorDirectoryName.isBlank()) { + throw new IllegalArgumentException("usersErrorDirectoryName cannot be null or empty"); + } + this.usersErrorDirectoryName = Paths.get(usersErrorDirectoryName); + } + + public Path usersErrorDirectory() { + return this.usersErrorDirectoryName; + } + +} diff --git a/src/main/java/life/qbic/data/processing/Provenance.java b/src/main/java/life/qbic/data/processing/Provenance.java new file mode 100644 index 0000000..9f3f950 --- /dev/null +++ b/src/main/java/life/qbic/data/processing/Provenance.java @@ -0,0 +1,117 @@ +package life.qbic.data.processing; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +/** + * Provenance Information of Datasets + *

+ * Captures some dataset provenance metadata for pre-processing purposes. + * + * @since 1.0.0 + */ +@JsonIgnoreProperties +public class Provenance { + + public static final String FILE_NAME = "provenance.json"; + + /** + * The path from where the dataset has been picked up originally. + */ + @JsonProperty("origin") + public String originPath; + + /** + * + */ + @JsonProperty("user") + public String userWorkDirectoryPath; + + @JsonProperty("measurementId") + public String qbicMeasurementID; + + /** + * A list of ordered processing folder stops the dataset has traversed and passed successfully. + *

+ * The time of processing is ordered from oldest to latest. + */ + @JsonProperty("history") + public List history; + + public static Provenance parse(Path json) throws ProvenanceException { + File provenanceFile = json.toFile(); + if (!provenanceFile.exists()) { + throw new ProvenanceException("File does not exist: %s".formatted(provenanceFile), + ERROR_CODE.NOT_FOUND); + } + if (!provenanceFile.canRead()) { + throw new ProvenanceException("Cannot read file: %s".formatted(provenanceFile), + ERROR_CODE.PERMISSION_DENIED); + } + ObjectMapper mapper = new ObjectMapper().configure( + DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + ; + Provenance provenance; + try { + provenance = mapper.readValue(Files.readString(json), Provenance.class); + } catch (JsonProcessingException e) { + throw new ProvenanceException("Cannot read content %s".formatted(json), e, + ERROR_CODE.UNKNOWN_CONTENT); + } catch (IOException e) { + throw new ProvenanceException("IO Error: %s".formatted(e.getMessage()), e, + ERROR_CODE.IO_ERROR); + } + return provenance; + } + + public static Optional findProvenance(Path directory) { + return Arrays.stream(Objects.requireNonNull(directory.toFile().listFiles())) + .filter(file -> file.getName().equals(Provenance.FILE_NAME)).findFirst(); + } + + public void addToHistory(String event) { + if (history == null) { + history = new ArrayList<>(); + } + history.add(event); + } + + public enum ERROR_CODE { + PERMISSION_DENIED, + UNKNOWN_CONTENT, + NOT_FOUND, + IO_ERROR + } + + public static class ProvenanceException extends RuntimeException { + + private final ERROR_CODE code; + + public ProvenanceException(String message, Throwable t, ERROR_CODE code) { + super(message, t); + this.code = code; + } + + public ProvenanceException(String message, ERROR_CODE code) { + super(message); + this.code = code; + } + + public ERROR_CODE code() { + return code; + } + + } +} diff --git a/src/main/java/life/qbic/data/processing/config/EvaluationWorkersConfig.java b/src/main/java/life/qbic/data/processing/config/EvaluationWorkersConfig.java new file mode 100644 index 0000000..aeb2d0d --- /dev/null +++ b/src/main/java/life/qbic/data/processing/config/EvaluationWorkersConfig.java @@ -0,0 +1,48 @@ +package life.qbic.data.processing.config; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.regex.Pattern; + +public class EvaluationWorkersConfig { + + private final int threads; + private final Path workingDirectory; + private final Path targetDirectory; + private final Pattern measurementIdPattern; + + public EvaluationWorkersConfig(int threads, String workingDirectory, String targetDirectory, String measurementIdPattern) { + if (threads < 1) { + throw new IllegalArgumentException("Number of evaluation worker threads must be greater than 0"); + } + this.threads = threads; + this.workingDirectory = Paths.get(workingDirectory); + if (!this.workingDirectory.toFile().exists()) { + throw new IllegalArgumentException("Evaluation worker directory does not exist"); + } + this.targetDirectory = Paths.get(targetDirectory); + if (!this.targetDirectory.toFile().exists()) { + throw new IllegalArgumentException("Evaluation target directory does not exist"); + } + if (measurementIdPattern.isBlank()) { + throw new IllegalArgumentException("Measurement id pattern cannot be blank"); + } + this.measurementIdPattern = Pattern.compile(measurementIdPattern); + } + + public int threads() { + return threads; + } + + public Path workingDirectory() { + return workingDirectory; + } + + public Path targetDirectory() { + return targetDirectory; + } + + public Pattern measurementIdPattern() { + return measurementIdPattern; + } +} diff --git a/src/main/java/life/qbic/data/processing/config/ProcessingWorkersConfig.java b/src/main/java/life/qbic/data/processing/config/ProcessingWorkersConfig.java new file mode 100644 index 0000000..d13a3b3 --- /dev/null +++ b/src/main/java/life/qbic/data/processing/config/ProcessingWorkersConfig.java @@ -0,0 +1,41 @@ +package life.qbic.data.processing.config; + +import java.nio.file.Path; + +public class ProcessingWorkersConfig { + + private final int threads; + + private final Path workingDirectory; + + private final Path targetDirectory; + + public ProcessingWorkersConfig(int threads, Path workingDirectory, Path targetDirectory) { + if (threads < 1) { + throw new IllegalArgumentException("threads must be greater than 0"); + } + this.threads = threads; + + if (!workingDirectory.toFile().exists()) { + throw new IllegalArgumentException("working directory does not exist"); + } + this.workingDirectory = workingDirectory; + + if (!targetDirectory.toFile().exists()) { + throw new IllegalArgumentException("target directory does not exist"); + } + this.targetDirectory = targetDirectory; + } + + public int threads() { + return threads; + } + + public Path workingDirectory() { + return workingDirectory; + } + + public Path targetDirectory() { + return targetDirectory; + } +} diff --git a/src/main/java/life/qbic/data/processing/config/RegistrationWorkersConfig.java b/src/main/java/life/qbic/data/processing/config/RegistrationWorkersConfig.java new file mode 100644 index 0000000..1ef08a8 --- /dev/null +++ b/src/main/java/life/qbic/data/processing/config/RegistrationWorkersConfig.java @@ -0,0 +1,42 @@ +package life.qbic.data.processing.config; + +import java.nio.file.Path; +import java.nio.file.Paths; + +public class RegistrationWorkersConfig { + + private final int amountOfWorkers; + + private final Path workingDirectory; + + private final Path targetDirectory; + + public RegistrationWorkersConfig(int threads, String workingDirectory, String targetDirectory) { + if (threads < 1) { + throw new IllegalArgumentException("Number of threads must be greater than 0"); + } + Path directory = Paths.get(workingDirectory); + if (!directory.toFile().exists()) { + throw new IllegalArgumentException("Directory " + directory + " does not exist"); + } + Path targetDirectoryPath = Paths.get(targetDirectory); + if (!targetDirectoryPath.toFile().exists()) { + throw new IllegalArgumentException("Target directory " + targetDirectory + " does not exist"); + } + this.workingDirectory = directory; + this.amountOfWorkers = threads; + this.targetDirectory = targetDirectoryPath; + } + + public int amountOfWorkers() { + return amountOfWorkers; + } + + public Path workingDirectory() { + return this.workingDirectory; + } + + public Path targetDirectory() { + return this.targetDirectory; + } +} diff --git a/src/main/java/life/qbic/data/processing/evaluation/EvaluationConfiguration.java b/src/main/java/life/qbic/data/processing/evaluation/EvaluationConfiguration.java new file mode 100644 index 0000000..4eeaee2 --- /dev/null +++ b/src/main/java/life/qbic/data/processing/evaluation/EvaluationConfiguration.java @@ -0,0 +1,56 @@ +package life.qbic.data.processing.evaluation; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.regex.Pattern; +import life.qbic.data.processing.GlobalConfig; + +/** + * Evaluation Configuration + *

+ * The configuration class for {@link EvaluationRequest} workers. + * + * @since 1.0.0 + */ +public class EvaluationConfiguration { + + private final Path workingDirectory; + private final Path targetDirectory; + private final Pattern measurementIdPattern; + private final Path usersErrorDirectory; + + public EvaluationConfiguration(String workingDirectory, String targetDirectory, + String measurementIdPattern, + GlobalConfig globalConfig) { + this.workingDirectory = Paths.get(workingDirectory); + if (!this.workingDirectory.toFile().exists()) { + throw new IllegalArgumentException("Evaluation worker directory does not exist"); + } + this.targetDirectory = Paths.get(targetDirectory); + if (!this.targetDirectory.toFile().exists()) { + throw new IllegalArgumentException("Evaluation target directory does not exist"); + } + if (measurementIdPattern.isBlank()) { + throw new IllegalArgumentException("Measurement id pattern cannot be blank"); + } + this.usersErrorDirectory = globalConfig.usersErrorDirectory(); + this.measurementIdPattern = Pattern.compile(measurementIdPattern); + } + + public Path workingDirectory() { + return workingDirectory; + } + + public Path targetDirectory() { + return targetDirectory; + } + + public Pattern measurementIdPattern() { + return measurementIdPattern; + } + + public Path usersErrorDirectory() { + return usersErrorDirectory; + } + +} diff --git a/src/main/java/life/qbic/data/processing/evaluation/EvaluationRequest.java b/src/main/java/life/qbic/data/processing/evaluation/EvaluationRequest.java new file mode 100644 index 0000000..4dc3523 --- /dev/null +++ b/src/main/java/life/qbic/data/processing/evaluation/EvaluationRequest.java @@ -0,0 +1,250 @@ +package life.qbic.data.processing.evaluation; + +import static org.apache.logging.log4j.LogManager.getLogger; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.ReentrantLock; +import java.util.regex.MatchResult; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import life.qbic.data.processing.ErrorSummary; +import life.qbic.data.processing.Provenance; +import life.qbic.data.processing.Provenance.ProvenanceException; +import org.apache.logging.log4j.Logger; + +/** + * Evaluation Request - Last process + * + *

Validates the presence of a QBiC measurement ID in the dataset root + * folder.

+ * If a valid measurement ID is found, the process updates the provenance file with the ID and moves + * the dataset to the openBIS ETL. After successful transfer, an openBIS marker-file is created, to integrate + * the dataset registration with openBIS ETL. + *

+ * If none is present, or the identifier does not match the requirements, it is moved back to the + * users error folder. + * + * @since 1.0.0 + */ +public class EvaluationRequest extends Thread { + + private static final String THREAD_NAME = "Evaluation-%s"; + private static final String INTERVENTION_DIRECTORY = "interventions"; + private static final Logger LOG = getLogger(EvaluationRequest.class); + private static final Set ACTIVE_TASKS = new HashSet<>(); + private static final ReentrantLock LOCK = new ReentrantLock(); + private static int threadNumber = 1; + private final Path interventionDirectory; + private final AtomicBoolean active = new AtomicBoolean(false); + private final AtomicBoolean terminated = new AtomicBoolean(false); + private final Path workingDirectory; + private final Path targetDirectory; + private final Pattern measurementIdPattern; + private final Path usersErrorDirectory; + + public EvaluationRequest(Path workingDirectory, Path targetDirectory, + Pattern measurementIdPattern, Path usersErrorDirectory) { + this.setName(THREAD_NAME.formatted(nextThreadNumber())); + this.workingDirectory = workingDirectory; + this.targetDirectory = targetDirectory; + this.measurementIdPattern = measurementIdPattern; + if (!workingDirectory.resolve(INTERVENTION_DIRECTORY).toFile().mkdir() + && !workingDirectory.resolve( + INTERVENTION_DIRECTORY).toFile().exists()) { + throw new RuntimeException( + "Could not create intervention directory for processing request at " + workingDirectory); + } + this.usersErrorDirectory = usersErrorDirectory; + this.interventionDirectory = workingDirectory.resolve(INTERVENTION_DIRECTORY); + } + + public EvaluationRequest(EvaluationConfiguration evaluationConfiguration) { + this(evaluationConfiguration.workingDirectory(), evaluationConfiguration.targetDirectory(), + evaluationConfiguration.measurementIdPattern(), + evaluationConfiguration.usersErrorDirectory()); + } + + private static int nextThreadNumber() { + return threadNumber++; + } + + private static boolean push(String taskId) { + LOCK.lock(); + boolean notActiveYet; + try { + notActiveYet = ACTIVE_TASKS.add(taskId); + } finally { + LOCK.unlock(); + } + return notActiveYet; + } + + @Override + public void run() { + while (true) { + active.set(true); + for (File taskDir : tasks()) { + if (push(taskDir.getAbsolutePath())) { + evaluateDirectory(taskDir); + removeTask(taskDir); + } + } + active.set(false); + if (terminated.get()) { + LOG.warn("Thread {} terminated", Thread.currentThread().getName()); + break; + } + try { + Thread.sleep(100); + } catch (InterruptedException e) { + // We don't want to interrupt the thread here, only explicit to enable graceful shutdown + // via its interrupt() method + } + } + } + + private void removeTask(File taskDir) { + LOCK.lock(); + try { + ACTIVE_TASKS.remove(taskDir.getAbsolutePath()); + } finally { + LOCK.unlock(); + } + } + + private void evaluateDirectory(File taskDir) { + var provenanceSearch = Provenance.findProvenance(taskDir.toPath()); + if (provenanceSearch.isEmpty()) { + LOG.error("No provenance file found: {}", taskDir.getAbsolutePath()); + moveToSystemIntervention(taskDir, "Provenance file was not found"); + return; + } + + Provenance provenance = null; + try { + provenance = Provenance.parse(provenanceSearch.get().toPath()); + } catch (ProvenanceException e) { + LOG.error("Could not parse provenance file: {}}", taskDir.getAbsolutePath(), e); + moveToSystemIntervention(taskDir, e.getMessage()); + return; + } + + var datasetSearch = findDataset(taskDir); + if (datasetSearch.isEmpty()) { + LOG.error("No dataset found: {}", taskDir.getAbsolutePath()); + moveBackToOrigin(taskDir, provenance, "No dataset directory found."); + return; + } + var dataset = datasetSearch.get(); + Matcher matcher = measurementIdPattern.matcher(dataset.getName()); + var measurementIdResult = matcher.results().map(MatchResult::group).findFirst(); + if (measurementIdResult.isPresent()) { + provenance.qbicMeasurementID = measurementIdResult.get(); + provenance.addToHistory(taskDir.getAbsolutePath()); + try { + updateProvenanceFile(provenanceSearch.get(), provenance); + } catch (IOException e) { + LOG.error("Could not update provenance file: {}", taskDir.getAbsolutePath(), e); + moveToSystemIntervention(taskDir, e.getMessage()); + } + moveToTargetDir(taskDir); + try { + createMarkerFile(targetDirectory, taskDir.getName()); + } catch (IOException e) { + LOG.error("Could not create marker file: {}", taskDir.getAbsolutePath(), e); + moveToSystemIntervention(taskDir, e.getMessage()); + } + return; + } + var errorMessage = ErrorSummary.createSimple(taskDir.getName(), dataset.getName(), "Missing QBiC measurement ID", + "For a successful registration please provide the pre-registered QBiC measurement ID"); + LOG.error( + "Missing measurement identifier: no known measurement id was found in the content of directory '{}' in task '{}'", dataset.getName(), taskDir.getName()); + moveBackToOrigin(taskDir, provenance, errorMessage.toString()); + } + + private void updateProvenanceFile(File provenanceFile, Provenance provenance) throws IOException { + var mapper = new ObjectMapper(); + mapper.writerWithDefaultPrettyPrinter().writeValue(provenanceFile, provenance); + } + + private boolean createMarkerFile(Path targetDirectory, String name) throws IOException { + Path markerFileName = Paths.get(".MARKER_is_finished_" + name); + return targetDirectory.resolve(markerFileName).toFile().createNewFile(); + } + + private Optional findDataset(File taskDir) { + return Arrays.stream(taskDir.listFiles()).filter(File::isDirectory).findFirst(); + } + + private void moveToSystemIntervention(File taskDir, String reason) { + try { + var errorFile = taskDir.toPath().resolve("error.txt").toFile(); + errorFile.createNewFile(); + Files.writeString(errorFile.toPath(), reason); + Files.move(taskDir.toPath(), interventionDirectory.resolve(taskDir.getName())); + } catch (IOException e) { + throw new RuntimeException("Cannot move task to intervention: %s".formatted(taskDir), e); + } + } + + private void moveBackToOrigin(File taskDir, Provenance provenance, String reason) { + LOG.info("Moving back to original user directory: " + taskDir.getAbsolutePath()); + try { + var errorFile = taskDir.toPath().resolve("error.txt").toFile(); + errorFile.createNewFile(); + Files.writeString(errorFile.toPath(), reason); + Paths.get(provenance.userWorkDirectoryPath).resolve(usersErrorDirectory).toFile().mkdir(); + Files.move(taskDir.toPath(), + Paths.get(provenance.userWorkDirectoryPath).resolve(usersErrorDirectory) + .resolve(taskDir.getName())); + } catch (IOException e) { + LOG.error("Cannot move task to user intervention: %s".formatted(provenance.originPath), e); + moveToSystemIntervention(taskDir, e.getMessage()); + } + } + + private void moveToTargetDir(File taskDir) { + LOG.info( + "Moving %s to target directory %s".formatted(taskDir.getAbsolutePath(), targetDirectory)); + try { + Files.move(taskDir.toPath(), targetDirectory.resolve(taskDir.getName())); + } catch (IOException e) { + LOG.error("Cannot move task to target directory: %s".formatted(targetDirectory), e); + moveToSystemIntervention(taskDir, + "Cannot move task to target directory: %s".formatted(targetDirectory)); + } + + } + + private List tasks() { + return Arrays.stream(workingDirectory.toFile().listFiles()).filter(File::isDirectory) + .filter(file -> !file.getName().equals(INTERVENTION_DIRECTORY)).toList(); + } + + public void interrupt() { + terminated.set(true); + while (active.get()) { + LOG.debug("Thread is still active..."); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + // we don't want to interrupt the worker thread before its task is done, since it might + // render the application in a non-recoverable state + } + } + LOG.debug("Task has been finished"); + } + +} diff --git a/src/main/java/life/qbic/data/processing/processing/ProcessingConfiguration.java b/src/main/java/life/qbic/data/processing/processing/ProcessingConfiguration.java new file mode 100644 index 0000000..6084645 --- /dev/null +++ b/src/main/java/life/qbic/data/processing/processing/ProcessingConfiguration.java @@ -0,0 +1,38 @@ +package life.qbic.data.processing.processing; + +import java.nio.file.Path; + +/** + * Processing Configuration + * + *

Holds processing worker configuration settings, such as the working directory of the process + * and the next target directory the dataset will be moved to, after a successful task + * performance.

+ * + * @since 1.0.0 + */ +public class ProcessingConfiguration { + + private final Path workingDirectory; + + private final Path targetDirectory; + + public ProcessingConfiguration(Path workingDirectory, Path targetDirectory) { + this.workingDirectory = workingDirectory; + if (!workingDirectory.toFile().exists()) { + throw new IllegalArgumentException("Working directory does not exist: " + workingDirectory); + } + this.targetDirectory = targetDirectory; + if (!targetDirectory.toFile().exists()) { + throw new IllegalArgumentException("Target directory does not exist: " + targetDirectory); + } + } + + public Path getWorkingDirectory() { + return workingDirectory; + } + + public Path getTargetDirectory() { + return targetDirectory; + } +} diff --git a/src/main/java/life/qbic/data/processing/processing/ProcessingRequest.java b/src/main/java/life/qbic/data/processing/processing/ProcessingRequest.java new file mode 100644 index 0000000..67279c0 --- /dev/null +++ b/src/main/java/life/qbic/data/processing/processing/ProcessingRequest.java @@ -0,0 +1,234 @@ +package life.qbic.data.processing.processing; + +import static org.apache.logging.log4j.LogManager.getLogger; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.ReentrantLock; +import life.qbic.data.processing.Provenance; +import life.qbic.data.processing.Provenance.ProvenanceException; +import org.apache.logging.log4j.Logger; + +/** + * + * + *

Does some simple checks: + * + *

    + *
  • the content is not empty
  • + *
  • there is a dataset and a provenance file
  • + *
  • the provenance file can be parsed and the content passes the sanity check
  • + *
  • package a dataset properly, if it is a file
  • + *
+ * + * @since 1.0.0 + */ +public class ProcessingRequest extends Thread { + + private static final Logger LOG = getLogger(ProcessingRequest.class); + private static final String THREAD_NAME = "Processing-%s"; + private static final Set ACTIVE_TASKS = new HashSet<>(); + private static final ReentrantLock LOCK = new ReentrantLock(); + private static final String INTERVENTION_DIRECTORY = "interventions"; + private static int threadNumber = 1; + private final Path workingDirectory; + private final Path targetDirectory; + private final AtomicBoolean active = new AtomicBoolean(false); + private final AtomicBoolean terminated = new AtomicBoolean(false); + private final Path interventionDirectory; + + public ProcessingRequest(ProcessingConfiguration processingConfiguration) { + this.setName(THREAD_NAME.formatted(nextThreadNumber())); + this.workingDirectory = processingConfiguration.getWorkingDirectory(); + this.targetDirectory = processingConfiguration.getTargetDirectory(); + if (!workingDirectory.resolve(INTERVENTION_DIRECTORY).toFile().mkdir() + && !workingDirectory.resolve( + INTERVENTION_DIRECTORY).toFile().exists()) { + throw new RuntimeException( + "Could not create intervention directory for processing request at " + workingDirectory); + } + this.interventionDirectory = workingDirectory.resolve(INTERVENTION_DIRECTORY); + } + + private static int nextThreadNumber() { + return threadNumber++; + } + + private static boolean push(String taskId) { + LOCK.lock(); + boolean notActiveYet; + try { + notActiveYet = ACTIVE_TASKS.add(taskId); + } finally { + LOCK.unlock(); + } + return notActiveYet; + } + + @Override + public void run() { + while (true) { + active.set(true); + for (File taskDir : tasks()) { + if (push(taskDir.getAbsolutePath())) { + LOG.info("Registering task " + taskDir.getAbsolutePath()); + processFile(taskDir); + clearTask(taskDir); + } + } + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + // This thread will only handle interrupted signals via its explicit + // API method call + } + active.set(false); + if (terminated.get()) { + LOG.warn("Thread {} terminated", Thread.currentThread().getName()); + break; + } + } + } + + /** + * @param taskDir + * @since + */ + private void processFile(File taskDir) { + var taskDirContent = Arrays.stream(Objects.requireNonNull(taskDir.listFiles())).toList(); + + if (checkForEmpty(taskDir, taskDirContent)) { + return; + } + + Optional provenanceFileSearch = findProvenanceFile(taskDirContent); + if (provenanceFileSearch.isEmpty()) { + LOG.error("Task {} has no provenance file", taskDir.getAbsolutePath()); + moveToSystemIntervention(taskDir, "No provenance file provided"); + return; + } + + Provenance provenance = null; + try { + provenance = Provenance.parse(provenanceFileSearch.get().toPath()); + } catch (ProvenanceException e) { + LOG.error("Error parsing provenance file", e); + switch (e.code()) { + case IO_ERROR, NOT_FOUND, UNKNOWN_CONTENT, PERMISSION_DENIED -> + moveToSystemIntervention(taskDir, e.getMessage()); + } + return; + } + + Provenance finalProvenance = provenance; + packageDataset(taskDir); + taskDirContent.stream().filter(file -> !file.getName().equals(Provenance.FILE_NAME)).findFirst() + .ifPresent(file -> { + finalProvenance.addToHistory(taskDir.getAbsolutePath()); + try { + writeProvenance(provenanceFileSearch.get(), finalProvenance); + } catch (IOException e) { + LOG.error("Could not write provenance file {}", file.getAbsolutePath(), e); + moveToSystemIntervention(taskDir, "Writing provenance file failed"); + } + try { + moveToTargetFolder(taskDir); + } catch (IOException e) { + LOG.error("Could not move task {} to target location", file.getAbsolutePath(), + e); + moveToSystemIntervention(taskDir, "Writing task directory failed"); + } + }); + } + + private void packageDataset(File taskDir) { + Optional datasetSearch = Arrays.stream(taskDir.listFiles()) + .filter(file -> !file.getName().equals(Provenance.FILE_NAME)).findFirst(); + datasetSearch.ifPresent(file -> { + if (file.isFile()) { + File datasetDir = taskDir.toPath().resolve(file.getName() + "_dataset").toFile(); + datasetDir.mkdir(); + try { + Files.move(file.toPath(), datasetDir.toPath().resolve(file.getName())); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }); + } + + private Optional findProvenanceFile(List taskDirContent) { + Optional provenanceFileSearch = taskDirContent.stream() + .filter(file -> file.getName().equals(Provenance.FILE_NAME)).findFirst(); + return provenanceFileSearch; + } + + private boolean checkForEmpty(File taskDir, List taskDirContent) { + if (taskDirContent.isEmpty()) { + LOG.error("Task {} has no files", taskDir.getAbsolutePath()); + clearTask(taskDir); + taskDir.delete(); + LOG.info("Empty task {} deleted", taskDir.getAbsolutePath()); + return true; + } + return false; + } + + private void moveToTargetFolder(File taskDir) throws IOException { + Files.move(taskDir.toPath(), targetDirectory.resolve(taskDir.getName())); + } + + private void writeProvenance(File provenanceFile, Provenance provenance) throws IOException { + var mapper = new ObjectMapper(); + mapper.writerWithDefaultPrettyPrinter().writeValue(provenanceFile, provenance); + } + + private void moveToSystemIntervention(File taskDir, String reason) { + try { + var errorFile = taskDir.toPath().resolve("error.txt").toFile(); + errorFile.createNewFile(); + Files.writeString(errorFile.toPath(), reason); + Files.move(taskDir.toPath(), interventionDirectory.resolve(taskDir.getName())); + } catch (IOException e) { + throw new RuntimeException("Cannot move task to intervention: %s".formatted(taskDir), e); + } + } + + private void clearTask(File taskDir) { + LOCK.lock(); + try { + ACTIVE_TASKS.remove(taskDir.getAbsolutePath()); + } finally { + LOCK.unlock(); + } + } + + private List tasks() { + return Arrays.stream(workingDirectory.toFile().listFiles()).filter(File::isDirectory) + .filter(file -> !file.getName().equals(INTERVENTION_DIRECTORY)).toList(); + } + + public void interrupt() { + terminated.set(true); + while (active.get()) { + LOG.debug("Thread is still active..."); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + // we don't want to interrupt the worker thread before its task is done, since it might + // render the application in a non-recoverable state + } + } + LOG.debug("Task has been finished"); + } +} diff --git a/src/main/java/life/qbic/data/processing/registration/ProcessRegistrationRequest.java b/src/main/java/life/qbic/data/processing/registration/ProcessRegistrationRequest.java new file mode 100644 index 0000000..c0c0a1d --- /dev/null +++ b/src/main/java/life/qbic/data/processing/registration/ProcessRegistrationRequest.java @@ -0,0 +1,115 @@ +package life.qbic.data.processing.registration; + +import static org.apache.logging.log4j.LogManager.getLogger; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicBoolean; +import life.qbic.data.processing.ConcurrentRegistrationQueue; +import life.qbic.data.processing.Provenance; +import org.apache.logging.log4j.Logger; +import org.springframework.lang.NonNull; + +/** + * Process Registration Request + *

+ * This must be the first process of handling a new incoming dataset. It will consume a processing + * request item, that is then used to prepare the dataset for the following downstream processes. + *

+ * The process polls the {@link ConcurrentRegistrationQueue} shared with the scanning thread. + * + *

+ * The process will do the following tasks: + *

    + *
  • Wraps a new dataset into a task directory with a random UUID as task name
  • + *
  • Creating a provenance JSON file, that is used on downstream processes and holds required provenance data
  • + *
  • Moving the dataset to the next processing directory
  • + *
+ * + * @since 1.0.0 + */ +public class ProcessRegistrationRequest extends Thread { + + private static final Logger log = getLogger(ProcessRegistrationRequest.class); + private static final String threadName = "Registration-%s"; + private static int threadNumber = 1; + private final ConcurrentRegistrationQueue registrationQueue; + private final Path workingDirectory; + private final Path targetDirectory; + private AtomicBoolean active = new AtomicBoolean(false); + + public ProcessRegistrationRequest(@NonNull ConcurrentRegistrationQueue registrationQueue, + @NonNull RegistrationConfiguration configuration) { + this.setName(threadName.formatted(nextThreadNumber())); + this.registrationQueue = registrationQueue; + this.workingDirectory = configuration.workingDirectory(); + this.targetDirectory = configuration.targetDirectory(); + } + + private static int nextThreadNumber() { + return threadNumber++; + } + + @Override + public void run() { + while (true) { + var request = registrationQueue.poll(); + active.set(true); + log.info("Processing request: {}", request); + try { + Path taskDir = createTaskDirectory(); + Path newLocation = taskDir.resolve(request.target().getFileName()); + Files.move(request.target(), newLocation); + writeProvenanceInformation(taskDir, newLocation, request); + Files.move(taskDir, targetDirectory.resolve(taskDir.getFileName())); + } catch (RuntimeException e) { + log.error("Error moving task directory", e); + // TODO move back to user folder + } catch (IOException e) { + log.error("Error while processing registration request", e); + // TODO move back to user folder + } finally { + active.set(false); + log.info("Processing completed: {}", request); + } + } + } + + private void writeProvenanceInformation(Path taskDir, Path newLocation, + RegistrationRequest request) + throws IOException { + Provenance provenance = new Provenance(); + provenance.originPath = request.origin().toString(); + provenance.history = new ArrayList<>(); + provenance.history.add(newLocation.toString()); + provenance.userWorkDirectoryPath = String.valueOf(request.userPath()); + ObjectMapper mapper = new ObjectMapper(); + mapper.writerWithDefaultPrettyPrinter() + .writeValue(taskDir.resolve("provenance.json").toFile(), provenance); + } + + private Path createTaskDirectory() { + UUID taskId = UUID.randomUUID(); + var taskDir = workingDirectory.resolve(taskId.toString()); + taskDir.toFile().mkdirs(); + return workingDirectory.resolve(taskId.toString()); + } + + public void interrupt() { + while (active.get()) { + log.debug("Thread is still active..."); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + // we don't want to interrupt the worker thread before its task is done, since it might + // render the application in a non-recoverable state + } + } + log.debug("Task has been finished"); + } + +} diff --git a/src/main/java/life/qbic/data/processing/registration/RegistrationConfiguration.java b/src/main/java/life/qbic/data/processing/registration/RegistrationConfiguration.java new file mode 100644 index 0000000..7737b55 --- /dev/null +++ b/src/main/java/life/qbic/data/processing/registration/RegistrationConfiguration.java @@ -0,0 +1,37 @@ +package life.qbic.data.processing.registration; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Objects; + +public class RegistrationConfiguration { + + + private final Path workingDirectory; + private final Path targetDirectory; + + public RegistrationConfiguration(String workingDirectory, String targetDirectory) { + this.workingDirectory = Paths.get(Objects.requireNonNull(workingDirectory, "workingDirectory must not be null")); + if (!workingDirectory().toFile().exists()) { + throw new IllegalArgumentException(targetDirectory + " does not exist"); + } + if (!workingDirectory().toFile().isDirectory()) { + throw new IllegalArgumentException(targetDirectory + " is not a directory"); + } + this.targetDirectory = Paths.get(Objects.requireNonNull(targetDirectory, "targetDirectory must not be null")); + if (!targetDirectory().toFile().exists()) { + throw new IllegalArgumentException(targetDirectory + " does not exist"); + } + if (!targetDirectory().toFile().isDirectory()) { + throw new IllegalArgumentException(targetDirectory + " is not a directory"); + } + } + + public Path workingDirectory() { + return workingDirectory; + } + + public Path targetDirectory() { + return targetDirectory; + } +} diff --git a/src/main/java/life/qbic/data/processing/registration/RegistrationRequest.java b/src/main/java/life/qbic/data/processing/registration/RegistrationRequest.java new file mode 100644 index 0000000..3c586d0 --- /dev/null +++ b/src/main/java/life/qbic/data/processing/registration/RegistrationRequest.java @@ -0,0 +1,26 @@ +package life.qbic.data.processing.registration; + +import java.nio.file.Path; +import java.time.Instant; +import java.util.Objects; + +public record RegistrationRequest(Instant timestamp, long lastModified, Path origin, Path target, Path userPath) { + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + RegistrationRequest that = (RegistrationRequest) o; + return Objects.equals(origin, that.origin) && Objects.equals(target, + that.target) && Objects.equals(lastModified, that.lastModified); + } + + @Override + public int hashCode() { + return Objects.hash(lastModified, origin, target); + } +} diff --git a/src/main/java/life/qbic/data/processing/scanner/Scanner.java b/src/main/java/life/qbic/data/processing/scanner/Scanner.java new file mode 100644 index 0000000..8ef44cd --- /dev/null +++ b/src/main/java/life/qbic/data/processing/scanner/Scanner.java @@ -0,0 +1,141 @@ +package life.qbic.data.processing.scanner; + +import static org.apache.logging.log4j.LogManager.getLogger; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import life.qbic.data.processing.ConcurrentRegistrationQueue; +import life.qbic.data.processing.GlobalConfig; +import life.qbic.data.processing.registration.RegistrationRequest; +import org.apache.logging.log4j.Logger; + +/** + * Scanner + *

+ * The scanner thread iterates over the specified directory and records every directory present as + * "user" directory. + *

+ * In the current configuration, the scanner detects activity in a directory named "registration" + * within each user directory. Every event outside this directory is ignored, since data that is + * currently uploaded must not be processed, which will lead to corrupt data. + * + * @since 1.0.0 + */ +public class Scanner extends Thread { + + private static final Logger log = getLogger(Scanner.class); + private static final Path REGISTRATION_PATH = Paths.get("registration"); + + private final Path scannerPath; + private final int scanInterval; + private final HashSet userProcessDirectories = new HashSet<>(); + private final ConcurrentRegistrationQueue registrationQueue; + private final HashSet submittedRequests = new HashSet<>(); + + public Scanner(ScannerConfiguration scannerConfiguration, + ConcurrentRegistrationQueue registrationQueue, GlobalConfig globalConfig) { + this.setName("Scanner-Thread"); + Objects.requireNonNull(scannerConfiguration, "scannerConfiguration must not be null"); + scannerPath = Path.of(scannerConfiguration.scannerDirectory()); + if (!scannerPath.toFile().exists()) { + throw new RuntimeException("Could not find scanner directory: " + scannerPath); + } + this.scanInterval = scannerConfiguration.scanInterval(); + this.registrationQueue = Objects.requireNonNull(registrationQueue, + "registrationQueue must not be null"); + } + + @Override + public void run() { + log.info("Started scanning '{}'", scannerPath); + while (!Thread.interrupted()) { + try { + var userFolderIterator = Arrays.stream( + Objects.requireNonNull(scannerPath.toFile().listFiles())).filter(File::isDirectory) + .toList().iterator(); + + while (userFolderIterator.hasNext()) { + fetchRegistrationDirectory(userFolderIterator.next().toPath()).ifPresent( + this::addRegistrationDirectory); + } + + List requests = detectDataForRegistration(); + for (RegistrationRequest request : requests) { + if (submittedRequests.contains(request)) { + log.info("Skipping registration request '{}'", request); + continue; + } + registrationQueue.add(request); + submittedRequests.add(request); + log.info("New registration requested: {}", request); + } + removePathZombies(); + Thread.sleep(scanInterval); + } catch (InterruptedException e) { + interrupt(); + } + } + log.info("Stopped scanning '{}'", scannerPath); + } + + private List detectDataForRegistration() { + return userProcessDirectories.parallelStream() + .map(Path::toFile) + .map(file -> createRequests(file.listFiles(), file.toPath())).flatMap( + Collection::stream).toList(); + } + + private List createRequests(File[] files, Path userDirectory) { + if (files == null || files.length == 0) { + return new ArrayList<>(); + } + return Arrays.stream(files).filter(file -> !file.isHidden()) + .map(file -> createRequest(file, userDirectory)).toList(); + } + + private RegistrationRequest createRequest(File file, Path userDirectory) { + return new RegistrationRequest(Instant.now(), file.lastModified(), + file.getParentFile().toPath(), file.toPath(), userDirectory.getParent()); + } + + private void removePathZombies() { + List zombies = new LinkedList<>(); + for (Path processFolder : userProcessDirectories) { + if (!processFolder.toFile().exists()) { + zombies.add(processFolder); + } + } + + zombies.forEach(zombie -> { + userProcessDirectories.remove(zombie); + log.warn("Removing orphaned process directory: '%s'".formatted(zombie)); + }); + + } + + private void addRegistrationDirectory(Path path) { + if (userProcessDirectories.add(path)) { + log.info("New user process directory found: '{}'", path.toString()); + } + } + + public Optional fetchRegistrationDirectory(Path userDirectory) { + Path resolvedPath = userDirectory.resolve(REGISTRATION_PATH); + return Optional.ofNullable(resolvedPath.toFile().exists() ? resolvedPath : null); + } + + @Override + public void interrupt() { + log.info("Interrupted scanning '{}'", scannerPath); + } +} diff --git a/src/main/java/life/qbic/data/processing/scanner/ScannerConfiguration.java b/src/main/java/life/qbic/data/processing/scanner/ScannerConfiguration.java new file mode 100644 index 0000000..052ed75 --- /dev/null +++ b/src/main/java/life/qbic/data/processing/scanner/ScannerConfiguration.java @@ -0,0 +1,23 @@ +package life.qbic.data.processing.scanner; + +public class ScannerConfiguration { + + private final String scannerDirectory; + private final int scanInterval; + + public ScannerConfiguration(String scannerDirectory, int interval) { + this.scannerDirectory = scannerDirectory; + if (interval <= 0) { + throw new IllegalArgumentException("Interval must be greater than 0"); + } + this.scanInterval = interval; + } + + public String scannerDirectory() { + return scannerDirectory; + } + + public int scanInterval() { + return scanInterval; + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 43f0696..a0731cb 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1 +1,55 @@ -messages.file=messages.txt +#================================ +# Data processing app properties +#================================ + +#------------------------ +# Global settings +#------------------------ +# Directory name that will be used for the manual intervention directory +# Created in the users' home folders +# e.g. /home//error +users.error.directory.name=error + +#-------------------------------------- +# Settings for the data scanning thread +#-------------------------------------- +# Path to the directory that contains all user directories +# e.g. /home in Linux or /Users in macOS +scanner.directory=${SCANNER_DIR:/home} +# The time interval (milliseconds) the scanner thread iterates through the scanner directory +# Value must be an integer > 0 +scanner.interval=1000 + +#---------------- +# Settings for the registration worker threads +#---------------- +registration.threads=2 +registration.working.dir=${WORKING_DIR:} +registration.target.dir=${PROCESSING_DIR:} + +#------------------------------------ +# Settings for the 1. processing step +# Proper packaging and provenance data, some simple checks +#------------------------------------ +processing.threads=2 +processing.working.dir=${PROCESSING_DIR} +processing.target.dir=${EVALUATION_DIR} + +#---------------------------------- +# Setting for the 2. processing step: +# Measurement ID evaluation +# --------------------------------- +evaluations.threads=2 +evaluation.working.dir=${EVALUATION_DIR} +evaluation.target.dir=${OPENBIS_ETL_DIR} +evaluation.measurement-id.pattern=^(MS|NGS)Q[A-Z0-9]{4}[0-9]{3}[A-Z0-9]{2}-[0-9]* + +# ---------------- +# Logging settings +# ---------------- +# We want logging being enabled even during shutdown procedure, so log information from active +# workers are still capture in the log output. +# Setting it to 'true' means that a thread is registered to the shutdown hook that will terminate the +# logging eventually. Log events in threads that are still running might get lost, which might not be desired +# Setting it to 'false' means that the logging remains active until the JVM completely stops. +logging.register-shutdown-hook=false diff --git a/src/test/groovy/life/qbic/springminimaltemplate/SpringMinimalTemplateApplicationTests.groovy b/src/test/groovy/life/qbic/springminimaltemplate/SpringMinimalTemplateApplicationTests.groovy deleted file mode 100644 index f472c3e..0000000 --- a/src/test/groovy/life/qbic/springminimaltemplate/SpringMinimalTemplateApplicationTests.groovy +++ /dev/null @@ -1,26 +0,0 @@ -package life.qbic.springminimaltemplate - -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.context.SpringBootTest -import spock.lang.Specification - -@SpringBootTest -class SpringMinimalTemplateApplicationTests extends Specification { - - @Test - void contextLoads() { - } - - @Autowired - private MessageService messageService - - def "autowired works"() { - when: - String messages = messageService.collectMessage() - println(messages) - then: - messages != null - } - -} From 2d45d0c65e07543126c8ccd873e7f0b42c5b942d Mon Sep 17 00:00:00 2001 From: Sven F <9976560+sven1103@users.noreply.github.com> Date: Mon, 22 Apr 2024 12:31:05 +0200 Subject: [PATCH 2/5] Introduce round robin for ETL dir assignment (#2) * Introduce round robin for etl target dirs * Add JDs --- .../life/qbic/data/processing/AppConfig.java | 9 +-- .../config/EvaluationWorkersConfig.java | 26 ++++++--- .../processing/config/RoundRobinDraw.java | 56 +++++++++++++++++++ .../evaluation/EvaluationConfiguration.java | 21 ++++--- .../evaluation/EvaluationRequest.java | 40 +++++++------ .../processing/ProcessingRequest.java | 9 ++- .../RegistrationConfiguration.java | 2 +- src/main/resources/application.properties | 7 ++- 8 files changed, 128 insertions(+), 42 deletions(-) create mode 100644 src/main/java/life/qbic/data/processing/config/RoundRobinDraw.java diff --git a/src/main/java/life/qbic/data/processing/AppConfig.java b/src/main/java/life/qbic/data/processing/AppConfig.java index 8547e01..900dc9a 100644 --- a/src/main/java/life/qbic/data/processing/AppConfig.java +++ b/src/main/java/life/qbic/data/processing/AppConfig.java @@ -1,6 +1,7 @@ package life.qbic.data.processing; import java.nio.file.Path; +import java.util.Arrays; import life.qbic.data.processing.config.EvaluationWorkersConfig; import life.qbic.data.processing.config.ProcessingWorkersConfig; import life.qbic.data.processing.config.RegistrationWorkersConfig; @@ -50,17 +51,17 @@ RegistrationConfiguration registrationConfiguration( EvaluationWorkersConfig evaluationWorkersConfig( @Value("${evaluations.threads}") int amountOfWorkers, @Value("${evaluation.working.dir}") String workingDirectory, - @Value("${evaluation.target.dir}") String targetDirectory, + @Value("${evaluation.target.dirs}") String[] targetDirectory, @Value("${evaluation.measurement-id.pattern}") String measurementIdPattern) { - return new EvaluationWorkersConfig(amountOfWorkers, workingDirectory, targetDirectory, - measurementIdPattern); + return new EvaluationWorkersConfig(amountOfWorkers, workingDirectory, + measurementIdPattern, Arrays.stream(targetDirectory).toList()); } @Bean EvaluationConfiguration evaluationConfiguration(EvaluationWorkersConfig evaluationWorkersConfig, GlobalConfig globalConfig) { return new EvaluationConfiguration(evaluationWorkersConfig.workingDirectory().toString(), - evaluationWorkersConfig.targetDirectory().toString(), + evaluationWorkersConfig.targetDirectories(), evaluationWorkersConfig.measurementIdPattern().toString(), globalConfig); } diff --git a/src/main/java/life/qbic/data/processing/config/EvaluationWorkersConfig.java b/src/main/java/life/qbic/data/processing/config/EvaluationWorkersConfig.java index aeb2d0d..a675921 100644 --- a/src/main/java/life/qbic/data/processing/config/EvaluationWorkersConfig.java +++ b/src/main/java/life/qbic/data/processing/config/EvaluationWorkersConfig.java @@ -2,28 +2,36 @@ import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Collection; import java.util.regex.Pattern; public class EvaluationWorkersConfig { private final int threads; private final Path workingDirectory; - private final Path targetDirectory; + private final Collection targetDirectories; private final Pattern measurementIdPattern; - public EvaluationWorkersConfig(int threads, String workingDirectory, String targetDirectory, String measurementIdPattern) { + public EvaluationWorkersConfig(int threads, String workingDirectory, String measurementIdPattern, + Collection targetDirectories) { if (threads < 1) { - throw new IllegalArgumentException("Number of evaluation worker threads must be greater than 0"); + throw new IllegalArgumentException( + "Number of evaluation worker threads must be greater than 0"); + } + if (targetDirectories.isEmpty()) { + throw new IllegalArgumentException( + "Target directories cannot be empty, please specify at least one target directory"); } this.threads = threads; this.workingDirectory = Paths.get(workingDirectory); if (!this.workingDirectory.toFile().exists()) { throw new IllegalArgumentException("Evaluation worker directory does not exist"); } - this.targetDirectory = Paths.get(targetDirectory); - if (!this.targetDirectory.toFile().exists()) { - throw new IllegalArgumentException("Evaluation target directory does not exist"); - } + this.targetDirectories = targetDirectories.stream().map(Paths::get).toList(); + this.targetDirectories.stream().filter(path -> !path.toFile().exists()).forEach(path -> { + throw new IllegalArgumentException( + "Evaluation target directory '%s' does not exist".formatted(path)); + }); if (measurementIdPattern.isBlank()) { throw new IllegalArgumentException("Measurement id pattern cannot be blank"); } @@ -38,8 +46,8 @@ public Path workingDirectory() { return workingDirectory; } - public Path targetDirectory() { - return targetDirectory; + public Collection targetDirectories() { + return targetDirectories; } public Pattern measurementIdPattern() { diff --git a/src/main/java/life/qbic/data/processing/config/RoundRobinDraw.java b/src/main/java/life/qbic/data/processing/config/RoundRobinDraw.java new file mode 100644 index 0000000..fec7fa7 --- /dev/null +++ b/src/main/java/life/qbic/data/processing/config/RoundRobinDraw.java @@ -0,0 +1,56 @@ +package life.qbic.data.processing.config; + +import java.util.ArrayList; +import java.util.Collection; + +/** + * Round Robin Draw + *

+ * Enables a thread-safe access of items in a given collection based on the round robin method. + * + * @since 1.0.0s + */ +public class RoundRobinDraw { + + private final ArrayList items; + private final int itemsAmount; + private int currentIndex = 0; + + private RoundRobinDraw(Collection items) { + this.items = new ArrayList<>(items); + this.itemsAmount = items.size(); + } + + /** + * Creates an instance of {@link RoundRobinDraw} based on the type {@link T} of the collection provided + * @param items a collection of items the round robin method shall be applied. + * @return an instance of this class + * @throws IllegalArgumentException if an empty collection is provided or the collection is null + * @since 1.0.0 + */ + public static RoundRobinDraw create(Collection items) throws IllegalArgumentException { + if (items == null || items.isEmpty()) { + throw new IllegalArgumentException("Collection must not be null or empty"); + } + return new RoundRobinDraw<>(items); + } + + /** + * Returns the next element {@link T} of a {@link RoundRobinDraw } instance. + *

+ * If the last item of the instance has been already been provided, it will start again from the + * first item. + * + * @return an object of type {@link T}. + * @since 1.0.0 + */ + public synchronized T next() { + if (currentIndex == itemsAmount) { + currentIndex = 0; + } + T value = items.get(currentIndex); + currentIndex++; + return value; + } + +} diff --git a/src/main/java/life/qbic/data/processing/evaluation/EvaluationConfiguration.java b/src/main/java/life/qbic/data/processing/evaluation/EvaluationConfiguration.java index 4eeaee2..1ab8c58 100644 --- a/src/main/java/life/qbic/data/processing/evaluation/EvaluationConfiguration.java +++ b/src/main/java/life/qbic/data/processing/evaluation/EvaluationConfiguration.java @@ -2,8 +2,10 @@ import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Collection; import java.util.regex.Pattern; import life.qbic.data.processing.GlobalConfig; +import life.qbic.data.processing.config.RoundRobinDraw; /** * Evaluation Configuration @@ -15,21 +17,24 @@ public class EvaluationConfiguration { private final Path workingDirectory; - private final Path targetDirectory; + private final Collection targetDirectories; private final Pattern measurementIdPattern; private final Path usersErrorDirectory; + private final RoundRobinDraw targetDirectoriesRoundRobinDraw; - public EvaluationConfiguration(String workingDirectory, String targetDirectory, + public EvaluationConfiguration(String workingDirectory, Collection targetDirectories, String measurementIdPattern, GlobalConfig globalConfig) { this.workingDirectory = Paths.get(workingDirectory); if (!this.workingDirectory.toFile().exists()) { throw new IllegalArgumentException("Evaluation worker directory does not exist"); } - this.targetDirectory = Paths.get(targetDirectory); - if (!this.targetDirectory.toFile().exists()) { - throw new IllegalArgumentException("Evaluation target directory does not exist"); - } + this.targetDirectories = targetDirectories.stream().toList(); + this.targetDirectories.stream().filter(path -> !path.toFile().exists()).forEach(path -> { + throw new IllegalArgumentException( + "Evaluation target directory '%s' does not exist".formatted(path)); + }); + this.targetDirectoriesRoundRobinDraw = RoundRobinDraw.create(targetDirectories); if (measurementIdPattern.isBlank()) { throw new IllegalArgumentException("Measurement id pattern cannot be blank"); } @@ -41,8 +46,8 @@ public Path workingDirectory() { return workingDirectory; } - public Path targetDirectory() { - return targetDirectory; + public RoundRobinDraw targetDirectories() { + return targetDirectoriesRoundRobinDraw; } public Pattern measurementIdPattern() { diff --git a/src/main/java/life/qbic/data/processing/evaluation/EvaluationRequest.java b/src/main/java/life/qbic/data/processing/evaluation/EvaluationRequest.java index 4dc3523..51cac39 100644 --- a/src/main/java/life/qbic/data/processing/evaluation/EvaluationRequest.java +++ b/src/main/java/life/qbic/data/processing/evaluation/EvaluationRequest.java @@ -21,16 +21,16 @@ import life.qbic.data.processing.ErrorSummary; import life.qbic.data.processing.Provenance; import life.qbic.data.processing.Provenance.ProvenanceException; +import life.qbic.data.processing.config.RoundRobinDraw; import org.apache.logging.log4j.Logger; /** * Evaluation Request - Last process * *

Validates the presence of a QBiC measurement ID in the dataset root - * folder.

- * If a valid measurement ID is found, the process updates the provenance file with the ID and moves - * the dataset to the openBIS ETL. After successful transfer, an openBIS marker-file is created, to integrate - * the dataset registration with openBIS ETL. + * folder.

If a valid measurement ID is found, the process updates the provenance file with the + * ID and moves the dataset to the openBIS ETL. After successful transfer, an openBIS marker-file is + * created, to integrate the dataset registration with openBIS ETL. *

* If none is present, or the identifier does not match the requirements, it is moved back to the * users error folder. @@ -49,15 +49,16 @@ public class EvaluationRequest extends Thread { private final AtomicBoolean active = new AtomicBoolean(false); private final AtomicBoolean terminated = new AtomicBoolean(false); private final Path workingDirectory; - private final Path targetDirectory; private final Pattern measurementIdPattern; private final Path usersErrorDirectory; + private final RoundRobinDraw targetDirectories; + private Path assignedTargetDirectory; - public EvaluationRequest(Path workingDirectory, Path targetDirectory, + public EvaluationRequest(Path workingDirectory, RoundRobinDraw targetDirectories, Pattern measurementIdPattern, Path usersErrorDirectory) { this.setName(THREAD_NAME.formatted(nextThreadNumber())); this.workingDirectory = workingDirectory; - this.targetDirectory = targetDirectory; + this.targetDirectories = targetDirectories; this.measurementIdPattern = measurementIdPattern; if (!workingDirectory.resolve(INTERVENTION_DIRECTORY).toFile().mkdir() && !workingDirectory.resolve( @@ -70,7 +71,7 @@ public EvaluationRequest(Path workingDirectory, Path targetDirectory, } public EvaluationRequest(EvaluationConfiguration evaluationConfiguration) { - this(evaluationConfiguration.workingDirectory(), evaluationConfiguration.targetDirectory(), + this(evaluationConfiguration.workingDirectory(), evaluationConfiguration.targetDirectories(), evaluationConfiguration.measurementIdPattern(), evaluationConfiguration.usersErrorDirectory()); } @@ -95,7 +96,8 @@ public void run() { while (true) { active.set(true); for (File taskDir : tasks()) { - if (push(taskDir.getAbsolutePath())) { + if (push(taskDir.getAbsolutePath()) && taskDir.exists()) { + assignedTargetDirectory = getAssignedTargetDir(); evaluateDirectory(taskDir); removeTask(taskDir); } @@ -114,6 +116,10 @@ public void run() { } } + private Path getAssignedTargetDir() { + return targetDirectories.next(); + } + private void removeTask(File taskDir) { LOCK.lock(); try { @@ -160,17 +166,19 @@ private void evaluateDirectory(File taskDir) { } moveToTargetDir(taskDir); try { - createMarkerFile(targetDirectory, taskDir.getName()); + createMarkerFile(assignedTargetDirectory, taskDir.getName()); } catch (IOException e) { LOG.error("Could not create marker file: {}", taskDir.getAbsolutePath(), e); moveToSystemIntervention(taskDir, e.getMessage()); } return; } - var errorMessage = ErrorSummary.createSimple(taskDir.getName(), dataset.getName(), "Missing QBiC measurement ID", + var errorMessage = ErrorSummary.createSimple(taskDir.getName(), dataset.getName(), + "Missing QBiC measurement ID", "For a successful registration please provide the pre-registered QBiC measurement ID"); LOG.error( - "Missing measurement identifier: no known measurement id was found in the content of directory '{}' in task '{}'", dataset.getName(), taskDir.getName()); + "Missing measurement identifier: no known measurement id was found in the content of directory '{}' in task '{}'", + dataset.getName(), taskDir.getName()); moveBackToOrigin(taskDir, provenance, errorMessage.toString()); } @@ -217,13 +225,13 @@ private void moveBackToOrigin(File taskDir, Provenance provenance, String reason private void moveToTargetDir(File taskDir) { LOG.info( - "Moving %s to target directory %s".formatted(taskDir.getAbsolutePath(), targetDirectory)); + "Moving %s to target directory %s".formatted(taskDir.getAbsolutePath(), assignedTargetDirectory)); try { - Files.move(taskDir.toPath(), targetDirectory.resolve(taskDir.getName())); + Files.move(taskDir.toPath(), assignedTargetDirectory.resolve(taskDir.getName())); } catch (IOException e) { - LOG.error("Cannot move task to target directory: %s".formatted(targetDirectory), e); + LOG.error("Cannot move task to target directory: %s".formatted(targetDirectories), e); moveToSystemIntervention(taskDir, - "Cannot move task to target directory: %s".formatted(targetDirectory)); + "Cannot move task to target directory: %s".formatted(targetDirectories)); } } diff --git a/src/main/java/life/qbic/data/processing/processing/ProcessingRequest.java b/src/main/java/life/qbic/data/processing/processing/ProcessingRequest.java index 67279c0..e292a67 100644 --- a/src/main/java/life/qbic/data/processing/processing/ProcessingRequest.java +++ b/src/main/java/life/qbic/data/processing/processing/ProcessingRequest.java @@ -64,7 +64,7 @@ private static int nextThreadNumber() { return threadNumber++; } - private static boolean push(String taskId) { + private boolean push(String taskId) { LOCK.lock(); boolean notActiveYet; try { @@ -80,10 +80,12 @@ public void run() { while (true) { active.set(true); for (File taskDir : tasks()) { - if (push(taskDir.getAbsolutePath())) { + if (push(taskDir.getAbsolutePath()) && taskDir.exists()) { LOG.info("Registering task " + taskDir.getAbsolutePath()); processFile(taskDir); clearTask(taskDir); + } else { + clearTask(taskDir); } } try { @@ -105,7 +107,7 @@ public void run() { * @since */ private void processFile(File taskDir) { - var taskDirContent = Arrays.stream(Objects.requireNonNull(taskDir.listFiles())).toList(); + var taskDirContent = Arrays.stream(Objects.requireNonNull(taskDir.listFiles(), "Task dir must not be null: " + taskDir)).toList(); if (checkForEmpty(taskDir, taskDirContent)) { return; @@ -185,6 +187,7 @@ private boolean checkForEmpty(File taskDir, List taskDirContent) { } private void moveToTargetFolder(File taskDir) throws IOException { + LOG.info("Moving task {} to target folder", taskDir.getAbsolutePath()); Files.move(taskDir.toPath(), targetDirectory.resolve(taskDir.getName())); } diff --git a/src/main/java/life/qbic/data/processing/registration/RegistrationConfiguration.java b/src/main/java/life/qbic/data/processing/registration/RegistrationConfiguration.java index 7737b55..aaa894a 100644 --- a/src/main/java/life/qbic/data/processing/registration/RegistrationConfiguration.java +++ b/src/main/java/life/qbic/data/processing/registration/RegistrationConfiguration.java @@ -18,7 +18,7 @@ public RegistrationConfiguration(String workingDirectory, String targetDirectory if (!workingDirectory().toFile().isDirectory()) { throw new IllegalArgumentException(targetDirectory + " is not a directory"); } - this.targetDirectory = Paths.get(Objects.requireNonNull(targetDirectory, "targetDirectory must not be null")); + this.targetDirectory = Paths.get(Objects.requireNonNull(targetDirectory, "targetDirectories must not be null")); if (!targetDirectory().toFile().exists()) { throw new IllegalArgumentException(targetDirectory + " does not exist"); } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index a0731cb..3900cde 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -41,7 +41,12 @@ processing.target.dir=${EVALUATION_DIR} # --------------------------------- evaluations.threads=2 evaluation.working.dir=${EVALUATION_DIR} -evaluation.target.dir=${OPENBIS_ETL_DIR} +# Define one or more target directories here +# Example single target dir: +# evaluation.target.dirs=/my/example/target/dir +# Example multiple target dir: +# evaluation.target.dirs=/my/example/target/dir1,/my/example/target/dir2,/my/example/target/dir3 +evaluation.target.dirs=${OPENBIS_ETL_DIRS} evaluation.measurement-id.pattern=^(MS|NGS)Q[A-Z0-9]{4}[0-9]{3}[A-Z0-9]{2}-[0-9]* # ---------------- From 5c46a6ca810c410e8e93c4838afcee78bf7fe4c0 Mon Sep 17 00:00:00 2001 From: Sven F <9976560+sven1103@users.noreply.github.com> Date: Wed, 24 Apr 2024 10:31:16 +0200 Subject: [PATCH 3/5] Introduce proper exit code and blacklisting of directories (#4) * Return exit status 0 on graceful shutdown * Enable ignore list for scanner process --- .../java/life/qbic/data/processing/AppConfig.java | 4 ++-- .../java/life/qbic/data/processing/Application.java | 2 ++ .../processing/evaluation/EvaluationRequest.java | 2 +- .../life/qbic/data/processing/scanner/Scanner.java | 11 +++++++++++ .../processing/scanner/ScannerConfiguration.java | 12 +++++++++++- src/main/resources/application.properties | 3 +++ 6 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/main/java/life/qbic/data/processing/AppConfig.java b/src/main/java/life/qbic/data/processing/AppConfig.java index 900dc9a..680597f 100644 --- a/src/main/java/life/qbic/data/processing/AppConfig.java +++ b/src/main/java/life/qbic/data/processing/AppConfig.java @@ -28,8 +28,8 @@ class AppConfig { @Bean ScannerConfiguration scannerConfiguration( @Value("${scanner.directory}") String scannerDirectory, - @Value("${scanner.interval}") int interval) { - return new ScannerConfiguration(scannerDirectory, interval); + @Value("${scanner.interval}") int interval, @Value("${scanner.ignore}") String[] ignore) { + return new ScannerConfiguration(scannerDirectory, interval, ignore); } @Bean diff --git a/src/main/java/life/qbic/data/processing/Application.java b/src/main/java/life/qbic/data/processing/Application.java index 268da61..1696c15 100644 --- a/src/main/java/life/qbic/data/processing/Application.java +++ b/src/main/java/life/qbic/data/processing/Application.java @@ -75,6 +75,8 @@ public static void main(String[] args) { registrationWorkers.forEach(Thread::interrupt); processingWorkers.forEach(Thread::interrupt); evaluationWorkers.forEach(Thread::interrupt); + // if every worker thread has shut down successfully, the application can exit with status code 0 + Runtime.getRuntime().halt(0); }, "Shutdown-thread")); } diff --git a/src/main/java/life/qbic/data/processing/evaluation/EvaluationRequest.java b/src/main/java/life/qbic/data/processing/evaluation/EvaluationRequest.java index 51cac39..59a3f80 100644 --- a/src/main/java/life/qbic/data/processing/evaluation/EvaluationRequest.java +++ b/src/main/java/life/qbic/data/processing/evaluation/EvaluationRequest.java @@ -218,7 +218,7 @@ private void moveBackToOrigin(File taskDir, Provenance provenance, String reason Paths.get(provenance.userWorkDirectoryPath).resolve(usersErrorDirectory) .resolve(taskDir.getName())); } catch (IOException e) { - LOG.error("Cannot move task to user intervention: %s".formatted(provenance.originPath), e); + LOG.error("Cannot move task to user intervention: %s".formatted(Paths.get(provenance.userWorkDirectoryPath).resolve(usersErrorDirectory)), e); moveToSystemIntervention(taskDir, e.getMessage()); } } diff --git a/src/main/java/life/qbic/data/processing/scanner/Scanner.java b/src/main/java/life/qbic/data/processing/scanner/Scanner.java index 8ef44cd..fa32692 100644 --- a/src/main/java/life/qbic/data/processing/scanner/Scanner.java +++ b/src/main/java/life/qbic/data/processing/scanner/Scanner.java @@ -14,6 +14,7 @@ import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.Set; import life.qbic.data.processing.ConcurrentRegistrationQueue; import life.qbic.data.processing.GlobalConfig; import life.qbic.data.processing.registration.RegistrationRequest; @@ -41,6 +42,7 @@ public class Scanner extends Thread { private final HashSet userProcessDirectories = new HashSet<>(); private final ConcurrentRegistrationQueue registrationQueue; private final HashSet submittedRequests = new HashSet<>(); + private final Set ignoredDirectories = new HashSet<>(); public Scanner(ScannerConfiguration scannerConfiguration, ConcurrentRegistrationQueue registrationQueue, GlobalConfig globalConfig) { @@ -53,6 +55,14 @@ public Scanner(ScannerConfiguration scannerConfiguration, this.scanInterval = scannerConfiguration.scanInterval(); this.registrationQueue = Objects.requireNonNull(registrationQueue, "registrationQueue must not be null"); + this.ignoredDirectories.addAll(scannerConfiguration.ignore()); + if (!this.ignoredDirectories.isEmpty()) { + log.info("Ignoring {} directories", ignoredDirectories.size()); + } + } + + private boolean notToIgnore(String filename) { + return !ignoredDirectories.contains(filename); } @Override @@ -62,6 +72,7 @@ public void run() { try { var userFolderIterator = Arrays.stream( Objects.requireNonNull(scannerPath.toFile().listFiles())).filter(File::isDirectory) + .filter(file -> notToIgnore(file.getName())) .toList().iterator(); while (userFolderIterator.hasNext()) { diff --git a/src/main/java/life/qbic/data/processing/scanner/ScannerConfiguration.java b/src/main/java/life/qbic/data/processing/scanner/ScannerConfiguration.java index 052ed75..ac558b1 100644 --- a/src/main/java/life/qbic/data/processing/scanner/ScannerConfiguration.java +++ b/src/main/java/life/qbic/data/processing/scanner/ScannerConfiguration.java @@ -1,16 +1,22 @@ package life.qbic.data.processing.scanner; +import java.util.Arrays; +import java.util.Collection; +import java.util.Objects; + public class ScannerConfiguration { private final String scannerDirectory; private final int scanInterval; + private final String[] ignore; - public ScannerConfiguration(String scannerDirectory, int interval) { + public ScannerConfiguration(String scannerDirectory, int interval, String[] ignore) { this.scannerDirectory = scannerDirectory; if (interval <= 0) { throw new IllegalArgumentException("Interval must be greater than 0"); } this.scanInterval = interval; + this.ignore = Arrays.copyOf(Objects.requireNonNull(ignore), ignore.length); } public String scannerDirectory() { @@ -20,4 +26,8 @@ public String scannerDirectory() { public int scanInterval() { return scanInterval; } + + public Collection ignore () { + return Arrays.stream(ignore).toList(); + } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 3900cde..10abf93 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -16,6 +16,9 @@ users.error.directory.name=error # Path to the directory that contains all user directories # e.g. /home in Linux or /Users in macOS scanner.directory=${SCANNER_DIR:/home} +# Ignored directories are skipped during scanning +# your can provide a list of comma-separated names if you want to ignore multiple directories +scanner.ignore=${SCANNER_IGNORE:} # The time interval (milliseconds) the scanner thread iterates through the scanner directory # Value must be an integer > 0 scanner.interval=1000 From 8a7e9413cace837048390781c3b91a8d72e116b8 Mon Sep 17 00:00:00 2001 From: Sven F <9976560+sven1103@users.noreply.github.com> Date: Tue, 14 May 2024 15:26:37 +0200 Subject: [PATCH 4/5] Support metadata file for registration (#5) Adds support for a metadata file, that contains a tab-separated list of measurement id and file pairs, which will be used for registration. Files that have the same measurement ID entry will be aggregated and processed as one task. --- .../life/qbic/data/processing/AppConfig.java | 9 +- .../qbic/data/processing/Application.java | 2 +- .../life/qbic/data/processing/Provenance.java | 21 ++ .../config/RegistrationWorkersConfig.java | 9 +- .../evaluation/EvaluationRequest.java | 20 +- .../processing/ProcessingRequest.java | 17 -- .../processing/registration/ErrorCode.java | 13 ++ .../ProcessRegistrationRequest.java | 194 +++++++++++++++--- .../RegistrationConfiguration.java | 9 +- .../registration/RegistrationMetadata.java | 5 + .../registration/ValidationException.java | 19 ++ src/main/resources/application.properties | 1 + 12 files changed, 259 insertions(+), 60 deletions(-) create mode 100644 src/main/java/life/qbic/data/processing/registration/ErrorCode.java create mode 100644 src/main/java/life/qbic/data/processing/registration/RegistrationMetadata.java create mode 100644 src/main/java/life/qbic/data/processing/registration/ValidationException.java diff --git a/src/main/java/life/qbic/data/processing/AppConfig.java b/src/main/java/life/qbic/data/processing/AppConfig.java index 680597f..fffe848 100644 --- a/src/main/java/life/qbic/data/processing/AppConfig.java +++ b/src/main/java/life/qbic/data/processing/AppConfig.java @@ -36,15 +36,18 @@ ScannerConfiguration scannerConfiguration( RegistrationWorkersConfig registrationWorkersConfig( @Value("${registration.threads}") int amountOfWorkers, @Value("${registration.working.dir}") String workingDirectory, - @Value("${registration.target.dir}") String targetDirectory) { - return new RegistrationWorkersConfig(amountOfWorkers, workingDirectory, targetDirectory); + @Value("${registration.target.dir}") String targetDirectory, + @Value("${registration.metadata.filename}") String metadataFileName) { + return new RegistrationWorkersConfig(amountOfWorkers, workingDirectory, targetDirectory, + metadataFileName); } @Bean RegistrationConfiguration registrationConfiguration( RegistrationWorkersConfig registrationWorkersConfig) { return new RegistrationConfiguration(registrationWorkersConfig.workingDirectory().toString(), - registrationWorkersConfig.targetDirectory().toString()); + registrationWorkersConfig.targetDirectory().toString(), + registrationWorkersConfig.metadataFileName()); } @Bean diff --git a/src/main/java/life/qbic/data/processing/Application.java b/src/main/java/life/qbic/data/processing/Application.java index 1696c15..0f7dd51 100644 --- a/src/main/java/life/qbic/data/processing/Application.java +++ b/src/main/java/life/qbic/data/processing/Application.java @@ -46,7 +46,7 @@ public static void main(String[] args) { List registrationWorkers = new LinkedList<>(); for (int i=0; i datasetFiles; + /** * A list of ordered processing folder stops the dataset has traversed and passed successfully. *

@@ -95,6 +100,22 @@ public enum ERROR_CODE { IO_ERROR } + public void addDatasetFiles(Collection datasetFiles) { + Objects.requireNonNull(datasetFiles); + if (this.datasetFiles == null) { + this.datasetFiles = new ArrayList<>(); + } + this.datasetFiles.addAll(datasetFiles); + } + + public void addDatasetFile(String datasetFile) { + addDatasetFiles(Collections.singletonList(datasetFile)); + } + + public Collection datasetFiles() { + return datasetFiles.stream().toList(); + } + public static class ProvenanceException extends RuntimeException { private final ERROR_CODE code; diff --git a/src/main/java/life/qbic/data/processing/config/RegistrationWorkersConfig.java b/src/main/java/life/qbic/data/processing/config/RegistrationWorkersConfig.java index 1ef08a8..44be342 100644 --- a/src/main/java/life/qbic/data/processing/config/RegistrationWorkersConfig.java +++ b/src/main/java/life/qbic/data/processing/config/RegistrationWorkersConfig.java @@ -11,7 +11,9 @@ public class RegistrationWorkersConfig { private final Path targetDirectory; - public RegistrationWorkersConfig(int threads, String workingDirectory, String targetDirectory) { + private final String metadataFileName; + + public RegistrationWorkersConfig(int threads, String workingDirectory, String targetDirectory, String metadataFileName) { if (threads < 1) { throw new IllegalArgumentException("Number of threads must be greater than 0"); } @@ -26,6 +28,7 @@ public RegistrationWorkersConfig(int threads, String workingDirectory, String ta this.workingDirectory = directory; this.amountOfWorkers = threads; this.targetDirectory = targetDirectoryPath; + this.metadataFileName = metadataFileName; } public int amountOfWorkers() { @@ -39,4 +42,8 @@ public Path workingDirectory() { public Path targetDirectory() { return this.targetDirectory; } + + public String metadataFileName() { + return this.metadataFileName; + } } diff --git a/src/main/java/life/qbic/data/processing/evaluation/EvaluationRequest.java b/src/main/java/life/qbic/data/processing/evaluation/EvaluationRequest.java index 59a3f80..2753d8d 100644 --- a/src/main/java/life/qbic/data/processing/evaluation/EvaluationRequest.java +++ b/src/main/java/life/qbic/data/processing/evaluation/EvaluationRequest.java @@ -18,6 +18,7 @@ import java.util.regex.MatchResult; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import life.qbic.data.processing.ErrorSummary; import life.qbic.data.processing.Provenance; import life.qbic.data.processing.Provenance.ProvenanceException; @@ -146,17 +147,8 @@ private void evaluateDirectory(File taskDir) { return; } - var datasetSearch = findDataset(taskDir); - if (datasetSearch.isEmpty()) { - LOG.error("No dataset found: {}", taskDir.getAbsolutePath()); - moveBackToOrigin(taskDir, provenance, "No dataset directory found."); - return; - } - var dataset = datasetSearch.get(); - Matcher matcher = measurementIdPattern.matcher(dataset.getName()); - var measurementIdResult = matcher.results().map(MatchResult::group).findFirst(); + var measurementIdResult = provenance.qbicMeasurementID == null || provenance.qbicMeasurementID.isBlank() ? Optional.empty() : Optional.of(provenance.qbicMeasurementID); if (measurementIdResult.isPresent()) { - provenance.qbicMeasurementID = measurementIdResult.get(); provenance.addToHistory(taskDir.getAbsolutePath()); try { updateProvenanceFile(provenanceSearch.get(), provenance); @@ -173,12 +165,13 @@ private void evaluateDirectory(File taskDir) { } return; } - var errorMessage = ErrorSummary.createSimple(taskDir.getName(), dataset.getName(), + var errorMessage = ErrorSummary.createSimple(taskDir.getName(), + String.join(", ", provenance.datasetFiles), "Missing QBiC measurement ID", "For a successful registration please provide the pre-registered QBiC measurement ID"); LOG.error( "Missing measurement identifier: no known measurement id was found in the content of directory '{}' in task '{}'", - dataset.getName(), taskDir.getName()); + String.join(", ", provenance.datasetFiles), taskDir.getName()); moveBackToOrigin(taskDir, provenance, errorMessage.toString()); } @@ -208,7 +201,8 @@ private void moveToSystemIntervention(File taskDir, String reason) { } private void moveBackToOrigin(File taskDir, Provenance provenance, String reason) { - LOG.info("Moving back to original user directory: " + taskDir.getAbsolutePath()); + LOG.info("Moving back to original user directory: {}", + Paths.get(provenance.userWorkDirectoryPath).resolve(usersErrorDirectory)); try { var errorFile = taskDir.toPath().resolve("error.txt").toFile(); errorFile.createNewFile(); diff --git a/src/main/java/life/qbic/data/processing/processing/ProcessingRequest.java b/src/main/java/life/qbic/data/processing/processing/ProcessingRequest.java index e292a67..fcfff17 100644 --- a/src/main/java/life/qbic/data/processing/processing/ProcessingRequest.java +++ b/src/main/java/life/qbic/data/processing/processing/ProcessingRequest.java @@ -133,7 +133,6 @@ private void processFile(File taskDir) { } Provenance finalProvenance = provenance; - packageDataset(taskDir); taskDirContent.stream().filter(file -> !file.getName().equals(Provenance.FILE_NAME)).findFirst() .ifPresent(file -> { finalProvenance.addToHistory(taskDir.getAbsolutePath()); @@ -153,22 +152,6 @@ private void processFile(File taskDir) { }); } - private void packageDataset(File taskDir) { - Optional datasetSearch = Arrays.stream(taskDir.listFiles()) - .filter(file -> !file.getName().equals(Provenance.FILE_NAME)).findFirst(); - datasetSearch.ifPresent(file -> { - if (file.isFile()) { - File datasetDir = taskDir.toPath().resolve(file.getName() + "_dataset").toFile(); - datasetDir.mkdir(); - try { - Files.move(file.toPath(), datasetDir.toPath().resolve(file.getName())); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - }); - } - private Optional findProvenanceFile(List taskDirContent) { Optional provenanceFileSearch = taskDirContent.stream() .filter(file -> file.getName().equals(Provenance.FILE_NAME)).findFirst(); diff --git a/src/main/java/life/qbic/data/processing/registration/ErrorCode.java b/src/main/java/life/qbic/data/processing/registration/ErrorCode.java new file mode 100644 index 0000000..cbf7726 --- /dev/null +++ b/src/main/java/life/qbic/data/processing/registration/ErrorCode.java @@ -0,0 +1,13 @@ +package life.qbic.data.processing.registration; + +/** + * + * + *

+ * + * @since + */ +public enum ErrorCode { + METADATA_FILE_NOT_FOUND, + INCOMPLETE_METADATA, FILE_NOT_FOUND, MISSING_FILE_ENTRY, IO_EXCEPTION +} diff --git a/src/main/java/life/qbic/data/processing/registration/ProcessRegistrationRequest.java b/src/main/java/life/qbic/data/processing/registration/ProcessRegistrationRequest.java index c0c0a1d..8a064b6 100644 --- a/src/main/java/life/qbic/data/processing/registration/ProcessRegistrationRequest.java +++ b/src/main/java/life/qbic/data/processing/registration/ProcessRegistrationRequest.java @@ -3,13 +3,23 @@ import static org.apache.logging.log4j.LogManager.getLogger; import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; import life.qbic.data.processing.ConcurrentRegistrationQueue; +import life.qbic.data.processing.GlobalConfig; import life.qbic.data.processing.Provenance; import org.apache.logging.log4j.Logger; import org.springframework.lang.NonNull; @@ -20,7 +30,8 @@ * This must be the first process of handling a new incoming dataset. It will consume a processing * request item, that is then used to prepare the dataset for the following downstream processes. *

- * The process polls the {@link ConcurrentRegistrationQueue} shared with the scanning thread. + * The process polls the {@link life.qbic.data.processing.ConcurrentRegistrationQueue} shared with + * the scanning thread. * *

* The process will do the following tasks: @@ -40,53 +51,135 @@ public class ProcessRegistrationRequest extends Thread { private final ConcurrentRegistrationQueue registrationQueue; private final Path workingDirectory; private final Path targetDirectory; + private final String metadataFileName; + private final Path userErrorDirectory; private AtomicBoolean active = new AtomicBoolean(false); public ProcessRegistrationRequest(@NonNull ConcurrentRegistrationQueue registrationQueue, - @NonNull RegistrationConfiguration configuration) { + @NonNull RegistrationConfiguration configuration, @NonNull GlobalConfig globalConfig) { this.setName(threadName.formatted(nextThreadNumber())); this.registrationQueue = registrationQueue; this.workingDirectory = configuration.workingDirectory(); this.targetDirectory = configuration.targetDirectory(); + this.metadataFileName = configuration.metadataFileName(); + this.userErrorDirectory = globalConfig.usersErrorDirectory(); } private static int nextThreadNumber() { return threadNumber++; } - @Override - public void run() { - while (true) { - var request = registrationQueue.poll(); - active.set(true); - log.info("Processing request: {}", request); - try { - Path taskDir = createTaskDirectory(); - Path newLocation = taskDir.resolve(request.target().getFileName()); - Files.move(request.target(), newLocation); - writeProvenanceInformation(taskDir, newLocation, request); - Files.move(taskDir, targetDirectory.resolve(taskDir.getFileName())); - } catch (RuntimeException e) { - log.error("Error moving task directory", e); - // TODO move back to user folder - } catch (IOException e) { - log.error("Error while processing registration request", e); - // TODO move back to user folder - } finally { - active.set(false); - log.info("Processing completed: {}", request); + private static void cleanup(Path workingTargetDir) throws IOException { + try (var content = Files.walk(workingTargetDir)) { + content.map(Path::toFile).sorted(Comparator.reverseOrder()).forEach(File::delete); + } + } + + private static void processMeasurement(String measurementId, + Map> aggregatedFilesByMeasurementId, Path workingTargetDir, + Path taskDir) throws IOException { + for (RegistrationMetadata metadataEntry : aggregatedFilesByMeasurementId.get( + measurementId)) { + Files.move(workingTargetDir.resolve(metadataEntry.file()), + taskDir.resolve(metadataEntry.file())); + } + } + + private void moveBackToOrigin(Path target, Path usersHomePath, String reason) { + log.info("Moving back to original user directory: {}", usersHomePath); + try { + Path taskDir = createTaskDirectory(); + Files.move(target, taskDir.resolve(target.getFileName())); + var errorFile = taskDir.resolve("error.txt").toFile(); + errorFile.createNewFile(); + Files.writeString(errorFile.toPath(), reason); + usersHomePath.resolve(userErrorDirectory).toFile().mkdir(); + Files.move(taskDir, + usersHomePath.resolve(userErrorDirectory) + .resolve(taskDir.toFile().getName())); + } catch (IOException e) { + log.error("Cannot move task to user intervention: %s".formatted( + usersHomePath.resolve(userErrorDirectory)), e); + } + } + + private void validateFileEntries(Collection metadata, Path request) + throws ValidationException { + for (RegistrationMetadata metadataEntry : metadata) { + if (!request.resolve(Paths.get(metadataEntry.file())).toFile().exists()) { + throw new ValidationException( + "Unknown file reference in metadata: %s".formatted(metadataEntry.file()), + ErrorCode.FILE_NOT_FOUND); + } + } + // To save the user from any trouble, let's also check if all dataset files are described by a metadata entry + var filesInMetadata = metadata.stream().map(RegistrationMetadata::file).toList(); + var physicalFiles = request.toFile().listFiles(); + for (File file : physicalFiles) { + // we ignore hidden files + if (file.isHidden()) { + continue; + } + if (file.getName().endsWith(metadataFileName)) { + continue; + } + if (!filesInMetadata.contains(file.getName())) { + throw new ValidationException( + "Found more files than described in the metadata file: %s".formatted(file.getName()), + ErrorCode.MISSING_FILE_ENTRY); } } } + private List findAndParseMetadata(Path request) throws ValidationException { + Optional metadataFile = findMetadataFile(request); + if (metadataFile.isEmpty()) { + throw new ValidationException("Metadata file does not exist", + ErrorCode.METADATA_FILE_NOT_FOUND); + } + + List content; + try { + content = Files.readAllLines(Paths.get(metadataFile.get().getPath())).stream() + .filter(row -> !row.isBlank()).toList(); + } catch (IOException e) { + log.error("Error reading metadata file", e); + throw new ValidationException("Cannot read metadata file", ErrorCode.IO_EXCEPTION); + } + + return content.stream().map(this::parseMetadataRow).toList(); + } + + private RegistrationMetadata parseMetadataRow(String value) throws ValidationException { + try { + var splitValues = value.split("\t"); + return new RegistrationMetadata(splitValues[0], splitValues[1]); + } catch (IndexOutOfBoundsException e) { + log.error("Error parsing metadata row: %s".formatted(value), e); + throw new ValidationException("Cannot parse metadata entry", ErrorCode.INCOMPLETE_METADATA); + } + } + + private Optional findMetadataFile(Path path) { + for (File file : Objects.requireNonNull(path.toFile().listFiles())) { + if (file.isFile() && file.getName().endsWith(metadataFileName)) { + return Optional.of(file); + } + } + return Optional.empty(); + } + private void writeProvenanceInformation(Path taskDir, Path newLocation, - RegistrationRequest request) + RegistrationRequest request, String measurementId, + List datasetFiles) throws IOException { Provenance provenance = new Provenance(); provenance.originPath = request.origin().toString(); provenance.history = new ArrayList<>(); provenance.history.add(newLocation.toString()); provenance.userWorkDirectoryPath = String.valueOf(request.userPath()); + provenance.qbicMeasurementID = measurementId; + provenance.addDatasetFiles(datasetFiles); ObjectMapper mapper = new ObjectMapper(); mapper.writerWithDefaultPrettyPrinter() .writeValue(taskDir.resolve("provenance.json").toFile(), provenance); @@ -112,4 +205,57 @@ public void interrupt() { log.debug("Task has been finished"); } + @Override + public void run() { + while (true) { + var request = registrationQueue.poll(); + active.set(true); + log.info("Processing request: {}", request); + var intermediateTaskDir = createTaskDirectory(); + try { + // Let's first move the registration request content to the working directory of the process + + Files.move(request.target(), intermediateTaskDir.resolve(request.target().getFileName())); + var workingTargetDir = intermediateTaskDir.resolve(request.target().getFileName()); + + var registrationMetadata = findAndParseMetadata(workingTargetDir); + validateFileEntries(registrationMetadata, workingTargetDir); + + var aggregatedFilesByMeasurementId = registrationMetadata.stream().collect( + Collectors.groupingBy(RegistrationMetadata::measurementId)); + + processAll(aggregatedFilesByMeasurementId, workingTargetDir, request); + + // Finally clean up the task directory, which should only contain the original metadata file + cleanup(workingTargetDir); + } catch (ValidationException e) { + log.error("Failed validation processing request: %s".formatted(request), e); + moveBackToOrigin(intermediateTaskDir, request.userPath(), e.getMessage()); + } catch (RuntimeException e) { + log.error("Error moving task directory", e); + // TODO move back to user folder + } catch (IOException e) { + log.error("Error while processing registration request", e); + // TODO move back to user folder + } finally { + active.set(false); + log.info("Processing completed: {}", request); + } + } + } + + private void processAll(Map> aggregatedFilesByMeasurementId, + Path workingTargetDir, RegistrationRequest request) throws IOException { + for (String measurementId : aggregatedFilesByMeasurementId.keySet()) { + // We now create individual task directories for every measurement dataset + Path taskDir = createTaskDirectory(); + processMeasurement(measurementId, aggregatedFilesByMeasurementId, workingTargetDir, taskDir); + + writeProvenanceInformation(taskDir, targetDirectory, request, measurementId, + aggregatedFilesByMeasurementId.get(measurementId).stream() + .map(RegistrationMetadata::file).toList()); + Files.move(taskDir, targetDirectory.resolve(taskDir.getFileName())); + } + } + } diff --git a/src/main/java/life/qbic/data/processing/registration/RegistrationConfiguration.java b/src/main/java/life/qbic/data/processing/registration/RegistrationConfiguration.java index aaa894a..67c8693 100644 --- a/src/main/java/life/qbic/data/processing/registration/RegistrationConfiguration.java +++ b/src/main/java/life/qbic/data/processing/registration/RegistrationConfiguration.java @@ -9,8 +9,9 @@ public class RegistrationConfiguration { private final Path workingDirectory; private final Path targetDirectory; + private final String metadataFileName; - public RegistrationConfiguration(String workingDirectory, String targetDirectory) { + public RegistrationConfiguration(String workingDirectory, String targetDirectory, String metadataFileName) { this.workingDirectory = Paths.get(Objects.requireNonNull(workingDirectory, "workingDirectory must not be null")); if (!workingDirectory().toFile().exists()) { throw new IllegalArgumentException(targetDirectory + " does not exist"); @@ -25,6 +26,10 @@ public RegistrationConfiguration(String workingDirectory, String targetDirectory if (!targetDirectory().toFile().isDirectory()) { throw new IllegalArgumentException(targetDirectory + " is not a directory"); } + if (metadataFileName == null || metadataFileName.isEmpty()) { + throw new IllegalArgumentException("metadataFileName must not be null or empty"); + } + this.metadataFileName = metadataFileName; } public Path workingDirectory() { @@ -34,4 +39,6 @@ public Path workingDirectory() { public Path targetDirectory() { return targetDirectory; } + + public String metadataFileName() { return metadataFileName; } } diff --git a/src/main/java/life/qbic/data/processing/registration/RegistrationMetadata.java b/src/main/java/life/qbic/data/processing/registration/RegistrationMetadata.java new file mode 100644 index 0000000..dfc6cdd --- /dev/null +++ b/src/main/java/life/qbic/data/processing/registration/RegistrationMetadata.java @@ -0,0 +1,5 @@ +package life.qbic.data.processing.registration; + +public record RegistrationMetadata(String measurementId, String file) { + +} diff --git a/src/main/java/life/qbic/data/processing/registration/ValidationException.java b/src/main/java/life/qbic/data/processing/registration/ValidationException.java new file mode 100644 index 0000000..cc33497 --- /dev/null +++ b/src/main/java/life/qbic/data/processing/registration/ValidationException.java @@ -0,0 +1,19 @@ +package life.qbic.data.processing.registration; + +public class ValidationException extends RuntimeException { + + private final ErrorCode errorCode; + + public ValidationException(ErrorCode errorCode) { + this.errorCode = errorCode; + } + + public ValidationException(String message, ErrorCode errorCode) { + super(message); + this.errorCode = errorCode; + } + + public ErrorCode errorCode() { + return errorCode; + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 10abf93..f782bf6 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -27,6 +27,7 @@ scanner.interval=1000 # Settings for the registration worker threads #---------------- registration.threads=2 +registration.metadata.filename=metadata.txt registration.working.dir=${WORKING_DIR:} registration.target.dir=${PROCESSING_DIR:} From 9e3f86e7eccf068296347ae0c63bc50ea1a4dc4a Mon Sep 17 00:00:00 2001 From: Sven F <9976560+sven1103@users.noreply.github.com> Date: Tue, 14 May 2024 16:56:36 +0200 Subject: [PATCH 5/5] Add documentation (#3) --- README.md | 339 +++++++++++------- img/process-flow.jpg | Bin 0 -> 258429 bytes .../life/qbic/data/processing/AppConfig.java | 5 +- .../qbic/data/processing/GlobalConfig.java | 12 +- src/main/resources/application.properties | 4 + 5 files changed, 225 insertions(+), 135 deletions(-) create mode 100644 img/process-flow.jpg diff --git a/README.md b/README.md index 6c46469..496371e 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,11 @@ -# Spring Boot Starter (template) +# Data processing -A minimal working starter template for a Spring Boot non-web applications using the -Spring [CommandLineRunner](https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/CommandLineRunner.html) -interface with a demonstration of Java -annotation-based [Inversion of Control](https://stackoverflow.com/questions/3058/what-is-inversion-of-control) -via [Dependency Injection](https://stackoverflow.com/questions/130794/what-is-dependency-injection). +A small Java application that listens to data ready for registration and performs several +pre-registration +checks before moving the dataset to an openBIS ETL routine. + +> [!NOTE] +> Requires Java SE 17 or newer. ## Run the app @@ -14,174 +15,248 @@ Checkout the latest code from `main` and run the Maven goal `spring-boot:run`: mvn spring-boot:run ``` -## What the app does - -This small app just parses a file with a collection of good coding prayers and creates a singleton -instance of an `CodingPrayersMessageService`. This concrete implementation uses the -interface `MessageService`, that comes with only one public method: `String collectMessage()`. - -This service is used to demonstrate the IoC principle. We have defined another interface `NewsMedia` -and provide a concrete implementation `DeveloperNews`, that will call the message service to receive -recent news and forward them to the caller. +You have to set different environment variables first to configure the individual process parts. +Have a look at the [configuration](#configuration) setting to learn more . -In the main app code, we just retrieve this Singleton instance or Bean in Spring lingua from the -loaded context and call the news media `getNEws()` method. The collected message is then printed out -to the command line interface: +## What the app does -``` +The following figure gives an overview of the process building blocks and flow: - . ____ _ __ _ _ - /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ -( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ - \\/ ___)| |_)| | | | | || (_| | ) ) ) ) - ' |____| .__|_| |_|_| |_\__, | / / / / - =========|_|==============|___/=/_/_/_/ - :: Spring Boot :: (v2.5.6) - -2021-11-18 09:17:37.640 INFO 68052 --- [ main] l.q.s.SpringMinimalTemplateApplication : Starting SpringMinimalTemplateApplication using Java 17.0.1 on imperator.am10.uni-tuebingen.de with PID 68052 (/Users/sven1103/git/spring-boot-starter-template/target/classes started by sven1103 in /Users/sven1103/git/spring-boot-starter-template) -2021-11-18 09:17:37.641 INFO 68052 --- [ main] l.q.s.SpringMinimalTemplateApplication : No active profile set, falling back to default profiles: default -2021-11-18 09:17:38.164 INFO 68052 --- [ main] l.q.s.SpringMinimalTemplateApplication : Started SpringMinimalTemplateApplication in 0.808 seconds (JVM running for 1.489) -####################### Message of the day ################## -Have you written unit tests yet? If not, do it! -############################################################## + -``` +The **basic process flow** can be best described with: -## Realisation of IoC and DPI +1. Scanning step +2. Registration step (preparation) +3. 1 to N processing steps -The messages collection is stored in a simple text file `messages.txt`, that is provided with the -apps `resources`. Just go ahead and change the content of the file and run the app! +The last processing step usually hands the dataset over to the actual registration system, in our +case it is several +openBIS ETL dropboxes. In the current implementation, a marker file is created after successful +transfer into the target folder: `.MARKER_is_finished_[task ID]` -grafik +The current implementation consists of 4 steps: _scanning, registration, processing, evaluation_ and +are described in the following subsections. +### Scanning -So how does the app know where to find this file and **load the messages content**? +In this step, the application scans a [pre-defined path](#scanner-step-config) and looks for +existing registration folders. +If a registration folder is present, it is recorded and will be investigated. All other files in a +user's directory will be ignored. -We have configured it as a **external property** in a file `application.properties` and load the -configuration on application startup! Cool eh? +> [!NOTE] +> It is important that the move operation of any dataset in the registration folder is **atomic**! +> Otherwise, data corruption will occur. Ideally the dataset is staged into the user's home folder +> first (e.g. a copy operation, an upload via SFTP or SSH) and then **moved** into the registration +> folder. +> +> Moving operations on the same file system are basically a rename of the file path and +> atomic. -grafik +Within a user's registration directory, the application expect a registration task to be bundled in one +folder, e.g.: -This is how the file content looks like: - -``` -messages.file=messages.txt +```bash +|- myuser/registration // registration folder for user `myuser` + |- my-registration-batch // folder name is irrelevant + |- file1_1.fastq.gz + |- file1_2.fastq.gz + |- file2_1.fastq.gz + |- file2_2.fastq.gz + |- metadata.txt // mandatory! ``` -So how do we access the value of the `messages.file` property in our application with Spring? - -Have a look in the class `AppConfig`, there the magic happens: +The folder ``my-registration-batch`` represents an atomic registration unit and must contain the `metadata.txt` with information +about the measurement ID and the files belonging to this measurement dataset. -```groovy -@Configuration -@PropertySource("application.properties") -class AppConfig { +Following the previous example, the content of a matching `metadata.txt` would look like this: - @Value('${messages.file}') - public String messagesFile +```bash +NGSQTEST001AE-1234512312 file1_1.fastq.gz +NGSQTEST001AE-1234512312 file1_2.fastq.gz +NGSQTEST002BC-3321314441 file2_1.fastq.gz +NGSQTEST002BC-3321314441 file2_2.fastq.gz ``` +Make sure that the columns are `TAB`-separated (`\t`)! -We define the property source, which is the file `application.properties` that is provided in the -resource folder of the app and available to the classpath. We also tell with the -annotation `@Configuration` Spring, hey this is a class that holds app configuration data! +Once a new registration unit is detected, it gets queued for registration and the next step will take over. -With the annotation `@Value('${messages.file}')` we tell Spring, which property's value should be -injected. Here we make use of field injection, other types of injection like method and constructor -injection are also possible. +A registration request gets only submitted once to the registration queue and will subsequently get +ignored by the scanning process, as long as the folder name or modification timestamp does not change. -So how is the concrete implementation of the `MessageService` presented to Spring? We can use -the `@Bean` annotation here, to tell Spring: _hey, this is sth you must load on startup and provide -to the context_. +If the application quits or stops unexpectedly, on re-start they will get detected and resubmitted +again. -```java -@Configuration -@PropertySource("application.properties") -class AppConfig { +### Registration - .... +This process step is preparing the dataset registration for subsequent pre-registration task, to +guarantee a unified structure and processing model, other steps can build on and take actions +accordingly (e.g. +harmonised error handling). - @Bean - MessageService messageService() { - return new CodingPrayersMessageService(messagesFile) - } -``` - -That is all there is, you can now load the bean in your main application code: +Its configuration parameters can be set via environment variables, see +the [registration step config](#registration-step-config) section to learn more. -```java -@SpringBootApplication -class SpringMinimalTemplateApplication { +In the current implementation, the registration step does several things: - static void main(String[] args) { - SpringApplication.run(SpringMinimalTemplateApplication, args) - // load the annotation context - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class) - // get the service bean - MessageService service = context.getBean("messageService", MessageService.class) - // collect the message and praise the magic - println service.collectMessage() +1. Validating the registration metadata file +2. Aggregate all measurement files per measurement ID +3. Assign every task (measurement) a unique task ID +4. Provide provenance information -``` +The task id is just a randomly generated UUID-4 to ensure that datasets with the same name do not +get +overwritten during the processing. -### Inversion of Control +The provenance information will be written into the task directory in an own file next to the +dataset +and is of type JSON. -grafik +The final task directory structure looks then like this (task dir name is an example): -You might have already spotted the interface `NewsMedia` and its implementing class `DeveloperNews` -in the app's source code. Here you can see an example for the magic of inversion of control. - -The `NewsMedia` interface is just an abstraction that we will later use, because we don't care about -the actual implementation details. By this, we also do not create any dependencies to concrete -implementation details but on actual behaviour. Concrete implementations can then later be exchanged -without causing any breaking changes in the client code base. - -The interface has only one method: `String getNews()`. Now let's have a closer look into the -class `DeveloperNews` that implements this interface: - -```java -class DeveloperNews implements NewsMedia{ - - private MessageService service +```bash provenance.json + |- 74c5d26f-b756-42c3-b6f4-2b4825670a2d + |- file1_1.fastq.gz + |- file1_2.fastq.gz + |- provenance.json +``` - DeveloperNews(MessageService service) { - this.service = service - } +Here is an example of the provenance file: - @Override - String getNews() { - return service.collectMessage() - } +```json +{ + "origin": "/Users/myuser/registration", + "user": "/Users/myuser", + "measurementId": "QTEST001AE-1234512312", + "history": [ + "/opt/scanner-app/scanner-processing-dir/74c5d26f-b756-42c3-b6f4-2b4825670a2d" + ] } ``` -When you check the constructor signature, you see that this method has only one argument, which is a -reference to an object of type `MessageService`. And when the `getNews()` method is called by the -client, the class delegates this request to the message service. Since we have stored the reference -in a private field, that is super easy, we known how to call the service. +> [!NOTE] +> The following properties can be expected after all process steps have been executed: +> +> `origin`: from which path the dataset has been detected during scanning +> +> `user`: from which user directory the dataset has been picked up +> +> `measurementId`: any valid QBiC measurement ID that has been found in the dataset (this might +> be `null`) in case the evaluation has not been done yet. +> +> `history`: a list of history items, which steps have been performed. The list is ordered by first +> processing steps being at the start and the latest at the end. + +### Processing + +In the current implementation, this process step only does some simple checks, and can be extended to e.g. +perform checksum validation. Feel free to use it as template for subsequent process steps. + +### Evaluation + +Last but not least, this step looks for any present QBiC measurement ID in the dataset name. If none +is given, the registration cannot be executed. + +In this case the process moves the task directory into the user's home error folder. After the user +has +provided a valid QBiC measurement id, they can move the dataset into registration again. + +## Configuration + +### Global settings + +```properties +#------------------------ +# Global settings +#------------------------ +# Directory name that will be used for the manual intervention directory +# Created in the users' home folders +# e.g. /home//error +users.error.directory.name=error +# Directory name that will be used for the detecting dropped datasets +# Needs to be present in the users' home folders +# e.g. /home//registration +users.registration.directory.name=registration +``` -So why is this inversion of control? +Configure the names of the two application directories for error handling and registration. + +> [!NOTE] +> The `registration` folder needs to be present, the application is not creating it automatically, +> no +> prevent accidental dataset overwrite. + +### Scanner step config + +```properties +#-------------------------------------- +# Settings for the data scanning thread +#-------------------------------------- +# Path to the directory that contains all user directories +# e.g. /home in Linux or /Users in macOS +scanner.directory=${SCANNER_DIR:/home} +# The time interval (milliseconds) the scanner thread iterates through the scanner directory +# Value must be an integer > 0 +scanner.interval=1000 +``` -Because the `DeveloperNews` class does not manage the instantiation of a concrete message service. -The configuration happened outside of the class, therefore the DeveloperNews class has no direct -control over the instantiation. If it had, it would look like this: +Sets the applications top level scanning directory and considers every folder in it as an own +user directory. -```java -DeveloperNews(String filePathToMessages) { - this.service = new CodingPrayersMessageService(filePathToMessages) -} -``` +The scanner interval is set to 1 second by default is not yet supposed to be configured via +environment variables (if required, override it with command line arguments). -That doesn't look good, does it? In order to create an instance of a message service, we would need -to know the conrete implementation and its required properties (here it is the file path to -the `messages.txt`). So the `DeveloperNews` class has the control over the message service. +### Registration step config -Instead, we would like to not take care about these details, so we invert the control and inject the -dependency via the constructor. +Sets the number of threads per process, its working directory and the target directory, to where +finished tasks are moved to after successful operation. -Please find more in depth documentation on the -official [Spring website](https://spring.io/projects/spring-framework). +```properties +#---------------- +# Settings for the registration worker threads +#---------------- +registration.threads=2 +registration.working.dir=${WORKING_DIR:} +registration.target.dir=${PROCESSING_DIR:} +``` +### Processing step config +Sets the number of threads per process, its working directory and the target directory, to where +finished tasks are moved to after successful operation. +```properties +#------------------------------------ +# Settings for the 1. processing step +# Proper packaging and provenance data, some simple checks +#------------------------------------ +processing.threads=2 +processing.working.dir=${PROCESSING_DIR} +processing.target.dir=${EVALUATION_DIR} +``` +### Evaluation step config + +Sets the number of threads per process, its working directory and the target directory, to where +finished tasks are moved to after successful operation. + +```properties +#---------------------------------- +# Setting for the 2. processing step: +# Measurement ID evaluation +# --------------------------------- +evaluations.threads=2 +evaluation.working.dir=${EVALUATION_DIR} +# Define one or more target directories here +# Example single target dir: +# evaluation.target.dirs=/my/example/target/dir +# Example multiple target dir: +# evaluation.target.dirs=/my/example/target/dir1,/my/example/target/dir2,/my/example/target/dir3 +evaluation.target.dirs=${OPENBIS_ETL_DIRS} +evaluation.measurement-id.pattern=^(MS|NGS)Q[A-Z0-9]{4}[0-9]{3}[A-Z0-9]{2}-[0-9]* +``` +> [!NOTE] +> You can define multiple target directories for this process! You just have to provide a `,`-separated list +> of target directory paths. The implementation will assign the target directories based on a round-robin draw. diff --git a/img/process-flow.jpg b/img/process-flow.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6bb527d2da1f331ab3a880e1981095cdaad18a58 GIT binary patch literal 258429 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/error users.error.directory.name=error +# Directory name that will be used for the detecting dropped datasets +# Needs to be present in the users' home folders +# e.g. /home//registration +users.registration.directory.name=registration #-------------------------------------- # Settings for the data scanning thread