mirror of
				https://github.com/Alfresco/alfresco-community-repo.git
				synced 2025-10-29 15:21:53 +00:00 
			
		
		
		
	Compare commits
	
		
			87 Commits
		
	
	
		
			23.3.0.19
			...
			feature/AC
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | cb863c646f | ||
|  | a2d1391aee | ||
|  | 818bcb09f3 | ||
|  | 72e85076ad | ||
|  | a69be867e8 | ||
|  | fa6e0ded45 | ||
|  | ed0bbb6699 | ||
|  | 8009a6d6dd | ||
|  | 785bdb72ea | ||
|  | 66aef18862 | ||
|  | 7b516f24b6 | ||
|  | 0de1aca0f6 | ||
|  | 592dc35b6d | ||
|  | 73724a8205 | ||
|  | c8c1102431 | ||
|  | c31ae1fe33 | ||
|  | 82e9f0452d | ||
|  | 377f546a9b | ||
|  | 07dcba972f | ||
|  | 8b45d19b00 | ||
|  | 0b4b1dac44 | ||
|  | f8becd3523 | ||
|  | 781b56f06d | ||
|  | 92656a68a9 | ||
|  | 99838a73e3 | ||
|  | d7f9ed1cf0 | ||
|  | 4eccb77fa8 | ||
|  | 723156a86a | ||
|  | 3f6692a63b | ||
|  | 32e72f204a | ||
|  | 07352336c5 | ||
|  | b5ce847bb1 | ||
|  | 34925b497b | ||
|  | f5868e7f45 | ||
|  | fd015de2c8 | ||
|  | ec2494f2b5 | ||
|  | afc7e73352 | ||
|  | 3519fd6a71 | ||
|  | 5e43f3d4ab | ||
|  | 337d0b8f8f | ||
|  | 3c7593265d | ||
|  | 8986d03a2f | ||
|  | 5dcecb7f19 | ||
|  | 0617a441d3 | ||
|  | d9abdc23d5 | ||
|  | f132ec0a91 | ||
|  | 1797781ce8 | ||
|  | 88c1c849ed | ||
|  | c4ac7621c2 | ||
|  | bf7432deb1 | ||
|  | 6f74667a5c | ||
|  | 502c996c9e | ||
|  | a22e7d23f0 | ||
|  | 1e1508b74e | ||
|  | da8ffa08a0 | ||
|  | 277dbd1054 | ||
|  | 43cfa0d872 | ||
|  | 1325ec5718 | ||
|  | ecde2b5b60 | ||
|  | a9f75638c6 | ||
|  | a80b996f7e | ||
|  | 05aa6b4cf6 | ||
|  | 50b1d3a52e | ||
|  | 38b763c635 | ||
|  | 762b6ce607 | ||
|  | 0d89010ae6 | ||
|  | b744f267c1 | ||
|  | e840726a7d | ||
|  | c53683df85 | ||
|  | d9e58483ff | ||
|  | 9e81472d06 | ||
|  | 3e9cdc6b77 | ||
|  | e8ac8c2602 | ||
|  | eea58f4ba3 | ||
|  | a95fa4a83d | ||
|  | 0b74d283e2 | ||
|  | 052e21e62d | ||
|  | 26f49e80e9 | ||
|  | c31158a113 | ||
|  | 2b4fc52203 | ||
|  | 6222e1dfde | ||
|  | 077c48dea9 | ||
|  | 1a0b8d8dee | ||
|  | 66d007e703 | ||
|  | 81a4c5bac0 | ||
|  | 018157c808 | ||
|  | 440e31fcdb | 
							
								
								
									
										4
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							| @@ -52,3 +52,7 @@ updates: | ||||
|     interval: "daily" | ||||
|     time: "22:00" | ||||
|     timezone: Africa/Abidjan | ||||
| - package-ecosystem: "github-actions" | ||||
|   directory: "/" | ||||
|   schedule: | ||||
|     interval: "monthly" | ||||
|   | ||||
							
								
								
									
										40
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										40
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							| @@ -41,7 +41,7 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|       !contains(github.event.head_commit.message, '[force') | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
| @@ -61,7 +61,7 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|       !contains(github.event.head_commit.message, '[force') | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
| @@ -86,7 +86,7 @@ jobs: | ||||
| #      !contains(github.event.head_commit.message, '[skip tests]') && | ||||
| #      !contains(github.event.head_commit.message, '[force') | ||||
| #    steps: | ||||
| #      - uses: actions/checkout@v3 | ||||
| #      - uses: actions/checkout@v4 | ||||
| #      - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
| #      - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2 | ||||
| #      - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
| @@ -169,7 +169,7 @@ jobs: | ||||
|             testModule: mmt | ||||
|             testAttributes: "-Dtest=AllMmtUnitTestSuite" | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
| @@ -241,7 +241,7 @@ jobs: | ||||
|     env: | ||||
|       REQUIRES_INSTALLED_ARTIFACTS: true | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
| @@ -311,7 +311,7 @@ jobs: | ||||
|       matrix: | ||||
|         version: ['10.2.18', '10.4', '10.5'] | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
| @@ -374,7 +374,7 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|       !contains(github.event.head_commit.message, '[force') | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
| @@ -437,7 +437,7 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|       !contains(github.event.head_commit.message, '[force') | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
| @@ -499,7 +499,7 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|       !contains(github.event.head_commit.message, '[force') | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
| @@ -561,7 +561,7 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|       !contains(github.event.head_commit.message, '[force') | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
| @@ -623,7 +623,7 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|       !contains(github.event.head_commit.message, '[force') | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
| @@ -683,7 +683,7 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|       !contains(github.event.head_commit.message, '[force') | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
| @@ -775,7 +775,7 @@ jobs: | ||||
|             disabledHostnameVerification: false | ||||
|             mvn-options: '-Dencryption.ssl.keystore.location=${CI_WORKSPACE}/keystores/alfresco/alfresco.keystore -Dencryption.ssl.truststore.location=${CI_WORKSPACE}/keystores/alfresco/alfresco.truststore' | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
| @@ -880,7 +880,7 @@ jobs: | ||||
|     env: | ||||
|       REQUIRES_LOCAL_IMAGES: true | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
| @@ -953,7 +953,7 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|       !contains(github.event.head_commit.message, '[force') | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
| @@ -1019,7 +1019,7 @@ jobs: | ||||
|     env: | ||||
|       REQUIRES_INSTALLED_ARTIFACTS: true | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
| @@ -1042,7 +1042,7 @@ jobs: | ||||
|           RP_OPTS: ${{ steps.rp-prepare.outputs.mvn-opts }} | ||||
|         run: | | ||||
|           eval "args=($RP_OPTS)" | ||||
|           mvn --file amps/ags/pom.xml -B verify -Dmaven.javadoc.skip=true -Dmaven.source.skip=true -Pags -Pstart-mysql -PagsAllTestSuitePt${{ matrix.part }} ${{ env.LOG_WARN }} "${args[@]}" | ||||
|           mvn --file amps/ags/pom.xml -B verify -Dmaven.javadoc.skip=true -Dmaven.source.skip=true -Pags -Pstart-postgres -PagsAllTestSuitePt${{ matrix.part }} ${{ env.LOG_WARN }} "${args[@]}" | ||||
|       - name: "Clean Maven cache" | ||||
|         run: bash ./scripts/ci/cleanup_cache.sh | ||||
|  | ||||
| @@ -1063,7 +1063,7 @@ jobs: | ||||
|     env: | ||||
|       REQUIRES_INSTALLED_ARTIFACTS: true | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
| @@ -1103,7 +1103,7 @@ jobs: | ||||
|     env: | ||||
|       REQUIRES_LOCAL_IMAGES: true | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
| @@ -1185,7 +1185,7 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|       !contains(github.event.head_commit.message, '[force]') | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
|   | ||||
							
								
								
									
										4
									
								
								.github/workflows/master_release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/master_release.yml
									
									
									
									
										vendored
									
									
								
							| @@ -31,7 +31,7 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[no release]') && | ||||
|       github.event_name != 'pull_request' | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|         with: | ||||
|           persist-credentials: false | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
| @@ -60,7 +60,7 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[no downstream]') && | ||||
|       github.event_name != 'pull_request' | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|         with: | ||||
|           persist-credentials: false | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-community-repo-amps</artifactId> | ||||
|       <version>23.3.0.19</version> | ||||
|       <version>23.3.0.38-SNAPSHOT</version> | ||||
|    </parent> | ||||
|  | ||||
|    <modules> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-governance-services-community-parent</artifactId> | ||||
|       <version>23.3.0.19</version> | ||||
|       <version>23.3.0.38-SNAPSHOT</version> | ||||
|    </parent> | ||||
|  | ||||
|    <modules> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-governance-services-automation-community-repo</artifactId> | ||||
|       <version>23.3.0.19</version> | ||||
|       <version>23.3.0.38-SNAPSHOT</version> | ||||
|    </parent> | ||||
|  | ||||
|    <build> | ||||
| @@ -84,6 +84,12 @@ | ||||
|          <artifactId>okhttp</artifactId> | ||||
|          <scope>test</scope> | ||||
|       </dependency> | ||||
|       <dependency> | ||||
|          <groupId>org.awaitility</groupId> | ||||
|          <artifactId>awaitility</artifactId> | ||||
|          <version>${dependency.awaitility.version}</version> | ||||
|          <scope>test</scope> | ||||
|       </dependency> | ||||
|       <dependency> | ||||
|          <groupId>org.apache.commons</groupId> | ||||
|          <artifactId>commons-collections4</artifactId> | ||||
|   | ||||
| @@ -0,0 +1,60 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2024 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software. | ||||
|  * - | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of | ||||
|  * the paid license agreement will prevail.  Otherwise, the software is | ||||
|  * provided under the following open source license terms: | ||||
|  * - | ||||
|  * Alfresco is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Lesser General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * - | ||||
|  * Alfresco is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Lesser General Public License for more details. | ||||
|  * - | ||||
|  * You should have received a copy of the GNU Lesser General Public License | ||||
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rest.rm.community.model.hold; | ||||
|  | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
|  | ||||
| import lombok.AllArgsConstructor; | ||||
| import lombok.Builder; | ||||
| import lombok.Data; | ||||
| import lombok.EqualsAndHashCode; | ||||
| import lombok.NoArgsConstructor; | ||||
| import org.alfresco.rest.search.RestRequestQueryModel; | ||||
| import org.alfresco.utility.model.TestModel; | ||||
|  | ||||
| /** | ||||
|  * POJO for hold bulk request | ||||
|  * | ||||
|  * @author Damian Ujma | ||||
|  */ | ||||
| @EqualsAndHashCode(callSuper = true) | ||||
| @Builder | ||||
| @Data | ||||
| @NoArgsConstructor | ||||
| @AllArgsConstructor | ||||
| public class HoldBulkOperation extends TestModel | ||||
| { | ||||
|     public enum HoldBulkOperationType | ||||
|     { | ||||
|         ADD | ||||
|     } | ||||
|  | ||||
|     @JsonProperty(required = true) | ||||
|     private RestRequestQueryModel query; | ||||
|     @JsonProperty(required = true) | ||||
|     private HoldBulkOperationType op; | ||||
| } | ||||
| @@ -0,0 +1,50 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2024 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software. | ||||
|  * - | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of | ||||
|  * the paid license agreement will prevail.  Otherwise, the software is | ||||
|  * provided under the following open source license terms: | ||||
|  * - | ||||
|  * Alfresco is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Lesser General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * - | ||||
|  * Alfresco is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Lesser General Public License for more details. | ||||
|  * - | ||||
|  * You should have received a copy of the GNU Lesser General Public License | ||||
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rest.rm.community.model.hold; | ||||
|  | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
|  | ||||
| import lombok.AllArgsConstructor; | ||||
| import lombok.Builder; | ||||
| import lombok.Data; | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| /** | ||||
|  * POJO for hold bulk request entry | ||||
|  * | ||||
|  * @author Damian Ujma | ||||
|  */ | ||||
| @Builder | ||||
| @Data | ||||
| @NoArgsConstructor | ||||
| @AllArgsConstructor | ||||
| public class HoldBulkOperationEntry | ||||
| { | ||||
|     private String bulkStatusId; | ||||
|  | ||||
|     private long totalItems; | ||||
| } | ||||
| @@ -0,0 +1,68 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2024 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software. | ||||
|  * - | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of | ||||
|  * the paid license agreement will prevail.  Otherwise, the software is | ||||
|  * provided under the following open source license terms: | ||||
|  * - | ||||
|  * Alfresco is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Lesser General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * - | ||||
|  * Alfresco is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Lesser General Public License for more details. | ||||
|  * - | ||||
|  * You should have received a copy of the GNU Lesser General Public License | ||||
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rest.rm.community.model.hold; | ||||
|  | ||||
| import lombok.AllArgsConstructor; | ||||
| import lombok.Builder; | ||||
| import lombok.Data; | ||||
| import lombok.NoArgsConstructor; | ||||
| import org.alfresco.utility.model.TestModel; | ||||
|  | ||||
| /** | ||||
|  * POJO for hold bulk request | ||||
|  * | ||||
|  * @author Damian Ujma | ||||
|  */ | ||||
| @Builder | ||||
| @Data | ||||
| @NoArgsConstructor | ||||
| @AllArgsConstructor | ||||
| public class HoldBulkStatus extends TestModel | ||||
| { | ||||
|     private String bulkStatusId; | ||||
|  | ||||
|     private String startTime; | ||||
|  | ||||
|     private String endTime; | ||||
|  | ||||
|     private long processedItems; | ||||
|  | ||||
|     private long errorsCount; | ||||
|  | ||||
|     private long totalItems; | ||||
|  | ||||
|     private String lastError; | ||||
|  | ||||
|     private Status status; | ||||
|  | ||||
|     public enum Status | ||||
|     { | ||||
|         PENDING, | ||||
|         IN_PROGRESS, | ||||
|         DONE | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,38 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2024 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software. | ||||
|  * - | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of | ||||
|  * the paid license agreement will prevail.  Otherwise, the software is | ||||
|  * provided under the following open source license terms: | ||||
|  * - | ||||
|  * Alfresco is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Lesser General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * - | ||||
|  * Alfresco is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Lesser General Public License for more details. | ||||
|  * - | ||||
|  * You should have received a copy of the GNU Lesser General Public License | ||||
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rest.rm.community.model.hold; | ||||
|  | ||||
| import org.alfresco.rest.core.RestModels; | ||||
|  | ||||
| /** | ||||
|  * Handle collection of {@link HoldBulkStatusEntry} | ||||
|  * | ||||
|  * @author Damian Ujma | ||||
|  */ | ||||
| public class HoldBulkStatusCollection extends RestModels<HoldBulkStatusEntry, HoldBulkStatusCollection> | ||||
| { | ||||
| } | ||||
| @@ -0,0 +1,46 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2024 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software. | ||||
|  * - | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of | ||||
|  * the paid license agreement will prevail.  Otherwise, the software is | ||||
|  * provided under the following open source license terms: | ||||
|  * - | ||||
|  * Alfresco is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Lesser General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * - | ||||
|  * Alfresco is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Lesser General Public License for more details. | ||||
|  * - | ||||
|  * You should have received a copy of the GNU Lesser General Public License | ||||
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rest.rm.community.model.hold; | ||||
|  | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
|  | ||||
| import lombok.AllArgsConstructor; | ||||
| import lombok.Builder; | ||||
| import lombok.Data; | ||||
| import lombok.EqualsAndHashCode; | ||||
| import lombok.NoArgsConstructor; | ||||
| import org.alfresco.rest.core.RestModels; | ||||
|  | ||||
| @Builder | ||||
| @Data | ||||
| @EqualsAndHashCode(callSuper = true) | ||||
| @NoArgsConstructor | ||||
| @AllArgsConstructor | ||||
| public class HoldBulkStatusEntry extends RestModels<HoldBulkStatus, HoldBulkStatusEntry> | ||||
| { | ||||
|     private HoldBulkStatus entry; | ||||
| } | ||||
| @@ -48,5 +48,5 @@ import org.alfresco.rest.core.RestModels; | ||||
| public class HoldChildEntry extends RestModels<Hold, HoldChildEntry> | ||||
| { | ||||
|     @JsonProperty | ||||
|     private HoldChildEntry entry; | ||||
|     private HoldChild entry; | ||||
| } | ||||
| @@ -39,6 +39,10 @@ import static org.springframework.http.HttpMethod.PUT; | ||||
|  | ||||
| import org.alfresco.rest.core.RMRestWrapper; | ||||
| import org.alfresco.rest.rm.community.model.hold.Hold; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldBulkOperation; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldBulkOperationEntry; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldBulkStatus; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldBulkStatusCollection; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldChild; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldChildCollection; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldDeletionReason; | ||||
| @@ -287,4 +291,113 @@ public class HoldsAPI extends RMModelRequest | ||||
|     { | ||||
|         deleteHoldChild(holdId, holdChildId, EMPTY); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Starts a bulk process for a hold. | ||||
|      * | ||||
|      * @param holdBulkOperation The bulk operation details | ||||
|      * @param hold The identifier of a hold | ||||
|      * @param parameters The URL parameters to add | ||||
|      * @return The {@link HoldBulkOperationEntry} for the started bulk process | ||||
|      * @throws RuntimeException for the following cases: | ||||
|      * <ul> | ||||
|      *  <li>{@code hold} or {@code holdBulkOperation} is invalid</li> | ||||
|      *  <li>authentication fails</li> | ||||
|      *  <li>current user does not have permission to start a bulk process for {@code hold}</li> | ||||
|      *  <li>{@code hold} does not exist</li> | ||||
|      * </ul> | ||||
|      */ | ||||
|     public HoldBulkOperationEntry startBulkProcess(HoldBulkOperation holdBulkOperation, String hold, String parameters) | ||||
|     { | ||||
|         mandatoryObject("holdBulkOperation", holdBulkOperation); | ||||
|         mandatoryString("hold", hold); | ||||
|  | ||||
|         return getRmRestWrapper().processModel(HoldBulkOperationEntry.class, requestWithBody( | ||||
|             POST, | ||||
|             toJson(holdBulkOperation), | ||||
|             "holds/{hold}/bulk", | ||||
|             hold, | ||||
|             parameters | ||||
|                                                                                           )); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * See {@link #startBulkProcess(HoldBulkOperation, String, String)} | ||||
|      */ | ||||
|     public HoldBulkOperationEntry startBulkProcess(HoldBulkOperation holdBulkOperation, String hold) | ||||
|     { | ||||
|         return startBulkProcess(holdBulkOperation, hold, EMPTY); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets the status of a bulk process for a hold. | ||||
|      * | ||||
|      * @param holdId The identifier of a hold | ||||
|      * @param holdBulkStatusId The identifier of a bulk status operation | ||||
|      * @param parameters The URL parameters to add | ||||
|      * @return The {@link HoldBulkStatus} for the given {@code holdId} and {@code holdBulkStatusId} | ||||
|      * @throws RuntimeException for the following cases: | ||||
|      * <ul> | ||||
|      *  <li>{@code holdId} or {@code holdBulkStatusId} is invalid</li> | ||||
|      *  <li>authentication fails</li> | ||||
|      *  <li>current user does not have permission to get the bulk status for {@code holdId}</li> | ||||
|      *  <li>{@code holdId} or {@code holdBulkStatusId} does not exist</li> | ||||
|      * </ul> | ||||
|      */ | ||||
|     public HoldBulkStatus getBulkStatus(String holdId, String holdBulkStatusId, String parameters) | ||||
|     { | ||||
|         mandatoryString("holdId", holdId); | ||||
|         mandatoryString("holdBulkStatusId", holdBulkStatusId); | ||||
|  | ||||
|         return getRmRestWrapper().processModel(HoldBulkStatus.class, simpleRequest( | ||||
|             GET, | ||||
|             "holds/{holdId}/bulk-statuses/{holdBulkStatusId}", | ||||
|             holdId, | ||||
|             holdBulkStatusId, | ||||
|             parameters | ||||
|                                                                                    )); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * See {@link #getBulkStatus(String, String, String)} | ||||
|      */ | ||||
|     public HoldBulkStatus getBulkStatus(String holdId, String holdBulkStatusId) | ||||
|     { | ||||
|         return getBulkStatus(holdId, holdBulkStatusId, EMPTY); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets the statuses of all bulk processes for a hold. | ||||
|      * | ||||
|      * @param holdId The identifier of a hold | ||||
|      * @param parameters The URL parameters to add | ||||
|      * @return The {@link HoldBulkStatusCollection} for the given {@code holdId} | ||||
|      * @throws RuntimeException for the following cases: | ||||
|      * <ul> | ||||
|      *     <li>{@code holdId} is invalid</li> | ||||
|      *     <li>authentication fails</li> | ||||
|      *     <li>current user does not have permission to get the bulk statuses for {@code holdId}</li> | ||||
|      *     <li>{@code holdId} does not exist</li> | ||||
|      * </ul> | ||||
|      */ | ||||
|     public HoldBulkStatusCollection getBulkStatuses(String holdId, String parameters) | ||||
|     { | ||||
|         mandatoryString("holdId", holdId); | ||||
|  | ||||
|         return getRmRestWrapper().processModels(HoldBulkStatusCollection.class, simpleRequest( | ||||
|             GET, | ||||
|             "holds/{holdId}/bulk-statuses", | ||||
|             holdId, | ||||
|             parameters | ||||
|                                                                                              )); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * See {@link #getBulkStatuses(String, String)} | ||||
|      */ | ||||
|     public HoldBulkStatusCollection getBulkStatuses(String holdId) | ||||
|     { | ||||
|         return getBulkStatuses(holdId, EMPTY); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,532 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2024 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software. | ||||
|  * - | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of | ||||
|  * the paid license agreement will prevail.  Otherwise, the software is | ||||
|  * provided under the following open source license terms: | ||||
|  * - | ||||
|  * Alfresco is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Lesser General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * - | ||||
|  * Alfresco is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Lesser General Public License for more details. | ||||
|  * - | ||||
|  * You should have received a copy of the GNU Lesser General Public License | ||||
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rest.rm.community.hold; | ||||
|  | ||||
| import static org.alfresco.rest.rm.community.base.TestData.HOLD_DESCRIPTION; | ||||
| import static org.alfresco.rest.rm.community.base.TestData.HOLD_REASON; | ||||
| import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS; | ||||
| import static org.alfresco.rest.rm.community.model.user.UserPermissions.PERMISSION_FILING; | ||||
| import static org.alfresco.rest.rm.community.model.user.UserPermissions.PERMISSION_READ_RECORDS; | ||||
| import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix; | ||||
| import static org.alfresco.utility.report.log.Step.STEP; | ||||
| import static org.awaitility.Awaitility.await; | ||||
| import static org.junit.Assert.assertEquals; | ||||
| import static org.junit.Assert.assertNotNull; | ||||
| import static org.junit.Assert.assertTrue; | ||||
| import static org.springframework.http.HttpStatus.ACCEPTED; | ||||
| import static org.springframework.http.HttpStatus.BAD_REQUEST; | ||||
| import static org.springframework.http.HttpStatus.FORBIDDEN; | ||||
| import static org.springframework.http.HttpStatus.NOT_FOUND; | ||||
| import static org.springframework.http.HttpStatus.UNAUTHORIZED; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.Arrays; | ||||
| import java.util.List; | ||||
| import java.util.concurrent.TimeUnit; | ||||
|  | ||||
| import org.alfresco.dataprep.CMISUtil; | ||||
| import org.alfresco.dataprep.ContentActions; | ||||
| import org.alfresco.rest.rm.community.base.BaseRMRestTest; | ||||
| import org.alfresco.rest.rm.community.model.hold.Hold; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldBulkOperation; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldBulkOperation.HoldBulkOperationType; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldBulkOperationEntry; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldBulkStatus; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldBulkStatus.Status; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldBulkStatusCollection; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldBulkStatusEntry; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldChild; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldChildEntry; | ||||
| import org.alfresco.rest.rm.community.model.user.UserRoles; | ||||
| import org.alfresco.rest.search.RestRequestQueryModel; | ||||
| import org.alfresco.rest.search.SearchRequest; | ||||
| import org.alfresco.rest.v0.service.RoleService; | ||||
| import org.alfresco.utility.constants.UserRole; | ||||
| import org.alfresco.utility.model.FileModel; | ||||
| import org.alfresco.utility.model.FolderModel; | ||||
| import org.alfresco.utility.model.UserModel; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.testng.annotations.AfterClass; | ||||
| import org.testng.annotations.BeforeClass; | ||||
| import org.testng.annotations.Test; | ||||
|  | ||||
| /** | ||||
|  * API tests for adding items to holds via the bulk process | ||||
|  */ | ||||
| public class AddToHoldsBulkV1Tests extends BaseRMRestTest | ||||
| { | ||||
|     private static final String ACCESS_DENIED_ERROR_MESSAGE = "Access Denied.  You do not have the appropriate " + | ||||
|         "permissions to perform this operation."; | ||||
|     private static final int NUMBER_OF_FILES = 5; | ||||
|     private final List<FileModel> addedFiles = new ArrayList<>(); | ||||
|     private final List<UserModel> users = new ArrayList<>(); | ||||
|     private final List<Hold> holds = new ArrayList<>(); | ||||
|     private Hold hold; | ||||
|     private Hold hold2; | ||||
|     private Hold hold3; | ||||
|     private FolderModel rootFolder; | ||||
|     private HoldBulkOperation holdBulkOperation; | ||||
|     @Autowired | ||||
|     private RoleService roleService; | ||||
|     @Autowired | ||||
|     private ContentActions contentActions; | ||||
|  | ||||
|     @BeforeClass(alwaysRun = true) | ||||
|     public void preconditionForAddContentToHold() | ||||
|     { | ||||
|         STEP("Create a hold."); | ||||
|         hold = getRestAPIFactory().getFilePlansAPI(getAdminUser()).createHold( | ||||
|             Hold.builder().name("HOLD" + generateTestPrefix(AddToHoldsV1Tests.class)).description(HOLD_DESCRIPTION) | ||||
|                 .reason(HOLD_REASON).build(), FILE_PLAN_ALIAS); | ||||
|         holds.add(hold); | ||||
|  | ||||
|         STEP("Create test files."); | ||||
|         testSite = dataSite.usingAdmin().createPublicRandomSite(); | ||||
|  | ||||
|         rootFolder = dataContent.usingAdmin().usingSite(testSite).createFolder(); | ||||
|         FolderModel folder1 = dataContent.usingAdmin().usingResource(rootFolder).createFolder(); | ||||
|         FolderModel folder2 = dataContent.usingAdmin().usingResource(folder1).createFolder(); | ||||
|  | ||||
|         // Add files to subfolders in the site | ||||
|         for (int i = 0; i < NUMBER_OF_FILES; i++) | ||||
|         { | ||||
|             FileModel documentHeld = dataContent.usingAdmin() | ||||
|                 .usingResource(i % 2 == 0 ? folder1 : folder2) | ||||
|                 .createContent(CMISUtil.DocumentType.TEXT_PLAIN); | ||||
|             addedFiles.add(documentHeld); | ||||
|         } | ||||
|  | ||||
|         RestRequestQueryModel queryReq = getContentFromSiteQuery(testSite.getId()); | ||||
|         SearchRequest searchRequest = new SearchRequest(); | ||||
|         searchRequest.setQuery(queryReq); | ||||
|  | ||||
|         STEP("Wait until all files are searchable."); | ||||
|         await().atMost(30, TimeUnit.SECONDS) | ||||
|             .until(() -> getRestAPIFactory().getSearchAPI(null).search(searchRequest).getPagination() | ||||
|                 .getTotalItems() == NUMBER_OF_FILES); | ||||
|  | ||||
|         holdBulkOperation = HoldBulkOperation.builder() | ||||
|             .query(queryReq) | ||||
|             .op(HoldBulkOperationType.ADD).build(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given a user with the add to hold capability and hold filing permission | ||||
|      * When the user adds content from a site to a hold using the bulk API | ||||
|      * Then the content is added to the hold and the status of the bulk operation is DONE | ||||
|      */ | ||||
|     @Test | ||||
|     public void addContentFromTestSiteToHoldUsingBulkAPI() | ||||
|     { | ||||
|         UserModel userAddHoldPermission = roleService.createUserWithSiteRoleRMRoleAndPermission(testSite, | ||||
|             UserRole.SiteCollaborator, hold.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING); | ||||
|         users.add(userAddHoldPermission); | ||||
|  | ||||
|         STEP("Add content from the site to the hold using the bulk API."); | ||||
|         HoldBulkOperationEntry bulkOperationEntry = getRestAPIFactory().getHoldsAPI(userAddHoldPermission) | ||||
|             .startBulkProcess(holdBulkOperation, hold.getId()); | ||||
|  | ||||
|         // Verify the status code | ||||
|         assertStatusCode(ACCEPTED); | ||||
|         assertEquals(NUMBER_OF_FILES, bulkOperationEntry.getTotalItems()); | ||||
|  | ||||
|         STEP("Wait until all files are added to the hold."); | ||||
|         await().atMost(20, TimeUnit.SECONDS).until( | ||||
|             () -> getRestAPIFactory().getHoldsAPI(getAdminUser()).getChildren(hold.getId()).getEntries().size() | ||||
|                 == NUMBER_OF_FILES); | ||||
|         List<String> holdChildrenNodeRefs = getRestAPIFactory().getHoldsAPI(userAddHoldPermission) | ||||
|             .getChildren(hold.getId()).getEntries().stream().map(HoldChildEntry::getEntry).map( | ||||
|                 HoldChild::getId).toList(); | ||||
|         assertEquals(addedFiles.stream().map(FileModel::getNodeRefWithoutVersion).sorted().toList(), | ||||
|             holdChildrenNodeRefs.stream().sorted().toList()); | ||||
|  | ||||
|         STEP("Check the bulk status."); | ||||
|         HoldBulkStatus holdBulkStatus = getRestAPIFactory().getHoldsAPI(userAddHoldPermission) | ||||
|             .getBulkStatus(hold.getId(), bulkOperationEntry.getBulkStatusId()); | ||||
|         assertBulkProcessStatus(holdBulkStatus, NUMBER_OF_FILES, 0, null); | ||||
|  | ||||
|         STEP("Check the bulk statuses."); | ||||
|         HoldBulkStatusCollection holdBulkStatusCollection = getRestAPIFactory().getHoldsAPI(userAddHoldPermission) | ||||
|             .getBulkStatuses(hold.getId()); | ||||
|         assertEquals(Arrays.asList(holdBulkStatus), holdBulkStatusCollection.getEntries().stream().map(HoldBulkStatusEntry::getEntry).toList()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given a user with the add to hold capability and hold filing permission | ||||
|      * When the user adds content from a folder and all subfolders to a hold using the bulk API | ||||
|      * Then the content is added to the hold and the status of the bulk operation is DONE | ||||
|      */ | ||||
|     @Test | ||||
|     public void addContentFromFolderAndAllSubfoldersToHoldUsingBulkAPI() | ||||
|     { | ||||
|         hold3 = getRestAPIFactory().getFilePlansAPI(getAdminUser()).createHold( | ||||
|             Hold.builder().name("HOLD" + generateTestPrefix(AddToHoldsV1Tests.class)).description(HOLD_DESCRIPTION) | ||||
|                 .reason(HOLD_REASON).build(), FILE_PLAN_ALIAS); | ||||
|         holds.add(hold3); | ||||
|  | ||||
|         UserModel userAddHoldPermission = roleService.createUserWithSiteRoleRMRoleAndPermission(testSite, | ||||
|             UserRole.SiteCollaborator, hold3.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING); | ||||
|         users.add(userAddHoldPermission); | ||||
|  | ||||
|         STEP("Add content from the site to the hold using the bulk API."); | ||||
|         // Get content from folder and all subfolders of the root folder | ||||
|         HoldBulkOperation bulkOperation = HoldBulkOperation.builder() | ||||
|             .query(getContentFromFolderAndAllSubfoldersQuery(rootFolder.getNodeRefWithoutVersion())) | ||||
|             .op(HoldBulkOperationType.ADD).build(); | ||||
|         HoldBulkOperationEntry bulkOperationEntry = getRestAPIFactory().getHoldsAPI(userAddHoldPermission) | ||||
|             .startBulkProcess(bulkOperation, hold3.getId()); | ||||
|  | ||||
|         // Verify the status code | ||||
|         assertStatusCode(ACCEPTED); | ||||
|         assertEquals(NUMBER_OF_FILES, bulkOperationEntry.getTotalItems()); | ||||
|  | ||||
|         STEP("Wait until all files are added to the hold."); | ||||
|         await().atMost(20, TimeUnit.SECONDS).until( | ||||
|             () -> getRestAPIFactory().getHoldsAPI(getAdminUser()).getChildren(hold3.getId()).getEntries().size() | ||||
|                 == NUMBER_OF_FILES); | ||||
|         List<String> holdChildrenNodeRefs = getRestAPIFactory().getHoldsAPI(userAddHoldPermission) | ||||
|             .getChildren(hold3.getId()).getEntries().stream().map(HoldChildEntry::getEntry).map( | ||||
|                 HoldChild::getId).toList(); | ||||
|         assertEquals(addedFiles.stream().map(FileModel::getNodeRefWithoutVersion).sorted().toList(), | ||||
|             holdChildrenNodeRefs.stream().sorted().toList()); | ||||
|  | ||||
|         STEP("Check the bulk status."); | ||||
|         HoldBulkStatus holdBulkStatus = getRestAPIFactory().getHoldsAPI(userAddHoldPermission) | ||||
|             .getBulkStatus(hold3.getId(), bulkOperationEntry.getBulkStatusId()); | ||||
|         assertBulkProcessStatus(holdBulkStatus, NUMBER_OF_FILES, 0, null); | ||||
|  | ||||
|         STEP("Check the bulk statuses."); | ||||
|         HoldBulkStatusCollection holdBulkStatusCollection = getRestAPIFactory().getHoldsAPI(userAddHoldPermission) | ||||
|             .getBulkStatuses(hold3.getId()); | ||||
|         assertEquals(Arrays.asList(holdBulkStatus), holdBulkStatusCollection.getEntries().stream().map(HoldBulkStatusEntry::getEntry).toList()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given a user without the add to hold capability | ||||
|      * When the user adds content from a site to a hold using the bulk API | ||||
|      * Then the user receives access denied error | ||||
|      */ | ||||
|     @Test | ||||
|     public void testBulkProcessWithUserWithoutAddToHoldCapability() | ||||
|     { | ||||
|         UserModel userWithoutAddToHoldCapability = roleService.createUserWithSiteRoleRMRoleAndPermission(testSite, | ||||
|             UserRole | ||||
|                 .SiteCollaborator, | ||||
|             hold.getId(), UserRoles.ROLE_RM_POWER_USER, PERMISSION_FILING); | ||||
|         users.add(userWithoutAddToHoldCapability); | ||||
|  | ||||
|         STEP("Add content from the site to the hold using the bulk API."); | ||||
|         getRestAPIFactory().getHoldsAPI(userWithoutAddToHoldCapability) | ||||
|             .startBulkProcess(holdBulkOperation, hold.getId()); | ||||
|  | ||||
|         STEP("Verify the response status code and the error message."); | ||||
|         assertStatusCode(FORBIDDEN); | ||||
|         getRestAPIFactory().getRmRestWrapper().assertLastError().containsSummary(ACCESS_DENIED_ERROR_MESSAGE); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given a user without the filing permission on a hold | ||||
|      * When the user adds content from a site to a hold using the bulk API | ||||
|      * Then the user receives access denied error | ||||
|      */ | ||||
|     @Test | ||||
|     public void testBulkProcessWithUserWithoutFilingPermissionOnAHold() | ||||
|     { | ||||
|         // User without filing permission on a hold | ||||
|         UserModel userWithoutPermission = roleService.createUserWithSiteRoleRMRoleAndPermission(testSite, | ||||
|             UserRole.SiteCollaborator, hold.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_READ_RECORDS); | ||||
|         users.add(userWithoutPermission); | ||||
|  | ||||
|         STEP("Add content from the site to the hold using the bulk API."); | ||||
|         getRestAPIFactory().getHoldsAPI(userWithoutPermission) | ||||
|             .startBulkProcess(holdBulkOperation, hold.getId()); | ||||
|  | ||||
|         STEP("Verify the response status code and the error message."); | ||||
|         assertStatusCode(FORBIDDEN); | ||||
|         getRestAPIFactory().getRmRestWrapper().assertLastError().containsSummary(ACCESS_DENIED_ERROR_MESSAGE); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given a user without the write permission on all the content | ||||
|      * When the user adds content from a site to a hold using the bulk API | ||||
|      * Then all processed items are marked as errors and the last error message contains access denied error | ||||
|      */ | ||||
|     @Test | ||||
|     public void testBulkProcessWithUserWithoutWritePermissionOnTheContent() | ||||
|     { | ||||
|         // User without write permission on the content | ||||
|         UserModel userWithoutPermission = roleService.createUserWithSiteRoleRMRoleAndPermission( | ||||
|             testSite, UserRole.SiteConsumer, | ||||
|             hold.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING); | ||||
|         users.add(userWithoutPermission); | ||||
|  | ||||
|         // Wait until permissions are reverted | ||||
|         SearchRequest searchRequest = new SearchRequest(); | ||||
|         searchRequest.setQuery(holdBulkOperation.getQuery()); | ||||
|         await().atMost(30, TimeUnit.SECONDS) | ||||
|             .until(() -> getRestAPIFactory().getSearchAPI(userWithoutPermission).search(searchRequest).getPagination() | ||||
|                 .getTotalItems() == NUMBER_OF_FILES); | ||||
|  | ||||
|         STEP("Add content from the site to the hold using the bulk API."); | ||||
|         HoldBulkOperationEntry bulkOperationEntry = getRestAPIFactory().getHoldsAPI( | ||||
|             userWithoutPermission).startBulkProcess(holdBulkOperation, hold.getId()); | ||||
|  | ||||
|         STEP("Verify the response."); | ||||
|         assertStatusCode(ACCEPTED); | ||||
|  | ||||
|         await().atMost(20, TimeUnit.SECONDS).until(() -> | ||||
|             getRestAPIFactory().getHoldsAPI(userWithoutPermission) | ||||
|                 .getBulkStatus(hold.getId(), bulkOperationEntry.getBulkStatusId()).getStatus() == Status.DONE); | ||||
|  | ||||
|         HoldBulkStatus holdBulkStatus = getRestAPIFactory().getHoldsAPI(userWithoutPermission) | ||||
|             .getBulkStatus(hold.getId(), bulkOperationEntry.getBulkStatusId()); | ||||
|         assertBulkProcessStatus(holdBulkStatus, NUMBER_OF_FILES, NUMBER_OF_FILES, ACCESS_DENIED_ERROR_MESSAGE); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given a user without the write permission on one file | ||||
|      * When the user adds content from a site to a hold using the bulk API | ||||
|      * Then all processed items are added to the hold except the one that the user does not have write permission | ||||
|      * And the status of the bulk operation is DONE, contains the error message and the number of errors is 1 | ||||
|      */ | ||||
|     @Test | ||||
|     public void testBulkProcessWithUserWithoutWritePermissionOnOneFile() | ||||
|     { | ||||
|         hold2 = getRestAPIFactory().getFilePlansAPI(getAdminUser()).createHold( | ||||
|             Hold.builder().name("HOLD" + generateTestPrefix(AddToHoldsV1Tests.class)).description(HOLD_DESCRIPTION) | ||||
|                 .reason(HOLD_REASON).build(), FILE_PLAN_ALIAS); | ||||
|         holds.add(hold2); | ||||
|  | ||||
|         UserModel userAddHoldPermission = roleService.createUserWithSiteRoleRMRoleAndPermission(testSite, | ||||
|             UserRole.SiteCollaborator, hold2.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING); | ||||
|         users.add(userAddHoldPermission); | ||||
|  | ||||
|         contentActions.setPermissionForUser(getAdminUser().getUsername(), getAdminUser().getPassword(), | ||||
|             testSite.getId(), addedFiles.get(0).getName(), userAddHoldPermission.getUsername(), | ||||
|             UserRole.SiteConsumer.getRoleId(), false); | ||||
|  | ||||
|         STEP("Add content from the site to the hold using the bulk API."); | ||||
|         HoldBulkOperationEntry bulkOperationEntry = getRestAPIFactory().getHoldsAPI(userAddHoldPermission) | ||||
|             .startBulkProcess(holdBulkOperation, hold2.getId()); | ||||
|  | ||||
|         // Verify the status code | ||||
|         assertStatusCode(ACCEPTED); | ||||
|         assertEquals(NUMBER_OF_FILES, bulkOperationEntry.getTotalItems()); | ||||
|  | ||||
|         STEP("Wait until all files are added to the hold."); | ||||
|         await().atMost(30, TimeUnit.SECONDS).until( | ||||
|             () -> getRestAPIFactory().getHoldsAPI(getAdminUser()).getChildren(hold2.getId()).getEntries().size() | ||||
|                 == NUMBER_OF_FILES - 1); | ||||
|         await().atMost(30, TimeUnit.SECONDS).until( | ||||
|             () -> getRestAPIFactory().getHoldsAPI(userAddHoldPermission) | ||||
|                 .getBulkStatus(hold2.getId(), bulkOperationEntry.getBulkStatusId()).getProcessedItems() == NUMBER_OF_FILES); | ||||
|         List<String> holdChildrenNodeRefs = getRestAPIFactory().getHoldsAPI(userAddHoldPermission) | ||||
|             .getChildren(hold2.getId()).getEntries().stream().map(HoldChildEntry::getEntry).map( | ||||
|                 HoldChild::getId).toList(); | ||||
|         assertEquals(addedFiles.stream().skip(1).map(FileModel::getNodeRefWithoutVersion).sorted().toList(), | ||||
|             holdChildrenNodeRefs.stream().sorted().toList()); | ||||
|  | ||||
|         STEP("Check the bulk status."); | ||||
|         HoldBulkStatus holdBulkStatus = getRestAPIFactory().getHoldsAPI(userAddHoldPermission) | ||||
|             .getBulkStatus(hold2.getId(), bulkOperationEntry.getBulkStatusId()); | ||||
|         assertBulkProcessStatus(holdBulkStatus, NUMBER_OF_FILES, 1, ACCESS_DENIED_ERROR_MESSAGE); | ||||
|  | ||||
|         STEP("Check the bulk statuses."); | ||||
|         HoldBulkStatusCollection holdBulkStatusCollection = getRestAPIFactory().getHoldsAPI(userAddHoldPermission) | ||||
|             .getBulkStatuses(hold2.getId()); | ||||
|         assertEquals(Arrays.asList(holdBulkStatus), holdBulkStatusCollection.getEntries().stream().map(HoldBulkStatusEntry::getEntry).toList()); | ||||
|  | ||||
|         // Revert the permissions | ||||
|         contentActions.setPermissionForUser(getAdminUser().getUsername(), getAdminUser().getPassword(), | ||||
|             testSite.getId(), addedFiles.get(0).getName(), userAddHoldPermission.getUsername(), | ||||
|             UserRole.SiteCollaborator.getRoleId(), true); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given an unauthenticated user | ||||
|      * When the user adds content from a site to a hold using the bulk API | ||||
|      * Then the user receives unauthorized error | ||||
|      */ | ||||
|     @Test | ||||
|     public void testBulkProcessAsUnauthenticatedUser() | ||||
|     { | ||||
|         STEP("Start bulk process as unauthenticated user"); | ||||
|         getRestAPIFactory().getHoldsAPI(new UserModel(getAdminUser().getUsername(), "wrongPassword")) | ||||
|             .startBulkProcess(holdBulkOperation, hold.getId()); | ||||
|  | ||||
|         STEP("Verify the response status code."); | ||||
|         assertStatusCode(UNAUTHORIZED); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given a user with the add to hold capability and hold filing permission | ||||
|      * When the user adds content from a site to a hold using the bulk API | ||||
|      * And the hold does not exist | ||||
|      * Then the user receives not found error | ||||
|      */ | ||||
|     @Test | ||||
|     public void testBulkProcessForNonExistentHold() | ||||
|     { | ||||
|         STEP("Start bulk process for non existent hold"); | ||||
|         getRestAPIFactory().getHoldsAPI(getAdminUser()).startBulkProcess(holdBulkOperation, "nonExistentHoldId"); | ||||
|  | ||||
|         STEP("Verify the response status code."); | ||||
|         assertStatusCode(NOT_FOUND); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given a user with the add to hold capability and hold filing permission | ||||
|      * When the user adds content from a site to a hold using the bulk API | ||||
|      * and the bulk operation is invalid | ||||
|      * Then the user receives bad request error | ||||
|      */ | ||||
|     @Test | ||||
|     public void testGetBulkStatusesForInvalidOperation() | ||||
|     { | ||||
|         STEP("Start bulk process for non existent hold"); | ||||
|  | ||||
|         HoldBulkOperation invalidHoldBulkOperation = HoldBulkOperation.builder().op(null) | ||||
|             .query(holdBulkOperation.getQuery()).build(); | ||||
|         getRestAPIFactory().getHoldsAPI(getAdminUser()).startBulkProcess(invalidHoldBulkOperation, hold.getId()); | ||||
|  | ||||
|         STEP("Verify the response status code."); | ||||
|         assertStatusCode(BAD_REQUEST); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given a user with the add to hold capability and hold filing permission | ||||
|      * When the user adds content from a site to a hold using the bulk API | ||||
|      * And the hold does not exist | ||||
|      * Then the user receives not found error | ||||
|      */ | ||||
|     @Test | ||||
|     public void testGetBulkStatusForNonExistentHold() | ||||
|     { | ||||
|         STEP("Start bulk process for non existent hold"); | ||||
|         getRestAPIFactory().getHoldsAPI(getAdminUser()).getBulkStatus("nonExistentHoldId", "nonExistenBulkStatusId"); | ||||
|  | ||||
|         STEP("Verify the response status code."); | ||||
|         assertStatusCode(NOT_FOUND); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given a user with the add to hold capability and hold filing permission | ||||
|      * When the user adds content from a site to a hold using the bulk API | ||||
|      * And the bulk status does not exist | ||||
|      * Then the user receives not found error | ||||
|      */ | ||||
|     @Test | ||||
|     public void testGetBulkStatusForNonExistentBulkStatus() | ||||
|     { | ||||
|         STEP("Start bulk process for non bulk status"); | ||||
|         getRestAPIFactory().getHoldsAPI(getAdminUser()).getBulkStatus(hold.getId(), "nonExistenBulkStatusId"); | ||||
|  | ||||
|         STEP("Verify the response status code."); | ||||
|         assertStatusCode(NOT_FOUND); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given a user with the add to hold capability and hold filing permission | ||||
|      * When the user adds content from a site to a hold using the bulk API | ||||
|      * And the hold does not exist | ||||
|      * Then the user receives not found error | ||||
|      */ | ||||
|     @Test | ||||
|     public void testGetBulkStatusesForNonExistentHold() | ||||
|     { | ||||
|         STEP("Start bulk process for non existent hold"); | ||||
|         getRestAPIFactory().getHoldsAPI(getAdminUser()).getBulkStatuses("nonExistentHoldId"); | ||||
|  | ||||
|         STEP("Verify the response status code."); | ||||
|         assertStatusCode(NOT_FOUND); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given a user with the add to hold capability and hold filing permission | ||||
|      * When the user adds content from all sites to a hold using the bulk API to exceed the limit (30 items) | ||||
|      * Then the user receives bad request error | ||||
|      */ | ||||
|     @Test | ||||
|     public void testExceedingBulkOperationLimit() | ||||
|     { | ||||
|         RestRequestQueryModel queryReq = new RestRequestQueryModel(); | ||||
|         queryReq.setQuery("TYPE:content"); | ||||
|         queryReq.setLanguage("afts"); | ||||
|  | ||||
|         HoldBulkOperation exceedLimitOp = HoldBulkOperation.builder() | ||||
|             .query(queryReq) | ||||
|             .op(HoldBulkOperationType.ADD).build(); | ||||
|  | ||||
|         STEP("Start bulk process to exceed the limit"); | ||||
|         getRestAPIFactory().getHoldsAPI(getAdminUser()).startBulkProcess(exceedLimitOp, hold.getId()); | ||||
|  | ||||
|         STEP("Verify the response status code."); | ||||
|         assertStatusCode(BAD_REQUEST); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     private void assertBulkProcessStatus(HoldBulkStatus holdBulkStatus, long expectedProcessedItems, | ||||
|         int expectedErrorsCount, String expectedErrorMessage) | ||||
|     { | ||||
|         assertEquals(Status.DONE, holdBulkStatus.getStatus()); | ||||
|         assertEquals(expectedProcessedItems, holdBulkStatus.getTotalItems()); | ||||
|         assertEquals(expectedProcessedItems, holdBulkStatus.getProcessedItems()); | ||||
|         assertEquals(expectedErrorsCount, holdBulkStatus.getErrorsCount()); | ||||
|         assertNotNull(holdBulkStatus.getStartTime()); | ||||
|         assertNotNull(holdBulkStatus.getEndTime()); | ||||
|  | ||||
|         if (expectedErrorMessage != null) | ||||
|         { | ||||
|             assertTrue(holdBulkStatus.getLastError().contains(expectedErrorMessage)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private RestRequestQueryModel getContentFromSiteQuery(String siteId) | ||||
|     { | ||||
|         RestRequestQueryModel queryReq = new RestRequestQueryModel(); | ||||
|         queryReq.setQuery("SITE:\"" + siteId + "\" and TYPE:content"); | ||||
|         queryReq.setLanguage("afts"); | ||||
|         return queryReq; | ||||
|     } | ||||
|  | ||||
|     private RestRequestQueryModel getContentFromFolderAndAllSubfoldersQuery(String folderId) | ||||
|     { | ||||
|         RestRequestQueryModel queryReq = new RestRequestQueryModel(); | ||||
|         queryReq.setQuery("ANCESTOR:\"workspace://SpacesStore/" + folderId + "\" and TYPE:content"); | ||||
|         queryReq.setLanguage("afts"); | ||||
|         return queryReq; | ||||
|     } | ||||
|  | ||||
|     @AfterClass(alwaysRun = true) | ||||
|     public void cleanupAddToHoldsBulkV1Tests() | ||||
|     { | ||||
|         dataSite.usingAdmin().deleteSite(testSite); | ||||
|         users.forEach(user -> getDataUser().usingAdmin().deleteUser(user)); | ||||
|         holds.forEach(hold -> getRestAPIFactory().getHoldsAPI(getAdminUser()).deleteHold(hold.getId())); | ||||
|     } | ||||
| } | ||||
| @@ -7,7 +7,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-governance-services-community-parent</artifactId> | ||||
|       <version>23.3.0.19</version> | ||||
|       <version>23.3.0.38-SNAPSHOT</version> | ||||
|    </parent> | ||||
|  | ||||
|    <modules> | ||||
|   | ||||
| @@ -1,3 +1,3 @@ | ||||
| SOLR6_TAG=2.0.8.1 | ||||
| SOLR6_TAG=2.0.10 | ||||
| POSTGRES_TAG=15.4 | ||||
| ACTIVEMQ_TAG=5.18.3-jre17-rockylinux8 | ||||
|   | ||||
| @@ -139,3 +139,21 @@ content.metadata.async.extract.6.enabled=false | ||||
|  | ||||
| # Max number of entries returned in Record search view | ||||
| rm.recordSearch.maxItems=500 | ||||
|  | ||||
| # | ||||
| # Hold bulk | ||||
| # | ||||
| # The number of worker threads. | ||||
| rm.hold.bulk.threadCount=2 | ||||
| # The maximum number of total items to process in a single bulk operation. | ||||
| rm.hold.bulk.maxItems=1000 | ||||
| # The number of entries to be fetched from the Search Service as a next set of work object to process. | ||||
| rm.hold.bulk.batchSize=100 | ||||
| # The number of entries to process before reporting progress. | ||||
| rm.hold.bulk.logging.interval=100 | ||||
| # The number of entries we process at a time in a transaction. | ||||
| rm.hold.bulk.itemsPerTransaction=1 | ||||
|  | ||||
| cache.bulkHoldStatusCache.cluster.type=fully-distributed | ||||
| cache.bulkHoldRegistryCache.cluster.type=fully-distributed | ||||
|  | ||||
|   | ||||
| @@ -89,6 +89,9 @@ | ||||
|    <!-- Import RM Audit --> | ||||
|    <import resource="classpath:alfresco/module/org_alfresco_module_rm/rm-audit-context.xml"/> | ||||
|  | ||||
|    <!-- Import RM Bulk --> | ||||
|    <import resource="classpath:alfresco/module/org_alfresco_module_rm/rm-bulk-context.xml"/> | ||||
|  | ||||
|    <!--  Import RM query context --> | ||||
|    <import resource="classpath:alfresco/module/org_alfresco_module_rm/query/rm-query-context.xml" /> | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,51 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <beans xmlns="http://www.springframework.org/schema/beans" | ||||
|        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||||
|        xmlns:context="http://www.springframework.org/schema/context" | ||||
|        xsi:schemaLocation="http://www.springframework.org/schema/beans | ||||
|                         http://www.springframework.org/schema/beans/spring-beans.xsd | ||||
|                         http://www.springframework.org/schema/context | ||||
|                         http://www.springframework.org/schema/context/spring-context.xsd"> | ||||
|  | ||||
|    <bean id="holdBulkService" | ||||
|          class="org.alfresco.module.org_alfresco_module_rm.bulk.hold.HoldBulkServiceImpl"> | ||||
|       <property name="serviceRegistry" ref="ServiceRegistry" /> | ||||
|       <property name="transactionService" ref="transactionService" /> | ||||
|       <property name="searchMapper" ref="searchapiSearchMapper" /> | ||||
|       <property name="bulkMonitor" ref="holdBulkMonitor" /> | ||||
|       <property name="holdService" ref="HoldService" /> | ||||
|       <property name="capabilityService" ref="CapabilityService" /> | ||||
|       <property name="permissionService" ref="PermissionService" /> | ||||
|       <property name="nodeService" ref="NodeService" /> | ||||
|       <property name="threadCount"> | ||||
|          <value>${rm.hold.bulk.threadCount}</value> | ||||
|       </property> | ||||
|       <property name="batchSize"> | ||||
|          <value>${rm.hold.bulk.batchSize}</value> | ||||
|       </property> | ||||
|       <property name="maxItems"> | ||||
|          <value>${rm.hold.bulk.maxItems}</value> | ||||
|       </property> | ||||
|       <property name="loggingInterval"> | ||||
|          <value>${rm.hold.bulk.logging.interval}</value> | ||||
|       </property> | ||||
|       <property name="itemsPerTransaction"> | ||||
|          <value>${rm.hold.bulk.itemsPerTransaction}</value> | ||||
|       </property> | ||||
|    </bean> | ||||
|  | ||||
|    <bean id="holdBulkMonitor" class="org.alfresco.module.org_alfresco_module_rm.bulk.hold.DefaultHoldBulkMonitor"> | ||||
|       <property name="holdProgressCache" ref="holdProgressCache" /> | ||||
|       <property name="holdProcessRegistry" ref="holdProcessRegistry" /> | ||||
|    </bean> | ||||
|  | ||||
|  | ||||
|    <bean name="holdProgressCache" factory-bean="cacheFactory" factory-method="createCache"> | ||||
|       <constructor-arg value="cache.bulkHoldStatusCache" /> | ||||
|    </bean> | ||||
|  | ||||
|    <bean name="holdProcessRegistry" factory-bean="cacheFactory" factory-method="createCache"> | ||||
|       <constructor-arg value="cache.bulkHoldRegistryCache" /> | ||||
|    </bean> | ||||
|  | ||||
| </beans> | ||||
| @@ -83,6 +83,13 @@ | ||||
|       <property name="nodesModelFactory" ref="nodesModelFactory" /> | ||||
|       <property name="fileFolderService" ref="FileFolderService" /> | ||||
|       <property name="transactionService" ref="transactionService" /> | ||||
|       <property name="holdBulkService" ref="holdBulkService" /> | ||||
|    </bean> | ||||
|  | ||||
|    <bean class="org.alfresco.rm.rest.api.holds.HoldsBulkStatusesRelation" > | ||||
|       <property name="holdBulkMonitor" ref="holdBulkMonitor" /> | ||||
|       <property name="apiUtils" ref="apiUtils" /> | ||||
|       <property name="permissionService" ref="PermissionService" /> | ||||
|    </bean> | ||||
|  | ||||
|    <bean class="org.alfresco.rm.rest.api.holds.HoldsChildrenRelation"> | ||||
|   | ||||
| @@ -41,6 +41,8 @@ services: | ||||
|                 -Daos.baseUrlOverwrite=http://localhost:8080/alfresco/aos | ||||
|                 -Dmessaging.broker.url=\"failover:(tcp://activemq:61616)?timeout=3000&jms.useCompression=true\" | ||||
|                 -DlocalTransform.core-aio.url=http://transform-core-aio:8090/ | ||||
|                 -Drm.hold.bulk.maxItems=5 | ||||
|                 -Drm.hold.bulk.batchSize=2 | ||||
|                 " | ||||
|         ports: | ||||
|             - 8080:8080 | ||||
|   | ||||
| @@ -8,7 +8,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-governance-services-community-repo-parent</artifactId> | ||||
|       <version>23.3.0.19</version> | ||||
|       <version>23.3.0.38-SNAPSHOT</version> | ||||
|    </parent> | ||||
|  | ||||
|    <properties> | ||||
|   | ||||
| @@ -0,0 +1,249 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2024 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software. | ||||
|  * - | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of | ||||
|  * the paid license agreement will prevail.  Otherwise, the software is | ||||
|  * provided under the following open source license terms: | ||||
|  * - | ||||
|  * Alfresco is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Lesser General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * - | ||||
|  * Alfresco is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Lesser General Public License for more details. | ||||
|  * - | ||||
|  * You should have received a copy of the GNU Lesser General Public License | ||||
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.module.org_alfresco_module_rm.bulk; | ||||
|  | ||||
| import java.util.UUID; | ||||
|  | ||||
| import org.alfresco.repo.batch.BatchProcessWorkProvider; | ||||
| import org.alfresco.repo.batch.BatchProcessor; | ||||
| import org.alfresco.repo.batch.BatchProcessor.BatchProcessWorker; | ||||
| import org.alfresco.rest.api.search.impl.SearchMapper; | ||||
| import org.alfresco.rest.api.search.model.Query; | ||||
| import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; | ||||
| import org.alfresco.service.ServiceRegistry; | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
| import org.alfresco.service.cmr.search.ResultSet; | ||||
| import org.alfresco.service.cmr.search.SearchParameters; | ||||
| import org.alfresco.service.cmr.search.SearchService; | ||||
| import org.alfresco.service.transaction.TransactionService; | ||||
| import org.apache.commons.logging.Log; | ||||
| import org.apache.commons.logging.LogFactory; | ||||
| import org.springframework.beans.factory.InitializingBean; | ||||
|  | ||||
| /** | ||||
|  * A base class for executing bulk operations on nodes based on search query results | ||||
|  */ | ||||
| public abstract class BulkBaseService<T> implements InitializingBean | ||||
| { | ||||
|     private static final Log LOG = LogFactory.getLog(BulkBaseService.class); | ||||
|  | ||||
|     protected ServiceRegistry serviceRegistry; | ||||
|     protected SearchService searchService; | ||||
|     protected TransactionService transactionService; | ||||
|     protected SearchMapper searchMapper; | ||||
|     protected BulkMonitor<T> bulkMonitor; | ||||
|  | ||||
|     protected int threadCount; | ||||
|     protected int batchSize; | ||||
|     protected int itemsPerTransaction; | ||||
|     protected int maxItems; | ||||
|     protected int loggingInterval; | ||||
|  | ||||
|     @Override | ||||
|     public void afterPropertiesSet() throws Exception | ||||
|     { | ||||
|         this.searchService = serviceRegistry.getSearchService(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Execute bulk operation on node based on the search query results | ||||
|      * | ||||
|      * @param nodeRef       node reference | ||||
|      * @param bulkOperation bulk operation | ||||
|      * @return bulk status | ||||
|      */ | ||||
|     public T execute(NodeRef nodeRef, BulkOperation bulkOperation) | ||||
|     { | ||||
|         checkPermissions(nodeRef, bulkOperation); | ||||
|  | ||||
|         ResultSet resultSet = getTotalItems(bulkOperation.searchQuery(), maxItems); | ||||
|         if (maxItems < resultSet.getNumberFound() || resultSet.hasMore()) | ||||
|         { | ||||
|             throw new InvalidArgumentException("Too many items to process. Please refine your query."); | ||||
|         } | ||||
|         long totalItems = resultSet.getNumberFound(); | ||||
|         // Generate a random process id | ||||
|         String processId = UUID.randomUUID().toString(); | ||||
|  | ||||
|         T initBulkStatus = getInitBulkStatus(processId, totalItems); | ||||
|         bulkMonitor.updateBulkStatus(initBulkStatus); | ||||
|         bulkMonitor.registerProcess(nodeRef, processId); | ||||
|  | ||||
|         BatchProcessWorker<NodeRef> batchProcessWorker = getWorkerProvider(nodeRef, bulkOperation); | ||||
|         BulkStatusUpdater bulkStatusUpdater = getBulkStatusUpdater(); | ||||
|  | ||||
|         BatchProcessor<NodeRef> batchProcessor = new BatchProcessor<>( | ||||
|             processId, | ||||
|             transactionService.getRetryingTransactionHelper(), | ||||
|             getWorkProvider(bulkOperation, totalItems, bulkStatusUpdater), | ||||
|             threadCount, | ||||
|             itemsPerTransaction, | ||||
|             bulkStatusUpdater, | ||||
|             LOG, | ||||
|             loggingInterval); | ||||
|  | ||||
|         runAsyncBatchProcessor(batchProcessor, batchProcessWorker, bulkStatusUpdater); | ||||
|         return initBulkStatus; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Run batch processor | ||||
|      */ | ||||
|     protected void runAsyncBatchProcessor(BatchProcessor<NodeRef> batchProcessor, | ||||
|         BatchProcessWorker<NodeRef> batchProcessWorker, BulkStatusUpdater bulkStatusUpdater) | ||||
|     { | ||||
|         Runnable backgroundLogic = () -> { | ||||
|             try | ||||
|             { | ||||
|                 if (LOG.isDebugEnabled()) | ||||
|                 { | ||||
|                     LOG.debug("Started processing batch with name: " + batchProcessor.getProcessName()); | ||||
|                 } | ||||
|                 batchProcessor.processLong(batchProcessWorker, true); | ||||
|                 if (LOG.isDebugEnabled()) | ||||
|                 { | ||||
|                     LOG.debug("Processing batch with name: " + batchProcessor.getProcessName() + " completed"); | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception exception) | ||||
|             { | ||||
|                 LOG.error("Error processing batch with name: " + batchProcessor.getProcessName(), exception); | ||||
|             } | ||||
|             finally | ||||
|             { | ||||
|                 bulkStatusUpdater.update(); | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         Thread backgroundThread = new Thread(backgroundLogic, "BulkBaseService-BackgroundThread"); | ||||
|         backgroundThread.start(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get initial bulk status | ||||
|      * | ||||
|      * @param processId  process id | ||||
|      * @param totalItems total items | ||||
|      * @return bulk status | ||||
|      */ | ||||
|     protected abstract T getInitBulkStatus(String processId, long totalItems); | ||||
|  | ||||
|     /** | ||||
|      * Get bulk status updater | ||||
|      * | ||||
|      * @return bulk status updater | ||||
|      */ | ||||
|     protected abstract BulkStatusUpdater getBulkStatusUpdater(); | ||||
|  | ||||
|     /** | ||||
|      * Get work provider | ||||
|      * | ||||
|      * @param bulkOperation     bulk operation | ||||
|      * @param totalItems        total items | ||||
|      * @param bulkStatusUpdater bulk status updater | ||||
|      * @return work provider | ||||
|      */ | ||||
|     protected abstract BatchProcessWorkProvider<NodeRef> getWorkProvider(BulkOperation bulkOperation, long totalItems, | ||||
|         BulkStatusUpdater bulkStatusUpdater); | ||||
|  | ||||
|     /** | ||||
|      * Get worker provider | ||||
|      * | ||||
|      * @param nodeRef       node reference | ||||
|      * @param bulkOperation bulk operation | ||||
|      * @return worker provider | ||||
|      */ | ||||
|     protected abstract BatchProcessWorker<NodeRef> getWorkerProvider(NodeRef nodeRef, BulkOperation bulkOperation); | ||||
|  | ||||
|     /** | ||||
|      * Check permissions | ||||
|      * | ||||
|      * @param nodeRef       node reference | ||||
|      * @param bulkOperation bulk operation | ||||
|      */ | ||||
|     protected abstract void checkPermissions(NodeRef nodeRef, BulkOperation bulkOperation); | ||||
|  | ||||
|     protected ResultSet getTotalItems(Query searchQuery, int skipCount) | ||||
|     { | ||||
|         SearchParameters searchParams = new SearchParameters(); | ||||
|         searchMapper.setDefaults(searchParams); | ||||
|         searchMapper.fromQuery(searchParams, searchQuery); | ||||
|         searchParams.setSkipCount(skipCount); | ||||
|         searchParams.setMaxItems(1); | ||||
|         return searchService.query(searchParams); | ||||
|     } | ||||
|  | ||||
|     public void setServiceRegistry(ServiceRegistry serviceRegistry) | ||||
|     { | ||||
|         this.serviceRegistry = serviceRegistry; | ||||
|     } | ||||
|  | ||||
|     public void setSearchService(SearchService searchService) | ||||
|     { | ||||
|         this.searchService = searchService; | ||||
|     } | ||||
|  | ||||
|     public void setTransactionService(TransactionService transactionService) | ||||
|     { | ||||
|         this.transactionService = transactionService; | ||||
|     } | ||||
|  | ||||
|     public void setSearchMapper(SearchMapper searchMapper) | ||||
|     { | ||||
|         this.searchMapper = searchMapper; | ||||
|     } | ||||
|  | ||||
|     public void setBulkMonitor(BulkMonitor<T> bulkMonitor) | ||||
|     { | ||||
|         this.bulkMonitor = bulkMonitor; | ||||
|     } | ||||
|  | ||||
|     public void setThreadCount(int threadCount) | ||||
|     { | ||||
|         this.threadCount = threadCount; | ||||
|     } | ||||
|  | ||||
|     public void setBatchSize(int batchSize) | ||||
|     { | ||||
|         this.batchSize = batchSize; | ||||
|     } | ||||
|  | ||||
|     public void setMaxItems(int maxItems) | ||||
|     { | ||||
|         this.maxItems = maxItems; | ||||
|     } | ||||
|  | ||||
|     public void setLoggingInterval(int loggingInterval) | ||||
|     { | ||||
|         this.loggingInterval = loggingInterval; | ||||
|     } | ||||
|  | ||||
|     public void setItemsPerTransaction(int itemsPerTransaction) | ||||
|     { | ||||
|         this.itemsPerTransaction = itemsPerTransaction; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,58 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2024 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software. | ||||
|  * - | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of | ||||
|  * the paid license agreement will prevail.  Otherwise, the software is | ||||
|  * provided under the following open source license terms: | ||||
|  * - | ||||
|  * Alfresco is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Lesser General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * - | ||||
|  * Alfresco is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Lesser General Public License for more details. | ||||
|  * - | ||||
|  * You should have received a copy of the GNU Lesser General Public License | ||||
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.module.org_alfresco_module_rm.bulk; | ||||
|  | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
|  | ||||
| /** | ||||
|  * An interface for monitoring the progress of a bulk operation | ||||
|  */ | ||||
| public interface BulkMonitor<T> | ||||
| { | ||||
|     /** | ||||
|      * Update the bulk status | ||||
|      * | ||||
|      * @param bulkStatus the bulk status | ||||
|      */ | ||||
|     void updateBulkStatus(T bulkStatus); | ||||
|  | ||||
|     /** | ||||
|      * Register a process | ||||
|      * | ||||
|      * @param nodeRef   the node reference | ||||
|      * @param processId the process id | ||||
|      */ | ||||
|     void registerProcess(NodeRef nodeRef, String processId); | ||||
|  | ||||
|     /** | ||||
|      * Get the bulk status | ||||
|      * | ||||
|      * @param bulkStatusId the bulk status id | ||||
|      * @return the bulk status | ||||
|      */ | ||||
|     T getBulkStatus(String bulkStatusId); | ||||
| } | ||||
| @@ -0,0 +1,44 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2024 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software. | ||||
|  * - | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of | ||||
|  * the paid license agreement will prevail.  Otherwise, the software is | ||||
|  * provided under the following open source license terms: | ||||
|  * - | ||||
|  * Alfresco is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Lesser General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * - | ||||
|  * Alfresco is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Lesser General Public License for more details. | ||||
|  * - | ||||
|  * You should have received a copy of the GNU Lesser General Public License | ||||
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.module.org_alfresco_module_rm.bulk; | ||||
|  | ||||
| import org.alfresco.rest.api.search.model.Query; | ||||
|  | ||||
| /** | ||||
|  * An immutable POJO to represent a bulk operation | ||||
|  */ | ||||
| public record BulkOperation(Query searchQuery, String operationType) | ||||
| { | ||||
|     public BulkOperation | ||||
|     { | ||||
|         if (operationType == null || searchQuery == null) | ||||
|         { | ||||
|             throw new IllegalArgumentException("Operation type and search query must not be null"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -0,0 +1,40 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2024 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software. | ||||
|  * - | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of | ||||
|  * the paid license agreement will prevail.  Otherwise, the software is | ||||
|  * provided under the following open source license terms: | ||||
|  * - | ||||
|  * Alfresco is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Lesser General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * - | ||||
|  * Alfresco is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Lesser General Public License for more details. | ||||
|  * - | ||||
|  * You should have received a copy of the GNU Lesser General Public License | ||||
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.module.org_alfresco_module_rm.bulk; | ||||
|  | ||||
| import org.springframework.context.ApplicationEventPublisher; | ||||
|  | ||||
| /** | ||||
|  * An interface for updating the status of a bulk operation | ||||
|  */ | ||||
| public interface BulkStatusUpdater extends ApplicationEventPublisher | ||||
| { | ||||
|     /** | ||||
|      * Update the bulk status | ||||
|      */ | ||||
|     void update(); | ||||
| } | ||||
| @@ -0,0 +1,109 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2024 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software. | ||||
|  * - | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of | ||||
|  * the paid license agreement will prevail.  Otherwise, the software is | ||||
|  * provided under the following open source license terms: | ||||
|  * - | ||||
|  * Alfresco is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Lesser General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * - | ||||
|  * Alfresco is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Lesser General Public License for more details. | ||||
|  * - | ||||
|  * You should have received a copy of the GNU Lesser General Public License | ||||
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.module.org_alfresco_module_rm.bulk.hold; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collections; | ||||
| import java.util.Comparator; | ||||
| import java.util.List; | ||||
| import java.util.Objects; | ||||
| import java.util.Optional; | ||||
|  | ||||
| import org.alfresco.repo.cache.SimpleCache; | ||||
| import org.alfresco.rm.rest.api.model.HoldBulkStatus; | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
| import org.springframework.context.ApplicationEvent; | ||||
| import org.springframework.extensions.surf.util.AbstractLifecycleBean; | ||||
|  | ||||
| /** | ||||
|  * Default hold bulk monitor implementation | ||||
|  */ | ||||
| public class DefaultHoldBulkMonitor extends AbstractLifecycleBean implements HoldBulkMonitor | ||||
| { | ||||
|     protected SimpleCache<String, HoldBulkStatus> holdProgressCache; | ||||
|     protected SimpleCache<String, List<HoldBulkProcessDetails>> holdProcessRegistry; | ||||
|  | ||||
|     @Override | ||||
|     public void updateBulkStatus(HoldBulkStatus holdBulkStatus) | ||||
|     { | ||||
|         holdProgressCache.put(holdBulkStatus.bulkStatusId(), holdBulkStatus); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void registerProcess(NodeRef holdRef, String processId) | ||||
|     { | ||||
|         List<HoldBulkProcessDetails> processIds = Optional.ofNullable(holdProcessRegistry.get(holdRef.getId())) | ||||
|             .orElse(new ArrayList<>()); | ||||
|         processIds.add(new HoldBulkProcessDetails(processId, null)); | ||||
|         holdProcessRegistry.put(holdRef.getId(), processIds); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public HoldBulkStatus getBulkStatus(String bulkStatusId) | ||||
|     { | ||||
|         return holdProgressCache.get(bulkStatusId); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public List<HoldBulkStatus> getBulkStatusesForHold(String holdId) | ||||
|     { | ||||
|         return Optional.ofNullable(holdProcessRegistry.get(holdId)) | ||||
|             .map(bulkProcessDetailsList -> bulkProcessDetailsList.stream() | ||||
|                 .map(HoldBulkProcessDetails::bulkStatusId) | ||||
|                 .map(this::getBulkStatus) | ||||
|                 .filter(Objects::nonNull) | ||||
|                 .sorted(Comparator.comparing(HoldBulkStatus::endTime, Comparator.nullsLast(Comparator.naturalOrder())) | ||||
|                     .thenComparing(HoldBulkStatus::startTime, Comparator.nullsLast(Comparator.naturalOrder())) | ||||
|                     .reversed()) | ||||
|                 .toList()) | ||||
|             .orElse(Collections.emptyList()); | ||||
|     } | ||||
|  | ||||
|     public void setHoldProgressCache( | ||||
|         SimpleCache<String, HoldBulkStatus> holdProgressCache) | ||||
|     { | ||||
|         this.holdProgressCache = holdProgressCache; | ||||
|     } | ||||
|  | ||||
|     public void setHoldProcessRegistry( | ||||
|         SimpleCache<String, List<HoldBulkProcessDetails>> holdProcessRegistry) | ||||
|     { | ||||
|         this.holdProcessRegistry = holdProcessRegistry; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onBootstrap(ApplicationEvent applicationEvent) | ||||
|     { | ||||
|         // NOOP | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onShutdown(ApplicationEvent applicationEvent) | ||||
|     { | ||||
|         // NOOP | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,46 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2024 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software. | ||||
|  * - | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of | ||||
|  * the paid license agreement will prevail.  Otherwise, the software is | ||||
|  * provided under the following open source license terms: | ||||
|  * - | ||||
|  * Alfresco is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Lesser General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * - | ||||
|  * Alfresco is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Lesser General Public License for more details. | ||||
|  * - | ||||
|  * You should have received a copy of the GNU Lesser General Public License | ||||
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.module.org_alfresco_module_rm.bulk.hold; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| import org.alfresco.module.org_alfresco_module_rm.bulk.BulkMonitor; | ||||
| import org.alfresco.rm.rest.api.model.HoldBulkStatus; | ||||
|  | ||||
| /** | ||||
|  * An interface for monitoring the progress of a bulk hold operation | ||||
|  */ | ||||
| public interface HoldBulkMonitor extends BulkMonitor<HoldBulkStatus> | ||||
| { | ||||
|     /** | ||||
|      * Get the bulk statuses for a hold | ||||
|      * | ||||
|      * @param holdId the hold id | ||||
|      * @return the bulk statuses | ||||
|      */ | ||||
|     List<HoldBulkStatus> getBulkStatusesForHold(String holdId); | ||||
| } | ||||
| @@ -0,0 +1,36 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2024 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software. | ||||
|  * - | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of | ||||
|  * the paid license agreement will prevail.  Otherwise, the software is | ||||
|  * provided under the following open source license terms: | ||||
|  * - | ||||
|  * Alfresco is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Lesser General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * - | ||||
|  * Alfresco is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Lesser General Public License for more details. | ||||
|  * - | ||||
|  * You should have received a copy of the GNU Lesser General Public License | ||||
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.module.org_alfresco_module_rm.bulk.hold; | ||||
|  | ||||
| import java.io.Serializable; | ||||
|  | ||||
| /** | ||||
|  * A simple immutable POJO to hold the details of a bulk hold process | ||||
|  */ | ||||
| public record HoldBulkProcessDetails(String bulkStatusId, String creatorInstance) implements Serializable | ||||
| { | ||||
| } | ||||
| @@ -0,0 +1,45 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2024 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software. | ||||
|  * - | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of | ||||
|  * the paid license agreement will prevail.  Otherwise, the software is | ||||
|  * provided under the following open source license terms: | ||||
|  * - | ||||
|  * Alfresco is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Lesser General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * - | ||||
|  * Alfresco is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Lesser General Public License for more details. | ||||
|  * - | ||||
|  * You should have received a copy of the GNU Lesser General Public License | ||||
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.module.org_alfresco_module_rm.bulk.hold; | ||||
|  | ||||
| import org.alfresco.module.org_alfresco_module_rm.bulk.BulkOperation; | ||||
| import org.alfresco.rm.rest.api.model.HoldBulkStatus; | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
|  | ||||
| /** | ||||
|  * Interface defining a hold bulk service. | ||||
|  */ | ||||
| public interface HoldBulkService | ||||
| { | ||||
|     /** | ||||
|      * Initiates a bulk operation on a hold. | ||||
|      * | ||||
|      * @param holdRef       The hold reference | ||||
|      * @param bulkOperation The bulk operation | ||||
|      */ | ||||
|     HoldBulkStatus execute(NodeRef holdRef, BulkOperation bulkOperation); | ||||
| } | ||||
| @@ -0,0 +1,258 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2024 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software. | ||||
|  * - | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of | ||||
|  * the paid license agreement will prevail.  Otherwise, the software is | ||||
|  * provided under the following open source license terms: | ||||
|  * - | ||||
|  * Alfresco is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Lesser General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * - | ||||
|  * Alfresco is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Lesser General Public License for more details. | ||||
|  * - | ||||
|  * You should have received a copy of the GNU Lesser General Public License | ||||
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.module.org_alfresco_module_rm.bulk.hold; | ||||
|  | ||||
| import static org.alfresco.model.ContentModel.PROP_NAME; | ||||
| import static org.alfresco.rm.rest.api.model.HoldBulkOperationType.ADD; | ||||
|  | ||||
| import java.util.Collection; | ||||
| import java.util.Collections; | ||||
| import java.util.Locale; | ||||
| import java.util.concurrent.atomic.AtomicInteger; | ||||
|  | ||||
| import org.alfresco.model.ContentModel; | ||||
| import org.alfresco.module.org_alfresco_module_rm.bulk.BulkBaseService; | ||||
| import org.alfresco.module.org_alfresco_module_rm.bulk.BulkOperation; | ||||
| import org.alfresco.module.org_alfresco_module_rm.bulk.BulkStatusUpdater; | ||||
| import org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService; | ||||
| import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; | ||||
| import org.alfresco.module.org_alfresco_module_rm.hold.HoldService; | ||||
| import org.alfresco.repo.batch.BatchProcessWorkProvider; | ||||
| import org.alfresco.repo.batch.BatchProcessor.BatchProcessWorker; | ||||
| import org.alfresco.repo.security.authentication.AuthenticationUtil; | ||||
| import org.alfresco.repo.security.permissions.AccessDeniedException; | ||||
| import org.alfresco.rest.api.search.model.Query; | ||||
| import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; | ||||
| import org.alfresco.rm.rest.api.model.HoldBulkOperationType; | ||||
| import org.alfresco.rm.rest.api.model.HoldBulkStatus; | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
| import org.alfresco.service.cmr.repository.NodeService; | ||||
| import org.alfresco.service.cmr.search.ResultSet; | ||||
| import org.alfresco.service.cmr.search.SearchParameters; | ||||
| import org.alfresco.service.cmr.security.AccessStatus; | ||||
| import org.alfresco.service.cmr.security.PermissionService; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| import org.springframework.extensions.surf.util.I18NUtil; | ||||
|  | ||||
| /** | ||||
|  * Implementation of the {@link HoldBulkService} interface. | ||||
|  */ | ||||
| @SuppressWarnings("PMD.PreserveStackTrace") | ||||
| public class HoldBulkServiceImpl extends BulkBaseService<HoldBulkStatus> implements HoldBulkService | ||||
| { | ||||
|     private static final Logger LOGGER = LoggerFactory.getLogger(HoldBulkServiceImpl.class); | ||||
|  | ||||
|     private HoldService holdService; | ||||
|     private static final String MSG_ERR_ACCESS_DENIED = "permissions.err_access_denied"; | ||||
|  | ||||
|     private CapabilityService capabilityService; | ||||
|     private PermissionService permissionService; | ||||
|     private NodeService nodeService; | ||||
|  | ||||
|     @Override | ||||
|     protected HoldBulkStatus getInitBulkStatus(String processId, long totalItems) | ||||
|     { | ||||
|         return new HoldBulkStatus(processId, null, null, 0, 0, totalItems, null); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected BulkStatusUpdater getBulkStatusUpdater() | ||||
|     { | ||||
|         return new HoldBulkStatusUpdater((HoldBulkMonitor) bulkMonitor); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected BatchProcessWorkProvider<NodeRef> getWorkProvider(BulkOperation bulkOperation, long totalItems, | ||||
|         BulkStatusUpdater bulkStatusUpdater) | ||||
|     { | ||||
|         return new AddToHoldWorkerProvider(new AtomicInteger(0), bulkOperation, totalItems, bulkStatusUpdater); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected BatchProcessWorker<NodeRef> getWorkerProvider(NodeRef nodeRef, BulkOperation bulkOperation) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             HoldBulkOperationType holdBulkOperationType = HoldBulkOperationType.valueOf(bulkOperation.operationType() | ||||
|                 .toUpperCase(Locale.ENGLISH)); | ||||
|             return switch (holdBulkOperationType) | ||||
|             { | ||||
|                 case ADD -> new AddToHoldWorkerBatch(nodeRef); | ||||
|             }; | ||||
|         } | ||||
|         catch (IllegalArgumentException e) | ||||
|         { | ||||
|             String errorMsg = "Unsupported action type when starting the bulk process: "; | ||||
|             if (LOGGER.isDebugEnabled()) | ||||
|             { | ||||
|                 LOGGER.debug("{} {}", errorMsg, bulkOperation.operationType(), e); | ||||
|             } | ||||
|             throw new InvalidArgumentException(errorMsg + bulkOperation.operationType()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void checkPermissions(NodeRef holdRef, BulkOperation bulkOperation) | ||||
|     { | ||||
|         if (!holdService.isHold(holdRef)) | ||||
|         { | ||||
|             final String holdName = (String) nodeService.getProperty(holdRef, PROP_NAME); | ||||
|             throw new InvalidArgumentException(I18NUtil.getMessage("rm.hold.not-hold", holdName), null); | ||||
|         } | ||||
|         if (ADD.name().equals(bulkOperation.operationType()) && (!AccessStatus.ALLOWED.equals( | ||||
|             capabilityService.getCapabilityAccessState(holdRef, RMPermissionModel.ADD_TO_HOLD)) || | ||||
|             permissionService.hasPermission(holdRef, RMPermissionModel.FILING) == AccessStatus.DENIED)) | ||||
|         { | ||||
|             throw new AccessDeniedException(I18NUtil.getMessage(MSG_ERR_ACCESS_DENIED)); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     private class AddToHoldWorkerBatch implements BatchProcessWorker<NodeRef> | ||||
|     { | ||||
|         private final NodeRef holdRef; | ||||
|         private final String currentUser; | ||||
|  | ||||
|         public AddToHoldWorkerBatch(NodeRef holdRef) | ||||
|         { | ||||
|             this.holdRef = holdRef; | ||||
|             currentUser = AuthenticationUtil.getFullyAuthenticatedUser(); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public String getIdentifier(NodeRef entry) | ||||
|         { | ||||
|             return entry.getId(); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void beforeProcess() | ||||
|         { | ||||
|             AuthenticationUtil.pushAuthentication(); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void process(NodeRef entry) throws Throwable | ||||
|         { | ||||
|             AuthenticationUtil.setFullyAuthenticatedUser(currentUser); | ||||
|             holdService.addToHold(holdRef, entry); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void afterProcess() | ||||
|         { | ||||
|             AuthenticationUtil.popAuthentication(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private class AddToHoldWorkerProvider implements BatchProcessWorkProvider<NodeRef> | ||||
|     { | ||||
|         private final AtomicInteger currentNodeNumber; | ||||
|         private final Query searchQuery; | ||||
|         private final String currentUser; | ||||
|         private final long totalItems; | ||||
|         private final BulkStatusUpdater bulkStatusUpdater; | ||||
|  | ||||
|         public AddToHoldWorkerProvider(AtomicInteger currentNodeNumber, BulkOperation bulkOperation, long totalItems, | ||||
|             BulkStatusUpdater bulkStatusUpdater) | ||||
|         { | ||||
|             this.currentNodeNumber = currentNodeNumber; | ||||
|             this.searchQuery = bulkOperation.searchQuery(); | ||||
|             this.totalItems = totalItems; | ||||
|             this.bulkStatusUpdater = bulkStatusUpdater; | ||||
|             currentUser = AuthenticationUtil.getFullyAuthenticatedUser(); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public int getTotalEstimatedWorkSize() | ||||
|         { | ||||
|             return (int) totalItems; | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public long getTotalEstimatedWorkSizeLong() | ||||
|         { | ||||
|             return totalItems; | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public Collection<NodeRef> getNextWork() | ||||
|         { | ||||
|             AuthenticationUtil.pushAuthentication(); | ||||
|             AuthenticationUtil.setFullyAuthenticatedUser(currentUser); | ||||
|             SearchParameters searchParams = getNextPageParameters(); | ||||
|             ResultSet result = searchService.query(searchParams); | ||||
|             if (result.getNodeRefs().isEmpty()) | ||||
|             { | ||||
|                 return Collections.emptyList(); | ||||
|             } | ||||
|             AuthenticationUtil.popAuthentication(); | ||||
|             if (LOGGER.isDebugEnabled()) | ||||
|             { | ||||
|                 LOGGER.debug("Processing the next work for the batch processor, skipCount={}, size={}", | ||||
|                     searchParams.getSkipCount(), result.getNumberFound()); | ||||
|             } | ||||
|             currentNodeNumber.addAndGet(batchSize); | ||||
|             bulkStatusUpdater.update(); | ||||
|             return result.getNodeRefs(); | ||||
|         } | ||||
|  | ||||
|         private SearchParameters getNextPageParameters() | ||||
|         { | ||||
|             SearchParameters searchParams = new SearchParameters(); | ||||
|             searchMapper.setDefaults(searchParams); | ||||
|             searchMapper.fromQuery(searchParams, searchQuery); | ||||
|             searchParams.setSkipCount(currentNodeNumber.get()); | ||||
|             searchParams.setMaxItems(batchSize); | ||||
|             searchParams.setLimit(batchSize); | ||||
|             searchParams.addSort("@" + ContentModel.PROP_CREATED, true); | ||||
|             return searchParams; | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     public void setHoldService(HoldService holdService) | ||||
|     { | ||||
|         this.holdService = holdService; | ||||
|     } | ||||
|  | ||||
|     public void setCapabilityService(CapabilityService capabilityService) | ||||
|     { | ||||
|         this.capabilityService = capabilityService; | ||||
|     } | ||||
|  | ||||
|     public void setPermissionService(PermissionService permissionService) | ||||
|     { | ||||
|         this.permissionService = permissionService; | ||||
|     } | ||||
|  | ||||
|     public void setNodeService(NodeService nodeService) | ||||
|     { | ||||
|         this.nodeService = nodeService; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,69 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2024 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software. | ||||
|  * - | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of | ||||
|  * the paid license agreement will prevail.  Otherwise, the software is | ||||
|  * provided under the following open source license terms: | ||||
|  * - | ||||
|  * Alfresco is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Lesser General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * - | ||||
|  * Alfresco is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Lesser General Public License for more details. | ||||
|  * - | ||||
|  * You should have received a copy of the GNU Lesser General Public License | ||||
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.module.org_alfresco_module_rm.bulk.hold; | ||||
|  | ||||
| import org.alfresco.module.org_alfresco_module_rm.bulk.BulkStatusUpdater; | ||||
| import org.alfresco.repo.batch.BatchMonitor; | ||||
| import org.alfresco.repo.batch.BatchMonitorEvent; | ||||
| import org.alfresco.rm.rest.api.model.HoldBulkStatus; | ||||
|  | ||||
| /** | ||||
|  * An implementation of {@link BulkStatusUpdater} for the hold bulk operation | ||||
|  */ | ||||
| public class HoldBulkStatusUpdater implements BulkStatusUpdater | ||||
| { | ||||
|     private final Runnable task; | ||||
|     private BatchMonitor batchMonitor; | ||||
|  | ||||
|     public HoldBulkStatusUpdater(HoldBulkMonitor holdBulkMonitor) | ||||
|     { | ||||
|         this.task = () -> holdBulkMonitor.updateBulkStatus( | ||||
|             new HoldBulkStatus(batchMonitor.getProcessName(), batchMonitor.getStartTime(), | ||||
|                 batchMonitor.getEndTime(), | ||||
|                 batchMonitor.getSuccessfullyProcessedEntriesLong() + batchMonitor.getTotalErrorsLong(), | ||||
|                 batchMonitor.getTotalErrorsLong(), batchMonitor.getTotalResultsLong(), | ||||
|                 batchMonitor.getLastError())); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void update() | ||||
|     { | ||||
|         if (task != null && batchMonitor != null) | ||||
|         { | ||||
|             task.run(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void publishEvent(Object event) | ||||
|     { | ||||
|         if (event instanceof BatchMonitorEvent batchMonitorEvent) | ||||
|         { | ||||
|             batchMonitor = batchMonitorEvent.getBatchMonitor(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -219,7 +219,7 @@ public class FilePlanServiceImpl extends ServiceBaseImpl | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return rmContainerCacheManager.get(storeRef); | ||||
|             return new HashSet<>(rmContainerCacheManager.get(storeRef)); | ||||
|         } | ||||
|  | ||||
|         return results; | ||||
|   | ||||
| @@ -0,0 +1,120 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2024 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software. | ||||
|  * - | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of | ||||
|  * the paid license agreement will prevail.  Otherwise, the software is | ||||
|  * provided under the following open source license terms: | ||||
|  * - | ||||
|  * Alfresco is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Lesser General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * - | ||||
|  * Alfresco is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Lesser General Public License for more details. | ||||
|  * - | ||||
|  * You should have received a copy of the GNU Lesser General Public License | ||||
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rm.rest.api.holds; | ||||
|  | ||||
| import static org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck.checkNotBlank; | ||||
| import static org.alfresco.util.ParameterCheck.mandatory; | ||||
|  | ||||
| import java.util.LinkedList; | ||||
| import java.util.List; | ||||
| import java.util.Optional; | ||||
| import java.util.stream.Collectors; | ||||
|  | ||||
| import org.alfresco.module.org_alfresco_module_rm.bulk.hold.HoldBulkMonitor; | ||||
| import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; | ||||
| import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException; | ||||
| import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException; | ||||
| import org.alfresco.rest.framework.core.exceptions.RelationshipResourceNotFoundException; | ||||
| import org.alfresco.rest.framework.resource.RelationshipResource; | ||||
| import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction; | ||||
| import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; | ||||
| import org.alfresco.rest.framework.resource.parameters.Parameters; | ||||
| import org.alfresco.rm.rest.api.impl.FilePlanComponentsApiUtils; | ||||
| import org.alfresco.rm.rest.api.model.HoldBulkStatus; | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
| import org.alfresco.service.cmr.security.AccessStatus; | ||||
| import org.alfresco.service.cmr.security.PermissionService; | ||||
| import org.springframework.extensions.surf.util.I18NUtil; | ||||
|  | ||||
| @RelationshipResource(name = "bulk-statuses", entityResource = HoldsEntityResource.class, title = "Bulk statuses of a hold") | ||||
| public class HoldsBulkStatusesRelation | ||||
|     implements RelationshipResourceAction.Read<HoldBulkStatus>, RelationshipResourceAction.ReadById<HoldBulkStatus> | ||||
| { | ||||
|     private HoldBulkMonitor holdBulkMonitor; | ||||
|     private FilePlanComponentsApiUtils apiUtils; | ||||
|     private PermissionService permissionService; | ||||
|  | ||||
|     @Override | ||||
|     public CollectionWithPagingInfo<HoldBulkStatus> readAll(String holdId, Parameters parameters) | ||||
|     { | ||||
|         // validate parameters | ||||
|         checkNotBlank("holdId", holdId); | ||||
|         mandatory("parameters", parameters); | ||||
|  | ||||
|         NodeRef holdRef = apiUtils.lookupAndValidateNodeType(holdId, RecordsManagementModel.TYPE_HOLD); | ||||
|  | ||||
|         checkReadPermissions(holdRef); | ||||
|  | ||||
|         List<HoldBulkStatus> statuses = holdBulkMonitor.getBulkStatusesForHold(holdId); | ||||
|         List<HoldBulkStatus> page = statuses.stream() | ||||
|             .skip(parameters.getPaging().getSkipCount()) | ||||
|             .limit(parameters.getPaging().getMaxItems()) | ||||
|             .collect(Collectors.toCollection(LinkedList::new)); | ||||
|  | ||||
|         int totalItems = statuses.size(); | ||||
|         boolean hasMore = parameters.getPaging().getSkipCount() + parameters.getPaging().getMaxItems() < totalItems; | ||||
|         return CollectionWithPagingInfo.asPaged(parameters.getPaging(), page, hasMore, totalItems); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public HoldBulkStatus readById(String holdId, String bulkStatusId, Parameters parameters) | ||||
|         throws RelationshipResourceNotFoundException | ||||
|     { | ||||
|         checkNotBlank("holdId", holdId); | ||||
|         checkNotBlank("bulkStatusId", bulkStatusId); | ||||
|         mandatory("parameters", parameters); | ||||
|  | ||||
|         NodeRef holdRef = apiUtils.lookupAndValidateNodeType(holdId, RecordsManagementModel.TYPE_HOLD); | ||||
|  | ||||
|         checkReadPermissions(holdRef); | ||||
|  | ||||
|         return Optional.ofNullable(holdBulkMonitor.getBulkStatus(bulkStatusId)).orElseThrow(() -> new EntityNotFoundException(bulkStatusId)); | ||||
|     } | ||||
|  | ||||
|     private void checkReadPermissions(NodeRef holdRef) | ||||
|     { | ||||
|         if (permissionService.hasReadPermission(holdRef) == AccessStatus.DENIED) | ||||
|         { | ||||
|             throw new PermissionDeniedException(I18NUtil.getMessage("permissions.err_access_denied")); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void setHoldBulkMonitor(HoldBulkMonitor holdBulkMonitor) | ||||
|     { | ||||
|         this.holdBulkMonitor = holdBulkMonitor; | ||||
|     } | ||||
|  | ||||
|     public void setApiUtils(FilePlanComponentsApiUtils apiUtils) | ||||
|     { | ||||
|         this.apiUtils = apiUtils; | ||||
|     } | ||||
|  | ||||
|     public void setPermissionService(PermissionService permissionService) | ||||
|     { | ||||
|         this.permissionService = permissionService; | ||||
|     } | ||||
| } | ||||
| @@ -30,6 +30,8 @@ import static org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck.c | ||||
| import static org.alfresco.util.ParameterCheck.mandatory; | ||||
|  | ||||
| import jakarta.servlet.http.HttpServletResponse; | ||||
| import org.alfresco.module.org_alfresco_module_rm.bulk.BulkOperation; | ||||
| import org.alfresco.module.org_alfresco_module_rm.bulk.hold.HoldBulkService; | ||||
| import org.alfresco.module.org_alfresco_module_rm.hold.HoldService; | ||||
| import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; | ||||
| import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; | ||||
| @@ -42,6 +44,9 @@ import org.alfresco.rest.framework.resource.parameters.Parameters; | ||||
| import org.alfresco.rest.framework.webscripts.WithResponse; | ||||
| import org.alfresco.rm.rest.api.impl.ApiNodesModelFactory; | ||||
| import org.alfresco.rm.rest.api.impl.FilePlanComponentsApiUtils; | ||||
| import org.alfresco.rm.rest.api.model.HoldBulkOperation; | ||||
| import org.alfresco.rm.rest.api.model.HoldBulkOperationEntry; | ||||
| import org.alfresco.rm.rest.api.model.HoldBulkStatus; | ||||
| import org.alfresco.rm.rest.api.model.HoldDeletionReason; | ||||
| import org.alfresco.rm.rest.api.model.HoldModel; | ||||
| import org.alfresco.service.cmr.model.FileFolderService; | ||||
| @@ -68,6 +73,7 @@ public class HoldsEntityResource implements | ||||
|     private ApiNodesModelFactory nodesModelFactory; | ||||
|     private HoldService holdService; | ||||
|     private TransactionService transactionService; | ||||
|     private HoldBulkService holdBulkService; | ||||
|  | ||||
|     @Override | ||||
|     public void afterPropertiesSet() throws Exception | ||||
| @@ -157,6 +163,23 @@ public class HoldsEntityResource implements | ||||
|         return reason; | ||||
|     } | ||||
|  | ||||
|     @Operation("bulk") | ||||
|     @WebApiDescription(title = "Start the hold bulk operation", | ||||
|         successStatus = HttpServletResponse.SC_ACCEPTED) | ||||
|     public HoldBulkOperationEntry bulk(String holdId, HoldBulkOperation holdBulkOperation, Parameters parameters, | ||||
|         WithResponse withResponse) | ||||
|     { | ||||
|         // validate parameters | ||||
|         checkNotBlank("holdId", holdId); | ||||
|         mandatory("parameters", parameters); | ||||
|  | ||||
|         NodeRef parentNodeRef = apiUtils.lookupAndValidateNodeType(holdId, RecordsManagementModel.TYPE_HOLD); | ||||
|  | ||||
|         HoldBulkStatus holdBulkStatus = holdBulkService.execute(parentNodeRef, | ||||
|             new BulkOperation(holdBulkOperation.query(), holdBulkOperation.op().name())); | ||||
|         return new HoldBulkOperationEntry(holdBulkStatus.bulkStatusId(), holdBulkStatus.totalItems()); | ||||
|     } | ||||
|  | ||||
|     public void setApiUtils(FilePlanComponentsApiUtils apiUtils) | ||||
|     { | ||||
|         this.apiUtils = apiUtils; | ||||
| @@ -181,4 +204,9 @@ public class HoldsEntityResource implements | ||||
|     { | ||||
|         this.transactionService = transactionService; | ||||
|     } | ||||
|  | ||||
|     public void setHoldBulkService(HoldBulkService holdBulkService) | ||||
|     { | ||||
|         this.holdBulkService = holdBulkService; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,33 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2024 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software. | ||||
|  * - | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of | ||||
|  * the paid license agreement will prevail.  Otherwise, the software is | ||||
|  * provided under the following open source license terms: | ||||
|  * - | ||||
|  * Alfresco is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Lesser General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * - | ||||
|  * Alfresco is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Lesser General Public License for more details. | ||||
|  * - | ||||
|  * You should have received a copy of the GNU Lesser General Public License | ||||
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rm.rest.api.model; | ||||
|  | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
|  | ||||
| import org.alfresco.rest.api.search.model.Query; | ||||
|  | ||||
| public record HoldBulkOperation(@JsonProperty(required = true) Query query, @JsonProperty(required = true) HoldBulkOperationType op) {} | ||||
| @@ -0,0 +1,29 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2024 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software. | ||||
|  * - | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of | ||||
|  * the paid license agreement will prevail.  Otherwise, the software is | ||||
|  * provided under the following open source license terms: | ||||
|  * - | ||||
|  * Alfresco is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Lesser General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * - | ||||
|  * Alfresco is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Lesser General Public License for more details. | ||||
|  * - | ||||
|  * You should have received a copy of the GNU Lesser General Public License | ||||
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rm.rest.api.model; | ||||
|  | ||||
| public record HoldBulkOperationEntry(String bulkStatusId, long totalItems){} | ||||
| @@ -0,0 +1,38 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2024 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software. | ||||
|  * - | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of | ||||
|  * the paid license agreement will prevail.  Otherwise, the software is | ||||
|  * provided under the following open source license terms: | ||||
|  * - | ||||
|  * Alfresco is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Lesser General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * - | ||||
|  * Alfresco is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Lesser General Public License for more details. | ||||
|  * - | ||||
|  * You should have received a copy of the GNU Lesser General Public License | ||||
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rm.rest.api.model; | ||||
|  | ||||
| /** | ||||
|  * This enum represents the types of bulk operations that can be performed on holds | ||||
|  */ | ||||
| public enum HoldBulkOperationType | ||||
| { | ||||
|     /** | ||||
|      * The ADD operation represents adding items to a hold in bulk. | ||||
|      */ | ||||
|     ADD | ||||
| } | ||||
| @@ -0,0 +1,69 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2024 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software. | ||||
|  * - | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of | ||||
|  * the paid license agreement will prevail.  Otherwise, the software is | ||||
|  * provided under the following open source license terms: | ||||
|  * - | ||||
|  * Alfresco is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Lesser General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * - | ||||
|  * Alfresco is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Lesser General Public License for more details. | ||||
|  * - | ||||
|  * You should have received a copy of the GNU Lesser General Public License | ||||
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rm.rest.api.model; | ||||
|  | ||||
| import java.io.Serializable; | ||||
| import java.util.Date; | ||||
|  | ||||
| public record HoldBulkStatus(String bulkStatusId, Date startTime, Date endTime, long processedItems, long errorsCount, | ||||
|                              long totalItems, String lastError) implements Serializable | ||||
| { | ||||
|     public enum Status | ||||
|     { | ||||
|         PENDING("PENDING"), | ||||
|         IN_PROGRESS("IN PROGRESS"), | ||||
|         DONE("DONE"); | ||||
|  | ||||
|         private final String value; | ||||
|  | ||||
|         Status(String value) | ||||
|         { | ||||
|             this.value = value; | ||||
|         } | ||||
|  | ||||
|         public String getValue() | ||||
|         { | ||||
|             return value; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public String getStatus() | ||||
|     { | ||||
|         if (startTime == null && endTime == null) | ||||
|         { | ||||
|             return Status.PENDING.getValue(); | ||||
|         } | ||||
|         else if (startTime != null && endTime == null) | ||||
|         { | ||||
|             return Status.IN_PROGRESS.getValue(); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return Status.DONE.getValue(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,119 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * Copyright (C) 2005 - 2024 Alfresco Software Limited | ||||
|  * %% | ||||
|  * This file is part of the Alfresco software. | ||||
|  * - | ||||
|  * If the software was purchased under a paid Alfresco license, the terms of | ||||
|  * the paid license agreement will prevail.  Otherwise, the software is | ||||
|  * provided under the following open source license terms: | ||||
|  * - | ||||
|  * Alfresco is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Lesser General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * - | ||||
|  * Alfresco is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Lesser General Public License for more details. | ||||
|  * - | ||||
|  * You should have received a copy of the GNU Lesser General Public License | ||||
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.module.org_alfresco_module_rm.bulk; | ||||
|  | ||||
| import static org.junit.Assert.assertEquals; | ||||
| import static org.mockito.Mockito.when; | ||||
|  | ||||
| import java.util.Arrays; | ||||
| import java.util.Collections; | ||||
| import java.util.Date; | ||||
| import java.util.List; | ||||
|  | ||||
| import org.alfresco.module.org_alfresco_module_rm.bulk.hold.DefaultHoldBulkMonitor; | ||||
| import org.alfresco.module.org_alfresco_module_rm.bulk.hold.HoldBulkProcessDetails; | ||||
| import org.alfresco.repo.cache.SimpleCache; | ||||
| import org.alfresco.rm.rest.api.model.HoldBulkStatus; | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
| import org.junit.Before; | ||||
| import org.junit.Test; | ||||
| import org.mockito.Mock; | ||||
| import org.mockito.Mockito; | ||||
| import org.mockito.MockitoAnnotations; | ||||
|  | ||||
| public class DefaultHoldBulkMonitorUnitTest | ||||
| { | ||||
|  | ||||
|     @Mock | ||||
|     private SimpleCache<String, HoldBulkStatus> holdProgressCache; | ||||
|  | ||||
|     @Mock | ||||
|     private SimpleCache<String, List<HoldBulkProcessDetails>> holdProcessRegistry; | ||||
|  | ||||
|     private DefaultHoldBulkMonitor holdBulkMonitor; | ||||
|  | ||||
|     @Before | ||||
|     public void setUp() | ||||
|     { | ||||
|         MockitoAnnotations.openMocks(this); | ||||
|         holdBulkMonitor = new DefaultHoldBulkMonitor(); | ||||
|         holdBulkMonitor.setHoldProgressCache(holdProgressCache); | ||||
|         holdBulkMonitor.setHoldProcessRegistry(holdProcessRegistry); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateBulkStatus() | ||||
|     { | ||||
|         HoldBulkStatus status = new HoldBulkStatus("bulkStatusId", null, null, 0L, 0L, 0L, null); | ||||
|  | ||||
|         holdBulkMonitor.updateBulkStatus(status); | ||||
|  | ||||
|         Mockito.verify(holdProgressCache).put("bulkStatusId", status); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testRegisterProcess() | ||||
|     { | ||||
|         NodeRef holdRef = new NodeRef("workspace://SpacesStore/holdId"); | ||||
|         String processId = "processId"; | ||||
|         when(holdProcessRegistry.get(holdRef.getId())).thenReturn(null); | ||||
|  | ||||
|         holdBulkMonitor.registerProcess(holdRef, processId); | ||||
|  | ||||
|         Mockito.verify(holdProcessRegistry) | ||||
|             .put(holdRef.getId(), Arrays.asList(new HoldBulkProcessDetails(processId, null))); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetBulkStatusesForHoldReturnsEmptyListWhenNoProcesses() | ||||
|     { | ||||
|         when(holdProcessRegistry.get("holdId")).thenReturn(null); | ||||
|         assertEquals(Collections.emptyList(), holdBulkMonitor.getBulkStatusesForHold("holdId")); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetBulkStatusesForHoldReturnsSortedStatuses() | ||||
|     { | ||||
|         HoldBulkStatus status1 = new HoldBulkStatus(null, new Date(1000), new Date(2000), 0L, 0L, 0L, null); | ||||
|         HoldBulkStatus status2 = new HoldBulkStatus(null, new Date(3000), null, 0L, 0L, 0L, null); | ||||
|         HoldBulkStatus status3 = new HoldBulkStatus(null, new Date(4000), null, 0L, 0L, 0L, null); | ||||
|         HoldBulkStatus status4 = new HoldBulkStatus(null, new Date(500), new Date(800), 0L, 0L, 0L, null); | ||||
|         HoldBulkStatus status5 = new HoldBulkStatus(null, null, null, 0L, 0L, 0L, null); | ||||
|  | ||||
|         when(holdProcessRegistry.get("holdId")).thenReturn( | ||||
|             Arrays.asList("process1", "process2", "process3", "process4", "process5") | ||||
|                 .stream().map(bulkStatusId -> new HoldBulkProcessDetails(bulkStatusId, null)).toList()); | ||||
|         when(holdProgressCache.get("process1")).thenReturn(status1); | ||||
|         when(holdProgressCache.get("process2")).thenReturn(status2); | ||||
|         when(holdProgressCache.get("process3")).thenReturn(status3); | ||||
|         when(holdProgressCache.get("process4")).thenReturn(status4); | ||||
|         when(holdProgressCache.get("process5")).thenReturn(status5); | ||||
|  | ||||
|         assertEquals(Arrays.asList(status5, status3, status2, status1, status4), | ||||
|             holdBulkMonitor.getBulkStatusesForHold("holdId")); | ||||
|     } | ||||
| } | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-governance-services-community-repo-parent</artifactId> | ||||
|         <version>23.3.0.19</version> | ||||
|         <version>23.3.0.38-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <build> | ||||
|   | ||||
| @@ -2314,6 +2314,112 @@ paths: | ||||
|           description: Unexpected error | ||||
|           schema: | ||||
|             $ref: '#/definitions/Error' | ||||
|   '/holds/{holdId}/bulk-statuses': | ||||
|     get: | ||||
|       tags: | ||||
|         - holds | ||||
|       operationId: listBulkStatuses | ||||
|       summary: Get bulk statuses | ||||
|       description: | | ||||
|         Gets bulk statuses for hold with id **holdId**. | ||||
|       parameters: | ||||
|         - $ref: '#/parameters/holdIdParam' | ||||
|         - $ref: '#/parameters/skipCountParam' | ||||
|         - $ref: '#/parameters/maxItemsParam' | ||||
|       responses: | ||||
|         '200': | ||||
|           description: Successful response | ||||
|           schema: | ||||
|             $ref: '#/definitions/HoldBulkStatusPaging' | ||||
|         '400': | ||||
|           description: | | ||||
|             Invalid parameter: **holdId** is not a valid format | ||||
|         '401': | ||||
|           description: Authentication failed | ||||
|         '403': | ||||
|           description: Current user does not have permission to read **holdId** | ||||
|         '404': | ||||
|           description: "**holdId** does not exist" | ||||
|         default: | ||||
|           description: Unexpected error | ||||
|           schema: | ||||
|             $ref: '#/definitions/Error' | ||||
|   '/holds/{holdId}/bulk-statuses/{bulkStatusId}': | ||||
|     get: | ||||
|       tags: | ||||
|         - holds | ||||
|       operationId: getBulkStatus | ||||
|       summary: Get a bulk status | ||||
|       description: | | ||||
|         Gets a bulk status specified by **bulkStatusId** for **holdId**. | ||||
|       parameters: | ||||
|         - $ref: '#/parameters/holdIdParam' | ||||
|         - $ref: '#/parameters/bulkStatusId' | ||||
|       responses: | ||||
|         '200': | ||||
|           description: Successful response | ||||
|           schema: | ||||
|             $ref: '#/definitions/HoldBulkStatus' | ||||
|         '400': | ||||
|           description: | | ||||
|             Invalid parameter: **holdId** or **bulkStatusId** is not a valid format | ||||
|         '401': | ||||
|           description: Authentication failed | ||||
|         '403': | ||||
|           description: Current user does not have permission to read **holdId** | ||||
|         '404': | ||||
|           description: "**holdId** or **bulkStatusId** does not exist" | ||||
|         default: | ||||
|           description: Unexpected error | ||||
|           schema: | ||||
|             $ref: '#/definitions/Error' | ||||
|   '/holds/{holdId}/bulk': | ||||
|     post: | ||||
|       tags: | ||||
|         - holds | ||||
|       operationId: startHoldBulkProcess | ||||
|       summary: Start the hold bulk process | ||||
|       description: | | ||||
|         Start the asynchronous bulk process for a hold with id **holdId** based on search query results. | ||||
|          | ||||
|         ```JSON | ||||
|         For example, the following JSON body starts the bulk process to add search query results | ||||
|         as children of a hold. | ||||
|          | ||||
|         { | ||||
|           "query": { | ||||
|             "query": "SITE:swsdp and TYPE:content", | ||||
|             "language": "afts" | ||||
|           }, | ||||
|           "op": "ADD" | ||||
|         } | ||||
|         ``` | ||||
|       parameters: | ||||
|         - $ref: '#/parameters/holdIdParam' | ||||
|         - in: body | ||||
|           name: holdBulkOperation | ||||
|           description: Bulk operation. | ||||
|           required: true | ||||
|           schema: | ||||
|             $ref: '#/definitions/HoldBulkOperation' | ||||
|       responses: | ||||
|         '202': | ||||
|           description: Successful response | ||||
|           schema: | ||||
|             $ref: '#/definitions/HoldBulkOperationEntry' | ||||
|         '400': | ||||
|           description: | | ||||
|             Invalid parameter: **holdId** is not a valid format or **HoldBulkOperation** is not valid | ||||
|         '401': | ||||
|           description: Authentication failed | ||||
|         '403': | ||||
|           description: Current user does not have permission to start the bulk process for **holdId** | ||||
|         '404': | ||||
|           description: "**holdId** does not exist" | ||||
|         default: | ||||
|           description: Unexpected error | ||||
|           schema: | ||||
|             $ref: '#/definitions/Error' | ||||
|   '/holds/{holdId}/delete': | ||||
|     post: | ||||
|       tags: | ||||
| @@ -2862,6 +2968,12 @@ parameters: | ||||
|     description: The identifier of a child of a hold. | ||||
|     required: true | ||||
|     type: string | ||||
|   bulkStatusId: | ||||
|     name: bulkStatusId | ||||
|     in: path | ||||
|     description: The identifier of a bulk process. | ||||
|     required: true | ||||
|     type: string | ||||
|   ## Record | ||||
|   recordIdParam: | ||||
|     name: recordId | ||||
| @@ -4018,6 +4130,91 @@ definitions: | ||||
|     properties: | ||||
|       reason: | ||||
|         type: string | ||||
|   SearchRequestQuery: | ||||
|     type: object | ||||
|     required: | ||||
|       - query | ||||
|     properties: | ||||
|       language: | ||||
|         description: The query language in which the query is written. | ||||
|         type: string | ||||
|         default: afts | ||||
|         enum: | ||||
|           - afts | ||||
|           - lucene | ||||
|           - cmis | ||||
|       userQuery: | ||||
|         description: The search request typed in by the user | ||||
|         type: string | ||||
|       query: | ||||
|         description: The query which may have been generated in some way from the userQuery | ||||
|         type: string | ||||
|   HoldBulkOperation: | ||||
|     type: object | ||||
|     properties: | ||||
|       query: | ||||
|         $ref: '#/definitions/SearchRequestQuery' | ||||
|       op: | ||||
|         description: The operation type. | ||||
|         type: string | ||||
|         default: ADD | ||||
|         enum: | ||||
|           - ADD | ||||
|   HoldBulkOperationEntry: | ||||
|     type: object | ||||
|     properties: | ||||
|       bulkStatusId: | ||||
|         type: string | ||||
|       totalItems: | ||||
|         type: integer | ||||
|         format: int64 | ||||
|   HoldBulkStatus: | ||||
|     type: object | ||||
|     properties: | ||||
|       bulkStatusId: | ||||
|         type: string | ||||
|       startTime: | ||||
|         type: string | ||||
|         format: date-time | ||||
|       endTime: | ||||
|         type: string | ||||
|         format: date-time | ||||
|       processedItems: | ||||
|         type: integer | ||||
|         format: int64 | ||||
|       errorsCount: | ||||
|         type: integer | ||||
|         format: int64 | ||||
|       totalItems: | ||||
|         type: integer | ||||
|         format: int64 | ||||
|       lastError: | ||||
|         type: string | ||||
|       status: | ||||
|         type: string | ||||
|         enum: | ||||
|           - PENDING | ||||
|           - IN PROGRESS | ||||
|           - DONE | ||||
|   HoldBulkStatusEntry: | ||||
|     type: object | ||||
|     required: | ||||
|       - entry | ||||
|     properties: | ||||
|       entry: | ||||
|         $ref: '#/definitions/HoldBulkStatus' | ||||
|   HoldBulkStatusPaging: | ||||
|     type: object | ||||
|     properties: | ||||
|       list: | ||||
|         type: object | ||||
|         properties: | ||||
|           pagination: | ||||
|             $ref: '#/definitions/Pagination' | ||||
|           entries: | ||||
|             type: array | ||||
|             items: | ||||
|               $ref: '#/definitions/HoldBulkStatusEntry' | ||||
|   ## | ||||
|   RequestBodyFile: | ||||
|     type: object | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo</artifactId> | ||||
|         <version>23.3.0.19</version> | ||||
|         <version>23.3.0.38-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <modules> | ||||
|   | ||||
| @@ -8,7 +8,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-amps</artifactId> | ||||
|         <version>23.3.0.19</version> | ||||
|         <version>23.3.0.38-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <properties> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-community-repo</artifactId> | ||||
|       <version>23.3.0.19</version> | ||||
|       <version>23.3.0.38-SNAPSHOT</version> | ||||
|    </parent> | ||||
|  | ||||
|    <dependencies> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo</artifactId> | ||||
|         <version>23.3.0.19</version> | ||||
|         <version>23.3.0.38-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <properties> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo</artifactId> | ||||
|         <version>23.3.0.19</version> | ||||
|         <version>23.3.0.38-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <dependencies> | ||||
|   | ||||
| @@ -9,6 +9,6 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-packaging</artifactId> | ||||
|         <version>23.3.0.19</version> | ||||
|         <version>23.3.0.38-SNAPSHOT</version> | ||||
|     </parent> | ||||
| </project> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-packaging</artifactId> | ||||
|         <version>23.3.0.19</version> | ||||
|         <version>23.3.0.38-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <properties> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo</artifactId> | ||||
|         <version>23.3.0.19</version> | ||||
|         <version>23.3.0.38-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <modules> | ||||
|   | ||||
| @@ -1,3 +1,3 @@ | ||||
| SOLR6_TAG=2.0.8.1 | ||||
| SOLR6_TAG=2.0.10 | ||||
| POSTGRES_TAG=15.4 | ||||
| ACTIVEMQ_TAG=5.18.3-jre17-rockylinux8 | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-packaging</artifactId> | ||||
|         <version>23.3.0.19</version> | ||||
|         <version>23.3.0.38-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <modules> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-tests</artifactId> | ||||
|         <version>23.3.0.19</version> | ||||
|         <version>23.3.0.38-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <organization> | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-tests</artifactId> | ||||
|         <version>23.3.0.19</version> | ||||
|         <version>23.3.0.38-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <developers> | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-tests</artifactId> | ||||
|         <version>23.3.0.19</version> | ||||
|         <version>23.3.0.38-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <developers> | ||||
|   | ||||
| @@ -8,7 +8,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-tests</artifactId> | ||||
|         <version>23.3.0.19</version> | ||||
|         <version>23.3.0.38-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <properties> | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-tests</artifactId> | ||||
|         <version>23.3.0.19</version> | ||||
|         <version>23.3.0.38-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <developers> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-packaging</artifactId> | ||||
|         <version>23.3.0.19</version> | ||||
|         <version>23.3.0.38-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <properties> | ||||
|   | ||||
							
								
								
									
										17
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								pom.xml
									
									
									
									
									
								
							| @@ -2,7 +2,7 @@ | ||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||||
|     <modelVersion>4.0.0</modelVersion> | ||||
|     <artifactId>alfresco-community-repo</artifactId> | ||||
|     <version>23.3.0.19</version> | ||||
|     <version>23.3.0.38-SNAPSHOT</version> | ||||
|     <packaging>pom</packaging> | ||||
|     <name>Alfresco Community Repo Parent</name> | ||||
|  | ||||
| @@ -51,13 +51,13 @@ | ||||
|         <dependency.alfresco-server-root.version>7.0.1</dependency.alfresco-server-root.version> | ||||
|         <dependency.activiti-engine.version>5.23.0</dependency.activiti-engine.version> | ||||
|         <dependency.activiti.version>5.23.0</dependency.activiti.version> | ||||
|         <dependency.alfresco-transform-core.version>5.1.1-A2</dependency.alfresco-transform-core.version> | ||||
|         <dependency.alfresco-transform-service.version>4.1.1-A2</dependency.alfresco-transform-service.version> | ||||
|         <dependency.alfresco-transform-core.version>5.1.2-A1</dependency.alfresco-transform-core.version> | ||||
|         <dependency.alfresco-transform-service.version>4.1.2-A1</dependency.alfresco-transform-service.version> | ||||
|         <dependency.alfresco-greenmail.version>7.0</dependency.alfresco-greenmail.version> | ||||
|         <dependency.acs-event-model.version>0.0.27</dependency.acs-event-model.version> | ||||
|  | ||||
|         <dependency.aspectj.version>1.9.20.1</dependency.aspectj.version> | ||||
|         <dependency.spring.version>6.0.17</dependency.spring.version> | ||||
|         <dependency.spring.version>6.0.19</dependency.spring.version> | ||||
|         <dependency.spring-security.version>6.2.2</dependency.spring-security.version> | ||||
|         <dependency.antlr.version>3.5.3</dependency.antlr.version> | ||||
|         <dependency.jackson.version>2.15.2</dependency.jackson.version> | ||||
| @@ -81,7 +81,7 @@ | ||||
|         <dependency.slf4j.version>2.0.9</dependency.slf4j.version> | ||||
|         <dependency.log4j.version>2.20.0</dependency.log4j.version> | ||||
|         <dependency.groovy.version>3.0.19</dependency.groovy.version> | ||||
|         <dependency.tika.version>2.9.1</dependency.tika.version> | ||||
|         <dependency.tika.version>2.9.2</dependency.tika.version> | ||||
|         <dependency.truezip.version>7.7.10</dependency.truezip.version> | ||||
|         <dependency.poi.version>5.2.5</dependency.poi.version> | ||||
|         <dependency.jboss.logging.version>3.5.0.Final</dependency.jboss.logging.version> | ||||
| @@ -95,7 +95,7 @@ | ||||
|         <dependency.maven-filtering.version>3.1.1</dependency.maven-filtering.version> | ||||
|         <dependency.maven-artifact.version>3.8.6</dependency.maven-artifact.version> | ||||
|         <dependency.jdom2.version>2.0.6.1</dependency.jdom2.version> | ||||
|         <dependency.pooled-jms.version>3.1.2</dependency.pooled-jms.version> | ||||
|         <dependency.pooled-jms.version>3.1.6</dependency.pooled-jms.version> | ||||
|  | ||||
|         <dependency.jakarta-ee-jaxb-api.version>4.0.0</dependency.jakarta-ee-jaxb-api.version> | ||||
|         <dependency.jakarta-ee-jaxb-impl.version>4.0.3</dependency.jakarta-ee-jaxb-impl.version> | ||||
| @@ -151,7 +151,7 @@ | ||||
|         <connection>scm:git:https://github.com/Alfresco/alfresco-community-repo.git</connection> | ||||
|         <developerConnection>scm:git:https://github.com/Alfresco/alfresco-community-repo.git</developerConnection> | ||||
|         <url>https://github.com/Alfresco/alfresco-community-repo</url> | ||||
|         <tag>23.3.0.19</tag> | ||||
|         <tag>HEAD</tag> | ||||
|     </scm> | ||||
|  | ||||
|     <distributionManagement> | ||||
| @@ -265,6 +265,7 @@ | ||||
|                 <version>${dependency.jakarta-ee-json-impl.version}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- This dependency was added to align dependency in the AI Rendition AMP [ACS-4844] --> | ||||
|             <dependency> | ||||
|                 <groupId>com.jayway.jsonpath</groupId> | ||||
|                 <artifactId>json-path</artifactId> | ||||
| @@ -410,7 +411,7 @@ | ||||
|             <dependency> | ||||
|                 <groupId>commons-codec</groupId> | ||||
|                 <artifactId>commons-codec</artifactId> | ||||
|                 <version>1.16.0</version> | ||||
|                 <version>1.17.0</version> | ||||
|             </dependency> | ||||
|             <dependency> | ||||
|                 <groupId>commons-lang</groupId> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo</artifactId> | ||||
|         <version>23.3.0.19</version> | ||||
|         <version>23.3.0.38-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <dependencies> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo</artifactId> | ||||
|         <version>23.3.0.19</version> | ||||
|         <version>23.3.0.38-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <dependencies> | ||||
|   | ||||
| @@ -53,8 +53,6 @@ import org.alfresco.service.cmr.transfer.TransferTarget; | ||||
| import org.alfresco.service.cmr.transfer.TransferVersion; | ||||
| import org.alfresco.util.HttpClientHelper; | ||||
| import org.alfresco.util.PropertyCheck; | ||||
| import org.alfresco.util.json.ExceptionJsonSerializer; | ||||
| import org.alfresco.util.json.JsonSerializer; | ||||
| import org.apache.commons.httpclient.Credentials; | ||||
| import org.apache.commons.httpclient.HostConfiguration; | ||||
| import org.apache.commons.httpclient.HttpClient; | ||||
| @@ -73,8 +71,10 @@ import org.apache.commons.httpclient.protocol.DefaultProtocolSocketFactory; | ||||
| import org.apache.commons.httpclient.protocol.Protocol; | ||||
| import org.apache.commons.httpclient.protocol.ProtocolSocketFactory; | ||||
| import org.apache.commons.httpclient.protocol.SSLProtocolSocketFactory; | ||||
| import org.apache.commons.lang3.StringUtils; | ||||
| import org.apache.commons.logging.Log; | ||||
| import org.apache.commons.logging.LogFactory; | ||||
| import org.json.JSONArray; | ||||
| import org.json.JSONObject; | ||||
|  | ||||
| /** | ||||
| @@ -102,7 +102,6 @@ public class HttpClientTransmitterImpl implements TransferTransmitter | ||||
|     private Protocol httpsProtocol = new Protocol(HTTPS_SCHEME_NAME, (ProtocolSocketFactory) new SSLProtocolSocketFactory(), DEFAULT_HTTPS_PORT); | ||||
|     private Map<String,Protocol> protocolMap = null; | ||||
|     private HttpMethodFactory httpMethodFactory = null; | ||||
|     private JsonSerializer<Throwable, JSONObject> jsonErrorSerializer; | ||||
|  | ||||
|     private ContentService contentService; | ||||
|  | ||||
| @@ -125,7 +124,6 @@ public class HttpClientTransmitterImpl implements TransferTransmitter | ||||
|         httpClient = new HttpClient(); | ||||
|         httpClient.setHttpConnectionManager(new MultiThreadedHttpConnectionManager()); | ||||
|         httpMethodFactory = new StandardHttpMethodFactoryImpl(); | ||||
|         jsonErrorSerializer = new ExceptionJsonSerializer(); | ||||
|  | ||||
|         // Create an HTTP Proxy Host if appropriate system properties are set | ||||
|         httpProxyHost = HttpClientHelper.createProxyHost("http.proxyHost", "http.proxyPort", DEFAULT_HTTP_PORT); | ||||
| @@ -852,7 +850,27 @@ public class HttpClientTransmitterImpl implements TransferTransmitter | ||||
|      */ | ||||
|     private Throwable rehydrateError(JSONObject errorJSON) | ||||
|     { | ||||
|         return jsonErrorSerializer.deserialize(errorJSON); | ||||
|         if (errorJSON == null) | ||||
|         { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         String errorMessage = errorJSON.optString("errorMessage", StringUtils.EMPTY); | ||||
|         String errorId = errorJSON.optString("alfrescoMessageId", null); | ||||
|  | ||||
|         Object[] errorParams = new Object[0]; | ||||
|         JSONArray errorParamArray = errorJSON.optJSONArray("alfrescoMessageParams"); | ||||
|         if (errorParamArray != null) | ||||
|         { | ||||
|             int length = errorParamArray.length(); | ||||
|             errorParams = new Object[length]; | ||||
|             for (int i = 0; i < length; ++i) | ||||
|             { | ||||
|                 errorParams[i] = errorParamArray.getString(i); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return new TransferException(errorId == null ? errorMessage : errorId, errorParams); | ||||
|     } | ||||
|  | ||||
|     public void setContentService(ContentService contentService) | ||||
| @@ -870,11 +888,6 @@ public class HttpClientTransmitterImpl implements TransferTransmitter | ||||
|         this.httpMethodFactory = httpMethodFactory; | ||||
|     } | ||||
|  | ||||
|     public void setJsonErrorSerializer(JsonSerializer<Throwable, JSONObject> jsonErrorSerializer) | ||||
|     { | ||||
|         this.jsonErrorSerializer = jsonErrorSerializer; | ||||
|     } | ||||
|  | ||||
|     public void setNodeService(NodeService nodeService) | ||||
|     { | ||||
|         this.nodeService = nodeService; | ||||
|   | ||||
| @@ -41,7 +41,8 @@ import org.json.JSONObject; | ||||
| public class ExceptionJsonSerializer implements JsonSerializer<Throwable, JSONObject> | ||||
| { | ||||
|     private final static Log log = LogFactory.getLog(ExceptionJsonSerializer.class); | ||||
|      | ||||
|  | ||||
|     @Deprecated | ||||
|     @Override | ||||
|     public Throwable deserialize(JSONObject errorJSON) | ||||
|     { | ||||
| @@ -89,38 +90,42 @@ public class ExceptionJsonSerializer implements JsonSerializer<Throwable, JSONOb | ||||
|             catch (ClassNotFoundException e) | ||||
|             { | ||||
|                 errorClass = Exception.class; | ||||
|             } | ||||
|             Constructor<?> constructor = null; | ||||
|             try | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     constructor = errorClass.getConstructor(String.class, Object[].class); | ||||
|                     createdObject = constructor.newInstance(errorId, errorParams); | ||||
|                 } | ||||
|                 catch (NoSuchMethodException e) | ||||
|                 { | ||||
|                     try | ||||
|                     { | ||||
|                         constructor = errorClass.getConstructor(String.class); | ||||
|                         createdObject = constructor.newInstance(errorId == null ? errorMessage : errorId); | ||||
|                     } | ||||
|                     catch (NoSuchMethodException e1) | ||||
|                     { | ||||
|                         try | ||||
|                         { | ||||
|                             constructor = errorClass.getConstructor(); | ||||
|                             createdObject = constructor.newInstance(); | ||||
|                         } | ||||
|                         catch (NoSuchMethodException e2) | ||||
|                         { | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             catch(Exception ex) | ||||
|             { | ||||
|                 //We don't need to do anything here. Code below will fix things up | ||||
|             } | ||||
|  | ||||
|             if (Throwable.class.isAssignableFrom(errorClass)) | ||||
|             { | ||||
|                 Constructor<?> constructor = null; | ||||
|                 try | ||||
|                 { | ||||
|                     try | ||||
|                     { | ||||
|                         constructor = errorClass.getConstructor(String.class, Object[].class); | ||||
|                         createdObject = constructor.newInstance(errorId, errorParams); | ||||
|                     } | ||||
|                     catch (NoSuchMethodException e) | ||||
|                     { | ||||
|                         try | ||||
|                         { | ||||
|                             constructor = errorClass.getConstructor(String.class); | ||||
|                             createdObject = constructor.newInstance(errorId == null ? errorMessage : errorId); | ||||
|                         } | ||||
|                         catch (NoSuchMethodException e1) | ||||
|                         { | ||||
|                             try | ||||
|                             { | ||||
|                                 constructor = errorClass.getConstructor(); | ||||
|                                 createdObject = constructor.newInstance(); | ||||
|                             } | ||||
|                             catch (NoSuchMethodException e2) | ||||
|                             { | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     // We don't need to do anything here. Code below will fix things up | ||||
|                 } | ||||
|             } | ||||
|             if (createdObject == null || !Throwable.class.isAssignableFrom(createdObject.getClass())) | ||||
|             { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user