diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4870b158ad..48c793de7f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -138,7 +138,7 @@ jobs: - 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 - - uses: Alfresco/ya-pmd-scan@v3.0.0 + - uses: Alfresco/ya-pmd-scan@v4.0.0 with: classpath-build-command: "mvn test-compile -ntp -Pags -pl \"-:alfresco-community-repo-docker\"" @@ -174,6 +174,7 @@ jobs: - name: "Init" run: bash ./scripts/ci/init.sh - name: "Prepare Report Portal" + if: github.ref_name == 'master' uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v5.1.0 id: rp-prepare with: @@ -181,24 +182,30 @@ jobs: rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} rp-project: ${{ env.RP_PROJECT }} rp-use-static-launch-name: true + continue-on-error: true - name: "Add GitHub Step Summary" + if: github.ref_name == 'master' env: RP_ENABLED: ${{ steps.rp-prepare.outputs.enabled }} RP_KEY: ${{ steps.rp-prepare.outputs.key }} RP_URL: ${{ steps.rp-prepare.outputs.url }} run: bash scripts/ci/add_step_summary.sh + continue-on-error: true - name: "Run tests" id: run-tests env: - RP_OPTS: ${{ steps.rp-prepare.outputs.mvn-opts }} + RP_OPTS: ${{ github.ref_name == 'master' && steps.rp-prepare.outputs.mvn-opts || '' }} run: | eval "args=($RP_OPTS)" mvn -B test -pl ${{ matrix.testModule }} -am ${{ matrix.testAttributes }} -DfailIfNoTests=false "${args[@]}" continue-on-error: true - name: "Update GitHub Step Summary" + if: github.ref_name == 'master' run: | echo "#### ⏱ After Tests: $(date -u +'%Y-%m-%d %H:%M:%S%:z')" >> $GITHUB_STEP_SUMMARY + continue-on-error: true - name: "Summarize Report Portal" + if: github.ref_name == 'master' uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v5.1.0 id: rp-summarize with: @@ -206,6 +213,7 @@ jobs: rp-launch-key: ${{ steps.rp-prepare.outputs.key }} rp-project: ${{ env.RP_PROJECT }} rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} + continue-on-error: true - name: "Exit on failure" if: steps.run-tests.outcome != 'success' run: | @@ -253,6 +261,7 @@ jobs: - name: "Set up the environment" run: docker compose -f ./scripts/ci/docker-compose/docker-compose.yaml --profile ${{ matrix.compose-profile }} up -d - name: "Prepare Report Portal" + if: github.ref_name == 'master' uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v5.1.0 id: rp-prepare with: @@ -260,24 +269,30 @@ jobs: rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} rp-project: ${{ env.RP_PROJECT }} rp-use-static-launch-name: true + continue-on-error: true - name: "Add GitHub Step Summary" + if: github.ref_name == 'master' env: RP_ENABLED: ${{ steps.rp-prepare.outputs.enabled }} RP_KEY: ${{ steps.rp-prepare.outputs.key }} RP_URL: ${{ steps.rp-prepare.outputs.url }} run: bash scripts/ci/add_step_summary.sh + continue-on-error: true - name: "Run tests" id: run-tests env: - RP_OPTS: ${{ steps.rp-prepare.outputs.mvn-opts }} + RP_OPTS: ${{ github.ref_name == 'master' && steps.rp-prepare.outputs.mvn-opts || '' }} run: | eval "args=($RP_OPTS)" mvn -B test -pl remote-api -Dtest=${{ matrix.testSuite }} -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco "${args[@]}" continue-on-error: true - name: "Update GitHub Step Summary" + if: github.ref_name == 'master' run: | echo "#### ⏱ After Tests: $(date -u +'%Y-%m-%d %H:%M:%S%:z')" >> $GITHUB_STEP_SUMMARY + continue-on-error: true - name: "Summarize Report Portal" + if: github.ref_name == 'master' uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v5.1.0 id: rp-summarize with: @@ -285,6 +300,7 @@ jobs: rp-launch-key: ${{ steps.rp-prepare.outputs.key }} rp-project: ${{ env.RP_PROJECT }} rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} + continue-on-error: true - name: "Exit on failure" if: steps.run-tests.outcome != 'success' run: | @@ -320,6 +336,7 @@ jobs: env: MARIADB_VERSION: ${{ matrix.version }} - name: "Prepare Report Portal" + if: github.ref_name == 'master' uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v5.1.0 id: rp-prepare with: @@ -327,24 +344,30 @@ jobs: rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} rp-project: ${{ env.RP_PROJECT }} rp-use-static-launch-name: true + continue-on-error: true - name: "Add GitHub Step Summary" + if: github.ref_name == 'master' env: RP_ENABLED: ${{ steps.rp-prepare.outputs.enabled }} RP_KEY: ${{ steps.rp-prepare.outputs.key }} RP_URL: ${{ steps.rp-prepare.outputs.url }} run: bash scripts/ci/add_step_summary.sh + continue-on-error: true - name: "Run tests" id: run-tests env: - RP_OPTS: ${{ steps.rp-prepare.outputs.mvn-opts }} + RP_OPTS: ${{ github.ref_name == 'master' && steps.rp-prepare.outputs.mvn-opts || '' }} run: | eval "args=($RP_OPTS)" mvn -B test -pl repository -am -Dtest=AllDBTestsTestSuite -DfailIfNoTests=false -Ddb.name=alfresco -Ddb.url=jdbc:mariadb://localhost:3307/alfresco?useUnicode=yes\&characterEncoding=UTF-8 -Ddb.username=alfresco -Ddb.password=alfresco -Ddb.driver=org.mariadb.jdbc.Driver "${args[@]}" continue-on-error: true - name: "Update GitHub Step Summary" + if: github.ref_name == 'master' run: | echo "#### ⏱ After Tests: $(date -u +'%Y-%m-%d %H:%M:%S%:z')" >> $GITHUB_STEP_SUMMARY + continue-on-error: true - name: "Summarize Report Portal" + if: github.ref_name == 'master' uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v5.1.0 id: rp-summarize with: @@ -352,6 +375,7 @@ jobs: rp-launch-key: ${{ steps.rp-prepare.outputs.key }} rp-project: ${{ env.RP_PROJECT }} rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} + continue-on-error: true - name: "Exit on failure" if: steps.run-tests.outcome != 'success' run: | @@ -383,6 +407,7 @@ jobs: env: MARIADB_VERSION: 10.6 - name: "Prepare Report Portal" + if: github.ref_name == 'master' uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v5.1.0 id: rp-prepare with: @@ -390,24 +415,30 @@ jobs: rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} rp-project: ${{ env.RP_PROJECT }} rp-use-static-launch-name: true + continue-on-error: true - name: "Add GitHub Step Summary" + if: github.ref_name == 'master' env: RP_ENABLED: ${{ steps.rp-prepare.outputs.enabled }} RP_KEY: ${{ steps.rp-prepare.outputs.key }} RP_URL: ${{ steps.rp-prepare.outputs.url }} run: bash scripts/ci/add_step_summary.sh + continue-on-error: true - name: "Run tests" id: run-tests env: - RP_OPTS: ${{ steps.rp-prepare.outputs.mvn-opts }} + RP_OPTS: ${{ github.ref_name == 'master' && steps.rp-prepare.outputs.mvn-opts || '' }} run: | eval "args=($RP_OPTS)" mvn -B test -pl repository -am -Dtest=AllDBTestsTestSuite -DfailIfNoTests=false -Ddb.name=alfresco -Ddb.url=jdbc:mariadb://localhost:3307/alfresco?useUnicode=yes\&characterEncoding=UTF-8 -Ddb.username=alfresco -Ddb.password=alfresco -Ddb.driver=org.mariadb.jdbc.Driver "${args[@]}" continue-on-error: true - name: "Update GitHub Step Summary" + if: github.ref_name == 'master' run: | echo "#### ⏱ After Tests: $(date -u +'%Y-%m-%d %H:%M:%S%:z')" >> $GITHUB_STEP_SUMMARY + continue-on-error: true - name: "Summarize Report Portal" + if: github.ref_name == 'master' uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v5.1.0 id: rp-summarize with: @@ -415,6 +446,7 @@ jobs: rp-launch-key: ${{ steps.rp-prepare.outputs.key }} rp-project: ${{ env.RP_PROJECT }} rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} + continue-on-error: true - name: "Exit on failure" if: steps.run-tests.outcome != 'success' run: | @@ -446,6 +478,7 @@ jobs: env: MYSQL_VERSION: 8 - name: "Prepare Report Portal" + if: github.ref_name == 'master' uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v5.1.0 id: rp-prepare with: @@ -453,24 +486,30 @@ jobs: rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} rp-project: ${{ env.RP_PROJECT }} rp-use-static-launch-name: true + continue-on-error: true - name: "Add GitHub Step Summary" + if: github.ref_name == 'master' env: RP_ENABLED: ${{ steps.rp-prepare.outputs.enabled }} RP_KEY: ${{ steps.rp-prepare.outputs.key }} RP_URL: ${{ steps.rp-prepare.outputs.url }} run: bash scripts/ci/add_step_summary.sh + continue-on-error: true - name: "Run tests" id: run-tests env: - RP_OPTS: ${{ steps.rp-prepare.outputs.mvn-opts }} + RP_OPTS: ${{ github.ref_name == 'master' && steps.rp-prepare.outputs.mvn-opts || '' }} run: | eval "args=($RP_OPTS)" mvn -B test -pl repository -am -Dtest=AllDBTestsTestSuite -DfailIfNoTests=false -Ddb.driver=com.mysql.jdbc.Driver -Ddb.name=alfresco -Ddb.url=jdbc:mysql://localhost:3307/alfresco -Ddb.username=alfresco -Ddb.password=alfresco "${args[@]}" continue-on-error: true - name: "Update GitHub Step Summary" + if: github.ref_name == 'master' run: | echo "#### ⏱ After Tests: $(date -u +'%Y-%m-%d %H:%M:%S%:z')" >> $GITHUB_STEP_SUMMARY + continue-on-error: true - name: "Summarize Report Portal" + if: github.ref_name == 'master' uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v5.1.0 id: rp-summarize with: @@ -478,6 +517,7 @@ jobs: rp-launch-key: ${{ steps.rp-prepare.outputs.key }} rp-project: ${{ env.RP_PROJECT }} rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} + continue-on-error: true - name: "Exit on failure" if: steps.run-tests.outcome != 'success' run: | @@ -508,6 +548,7 @@ jobs: env: POSTGRES_VERSION: 13.12 - name: "Prepare Report Portal" + if: github.ref_name == 'master' uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v5.1.0 id: rp-prepare with: @@ -515,24 +556,30 @@ jobs: rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} rp-project: ${{ env.RP_PROJECT }} rp-use-static-launch-name: true + continue-on-error: true - name: "Add GitHub Step Summary" + if: github.ref_name == 'master' env: RP_ENABLED: ${{ steps.rp-prepare.outputs.enabled }} RP_KEY: ${{ steps.rp-prepare.outputs.key }} RP_URL: ${{ steps.rp-prepare.outputs.url }} run: bash scripts/ci/add_step_summary.sh + continue-on-error: true - name: "Run tests" id: run-tests env: - RP_OPTS: ${{ steps.rp-prepare.outputs.mvn-opts }} + RP_OPTS: ${{ github.ref_name == 'master' && steps.rp-prepare.outputs.mvn-opts || '' }} run: | eval "args=($RP_OPTS)" mvn -B test -pl repository -am -Dtest=AllDBTestsTestSuite -DfailIfNoTests=false -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco "${args[@]}" continue-on-error: true - name: "Update GitHub Step Summary" + if: github.ref_name == 'master' run: | echo "#### ⏱ After Tests: $(date -u +'%Y-%m-%d %H:%M:%S%:z')" >> $GITHUB_STEP_SUMMARY + continue-on-error: true - name: "Summarize Report Portal" + if: github.ref_name == 'master' uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v5.1.0 id: rp-summarize with: @@ -540,6 +587,7 @@ jobs: rp-launch-key: ${{ steps.rp-prepare.outputs.key }} rp-project: ${{ env.RP_PROJECT }} rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} + continue-on-error: true - name: "Exit on failure" if: steps.run-tests.outcome != 'success' run: | @@ -570,6 +618,7 @@ jobs: env: POSTGRES_VERSION: 14.9 - name: "Prepare Report Portal" + if: github.ref_name == 'master' uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v5.1.0 id: rp-prepare with: @@ -577,24 +626,30 @@ jobs: rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} rp-project: ${{ env.RP_PROJECT }} rp-use-static-launch-name: true + continue-on-error: true - name: "Add GitHub Step Summary" + if: github.ref_name == 'master' env: RP_ENABLED: ${{ steps.rp-prepare.outputs.enabled }} RP_KEY: ${{ steps.rp-prepare.outputs.key }} RP_URL: ${{ steps.rp-prepare.outputs.url }} run: bash scripts/ci/add_step_summary.sh + continue-on-error: true - name: "Run tests" id: run-tests env: - RP_OPTS: ${{ steps.rp-prepare.outputs.mvn-opts }} + RP_OPTS: ${{ github.ref_name == 'master' && steps.rp-prepare.outputs.mvn-opts || '' }} run: | eval "args=($RP_OPTS)" mvn -B test -pl repository -am -Dtest=AllDBTestsTestSuite -DfailIfNoTests=false -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco "${args[@]}" continue-on-error: true - name: "Update GitHub Step Summary" + if: github.ref_name == 'master' run: | echo "#### ⏱ After Tests: $(date -u +'%Y-%m-%d %H:%M:%S%:z')" >> $GITHUB_STEP_SUMMARY + continue-on-error: true - name: "Summarize Report Portal" + if: github.ref_name == 'master' uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v5.1.0 id: rp-summarize with: @@ -602,6 +657,7 @@ jobs: rp-launch-key: ${{ steps.rp-prepare.outputs.key }} rp-project: ${{ env.RP_PROJECT }} rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} + continue-on-error: true - name: "Exit on failure" if: steps.run-tests.outcome != 'success' run: | @@ -632,6 +688,7 @@ jobs: env: POSTGRES_VERSION: 15.4 - name: "Prepare Report Portal" + if: github.ref_name == 'master' uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v5.1.0 id: rp-prepare with: @@ -639,24 +696,30 @@ jobs: rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} rp-project: ${{ env.RP_PROJECT }} rp-use-static-launch-name: true + continue-on-error: true - name: "Add GitHub Step Summary" + if: github.ref_name == 'master' env: RP_ENABLED: ${{ steps.rp-prepare.outputs.enabled }} RP_KEY: ${{ steps.rp-prepare.outputs.key }} RP_URL: ${{ steps.rp-prepare.outputs.url }} run: bash scripts/ci/add_step_summary.sh + continue-on-error: true - name: "Run tests" id: run-tests env: - RP_OPTS: ${{ steps.rp-prepare.outputs.mvn-opts }} + RP_OPTS: ${{ github.ref_name == 'master' && steps.rp-prepare.outputs.mvn-opts || '' }} run: | eval "args=($RP_OPTS)" mvn -B test -pl repository -am -Dtest=AllDBTestsTestSuite -DfailIfNoTests=false -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco "${args[@]}" continue-on-error: true - name: "Update GitHub Step Summary" + if: github.ref_name == 'master' run: | echo "#### ⏱ After Tests: $(date -u +'%Y-%m-%d %H:%M:%S%:z')" >> $GITHUB_STEP_SUMMARY + continue-on-error: true - name: "Summarize Report Portal" + if: github.ref_name == 'master' uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v5.1.0 id: rp-summarize with: @@ -664,6 +727,7 @@ jobs: rp-launch-key: ${{ steps.rp-prepare.outputs.key }} rp-project: ${{ env.RP_PROJECT }} rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} + continue-on-error: true - name: "Exit on failure" if: steps.run-tests.outcome != 'success' run: | @@ -690,6 +754,7 @@ jobs: - name: "Run ActiveMQ" run: docker compose -f ./scripts/ci/docker-compose/docker-compose.yaml --profile activemq up -d - name: "Prepare Report Portal" + if: github.ref_name == 'master' uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v5.13.1 id: rp-prepare with: @@ -697,24 +762,30 @@ jobs: rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} rp-project: ${{ env.RP_PROJECT }} rp-use-static-launch-name: true + continue-on-error: true - name: "Add GitHub Step Summary" + if: github.ref_name == 'master' env: RP_ENABLED: ${{ steps.rp-prepare.outputs.enabled }} RP_KEY: ${{ steps.rp-prepare.outputs.key }} RP_URL: ${{ steps.rp-prepare.outputs.url }} run: bash scripts/ci/add_step_summary.sh + continue-on-error: true - name: "Run tests" id: run-tests env: - RP_OPTS: ${{ steps.rp-prepare.outputs.mvn-opts }} + RP_OPTS: ${{ github.ref_name == 'master' && steps.rp-prepare.outputs.mvn-opts || '' }} run: | eval "args=($RP_OPTS)" mvn -B test -pl repository -am -Dtest=MessagingUnitTestSuite -DfailIfNoTests=false "${args[@]}" continue-on-error: true - name: "Update GitHub Step Summary" + if: github.ref_name == 'master' run: | echo "#### ⏱ After Tests: $(date -u +'%Y-%m-%d %H:%M:%S%:z')" >> $GITHUB_STEP_SUMMARY + continue-on-error: true - name: "Summarize Report Portal" + if: github.ref_name == 'master' uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v5.13.1 id: rp-summarize with: @@ -722,6 +793,7 @@ jobs: rp-launch-key: ${{ steps.rp-prepare.outputs.key }} rp-project: ${{ env.RP_PROJECT }} rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} + continue-on-error: true - name: "Exit on failure" if: steps.run-tests.outcome != 'success' run: | @@ -797,6 +869,7 @@ jobs: - name: "Set up the environment" run: docker compose -f ./scripts/ci/docker-compose/docker-compose.yaml --profile ${{ matrix.compose-profile }} up -d - name: "Prepare Report Portal" + if: github.ref_name == 'master' uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v5.1.0 id: rp-prepare with: @@ -804,24 +877,30 @@ jobs: rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} rp-project: ${{ env.RP_PROJECT }} rp-use-static-launch-name: true + continue-on-error: true - name: "Add GitHub Step Summary" + if: github.ref_name == 'master' env: RP_ENABLED: ${{ steps.rp-prepare.outputs.enabled }} RP_KEY: ${{ steps.rp-prepare.outputs.key }} RP_URL: ${{ steps.rp-prepare.outputs.url }} run: bash scripts/ci/add_step_summary.sh + continue-on-error: true - name: "Run tests" id: run-tests env: - RP_OPTS: ${{ steps.rp-prepare.outputs.mvn-opts }} + RP_OPTS: ${{ github.ref_name == 'master' && steps.rp-prepare.outputs.mvn-opts || '' }} run: | eval "args=($RP_OPTS)" mvn -B test -pl repository -am -Dtest=${{ matrix.testSuite }} -DfailIfNoTests=false -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco ${{ matrix.mvn-options }} "${args[@]}" continue-on-error: true - name: "Update GitHub Step Summary" + if: github.ref_name == 'master' run: | echo "#### ⏱ After Tests: $(date -u +'%Y-%m-%d %H:%M:%S%:z')" >> $GITHUB_STEP_SUMMARY + continue-on-error: true - name: "Summarize Report Portal" + if: github.ref_name == 'master' uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v5.1.0 id: rp-summarize with: @@ -829,6 +908,7 @@ jobs: rp-launch-key: ${{ steps.rp-prepare.outputs.key }} rp-project: ${{ env.RP_PROJECT }} rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} + continue-on-error: true - name: "Exit on failure" if: steps.run-tests.outcome != 'success' run: | @@ -895,6 +975,7 @@ jobs: if: ${{ matrix.test-name }} == 'Integration TAS tests' run: mvn install -pl :alfresco-community-repo-integration-test -am -DskipTests -Pall-tas-tests - name: "Prepare Report Portal" + if: github.ref_name == 'master' uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v5.1.0 id: rp-prepare with: @@ -902,16 +983,19 @@ jobs: rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} rp-project: ${{ env.RP_PROJECT }} rp-use-static-launch-name: true + continue-on-error: true - name: "Add GitHub Step Summary" + if: github.ref_name == 'master' env: RP_ENABLED: ${{ steps.rp-prepare.outputs.enabled }} RP_KEY: ${{ steps.rp-prepare.outputs.key }} RP_URL: ${{ steps.rp-prepare.outputs.url }} run: bash scripts/ci/add_step_summary.sh + continue-on-error: true - name: "Run tests" id: tests env: - RP_OPTS: ${{ steps.rp-prepare.outputs.mvn-opts }} + RP_OPTS: ${{ github.ref_name == 'master' && steps.rp-prepare.outputs.mvn-opts || '' }} timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }} run: | eval "args=($RP_OPTS)" @@ -924,9 +1008,12 @@ jobs: if: ${{ always() && steps.tests.outcome == 'failure' }} run: ${TAS_SCRIPTS}/output_logs_for_failures.sh "packaging/tests/${{ matrix.pom-dir }}" - name: "Update GitHub Step Summary" + if: github.ref_name == 'master' run: | echo "#### ⏱ After Tests: $(date -u +'%Y-%m-%d %H:%M:%S%:z')" >> $GITHUB_STEP_SUMMARY + continue-on-error: true - name: "Summarize Report Portal" + if: github.ref_name == 'master' uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v5.1.0 id: rp-summarize with: @@ -934,6 +1021,7 @@ jobs: rp-launch-key: ${{ steps.rp-prepare.outputs.key }} rp-project: ${{ env.RP_PROJECT }} rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} + continue-on-error: true - name: "Exit on failure" if: steps.tests.outcome != 'success' run: | @@ -960,6 +1048,7 @@ jobs: - name: "Run Postgres 15.4 database" run: docker compose -f ./scripts/ci/docker-compose/docker-compose.yaml --profile postgres up -d - name: "Prepare Report Portal" + if: github.ref_name == 'master' uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v5.1.0 id: rp-prepare with: @@ -967,24 +1056,30 @@ jobs: rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} rp-project: ${{ env.RP_PROJECT }} rp-use-static-launch-name: true + continue-on-error: true - name: "Add GitHub Step Summary" + if: github.ref_name == 'master' env: RP_ENABLED: ${{ steps.rp-prepare.outputs.enabled }} RP_KEY: ${{ steps.rp-prepare.outputs.key }} RP_URL: ${{ steps.rp-prepare.outputs.url }} run: bash scripts/ci/add_step_summary.sh + continue-on-error: true - name: "Run tests" id: run-tests env: - RP_OPTS: ${{ steps.rp-prepare.outputs.mvn-opts }} + RP_OPTS: ${{ github.ref_name == 'master' && steps.rp-prepare.outputs.mvn-opts || '' }} run: | eval "args=($RP_OPTS)" mvn -B test -pl :alfresco-share-services -am -Dtest=ShareServicesTestSuite -DfailIfNoTests=false -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco "${args[@]}" continue-on-error: true - name: "Update GitHub Step Summary" + if: github.ref_name == 'master' run: | echo "#### ⏱ After Tests: $(date -u +'%Y-%m-%d %H:%M:%S%:z')" >> $GITHUB_STEP_SUMMARY + continue-on-error: true - name: "Summarize Report Portal" + if: github.ref_name == 'master' uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v5.1.0 id: rp-summarize with: @@ -992,6 +1087,7 @@ jobs: rp-launch-key: ${{ steps.rp-prepare.outputs.key }} rp-project: ${{ env.RP_PROJECT }} rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} + continue-on-error: true - name: "Exit on failure" if: steps.run-tests.outcome != 'success' run: | @@ -1027,6 +1123,7 @@ jobs: bash ./scripts/ci/init.sh bash ./scripts/ci/build.sh - name: "Prepare Report Portal" + if: github.ref_name == 'master' uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v5.1.0 id: rp-prepare with: @@ -1034,10 +1131,11 @@ jobs: rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} rp-project: ${{ env.RP_PROJECT }} rp-use-static-launch-name: true + continue-on-error: true - name: "Verify" timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }} env: - RP_OPTS: ${{ steps.rp-prepare.outputs.mvn-opts }} + RP_OPTS: ${{ github.ref_name == 'master' && 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-postgres -PagsAllTestSuitePt${{ matrix.part }} ${{ env.LOG_WARN }} "${args[@]}" @@ -1071,6 +1169,7 @@ jobs: bash ./scripts/ci/init.sh bash ./scripts/ci/build.sh - name: "Prepare Report Portal" + if: github.ref_name == 'master' uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v5.1.0 id: rp-prepare with: @@ -1078,10 +1177,11 @@ jobs: rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} rp-project: ${{ env.RP_PROJECT }} rp-use-static-launch-name: true + continue-on-error: true - name: "Verify" timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }} env: - RP_OPTS: ${{ steps.rp-prepare.outputs.mvn-opts }} + RP_OPTS: ${{ github.ref_name == 'master' && 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[@]}" @@ -1117,6 +1217,7 @@ jobs: ${{ env.TAS_SCRIPTS }}/wait-for-alfresco-start.sh "http://localhost:8080/alfresco" mvn -B install -pl :alfresco-governance-services-automation-community-rest-api -am -Pags -Pall-tas-tests -DskipTests - name: "Prepare Report Portal" + if: github.ref_name == 'master' uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v5.1.0 id: rp-prepare with: @@ -1124,25 +1225,31 @@ jobs: rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} rp-project: ${{ env.RP_PROJECT }} rp-use-static-launch-name: true + continue-on-error: true - name: "Add GitHub Step Summary" + if: github.ref_name == 'master' env: RP_ENABLED: ${{ steps.rp-prepare.outputs.enabled }} RP_KEY: ${{ steps.rp-prepare.outputs.key }} RP_URL: ${{ steps.rp-prepare.outputs.url }} run: bash scripts/ci/add_step_summary.sh + continue-on-error: true - name: "Test" id: run-tests timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }} env: - RP_OPTS: ${{ steps.rp-prepare.outputs.mvn-opts }} + RP_OPTS: ${{ github.ref_name == 'master' && steps.rp-prepare.outputs.mvn-opts || '' }} run: | eval "args=($RP_OPTS)" mvn -B test -pl :alfresco-governance-services-automation-community-rest-api -Dskip.automationtests=false -Pags -Pall-tas-tests "${args[@]}" continue-on-error: true - name: "Update GitHub Step Summary" + if: github.ref_name == 'master' run: | echo "#### ⏱ After Tests: $(date -u +'%Y-%m-%d %H:%M:%S%:z')" >> $GITHUB_STEP_SUMMARY + continue-on-error: true - name: "Summarize Report Portal" + if: github.ref_name == 'master' uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v5.1.0 id: rp-summarize with: @@ -1150,6 +1257,7 @@ jobs: rp-launch-key: ${{ steps.rp-prepare.outputs.key }} rp-project: ${{ env.RP_PROJECT }} rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} + continue-on-error: true - name: "Exit on failure" if: steps.run-tests.outcome != 'success' run: | diff --git a/amps/ags/pom.xml b/amps/ags/pom.xml index da604dde9f..154d14bacc 100644 --- a/amps/ags/pom.xml +++ b/amps/ags/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-community-repo-amps - 23.3.0.63-SNAPSHOT + 23.3.0.86-SNAPSHOT diff --git a/amps/ags/rm-automation/pom.xml b/amps/ags/rm-automation/pom.xml index 369f16ff48..2b99744168 100644 --- a/amps/ags/rm-automation/pom.xml +++ b/amps/ags/rm-automation/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-governance-services-community-parent - 23.3.0.63-SNAPSHOT + 23.3.0.86-SNAPSHOT diff --git a/amps/ags/rm-automation/rm-automation-community-rest-api/pom.xml b/amps/ags/rm-automation/rm-automation-community-rest-api/pom.xml index d0a21c4cb0..b9eec43068 100644 --- a/amps/ags/rm-automation/rm-automation-community-rest-api/pom.xml +++ b/amps/ags/rm-automation/rm-automation-community-rest-api/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-governance-services-automation-community-repo - 23.3.0.63-SNAPSHOT + 23.3.0.86-SNAPSHOT @@ -98,7 +98,7 @@ com.github.docker-java docker-java - 3.3.2 + 3.3.6 org.bouncycastle diff --git a/amps/ags/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/retentionschedule/RetentionScheduleActionDefinition.java b/amps/ags/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/retentionschedule/RetentionScheduleActionDefinition.java index 0c79e9f543..e743e904de 100644 --- a/amps/ags/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/retentionschedule/RetentionScheduleActionDefinition.java +++ b/amps/ags/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/retentionschedule/RetentionScheduleActionDefinition.java @@ -40,7 +40,7 @@ public class RetentionScheduleActionDefinition private int periodAmount; private String period; private String periodProperty; - private boolean combineDispositionStepConditions; + private boolean combineRetentionStepConditions; private List events; private boolean eligibleOnFirstCompleteEvent; private String description; diff --git a/amps/ags/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/retentionschedule/RetentionScheduleStepTests.java b/amps/ags/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/retentionschedule/RetentionScheduleStepTests.java index 60359e6f74..060c22fae6 100644 --- a/amps/ags/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/retentionschedule/RetentionScheduleStepTests.java +++ b/amps/ags/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/retentionschedule/RetentionScheduleStepTests.java @@ -79,7 +79,7 @@ public class RetentionScheduleStepTests extends BaseRMRestTest private static final String RETAIN_STEP = "retain"; private static final String INVALID_PERIOD = "random"; private static final String CUTOFF_STEP = "cutoff"; - private static final String DESTROY_STEP = "destroy"; + private static final String DESTROY_STEP = "destroyContent"; private static final String INVALID_PASSWORD = "wrongPassword"; @Autowired @@ -107,7 +107,7 @@ public class RetentionScheduleStepTests extends BaseRMRestTest retentionScheduleActionDefinition.setPeriodAmount(PERIOD_AMOUNT); retentionScheduleActionDefinition.setPeriodProperty(PERIOD_PROPERTY); retentionScheduleActionDefinition.setPeriod(PERIOD); - retentionScheduleActionDefinition.setCombineDispositionStepConditions(false); + retentionScheduleActionDefinition.setCombineRetentionStepConditions(false); retentionScheduleActionDefinition.setEligibleOnFirstCompleteEvent(true); retentionScheduleActionDefinition.setEvents(EVENTS); } @@ -118,6 +118,7 @@ public class RetentionScheduleStepTests extends BaseRMRestTest RetentionScheduleActionDefinition actionDefinition = getRetentionScheduleActionDefinition(); //Creating the first action "transfer" should give 422 actionDefinition.setName(TRANSFER_STEP); + actionDefinition.setLocation("location"); //Create retention schedule action definition getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(actionDefinition,createdRetentionSchedule.getId()); // Verify the status code @@ -151,6 +152,7 @@ public class RetentionScheduleStepTests extends BaseRMRestTest RetentionScheduleActionDefinition actionDefinition1 = getRetentionScheduleActionDefinition(); actionDefinition1.setName(TRANSFER_STEP); + actionDefinition1.setLocation("location"); getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(actionDefinition1,retentionSchedule.getId()); assertStatusCode(CREATED); @@ -190,6 +192,20 @@ public class RetentionScheduleStepTests extends BaseRMRestTest } @Test(priority = 5) + public void combineRetentionStepConditionsNotValidForNonAccessionStep() + { + RecordCategory recordCategory = createRootCategory(getRandomName(RECORD_CATEGORY)); + recordCategories.add(recordCategory.getId()); + RetentionSchedule retentionSchedule = createRetentionSchedule(recordCategory); + RetentionScheduleActionDefinition actionDefinition = getRetentionScheduleActionDefinition(); + actionDefinition.setName(RETAIN_STEP); + actionDefinition.setCombineRetentionStepConditions(true); + getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(actionDefinition,retentionSchedule.getId()); + + assertStatusCode(BAD_REQUEST); + } + + @Test(priority = 6) public void createRetentionScheduleWithSameStep() { RecordCategory recordCategory = createRootCategory(getRandomName(RECORD_CATEGORY)); @@ -209,7 +225,7 @@ public class RetentionScheduleStepTests extends BaseRMRestTest assertStatusCode(CONFLICT); } - @Test(priority = 6) + @Test(priority = 7) public void createRetentionScheduleWithMultipleTransferStep() { RecordCategory recordCategory = createRootCategory(getRandomName(RECORD_CATEGORY)); @@ -223,16 +239,19 @@ public class RetentionScheduleStepTests extends BaseRMRestTest RetentionScheduleActionDefinition actionDefinition1 = getRetentionScheduleActionDefinition(); actionDefinition1.setName(TRANSFER_STEP); + actionDefinition1.setLocation("location"); + getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(actionDefinition1, retentionSchedule.getId()); RetentionScheduleActionDefinition actionDefinition2 = getRetentionScheduleActionDefinition(); actionDefinition2.setName(TRANSFER_STEP); + actionDefinition2.setLocation("location"); getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(actionDefinition2, retentionSchedule.getId()); // Verify the status code assertStatusCode(CREATED); } - @Test(priority = 7) + @Test(priority = 8) public void createRetentionScheduleStepFor201() { //Create retention schedule action definition @@ -243,11 +262,11 @@ public class RetentionScheduleStepTests extends BaseRMRestTest assertEquals(createdRetentionActionDefinition.getName(), retentionScheduleActionDefinition.getName()); assertEquals(createdRetentionActionDefinition.getDescription(), retentionScheduleActionDefinition.getDescription()); assertEquals(createdRetentionActionDefinition.getPeriodAmount(), retentionScheduleActionDefinition.getPeriodAmount()); - assertEquals(createdRetentionActionDefinition.isCombineDispositionStepConditions(), retentionScheduleActionDefinition.isCombineDispositionStepConditions()); + assertEquals(createdRetentionActionDefinition.isCombineRetentionStepConditions(), retentionScheduleActionDefinition.isCombineRetentionStepConditions()); assertEquals(createdRetentionActionDefinition.isEligibleOnFirstCompleteEvent(), retentionScheduleActionDefinition.isEligibleOnFirstCompleteEvent()); } - @Test(priority = 8) + @Test(priority = 9) public void createRetentionScheduleStepFor401() { //Create retention schedule action definition @@ -256,7 +275,7 @@ public class RetentionScheduleStepTests extends BaseRMRestTest assertStatusCode(UNAUTHORIZED); } - @Test(priority = 9) + @Test(priority = 10) public void createRetentionScheduleStepFor403() { //Create retention schedule action definition @@ -265,7 +284,7 @@ public class RetentionScheduleStepTests extends BaseRMRestTest assertStatusCode(FORBIDDEN); } - @Test(priority = 10) + @Test(priority = 11) public void retentionScheduleStepFor400() { getRestAPIFactory().getRetentionScheduleAPI().getRetentionScheduleStep(recordCategory.getId()); @@ -273,7 +292,7 @@ public class RetentionScheduleStepTests extends BaseRMRestTest assertStatusCode(BAD_REQUEST); } - @Test(priority = 11) + @Test(priority = 12) public void createRetentionScheduleStepFor404() { //Create retention schedule action definition @@ -282,7 +301,7 @@ public class RetentionScheduleStepTests extends BaseRMRestTest assertStatusCode(NOT_FOUND); } - @Test(priority = 12) + @Test(priority = 13) public void retentionScheduleStepFor403() { // Get retention schedule steps with user having no rights @@ -291,7 +310,7 @@ public class RetentionScheduleStepTests extends BaseRMRestTest assertStatusCode(FORBIDDEN); } - @Test(priority = 13) + @Test(priority = 14) public void retentionScheduleStepFor401() { getRestAPIFactory().getRetentionScheduleAPI(new UserModel(getAdminUser().getUsername(), INVALID_PASSWORD)).getRetentionScheduleStep(createdRetentionSchedule.getId()); @@ -299,7 +318,7 @@ public class RetentionScheduleStepTests extends BaseRMRestTest assertStatusCode(UNAUTHORIZED); } - @Test(priority = 14) + @Test(priority = 15) public void retentionScheduleStepWith200() { RetentionScheduleStepCollection receiveRetentionStepCollection = getRestAPIFactory().getRetentionScheduleAPI().getRetentionScheduleStep(createdRetentionSchedule.getId()); @@ -315,7 +334,7 @@ public class RetentionScheduleStepTests extends BaseRMRestTest assertEquals(createdRetentionActionDefinition.getDescription(), retentionActionDef.getDescription()); assertEquals(createdRetentionActionDefinition.getPeriod(), retentionActionDef.getPeriod()); assertEquals(createdRetentionActionDefinition.getPeriodAmount(), retentionActionDef.getPeriodAmount()); - assertEquals(createdRetentionActionDefinition.isCombineDispositionStepConditions(), retentionActionDef.isCombineDispositionStepConditions()); + assertEquals(createdRetentionActionDefinition.isCombineRetentionStepConditions(), retentionActionDef.isCombineRetentionStepConditions()); assertEquals(createdRetentionActionDefinition.isEligibleOnFirstCompleteEvent(), retentionActionDef.isEligibleOnFirstCompleteEvent()); }); } @@ -341,7 +360,7 @@ public class RetentionScheduleStepTests extends BaseRMRestTest actionDefinition.setPeriodAmount(PERIOD_AMOUNT); actionDefinition.setPeriodProperty(PERIOD_PROPERTY); actionDefinition.setPeriod(PERIOD); - actionDefinition.setCombineDispositionStepConditions(false); + actionDefinition.setCombineRetentionStepConditions(false); actionDefinition.setEligibleOnFirstCompleteEvent(true); actionDefinition.setEvents(EVENTS); return actionDefinition; diff --git a/amps/ags/rm-community/pom.xml b/amps/ags/rm-community/pom.xml index 7d13b2a3c8..3590017280 100644 --- a/amps/ags/rm-community/pom.xml +++ b/amps/ags/rm-community/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-governance-services-community-parent - 23.3.0.63-SNAPSHOT + 23.3.0.86-SNAPSHOT diff --git a/amps/ags/rm-community/rm-community-repo/.env b/amps/ags/rm-community/rm-community-repo/.env index dfa05f01bd..c528c95c4a 100644 --- a/amps/ags/rm-community/rm-community-repo/.env +++ b/amps/ags/rm-community/rm-community-repo/.env @@ -1,3 +1,3 @@ -SOLR6_TAG=2.0.11-A6 +SOLR6_TAG=2.0.11 POSTGRES_TAG=15.4 ACTIVEMQ_TAG=5.18.3-jre17-rockylinux8 diff --git a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-recordfolder-context.xml b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-recordfolder-context.xml index 97f9aee406..5cfeda829e 100644 --- a/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-recordfolder-context.xml +++ b/amps/ags/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/capability/rm-capabilities-recordfolder-context.xml @@ -125,7 +125,7 @@ parent="declarativeCapability"> - + RECORD_FOLDER diff --git a/amps/ags/rm-community/rm-community-repo/pom.xml b/amps/ags/rm-community/rm-community-repo/pom.xml index 37c330acea..d8d84dc294 100644 --- a/amps/ags/rm-community/rm-community-repo/pom.xml +++ b/amps/ags/rm-community/rm-community-repo/pom.xml @@ -8,7 +8,7 @@ org.alfresco alfresco-governance-services-community-repo-parent - 23.3.0.63-SNAPSHOT + 23.3.0.86-SNAPSHOT diff --git a/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/ApiNodesModelFactory.java b/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/ApiNodesModelFactory.java index b392993023..9260cde66b 100644 --- a/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/ApiNodesModelFactory.java +++ b/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/ApiNodesModelFactory.java @@ -939,7 +939,7 @@ public class ApiNodesModelFactory { RetentionSchedule retentionSchedule = new RetentionSchedule(); retentionSchedule.setId(dispositionSchedule.getNodeRef().getId()); - if(dispositionSchedule.getNodeRef() != null) { + if (dispositionSchedule.getNodeRef() != null) { NodeRef parent = this.nodeService.getPrimaryParent(dispositionSchedule.getNodeRef()).getParentRef(); retentionSchedule.setParentId(parent.getId()); } @@ -984,7 +984,7 @@ public class ApiNodesModelFactory retentionScheduleActionDefinition.setEligibleOnFirstCompleteEvent(dispositionActionDefinition.eligibleOnFirstCompleteEvent()); if (nodeService.getProperty(dispositionActionDefinition.getNodeRef(), RecordsManagementModel.PROP_COMBINE_DISPOSITION_STEP_CONDITIONS) != null) { - retentionScheduleActionDefinition.setCombineDispositionStepConditions((Boolean) nodeService.getProperty(dispositionActionDefinition.getNodeRef(), RecordsManagementModel.PROP_COMBINE_DISPOSITION_STEP_CONDITIONS)); + retentionScheduleActionDefinition.setCombineRetentionStepConditions((Boolean) nodeService.getProperty(dispositionActionDefinition.getNodeRef(), RecordsManagementModel.PROP_COMBINE_DISPOSITION_STEP_CONDITIONS)); } retentionScheduleActionDefinition.setLocation(dispositionActionDefinition.getLocation()); if (dispositionActionDefinition.getGhostOnDestroy() != null) @@ -1001,7 +1001,7 @@ public class ApiNodesModelFactory */ private void mapPeriodProperties(DispositionActionDefinition dispositionActionDefinition, RetentionScheduleActionDefinition retentionScheduleActionDefinition) { - if(dispositionActionDefinition.getPeriodProperty() != null) + if (dispositionActionDefinition.getPeriodProperty() != null) { retentionScheduleActionDefinition.setPeriodProperty(dispositionActionDefinition.getPeriodProperty().toPrefixString(namespaceService)); } @@ -1078,10 +1078,20 @@ public class ApiNodesModelFactory public Map createRetentionActionDefinitionParams(RetentionScheduleActionDefinition nodeInfo) { Map actionDefinitionParams= new HashMap<>(); - actionDefinitionParams.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME, nodeInfo.getName()); + + String retentionActionName = nodeInfo.getName(); + + if (nodeInfo.getName().equals(RetentionSteps.DESTROY_NODE.stepName) || + nodeInfo.getName().equals(RetentionSteps.DESTROY_CONTENT.stepName)) + { + retentionActionName = "destroy"; + } + + actionDefinitionParams.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME, retentionActionName); actionDefinitionParams.put(RecordsManagementModel.PROP_DISPOSITION_DESCRIPTION, nodeInfo.getDescription()); StringBuilder retentionPeriod = new StringBuilder(nodeInfo.getPeriod()).append("|"); - if(isPeriodAmountApplicable(nodeInfo.getPeriod())) + + if (isPeriodAmountApplicable(nodeInfo.getPeriod())) { retentionPeriod.append(nodeInfo.getPeriodAmount()); } @@ -1090,17 +1100,22 @@ public class ApiNodesModelFactory actionDefinitionParams.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD_PROPERTY, periodProperty); actionDefinitionParams.put(RecordsManagementModel.PROP_DISPOSITION_EVENT_COMBINATION, nodeInfo.isEligibleOnFirstCompleteEvent()); - actionDefinitionParams.put(RecordsManagementModel.PROP_COMBINE_DISPOSITION_STEP_CONDITIONS, - nodeInfo.isCombineDispositionStepConditions()); - actionDefinitionParams.put(RecordsManagementModel.PROP_DISPOSITION_LOCATION, - nodeInfo.getLocation()); + boolean combineConditions = nodeInfo.getName().equals(RetentionSteps.ACCESSION.stepName) && nodeInfo.isCombineRetentionStepConditions(); + actionDefinitionParams.put(RecordsManagementModel.PROP_COMBINE_DISPOSITION_STEP_CONDITIONS, combineConditions); + + if(nodeInfo.getLocation() != null && nodeInfo.getName().equals(RetentionSteps.TRANSFER.stepName)) + { + actionDefinitionParams.put(RecordsManagementModel.PROP_DISPOSITION_LOCATION, + nodeInfo.getLocation()); + } List inputEvents = nodeInfo.getEvents(); actionDefinitionParams.put(RecordsManagementModel.PROP_DISPOSITION_EVENT, (Serializable) inputEvents); - if (RetentionSteps.DESTROY.stepName.equals(nodeInfo.getName()) && nodeInfo.isRetainRecordMetadataAfterDestruction()) + + if (RetentionSteps.DESTROY_CONTENT.stepName.equals(nodeInfo.getName())) { actionDefinitionParams.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_GHOST_ON_DESTROY, "ghost"); } - else + else if (RetentionSteps.DESTROY_NODE.stepName.equals(nodeInfo.getName())) { actionDefinitionParams.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_GHOST_ON_DESTROY, "delete"); } diff --git a/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/SearchTypesFactory.java b/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/SearchTypesFactory.java index 1989162c6b..e8a1f41a9e 100644 --- a/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/SearchTypesFactory.java +++ b/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/impl/SearchTypesFactory.java @@ -92,7 +92,7 @@ public class SearchTypesFactory boolean includeRecords = false; boolean includeSubTypes = false; - if (q != null) + if (q != null && q.getTree() != null) { // filtering via "where" clause MapBasedQueryWalker propertyWalker = new MapBasedQueryWalker(listFolderChildrenEqualsQueryProperties, null); @@ -101,11 +101,11 @@ public class SearchTypesFactory Boolean isUnfiledRecordFolder = propertyWalker.getProperty(UnfiledChild.PARAM_IS_UNFILED_RECORD_FOLDER, WhereClauseParser.EQUALS, Boolean.class); Boolean isRecord = propertyWalker.getProperty(UnfiledChild.PARAM_IS_RECORD, WhereClauseParser.EQUALS, Boolean.class); - if ((isUnfiledRecordFolder != null && isUnfiledRecordFolder.booleanValue()) || (isRecord != null && !isRecord.booleanValue())) + if (checkIncludeUnfiledRecordFolders(isUnfiledRecordFolder, isRecord)) { includeUnfiledRecordFolders = true; } - else if ((isUnfiledRecordFolder != null && !isUnfiledRecordFolder.booleanValue()) || (isRecord != null && isRecord.booleanValue())) + else if (checkIncludeRecords(isUnfiledRecordFolder, isRecord)) { includeRecords = true; } @@ -199,11 +199,11 @@ public class SearchTypesFactory WhereClauseParser.EQUALS, Boolean.class); Boolean isRecordCategory = propertyWalker.getProperty(RecordCategoryChild.PARAM_IS_RECORD_CATEGORY, WhereClauseParser.EQUALS, Boolean.class); - if ((isRecordFolder != null && isRecordFolder.booleanValue()) || (isRecordCategory != null && !isRecordCategory.booleanValue())) + if (checkIncludeUnfiledRecordFolders(isRecordFolder, isRecordCategory)) { includeRecordFolders = true; } - else if ((isRecordFolder != null && !isRecordFolder.booleanValue()) || (isRecordCategory != null && isRecordCategory.booleanValue())) + else if (checkIncludeRecords(isRecordFolder, isRecordCategory)) { includeRecordCategories = true; } @@ -291,4 +291,16 @@ public class SearchTypesFactory return new Pair<>(filterNodeTypeQName, filterIncludeSubTypes); } + + private static boolean checkIncludeRecords(Boolean isUnfiledRecordFolder, Boolean isRecord) + { + return (isUnfiledRecordFolder != null && !isUnfiledRecordFolder.booleanValue()) || (isRecord != null + && isRecord.booleanValue()); + } + + private static boolean checkIncludeUnfiledRecordFolders(Boolean isUnfiledRecordFolder, Boolean isRecord) + { + return (isUnfiledRecordFolder != null && isUnfiledRecordFolder.booleanValue()) || (isRecord != null + && !isRecord.booleanValue()); + } } diff --git a/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RetentionScheduleActionDefinition.java b/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RetentionScheduleActionDefinition.java index d9806e01df..3d472cfff3 100644 --- a/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RetentionScheduleActionDefinition.java +++ b/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RetentionScheduleActionDefinition.java @@ -38,13 +38,13 @@ public class RetentionScheduleActionDefinition { private String id; private String name; + private String description; private int periodAmount; private String period; private String periodProperty; - private boolean combineDispositionStepConditions; + private boolean combineRetentionStepConditions; private List events; private boolean eligibleOnFirstCompleteEvent; - private String description; private boolean retainRecordMetadataAfterDestruction; private String location; private int index; diff --git a/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RetentionSteps.java b/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RetentionSteps.java index e8ecbd32bf..ddc5da107b 100644 --- a/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RetentionSteps.java +++ b/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/model/RetentionSteps.java @@ -35,7 +35,8 @@ public enum RetentionSteps CUTOFF("cutoff"), TRANSFER("transfer"), ACCESSION("accession"), - DESTROY("destroy"); + DESTROY_CONTENT("destroyContent"), + DESTROY_NODE("destroyNode"); public final String stepName; diff --git a/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/retentionschedule/RetentionScheduleActionRelation.java b/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/retentionschedule/RetentionScheduleActionRelation.java index 8e4c443751..269b6bde08 100644 --- a/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/retentionschedule/RetentionScheduleActionRelation.java +++ b/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/retentionschedule/RetentionScheduleActionRelation.java @@ -146,6 +146,11 @@ public class RetentionScheduleActionRelation implements RelationshipResourceActi */ private void retentionScheduleStepValidation(NodeRef retentionScheduleNodeRef, RetentionScheduleActionDefinition retentionScheduleActionDefinition) { + if (checkStepNameIsEmpty(retentionScheduleActionDefinition.getName())) + { + throw new IllegalArgumentException("'name' parameter is mandatory when creating a disposition action definition"); + } + List actions = nodesModelFactory.getRetentionActions(retentionScheduleNodeRef); Set completedActions = new HashSet<>(); if (!actions.isEmpty()) @@ -155,9 +160,9 @@ public class RetentionScheduleActionRelation implements RelationshipResourceActi .collect(Collectors.toSet()); } - if (completedActions.contains(RetentionSteps.DESTROY.stepName)) + if (completedActions.contains("destroy")) { - throw new ConstraintViolatedException("Invalid Step - destroy action is already added . No other action is allowed after Destroy."); + throw new ConstraintViolatedException("Invalid Step - destroy action is already added. No other action is allowed after Destroy."); } if (checkStepAlreadyExists(completedActions, retentionScheduleActionDefinition.getName())) @@ -176,6 +181,11 @@ public class RetentionScheduleActionRelation implements RelationshipResourceActi } } + private boolean checkStepNameIsEmpty(String name) + { + return name == null || name.isEmpty(); + } + /** * this method is used to validate the request of the retention schedule * @param retentionScheduleActionDefinition retention schedule action definition @@ -187,16 +197,34 @@ public class RetentionScheduleActionRelation implements RelationshipResourceActi { throw new InvalidArgumentException("name value is invalid : " +retentionScheduleActionDefinition.getName()); } - // period value validation - if (invalidPeriodCheck(retentionScheduleActionDefinition.getPeriod())) - { - throw new InvalidArgumentException("period value is invalid : " +retentionScheduleActionDefinition.getPeriod()); - } + + validatePeriodAndPeriodProperty(retentionScheduleActionDefinition); + // event name validation if (invalidEventNameCheck(retentionScheduleActionDefinition.getEvents())) { throw new InvalidArgumentException("event value is invalid: " + retentionScheduleActionDefinition.getEvents()); } + + if (validateCombineRetentionStepConditionsForNonAccessionStep(retentionScheduleActionDefinition)) + { + throw new IllegalArgumentException("combineRetentionStepConditions property is only valid for accession step. Not valid for :" + retentionScheduleActionDefinition.getName()); + } + + if (validateLocationForNonTransferStep(retentionScheduleActionDefinition)) + { + throw new IllegalArgumentException("location property is only valid for transfer step. Not valid for :" + retentionScheduleActionDefinition.getName()); + } + } + + private void validatePeriodAndPeriodProperty(RetentionScheduleActionDefinition retentionScheduleActionDefinition) + { + // period value validation + if (invalidPeriodCheck(retentionScheduleActionDefinition.getPeriod())) + { + throw new InvalidArgumentException("period value is invalid : " +retentionScheduleActionDefinition.getPeriod()); + } + // periodProperty validation List validPeriodProperties = Arrays.asList("cm:created", "rma:cutOffDate", "rma:dispositionAsOf"); if (validPeriodProperties.stream().noneMatch(retentionScheduleActionDefinition.getPeriodProperty()::equals)) @@ -205,6 +233,19 @@ public class RetentionScheduleActionRelation implements RelationshipResourceActi } } + private boolean validateCombineRetentionStepConditionsForNonAccessionStep(RetentionScheduleActionDefinition retentionScheduleActionDefinition) + { + return !retentionScheduleActionDefinition.getName().equals(RetentionSteps.ACCESSION.stepName) + && retentionScheduleActionDefinition.isCombineRetentionStepConditions(); + } + + private boolean validateLocationForNonTransferStep(RetentionScheduleActionDefinition retentionScheduleActionDefinition) + { + return retentionScheduleActionDefinition.getLocation() != null + && !retentionScheduleActionDefinition.getName().equals(RetentionSteps.TRANSFER.stepName) + && !retentionScheduleActionDefinition.getLocation().isEmpty(); + } + private boolean checkStepAlreadyExists(Set completedActions, String stepName) { return completedActions.contains(stepName) && !stepName.equals(RetentionSteps.TRANSFER.stepName); diff --git a/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/retentionschedule/RetentionScheduleRelation.java b/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/retentionschedule/RetentionScheduleRelation.java index 86583c27da..b6f1b034a1 100644 --- a/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/retentionschedule/RetentionScheduleRelation.java +++ b/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/retentionschedule/RetentionScheduleRelation.java @@ -26,9 +26,12 @@ */ package org.alfresco.rm.rest.api.retentionschedule; +import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule; import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService; +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; import org.alfresco.rest.framework.WebApiDescription; +import org.alfresco.rest.framework.core.exceptions.UnprocessableContentException; import org.alfresco.rest.framework.resource.RelationshipResource; import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction; import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; @@ -37,10 +40,12 @@ import org.alfresco.rm.rest.api.impl.ApiNodesModelFactory; import org.alfresco.rm.rest.api.impl.FilePlanComponentsApiUtils; import org.alfresco.rm.rest.api.model.RetentionSchedule; import org.alfresco.rm.rest.api.recordcategories.RecordCategoriesEntityResource; +import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; import java.io.Serializable; import java.util.ArrayList; @@ -96,6 +101,11 @@ public class RetentionScheduleRelation implements RelationshipResourceAction.Rea mandatory("entity", nodeInfos); mandatory("parameters", parameters); NodeRef parentNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, recordCategoryId); + + if (checkCategoryHasAssocFolder(parentNodeRef) && nodeInfos.get(0).getIsRecordLevel()) + { + throw new UnprocessableContentException("Record level retention schedule cannot be created for a record category having folder associated."); + } List result = new ArrayList<>(); // Create the disposition schedule Map dsProps = new HashMap<>(); @@ -108,6 +118,14 @@ public class RetentionScheduleRelation implements RelationshipResourceAction.Rea return result; } + private boolean checkCategoryHasAssocFolder(NodeRef nodeRef) + { + List assocs = nodeService.getChildAssocs(nodeRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + return assocs.stream() + .map(assoc -> nodeService.getType(assoc.getChildRef())) + .anyMatch(nodeType -> nodeType.equals(RecordsManagementModel.TYPE_RECORD_FOLDER)); + } + @Override @WebApiDescription(title = "Return a paged list of retention schedule based on the 'recordCategoryId'") public CollectionWithPagingInfo readAll(String recordCategoryId, Parameters parameters) diff --git a/amps/ags/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/patch/v33/RMv33HoldAuditEntryValuesPatchUnitTest.java b/amps/ags/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/patch/v33/RMv33HoldAuditEntryValuesPatchUnitTest.java index 44be8da09d..e89f055e45 100644 --- a/amps/ags/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/patch/v33/RMv33HoldAuditEntryValuesPatchUnitTest.java +++ b/amps/ags/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/patch/v33/RMv33HoldAuditEntryValuesPatchUnitTest.java @@ -93,15 +93,15 @@ public class RMv33HoldAuditEntryValuesPatchUnitTest verify(mockedRecordsManagementQueryDAO, times(1)).updatePropertyStringValueEntity(deleteHoldPropertyStringValueEntity); assertEquals("Add To Hold", addToHoldPropertyStringValueEntity.getStringValue()); - assertEquals("add to hold", addToHoldPropertyStringValueEntity.getStringEndLower()); + assertEquals("add to hold", addToHoldPropertyStringValueEntity.getStringLower()); assertEquals(Long.valueOf(770_786_109L), addToHoldPropertyStringValueEntity.getStringCrc()); assertEquals("Remove From Hold", removeFromHoldPropertyStringValueEntity.getStringValue()); - assertEquals("remove from hold", removeFromHoldPropertyStringValueEntity.getStringEndLower()); + assertEquals("remove from hold", removeFromHoldPropertyStringValueEntity.getStringLower()); assertEquals(Long.valueOf(2_967_613_012L), removeFromHoldPropertyStringValueEntity.getStringCrc()); assertEquals("Delete Hold", deleteHoldPropertyStringValueEntity.getStringValue()); - assertEquals("delete hold", deleteHoldPropertyStringValueEntity.getStringEndLower()); + assertEquals("delete hold", deleteHoldPropertyStringValueEntity.getStringLower()); assertEquals(Long.valueOf(132_640_810L), deleteHoldPropertyStringValueEntity.getStringCrc()); } diff --git a/amps/ags/rm-community/rm-community-rest-api-explorer/pom.xml b/amps/ags/rm-community/rm-community-rest-api-explorer/pom.xml index 461d366003..f64993a239 100644 --- a/amps/ags/rm-community/rm-community-rest-api-explorer/pom.xml +++ b/amps/ags/rm-community/rm-community-rest-api-explorer/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-governance-services-community-repo-parent - 23.3.0.63-SNAPSHOT + 23.3.0.86-SNAPSHOT diff --git a/amps/ags/rm-community/rm-community-rest-api-explorer/src/main/webapp/definitions/gs-core-api.yaml b/amps/ags/rm-community/rm-community-rest-api-explorer/src/main/webapp/definitions/gs-core-api.yaml index 6ab10c5079..354cec9900 100644 --- a/amps/ags/rm-community/rm-community-rest-api-explorer/src/main/webapp/definitions/gs-core-api.yaml +++ b/amps/ags/rm-community/rm-community-rest-api-explorer/src/main/webapp/definitions/gs-core-api.yaml @@ -2683,6 +2683,8 @@ paths: description: recordCategoryId does not exist '409': description: Retention schedule already exist for the given recordCategoryId + '422': + description: Record level retention schedule cannot be created for a record category having folder associated default: description: Unexpected error schema: @@ -2742,13 +2744,13 @@ paths: ```JSON { "name":"accession", + "description":"Step Description", "periodAmount": 2, "period":"month", "periodProperty":"cm:created", - "combineDispositionStepConditions": false, + "combineRetentionStepConditions": false, "events":["versioned"], - "eligibleOnFirstCompleteEvent": true, - "description":"Step Description" + "eligibleOnFirstCompleteEvent": true } ``` operationId: createRetentionScheduleAction @@ -4534,20 +4536,20 @@ definitions: type: string name: type: string + description: + type: string periodAmount: type: integer period: type: string periodProperty: type: string - combineDispositionStepConditions: + combineRetentionStepConditions: type: boolean default: false eligibleOnFirstCompleteEvent: type: boolean default: true - description: - type: string retainRecordMetadataAfterDestruction: type: boolean location: @@ -4560,6 +4562,9 @@ definitions: type: integer RetentionStepNodeBodyCreate: type: object + required: + - name + - description properties: name: type: string @@ -4569,7 +4574,15 @@ definitions: * cutoff * accession * transfer - * destroy + * destroyContent + * destroyNode + + destroyNode step can be used to destroy content along with record metadata. + In case, record metadata needs to be retained, then destroyContent step should be used. + description: + type: string + description: | + This property is used to provide the step description. periodAmount: type: integer description: | @@ -4618,7 +4631,7 @@ definitions: * cm:created = Created Date (defult value) * rma:cutOffDate = Cut Off Date * rma:dispositionAsOf = Retention Action - combineDispositionStepConditions: + combineRetentionStepConditions: type: boolean description: | This property is only valid for **accession** step. @@ -4653,14 +4666,6 @@ definitions: description: | * false = When all events have happened * true = Whichever event is earlier - description: - type: string - description: | - This property is used to provide the step description. - retainRecordMetadataAfterDestruction: - type: boolean - description: | - This property is used to retain the metadata after record destruction. location: type: string description: | diff --git a/amps/pom.xml b/amps/pom.xml index 8833e10762..321a3b0e4f 100644 --- a/amps/pom.xml +++ b/amps/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-community-repo - 23.3.0.63-SNAPSHOT + 23.3.0.86-SNAPSHOT diff --git a/amps/share-services/pom.xml b/amps/share-services/pom.xml index 2ffc74bc31..e942744bc4 100644 --- a/amps/share-services/pom.xml +++ b/amps/share-services/pom.xml @@ -8,7 +8,7 @@ org.alfresco alfresco-community-repo-amps - 23.3.0.63-SNAPSHOT + 23.3.0.86-SNAPSHOT diff --git a/core/pom.xml b/core/pom.xml index b1658281c6..cd35445e37 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-community-repo - 23.3.0.63-SNAPSHOT + 23.3.0.86-SNAPSHOT @@ -47,11 +47,11 @@ commons-math3 3.6.1 + - org.safehaus.jug - jug - 2.0.0 - asl + com.fasterxml.uuid + java-uuid-generator + 5.1.0 org.apache.logging.log4j diff --git a/core/src/main/java/org/alfresco/util/GUID.java b/core/src/main/java/org/alfresco/util/GUID.java index 227e928a5e..2e65c3975f 100644 --- a/core/src/main/java/org/alfresco/util/GUID.java +++ b/core/src/main/java/org/alfresco/util/GUID.java @@ -21,7 +21,7 @@ package org.alfresco.util; import java.security.SecureRandom; import java.util.Random; -import org.safehaus.uuid.UUIDGenerator; +import com.fasterxml.uuid.Generators; import org.alfresco.api.AlfrescoPublicApi; /** @@ -69,7 +69,7 @@ public final class GUID public static String generate() { int randomInt = RANDOM.nextInt(SECURE_RANDOM_POOL_MAX_ITEMS); - return UUIDGenerator.getInstance().generateRandomBasedUUID(SECURE_RANDOM_POOL[randomInt]).toString(); + return Generators.randomBasedGenerator(SECURE_RANDOM_POOL[randomInt]).generate().toString(); } // == Not sure if we need this functionality again (derekh) == diff --git a/data-model/pom.xml b/data-model/pom.xml index 37b59eb6a9..332902e75b 100644 --- a/data-model/pom.xml +++ b/data-model/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-community-repo - 23.3.0.63-SNAPSHOT + 23.3.0.86-SNAPSHOT diff --git a/data-model/src/main/java/org/alfresco/repo/security/authentication/InMemoryTicketComponentImpl.java b/data-model/src/main/java/org/alfresco/repo/security/authentication/InMemoryTicketComponentImpl.java index 4d37e707ef..7ee39afe3d 100644 --- a/data-model/src/main/java/org/alfresco/repo/security/authentication/InMemoryTicketComponentImpl.java +++ b/data-model/src/main/java/org/alfresco/repo/security/authentication/InMemoryTicketComponentImpl.java @@ -33,13 +33,13 @@ import java.util.HashSet; import java.util.Set; import java.util.zip.CRC32; +import com.fasterxml.uuid.Generators; import org.alfresco.repo.cache.SimpleCache; import org.alfresco.service.cmr.repository.datatype.Duration; import org.alfresco.util.GUID; import org.apache.commons.codec.binary.Hex; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.safehaus.uuid.UUIDGenerator; import org.alfresco.util.ParameterCheck; /** @@ -497,7 +497,7 @@ public class InMemoryTicketComponentImpl implements TicketComponent this.userName = userName; this.validDuration = validDuration; this.testDuration = validDuration.divide(2); - final String guid = UUIDGenerator.getInstance().generateRandomBasedUUID().toString(); + final String guid = Generators.randomBasedGenerator().generate().toString(); this.ticketId = computeTicketId(expires, expiryDate, userName, guid); diff --git a/mmt/pom.xml b/mmt/pom.xml index deef149f0e..bd86a5e488 100644 --- a/mmt/pom.xml +++ b/mmt/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-community-repo - 23.3.0.63-SNAPSHOT + 23.3.0.86-SNAPSHOT @@ -84,7 +84,7 @@ org.alfresco:alfresco-core org.alfresco:alfresco-repository org.apache.commons:commons-compress - org.safehaus.jug:jug + com.fasterxml.uuid:java-uuid-generator org.alfresco.surf:spring-surf-core org.tukaani:xz org.apache.maven:maven-artifact diff --git a/mmt/src/main/java/org/alfresco/repo/module/tool/ModuleManagementTool.java b/mmt/src/main/java/org/alfresco/repo/module/tool/ModuleManagementTool.java index a8f7f57dd2..631fe810a6 100644 --- a/mmt/src/main/java/org/alfresco/repo/module/tool/ModuleManagementTool.java +++ b/mmt/src/main/java/org/alfresco/repo/module/tool/ModuleManagementTool.java @@ -25,6 +25,7 @@ */ package org.alfresco.repo.module.tool; +import com.fasterxml.uuid.Generators; import de.schlichtherle.truezip.file.*; import de.schlichtherle.truezip.fs.FsSyncException; import de.schlichtherle.truezip.fs.archive.zip.JarDriver; @@ -34,7 +35,6 @@ import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.repo.module.ModuleVersionNumber; import org.alfresco.service.cmr.module.ModuleDetails; import org.alfresco.service.cmr.module.ModuleInstallState; -import org.safehaus.uuid.UUIDGenerator; import java.io.BufferedInputStream; import java.io.IOException; @@ -916,7 +916,7 @@ public class ModuleManagementTool implements LogOutput */ private static String generateGuid() { - return UUIDGenerator.getInstance().generateTimeBasedUUID().toString(); + return Generators.timeBasedGenerator().generate().toString(); } /** diff --git a/packaging/distribution/pom.xml b/packaging/distribution/pom.xml index 3a5d0ebad0..406118bf79 100644 --- a/packaging/distribution/pom.xml +++ b/packaging/distribution/pom.xml @@ -9,6 +9,6 @@ org.alfresco alfresco-community-repo-packaging - 23.3.0.63-SNAPSHOT + 23.3.0.86-SNAPSHOT diff --git a/packaging/docker-alfresco/pom.xml b/packaging/docker-alfresco/pom.xml index deeb184100..b46c5fdca4 100644 --- a/packaging/docker-alfresco/pom.xml +++ b/packaging/docker-alfresco/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-community-repo-packaging - 23.3.0.63-SNAPSHOT + 23.3.0.86-SNAPSHOT diff --git a/packaging/pom.xml b/packaging/pom.xml index 15bcb302d6..c8d043eeee 100644 --- a/packaging/pom.xml +++ b/packaging/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-community-repo - 23.3.0.63-SNAPSHOT + 23.3.0.86-SNAPSHOT diff --git a/packaging/tests/environment/.env b/packaging/tests/environment/.env index dfa05f01bd..c528c95c4a 100644 --- a/packaging/tests/environment/.env +++ b/packaging/tests/environment/.env @@ -1,3 +1,3 @@ -SOLR6_TAG=2.0.11-A6 +SOLR6_TAG=2.0.11 POSTGRES_TAG=15.4 ACTIVEMQ_TAG=5.18.3-jre17-rockylinux8 diff --git a/packaging/tests/pom.xml b/packaging/tests/pom.xml index 6712aa55a2..d56592fb96 100644 --- a/packaging/tests/pom.xml +++ b/packaging/tests/pom.xml @@ -6,7 +6,7 @@ org.alfresco alfresco-community-repo-packaging - 23.3.0.63-SNAPSHOT + 23.3.0.86-SNAPSHOT diff --git a/packaging/tests/tas-cmis/pom.xml b/packaging/tests/tas-cmis/pom.xml index 26f15be8ee..fea2fb2287 100644 --- a/packaging/tests/tas-cmis/pom.xml +++ b/packaging/tests/tas-cmis/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-community-repo-tests - 23.3.0.63-SNAPSHOT + 23.3.0.86-SNAPSHOT diff --git a/packaging/tests/tas-email/pom.xml b/packaging/tests/tas-email/pom.xml index f6d07fe019..77ecceba2e 100644 --- a/packaging/tests/tas-email/pom.xml +++ b/packaging/tests/tas-email/pom.xml @@ -9,7 +9,7 @@ org.alfresco alfresco-community-repo-tests - 23.3.0.63-SNAPSHOT + 23.3.0.86-SNAPSHOT diff --git a/packaging/tests/tas-integration/pom.xml b/packaging/tests/tas-integration/pom.xml index 0665997e70..df1b40a5b9 100644 --- a/packaging/tests/tas-integration/pom.xml +++ b/packaging/tests/tas-integration/pom.xml @@ -9,7 +9,7 @@ org.alfresco alfresco-community-repo-tests - 23.3.0.63-SNAPSHOT + 23.3.0.86-SNAPSHOT diff --git a/packaging/tests/tas-restapi/pom.xml b/packaging/tests/tas-restapi/pom.xml index 4d13def062..1f2348ff9f 100644 --- a/packaging/tests/tas-restapi/pom.xml +++ b/packaging/tests/tas-restapi/pom.xml @@ -8,7 +8,7 @@ org.alfresco alfresco-community-repo-tests - 23.3.0.63-SNAPSHOT + 23.3.0.86-SNAPSHOT @@ -17,7 +17,7 @@ UTF-8 master 4.5.6 - 3.13.0 + 3.14.0 8.3.3 17 @@ -171,14 +171,14 @@ org.codehaus.groovy groovy - 3.0.19 + 3.0.22 org.codehaus.groovy groovy-json - 3.0.19 + 3.0.22 diff --git a/packaging/tests/tas-webdav/pom.xml b/packaging/tests/tas-webdav/pom.xml index 09c252b4f1..3ce0d6125f 100644 --- a/packaging/tests/tas-webdav/pom.xml +++ b/packaging/tests/tas-webdav/pom.xml @@ -9,7 +9,7 @@ org.alfresco alfresco-community-repo-tests - 23.3.0.63-SNAPSHOT + 23.3.0.86-SNAPSHOT diff --git a/packaging/war/pom.xml b/packaging/war/pom.xml index d4208eefbb..0204ed928d 100644 --- a/packaging/war/pom.xml +++ b/packaging/war/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-community-repo-packaging - 23.3.0.63-SNAPSHOT + 23.3.0.86-SNAPSHOT @@ -110,6 +110,11 @@ mysql-connector-java test + + org.owasp.encoder + encoder + 1.2.3 + diff --git a/packaging/war/src/main/webapp/index.jsp b/packaging/war/src/main/webapp/index.jsp index 97357660e6..3628992c3b 100644 --- a/packaging/war/src/main/webapp/index.jsp +++ b/packaging/war/src/main/webapp/index.jsp @@ -34,6 +34,7 @@ <%@ page import="org.alfresco.service.cmr.module.ModuleDetails" %> <%@ page import="org.alfresco.service.cmr.module.ModuleInstallState" %> <%@ page import="java.util.Calendar" %> +<%@ page import="org.owasp.encoder.Encode" %> <% @@ -88,7 +89,7 @@ ModuleDetails shareServicesModule = moduleService.getModule("alfresco-share-serv

Alfresco WebScripts Home (admin only - INTERNAL)

-

Alfresco API Explorer

+

Alfresco API Explorer

<% if (descriptorService.getLicenseDescriptor() == null && transactionService.isReadOnly()) { diff --git a/pom.xml b/pom.xml index e5d7153fcb..afb283668d 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 alfresco-community-repo - 23.3.0.63-SNAPSHOT + 23.3.0.86-SNAPSHOT pom Alfresco Community Repo Parent @@ -51,54 +51,54 @@ 7.0.1 5.23.0 5.23.0 - 5.1.3-A3 - 4.1.3-A2 + 5.1.3 + 4.1.3 7.0 0.0.27 - 1.9.20.1 + 1.9.22.1 6.0.19 - 6.3.0 + 6.3.1 3.5.3 2.15.2 - 4.0.2 + 4.0.5 1.0.0-jakarta-1 9.0 1.78.1 - 5.4.0 - 3.24.2 + 5.12.0 + 3.26.3 20231013 - 2.9.0 - 2.14.0 - 2.10.1 - 32.1.2-jre + 2.12.0 + 2.16.1 + 2.11.0 + 33.2.1-jre 4.5.14 4.4.16 5.2.1 - 5.2.3 + 5.2.5 3.1-HTTPCLIENT-1265 2.12.2 - 2.0.9 - 2.20.0 - 3.0.19 + 2.0.13 + 2.23.1 + 3.0.22 2.9.2 7.7.10 5.2.5 3.5.0.Final - 4.0.5 + 4.6.0 4.1.110.Final 5.18.3 - 1.26.0 - 4.2.0 + 1.26.2 + 4.2.1 4.1.3 - 1.0.67 + 1.0.71 3.1.1 3.8.6 2.0.6.1 3.1.6 - 4.0.0 - 4.0.3 + 4.0.2 + 4.0.5 3.0.1 2.0.1 2.1.1 @@ -106,12 +106,12 @@ 3.0.0 2.0.1 2.0.1 - 3.0.0 + 3.1.0 1.2.0 - 2.1.2 - 1.1.4 + 2.1.3 + 1.1.6 2.9.0 - 2.5.0 + 2.5.1 4.1.0 3.1.0-A1 23.2.0 @@ -124,7 +124,7 @@ 8 2.7.4 5.0.1 - 5.3.2 + 5.5.0 2.0.0 1.21 1.19 @@ -401,7 +401,7 @@ commons-logging commons-logging - 1.2 + 1.3.3 commons-beanutils @@ -442,7 +442,7 @@ commons-net commons-net - 3.9.0 + 3.11.1 org.apache.httpcomponents @@ -462,7 +462,7 @@ org.apache.xmlbeans xmlbeans - 5.2.0 + 5.2.1 org.json @@ -621,7 +621,7 @@ org.apache.commons commons-email - 1.5 + 1.6.0 org.alfresco.aos-module @@ -700,13 +700,13 @@ com.networknt json-schema-validator - 1.0.86 + 1.5.0 org.jsoup jsoup - 1.16.1 + 1.18.1 @@ -851,6 +851,17 @@ org.apache.commons commons-dbcp2 ${dependency.commons-dbcp.version} + + + + jakarta.transaction + jakarta.transaction-api + + com.google.code.gson @@ -936,7 +947,7 @@ org.projectlombok lombok - 1.18.30 + 1.18.34 provided @@ -975,7 +986,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.11.0 + 3.13.0 -parameters @@ -1005,7 +1016,7 @@ maven-jar-plugin - 3.3.0 + 3.4.2 maven-war-plugin @@ -1014,7 +1025,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.5.0 + 3.7.0 org.apache.maven.plugins @@ -1024,11 +1035,11 @@ org.apache.maven.plugins maven-dependency-plugin - 3.6.0 + 3.7.1 maven-assembly-plugin - 3.6.0 + 3.7.1 org.alfresco.maven.plugin @@ -1074,7 +1085,7 @@ org.codehaus.cargo cargo-maven3-plugin - 1.10.9 + 1.10.14 org.apache.maven.plugins diff --git a/remote-api/pom.xml b/remote-api/pom.xml index a551b5bd31..4d8a321ae5 100644 --- a/remote-api/pom.xml +++ b/remote-api/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-community-repo - 23.3.0.63-SNAPSHOT + 23.3.0.86-SNAPSHOT @@ -143,7 +143,7 @@ org.eclipse.jetty jetty-webapp - 11.0.16 + 11.0.22 test diff --git a/remote-api/src/main/java/org/alfresco/rest/api/impl/AuditImpl.java b/remote-api/src/main/java/org/alfresco/rest/api/impl/AuditImpl.java index cf44a92f9c..d2259e3c6c 100644 --- a/remote-api/src/main/java/org/alfresco/rest/api/impl/AuditImpl.java +++ b/remote-api/src/main/java/org/alfresco/rest/api/impl/AuditImpl.java @@ -2,7 +2,7 @@ * #%L * Alfresco Remote API * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * 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 @@ -909,16 +909,14 @@ public class AuditImpl implements Audit public int getAuditEntriesCountByAppAndProperties(AuditService.AuditApplication auditApplication, AuditEntryQueryWalker propertyWalker) { - final String applicationName = auditApplication.getKey().substring(1); - AuditQueryParameters parameters = new AuditQueryParameters(); - parameters.setApplicationName(applicationName); + parameters.setApplicationName(auditApplication.getName()); parameters.setFromTime(propertyWalker.getFromTime()); parameters.setToTime(propertyWalker.getToTime()); parameters.setFromId(propertyWalker.getFromId()); parameters.setToId(propertyWalker.getToId()); parameters.setUser(propertyWalker.getCreatedByUser()); - return auditService.getAuditEntriesCountByAppAndProperties(applicationName, parameters); + return auditService.getAuditEntriesCountByAppAndProperties(parameters); } } diff --git a/remote-api/src/main/java/org/alfresco/rest/api/impl/PeopleImpl.java b/remote-api/src/main/java/org/alfresco/rest/api/impl/PeopleImpl.java index 706e5238dd..d789a58d4e 100644 --- a/remote-api/src/main/java/org/alfresco/rest/api/impl/PeopleImpl.java +++ b/remote-api/src/main/java/org/alfresco/rest/api/impl/PeopleImpl.java @@ -125,7 +125,7 @@ public class PeopleImpl implements People protected ResetPasswordService resetPasswordService; protected UserRegistrySynchronizer userRegistrySynchronizer; protected Renditions renditions; - + private Boolean allowImmutableEnabledUpdate; private final static Map sort_params_to_qnames; static @@ -202,6 +202,11 @@ public class PeopleImpl implements People this.userRegistrySynchronizer = userRegistrySynchronizer; } + public void setAllowImmutableEnabledUpdate(Boolean allowImmutableEnabledUpdate) + { + this.allowImmutableEnabledUpdate = allowImmutableEnabledUpdate; + } + /** * Validate, perform -me- substitution and canonicalize the person ID. * @@ -708,16 +713,26 @@ public class PeopleImpl implements People // if requested, update password updatePassword(isAdmin, personIdToUpdate, person); - if (person.isEnabled() != null) + Set immutableProperties = userRegistrySynchronizer.getPersonMappedProperties(personIdToUpdate); + + Boolean isEnabled = person.isEnabled(); + if (isEnabled != null) { if (isAdminAuthority(personIdToUpdate)) { throw new PermissionDeniedException("Admin authority cannot be disabled."); } - // note: if current user is not an admin then permission denied exception is thrown - MutableAuthenticationService mutableAuthenticationService = (MutableAuthenticationService) authenticationService; - mutableAuthenticationService.setAuthenticationEnabled(personIdToUpdate, person.isEnabled()); + if (allowImmutableEnabledStatusUpdate(personIdToUpdate, isAdmin, immutableProperties)) + { + LOGGER.info("User " + personIdToUpdate + " is immutable but enabled status will be set to: " + isEnabled); + } + else + { + // note: if current user is not an admin then permission denied exception is thrown + MutableAuthenticationService mutableAuthenticationService = (MutableAuthenticationService) authenticationService; + mutableAuthenticationService.setAuthenticationEnabled(personIdToUpdate, person.isEnabled()); + } } NodeRef personNodeRef = personService.getPerson(personIdToUpdate, false); @@ -742,9 +757,7 @@ public class PeopleImpl implements People properties.putAll(nodes.mapToNodeProperties(customProps)); } - // MNT-21150 LDAP synced attributes can be changed using REST API - Set immutableProperties = userRegistrySynchronizer.getPersonMappedProperties(personIdToUpdate); - + // MNT-21150 LDAP synced attributes can't be changed using REST API immutableProperties.forEach(immutableProperty -> { if (properties.containsKey(immutableProperty)) { @@ -768,6 +781,28 @@ public class PeopleImpl implements People return getPerson(personId); } + private boolean allowImmutableEnabledStatusUpdate(String userId, boolean isAdmin, Set immutableProperties) + { + if (allowImmutableEnabledUpdate) + { + boolean containLdapUserAccountStatus = false; + QName propertyNameToCheck = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "userAccountStatusProperty"); + + for (QName immutableProperty : immutableProperties) + { + if (immutableProperty.equals(propertyNameToCheck)) + { + containLdapUserAccountStatus = true; + break; + } + } + + return isAdmin && !containLdapUserAccountStatus && !isMutableAuthority(userId); + } + + return false; + } + private boolean checkCurrentUserOrAdmin(String personId) { boolean isAdmin = isAdminAuthority(); diff --git a/remote-api/src/main/resources/alfresco/public-rest-context.xml b/remote-api/src/main/resources/alfresco/public-rest-context.xml index 8a094f000a..dddc92113d 100644 --- a/remote-api/src/main/resources/alfresco/public-rest-context.xml +++ b/remote-api/src/main/resources/alfresco/public-rest-context.xml @@ -764,6 +764,7 @@ + diff --git a/repository/pom.xml b/repository/pom.xml index 1290166374..0de9b0aac4 100644 --- a/repository/pom.xml +++ b/repository/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-community-repo - 23.3.0.63-SNAPSHOT + 23.3.0.86-SNAPSHOT @@ -94,7 +94,7 @@ org.apache.commons commons-lang3 - 3.13.0 + 3.14.0 commons-codec @@ -120,7 +120,7 @@ commons-validator commons-validator - 1.7 + 1.9.0 org.json @@ -133,7 +133,7 @@ com.ibm.icu icu4j - 73.2 + 75.1 com.googlecode.json-simple @@ -273,7 +273,7 @@ com.sun.xml.fastinfoset FastInfoset - 2.1.0 + 2.1.1 org.htmlparser @@ -402,7 +402,7 @@ org.mybatis mybatis-spring - 3.0.2 + 3.0.3 @@ -839,7 +839,7 @@ org.codehaus.mojo aspectj-maven-plugin - 1.14.0 + 1.15.0 diff --git a/repository/src/main/java/org/alfresco/ibatis/IdsEntity.java b/repository/src/main/java/org/alfresco/ibatis/IdsEntity.java index 8178745189..96a49a4f07 100644 --- a/repository/src/main/java/org/alfresco/ibatis/IdsEntity.java +++ b/repository/src/main/java/org/alfresco/ibatis/IdsEntity.java @@ -41,6 +41,8 @@ public class IdsEntity private Long idFour; private List ids; private boolean ordered; + private Integer maxResults; + public Long getIdOne() { return idOne; @@ -89,4 +91,12 @@ public class IdsEntity { this.ordered = ordered; } + public int getMaxResults() + { + return maxResults; + } + public void setMaxResults(Integer maxResults) + { + this.maxResults = maxResults; + } } diff --git a/repository/src/main/java/org/alfresco/repo/audit/AuditComponent.java b/repository/src/main/java/org/alfresco/repo/audit/AuditComponent.java index 821c0c4759..29955432a7 100644 --- a/repository/src/main/java/org/alfresco/repo/audit/AuditComponent.java +++ b/repository/src/main/java/org/alfresco/repo/audit/AuditComponent.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * 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 @@ -276,11 +276,10 @@ public interface AuditComponent /** * Issue an audit query to retrieve count of records for a given application and properties * - * @param applicationName the name of the application * @param parameters audit parameters provided by the where clause on the ReST API * @return a map containing min/max and the associated value */ - default int getAuditEntriesCountByAppAndProperties(String applicationName, AuditQueryParameters parameters) + default int getAuditEntriesCountByAppAndProperties(AuditQueryParameters parameters) { return -1; } diff --git a/repository/src/main/java/org/alfresco/repo/audit/AuditComponentImpl.java b/repository/src/main/java/org/alfresco/repo/audit/AuditComponentImpl.java index 38fbd5ff69..98479db3af 100644 --- a/repository/src/main/java/org/alfresco/repo/audit/AuditComponentImpl.java +++ b/repository/src/main/java/org/alfresco/repo/audit/AuditComponentImpl.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * 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 @@ -956,10 +956,8 @@ public class AuditComponentImpl implements AuditComponent return auditDAO.getAuditEntriesCountByApp(applicationId); } - @Override public int getAuditEntriesCountByAppAndProperties(String applicationName, AuditQueryParameters parameters) + @Override public int getAuditEntriesCountByAppAndProperties(AuditQueryParameters parameters) { - org.alfresco.repo.domain.audit.AuditQueryParameters dbParameters = new org.alfresco.repo.domain.audit.AuditQueryParameters(); - - return auditDAO.getAuditEntriesCountByAppAndProperties(applicationName, parameters); + return auditDAO.getAuditEntriesCountByAppAndProperties(parameters); } -} +} \ No newline at end of file diff --git a/repository/src/main/java/org/alfresco/repo/audit/AuditServiceImpl.java b/repository/src/main/java/org/alfresco/repo/audit/AuditServiceImpl.java index 7c92ec8e3f..4824914e51 100644 --- a/repository/src/main/java/org/alfresco/repo/audit/AuditServiceImpl.java +++ b/repository/src/main/java/org/alfresco/repo/audit/AuditServiceImpl.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * 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 @@ -190,8 +190,8 @@ public class AuditServiceImpl implements AuditService /** * {@inheritDoc} */ - @Override public int getAuditEntriesCountByAppAndProperties(String applicationName, AuditQueryParameters parameters) + @Override public int getAuditEntriesCountByAppAndProperties(AuditQueryParameters parameters) { - return auditComponent.getAuditEntriesCountByAppAndProperties(applicationName, parameters); + return auditComponent.getAuditEntriesCountByAppAndProperties(parameters); } } \ No newline at end of file diff --git a/repository/src/main/java/org/alfresco/repo/domain/CrcHelper.java b/repository/src/main/java/org/alfresco/repo/domain/CrcHelper.java index 8c71789809..d3cdb157b6 100644 --- a/repository/src/main/java/org/alfresco/repo/domain/CrcHelper.java +++ b/repository/src/main/java/org/alfresco/repo/domain/CrcHelper.java @@ -1,28 +1,28 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2016 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 . - * #L% - */ +/* + * #%L + * Alfresco Repository + * %% + * 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 . + * #L% + */ package org.alfresco.repo.domain; @@ -102,21 +102,22 @@ public class CrcHelper { throw new RuntimeException("UTF-8 encoding is not supported"); } - // Get the short value (case-sensitive or not) + // Crc Value will change based on the case-sensitive, So we need to get the short value based on case-sensitive String valueShort = null; - int valueLen = valueLowerCase.length(); + String currentValue = caseSensitive ? value : valueLowerCase; + int valueLen = currentValue.length(); if (valueLen < dataLength) { - valueShort = valueLowerCase; + valueShort = currentValue; } else if (useCharsFromStart) { - valueShort = valueLowerCase.substring(0, dataLength - 1); + valueShort = currentValue.substring(0, dataLength - 1); } else { - valueShort = valueLowerCase.substring(valueLen - dataLength); + valueShort = currentValue.substring(valueLen - dataLength); } return new Pair(valueShort, valueCrc); } -} +} \ No newline at end of file diff --git a/repository/src/main/java/org/alfresco/repo/domain/audit/AuditDAO.java b/repository/src/main/java/org/alfresco/repo/domain/audit/AuditDAO.java index f8c61d0a8c..cac517706e 100644 --- a/repository/src/main/java/org/alfresco/repo/domain/audit/AuditDAO.java +++ b/repository/src/main/java/org/alfresco/repo/domain/audit/AuditDAO.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * 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 @@ -248,11 +248,10 @@ public interface AuditDAO /** * Issue an audit query to retrieve count of records for a given application and properties * - * @param applicationName name of the application to be queried * @param parameters audit parameters provided by the where clause on the ReST API * @return a map containing min/max and the associated value */ - default int getAuditEntriesCountByAppAndProperties(String applicationName, org.alfresco.service.cmr.audit.AuditQueryParameters parameters) + default int getAuditEntriesCountByAppAndProperties(org.alfresco.service.cmr.audit.AuditQueryParameters parameters) { return -1; } diff --git a/repository/src/main/java/org/alfresco/repo/domain/audit/ibatis/AuditDAOImpl.java b/repository/src/main/java/org/alfresco/repo/domain/audit/ibatis/AuditDAOImpl.java index 89f602dba5..12468dbf8e 100644 --- a/repository/src/main/java/org/alfresco/repo/domain/audit/ibatis/AuditDAOImpl.java +++ b/repository/src/main/java/org/alfresco/repo/domain/audit/ibatis/AuditDAOImpl.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * 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 @@ -237,7 +237,7 @@ public class AuditDAOImpl extends AbstractAuditDAOImpl } @Override - public int getAuditEntriesCountByAppAndProperties(String applicationName, org.alfresco.service.cmr.audit.AuditQueryParameters parameters) + public int getAuditEntriesCountByAppAndProperties(org.alfresco.service.cmr.audit.AuditQueryParameters parameters) { AuditQueryParameters dbParameters = convertFromRestAuditQueryParameters(parameters); diff --git a/repository/src/main/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java b/repository/src/main/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java index 1095e7448d..9b223788c5 100644 --- a/repository/src/main/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java +++ b/repository/src/main/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java @@ -2785,6 +2785,23 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO selectNodesWithAspects(qnameIds, minNodeId, maxNodeId, ordered, resultsCallback); } + @Override + public void getNodesWithAspects( + Set aspectQNames, + Long minNodeId, Long maxNodeId, boolean ordered, + int maxResults, + NodeRefQueryCallback resultsCallback) + { + Set qnameIdsSet = qnameDAO.convertQNamesToIds(aspectQNames, false); + if (qnameIdsSet.isEmpty()) + { + // No point running a query + return; + } + List qnameIds = new ArrayList<>(qnameIdsSet); + selectNodesWithAspects(qnameIds, minNodeId, maxNodeId, ordered, maxResults, resultsCallback); + } + /** * @return Returns a writable copy of the cached aspects set */ @@ -4960,6 +4977,10 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO List qnameIds, Long minNodeId, Long maxNodeId, boolean ordered, NodeRefQueryCallback resultsCallback); + protected abstract void selectNodesWithAspects( + List qnameIds, + Long minNodeId, Long maxNodeId, boolean ordered, int maxResults, + NodeRefQueryCallback resultsCallback); protected abstract Long insertNodeAssoc(Long sourceNodeId, Long targetNodeId, Long assocTypeQNameId, int assocIndex); protected abstract int updateNodeAssoc(Long id, int assocIndex); protected abstract int deleteNodeAssoc(Long sourceNodeId, Long targetNodeId, Long assocTypeQNameId); @@ -5088,4 +5109,5 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO protected abstract Long selectMinTxInNodeIdRange(Long fromNodeId, Long toNodeId); protected abstract Long selectMaxTxInNodeIdRange(Long fromNodeId, Long toNodeId); protected abstract Long selectNextTxCommitTime(Long fromCommitTime); + } diff --git a/repository/src/main/java/org/alfresco/repo/domain/node/NodeDAO.java b/repository/src/main/java/org/alfresco/repo/domain/node/NodeDAO.java index 3c1bb2675d..6ef10852ab 100644 --- a/repository/src/main/java/org/alfresco/repo/domain/node/NodeDAO.java +++ b/repository/src/main/java/org/alfresco/repo/domain/node/NodeDAO.java @@ -427,6 +427,22 @@ public interface NodeDAO extends NodeBulkLoader Long minNodeId, Long maxNodeId, boolean ordered, NodeRefQueryCallback resultsCallback); + /** + * Get nodes with aspects between the given ranges, ordering the results optionally + * and limit the result set + * + * @param aspectQNames the aspects that must be on the nodes + * @param minNodeId the minimum node ID (inclusive) + * @param maxNodeId the maximum node ID (exclusive) + * @param ordered if the results are to be ordered by nodeID + * @param maxResults limit query to maxResults + * @param resultsCallback callback to process results + */ + public void getNodesWithAspects( + Set aspectQNames, + Long minNodeId, Long maxNodeId, boolean ordered, int maxResults, + NodeRefQueryCallback resultsCallback); + /* * Node Assocs */ diff --git a/repository/src/main/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java b/repository/src/main/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java index a387385de0..1357975404 100644 --- a/repository/src/main/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java +++ b/repository/src/main/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java @@ -116,6 +116,7 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl private static final String SELECT_NODE_MAX_ID = "alfresco.node.select_NodeMaxId"; private static final String SELECT_NODE_INTERVAL_BY_TYPE = "alfresco.node.select_MinMaxNodeIdForNodeType"; private static final String SELECT_NODES_WITH_ASPECT_IDS = "alfresco.node.select_NodesWithAspectIds"; + private static final String SELECT_NODES_WITH_ASPECT_IDS_LIMITED = "alfresco.node.select_NodesWithAspectIds_Limited"; private static final String INSERT_NODE_ASSOC = "alfresco.node.insert.insert_NodeAssoc"; private static final String UPDATE_NODE_ASSOC = "alfresco.node.update_NodeAssoc"; private static final String DELETE_NODE_ASSOC = "alfresco.node.delete_NodeAssoc"; @@ -799,6 +800,33 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl template.select(SELECT_NODES_WITH_ASPECT_IDS, parameters, resultHandler); } + @Override + protected void selectNodesWithAspects( + List qnameIds, + Long minNodeId, Long maxNodeId, boolean ordered, + final int maxResults, + final NodeRefQueryCallback resultsCallback) + { + @SuppressWarnings("rawtypes") + ResultHandler resultHandler = new ResultHandler() + { + public void handleResult(ResultContext context) + { + NodeEntity entity = (NodeEntity) context.getResultObject(); + Pair nodePair = new Pair<>(entity.getId(), entity.getNodeRef()); + resultsCallback.handle(nodePair); + } + }; + + IdsEntity parameters = new IdsEntity(); + parameters.setIdOne(minNodeId); + parameters.setIdTwo(maxNodeId); + parameters.setIds(qnameIds); + parameters.setOrdered(ordered); + parameters.setMaxResults(maxResults); + template.select(SELECT_NODES_WITH_ASPECT_IDS_LIMITED, parameters, resultHandler); + } + @Override protected Long insertNodeAssoc(Long sourceNodeId, Long targetNodeId, Long assocTypeQNameId, int assocIndex) { diff --git a/repository/src/main/java/org/alfresco/repo/domain/permissions/FixedAclUpdater.java b/repository/src/main/java/org/alfresco/repo/domain/permissions/FixedAclUpdater.java index f313c25dd8..b9da62f6e3 100644 --- a/repository/src/main/java/org/alfresco/repo/domain/permissions/FixedAclUpdater.java +++ b/repository/src/main/java/org/alfresco/repo/domain/permissions/FixedAclUpdater.java @@ -85,8 +85,11 @@ public class FixedAclUpdater extends TransactionListenerAdapter implements Appli public static final String FIXED_ACL_ASYNC_REQUIRED_KEY = "FIXED_ACL_ASYNC_REQUIRED"; public static final String FIXED_ACL_ASYNC_CALL_KEY = "FIXED_ACL_ASYNC_CALL"; + protected static final QName LOCK_Q_NAME = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "FixedAclUpdater"); + private static final int DEFAULT_MAX_ITEMS = Integer.MAX_VALUE; + /** A set of listeners to receive callback events whenever permissions are updated by this class. */ private static Set listeners = Sets.newConcurrentHashSet(); @@ -101,6 +104,8 @@ public class FixedAclUpdater extends TransactionListenerAdapter implements Appli private int maxItemBatchSize = 100; private int numThreads = 4; private boolean forceSharedACL = false; + private int maxItems = DEFAULT_MAX_ITEMS; + private boolean orderNodes = true; private ClassPolicyDelegate onInheritPermissionsDisabledDelegate; private PolicyComponent policyComponent; @@ -147,12 +152,22 @@ public class FixedAclUpdater extends TransactionListenerAdapter implements Appli this.forceSharedACL = forceSharedACL; } + public void setOrderNodes(boolean orderNodes) + { + this.orderNodes = orderNodes; + } + public void setLockTimeToLive(long lockTimeToLive) { this.lockTimeToLive = lockTimeToLive; this.lockRefreshTime = lockTimeToLive / 2; } + public void setMaxItems(int maxItems) + { + this.maxItems = maxItems > 0 ? maxItems : DEFAULT_MAX_ITEMS; + } + public void setPolicyComponent(PolicyComponent policyComponent) { this.policyComponent = policyComponent; @@ -209,7 +224,7 @@ public class FixedAclUpdater extends TransactionListenerAdapter implements Appli public List execute() throws Throwable { getNodesCallback.init(); - nodeDAO.getNodesWithAspects(aspects, getNodesCallback.getMinNodeId(), null, true, getNodesCallback); + nodeDAO.getNodesWithAspects(aspects, getNodesCallback.getMinNodeId(), null, orderNodes, maxItemBatchSize, getNodesCallback); getNodesCallback.done(); return getNodesCallback.getNodes(); @@ -220,6 +235,11 @@ public class FixedAclUpdater extends TransactionListenerAdapter implements Appli int countNodesWithAspects() { + if (maxItems < DEFAULT_MAX_ITEMS) { + log.info("Job limited to process a maximum of " + maxItems + " Pending Acls"); + return maxItems; + } + final CountNodesWithAspectCallback countNodesCallback = new CountNodesWithAspectCallback(); int count = transactionService.getRetryingTransactionHelper() .doInTransaction(new RetryingTransactionCallback() @@ -238,6 +258,9 @@ public class FixedAclUpdater extends TransactionListenerAdapter implements Appli private class AclWorkProvider implements BatchProcessWorkProvider { private GetNodesWithAspects getNodesWithAspects; + private long estimatedUpdatedItems; + private long execTime; + private long execBatches; AclWorkProvider() { @@ -259,8 +282,37 @@ public class FixedAclUpdater extends TransactionListenerAdapter implements Appli @Override public Collection getNextWork() { - return getNodesWithAspects.getNodesWithAspects(); + if(estimatedUpdatedItems >= maxItems) + { + log.info("Reached max items to process. Nodes Processed: " + estimatedUpdatedItems + "/" + maxItems); + return Collections.emptyList(); + } + + long initTime = System.currentTimeMillis(); + Collection batchNodes = getNodesWithAspects.getNodesWithAspects(); + long endTime = System.currentTimeMillis(); + + if (log.isDebugEnabled()) + { + log.debug("Query for batch executed in " + (endTime-initTime) + " ms"); + } + + if (!batchNodes.isEmpty()) + { + // Increment estimatedUpdatedItems with the expected number of nodes to process + estimatedUpdatedItems += batchNodes.size(); + execTime+=endTime-initTime; + execBatches++; + } + + return batchNodes; } + + public double getAverageQueryExecutionTime() + { + return execBatches > 0 ? execTime/execBatches : 0; + } + } protected class AclWorker implements BatchProcessor.BatchProcessWorker @@ -451,6 +503,7 @@ public class FixedAclUpdater extends TransactionListenerAdapter implements Appli try { + log.info("Running FixedAclUpdater. Max Items: " + maxItems + ", Impose order: " + orderNodes); lockToken = jobLockService.getLock(LOCK_Q_NAME, lockTimeToLive, 0, 1); jobLockService.refreshLock(lockToken, LOCK_Q_NAME, lockRefreshTime, jobLockRefreshCallback); @@ -460,6 +513,7 @@ public class FixedAclUpdater extends TransactionListenerAdapter implements Appli transactionService.getRetryingTransactionHelper(), provider, numThreads, maxItemBatchSize, applicationContext, log, 100); int count = bp.process(worker, true); + log.info("FixedAclUpdater updated " + count + ". Average query time " + provider.getAverageQueryExecutionTime() + " ms"); return count; } catch (LockAcquisitionException e) diff --git a/repository/src/main/java/org/alfresco/repo/domain/propval/PropertyStringValueEntity.java b/repository/src/main/java/org/alfresco/repo/domain/propval/PropertyStringValueEntity.java index dbc3090298..56e188fbf6 100644 --- a/repository/src/main/java/org/alfresco/repo/domain/propval/PropertyStringValueEntity.java +++ b/repository/src/main/java/org/alfresco/repo/domain/propval/PropertyStringValueEntity.java @@ -1,28 +1,28 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2016 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 . - * #L% - */ +/* + * #%L + * Alfresco Repository + * %% + * 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 . + * #L% + */ package org.alfresco.repo.domain.propval; import org.alfresco.repo.domain.CrcHelper; @@ -44,6 +44,7 @@ public class PropertyStringValueEntity private String stringValue; private String stringEndLower; private Long stringCrc; + private String stringLower; public PropertyStringValueEntity() { @@ -115,6 +116,9 @@ public class PropertyStringValueEntity Pair crcPair = CrcHelper.getStringCrcPair(value, 16, false, true); stringEndLower = crcPair.getFirst(); stringCrc = crcPair.getSecond(); + // Calculate the crc value with case-insensitive + Pair crcPairWithCaseInSensitive = CrcHelper.getStringCrcPair(value, 16, false, false); + stringLower = crcPairWithCaseInSensitive.getFirst(); } public Long getId() @@ -156,4 +160,14 @@ public class PropertyStringValueEntity { this.stringCrc = stringCrc; } + + public String getStringLower() + { + return stringLower; + } + + public void setStringLower(String stringLower) + { + this.stringLower = stringLower; + } } diff --git a/repository/src/main/java/org/alfresco/repo/event2/DirectEventSender.java b/repository/src/main/java/org/alfresco/repo/event2/DirectEventSender.java index f2af8666ea..3f3805be09 100644 --- a/repository/src/main/java/org/alfresco/repo/event2/DirectEventSender.java +++ b/repository/src/main/java/org/alfresco/repo/event2/DirectEventSender.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2023 Alfresco Software Limited + * 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 @@ -36,18 +36,13 @@ import org.springframework.beans.factory.InitializingBean; /** * Sends a message to a destination in the current thread. */ -public class DirectEventSender implements EventSender, InitializingBean +public class DirectEventSender implements EventSender { - protected Event2MessageProducer event2MessageProducer; + protected final Event2MessageProducer event2MessageProducer; - @Override - public void afterPropertiesSet() + public DirectEventSender(Event2MessageProducer event2MessageProducer) { PropertyCheck.mandatory(this, "event2MessageProducer", event2MessageProducer); - } - - public void setEvent2MessageProducer(Event2MessageProducer event2MessageProducer) - { this.event2MessageProducer = event2MessageProducer; } diff --git a/repository/src/main/java/org/alfresco/repo/event2/EnqueuingEventSender.java b/repository/src/main/java/org/alfresco/repo/event2/EnqueuingEventSender.java index 08a425f218..7615cba81f 100644 --- a/repository/src/main/java/org/alfresco/repo/event2/EnqueuingEventSender.java +++ b/repository/src/main/java/org/alfresco/repo/event2/EnqueuingEventSender.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2023 Alfresco Software Limited + * 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 @@ -46,28 +46,18 @@ public class EnqueuingEventSender extends DirectEventSender { protected static final Log LOGGER = LogFactory.getLog(EnqueuingEventSender.class); - protected Executor enqueueThreadPoolExecutor; - protected Executor dequeueThreadPoolExecutor; + protected final Executor enqueueThreadPoolExecutor; + protected final Executor dequeueThreadPoolExecutor; protected BlockingQueue queue = new LinkedBlockingQueue<>(); protected Runnable listener = createListener(); - @Override - public void afterPropertiesSet() + public EnqueuingEventSender(Event2MessageProducer event2MessageProducer, Executor enqueueThreadPoolExecutor, Executor dequeueThreadPoolExecutor) { - super.afterPropertiesSet(); + super(event2MessageProducer); PropertyCheck.mandatory(this, "enqueueThreadPoolExecutor", enqueueThreadPoolExecutor); PropertyCheck.mandatory(this, "dequeueThreadPoolExecutor", dequeueThreadPoolExecutor); - } - - public void setEnqueueThreadPoolExecutor(Executor enqueueThreadPoolExecutor) - { this.enqueueThreadPoolExecutor = enqueueThreadPoolExecutor; - } - - public void setDequeueThreadPoolExecutor(Executor dequeueThreadPoolExecutor) - { this.dequeueThreadPoolExecutor = dequeueThreadPoolExecutor; - dequeueThreadPoolExecutor.execute(listener); } /** @@ -91,6 +81,12 @@ public class EnqueuingEventSender extends DirectEventSender }); } + @Override + public void initialize() + { + dequeueThreadPoolExecutor.execute(listener); + } + /** * Create listener task in charge of dequeuing and sending events ready to be sent. * @return The task in charge of dequeuing and sending events ready to be sent. diff --git a/repository/src/main/java/org/alfresco/repo/event2/EventSender.java b/repository/src/main/java/org/alfresco/repo/event2/EventSender.java index 798c66f111..e23aa1692d 100644 --- a/repository/src/main/java/org/alfresco/repo/event2/EventSender.java +++ b/repository/src/main/java/org/alfresco/repo/event2/EventSender.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2023 Alfresco Software Limited + * 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 @@ -40,4 +40,13 @@ public interface EventSender * @param eventProducer - callback function that creates an event */ void accept(Callable>> eventProducer); + + /** + * It's called right after event sender instantiation (see {@link org.alfresco.repo.event2.EventSenderFactoryBean}). + * It might be used to initialize the sender implementation. + */ + default void initialize() + { + //no initialization by default + } } diff --git a/repository/src/main/java/org/alfresco/repo/event2/EventSenderFactoryBean.java b/repository/src/main/java/org/alfresco/repo/event2/EventSenderFactoryBean.java new file mode 100644 index 0000000000..2f0f216e9a --- /dev/null +++ b/repository/src/main/java/org/alfresco/repo/event2/EventSenderFactoryBean.java @@ -0,0 +1,158 @@ +/* + * #%L + * Alfresco Repository + * %% + * 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 . + * #L% + */ +package org.alfresco.repo.event2; + +import jakarta.annotation.Nonnull; +import org.alfresco.util.PropertyCheck; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.beans.factory.config.AbstractFactoryBean; +import org.springframework.core.env.PropertyResolver; + +import java.util.Optional; +import java.util.concurrent.Executor; + +public class EventSenderFactoryBean extends AbstractFactoryBean +{ + static final String LEGACY_SKIP_QUEUE_PROPERTY = "repo.event2.queue.skip"; + static final String EVENT_SEND_STRATEGY_PROPERTY = "repo.event2.send.strategy"; + private static final String DIRECT_EVENT_SENDER_NAME = "direct"; + private static final String ASYNC_EVENT_SENDER_NAME = "async"; + + private final PropertyResolver propertyResolver; + private final Event2MessageProducer event2MessageProducer; + private final Executor enqueueThreadPoolExecutor; + private final Executor dequeueThreadPoolExecutor; + + private String configuredSenderName; + private boolean legacySkipQueueConfig; + + public EventSenderFactoryBean(@Autowired PropertyResolver propertyResolver, Event2MessageProducer event2MessageProducer, + Executor enqueueThreadPoolExecutor, Executor dequeueThreadPoolExecutor) + { + super(); + PropertyCheck.mandatory(this, "propertyResolver", propertyResolver); + PropertyCheck.mandatory(this, "event2MessageProducer", event2MessageProducer); + PropertyCheck.mandatory(this, "enqueueThreadPoolExecutor", enqueueThreadPoolExecutor); + PropertyCheck.mandatory(this, "dequeueThreadPoolExecutor", dequeueThreadPoolExecutor); + this.propertyResolver = propertyResolver; + this.event2MessageProducer = event2MessageProducer; + this.enqueueThreadPoolExecutor = enqueueThreadPoolExecutor; + this.dequeueThreadPoolExecutor = dequeueThreadPoolExecutor; + } + + @Value("${" + LEGACY_SKIP_QUEUE_PROPERTY + "}") + public void setLegacySkipQueueConfig(boolean legacySkipQueueConfig) + { + this.legacySkipQueueConfig = legacySkipQueueConfig; + } + + @Value("${" + EVENT_SEND_STRATEGY_PROPERTY + "}") + public void setConfiguredSenderName(String configuredSenderName) + { + this.configuredSenderName = configuredSenderName; + } + + @Override + public Class getObjectType() + { + return EventSender.class; + } + + @Override + @Nonnull + protected EventSender createInstance() throws Exception + { + EventSender sender = instantiateConfiguredSender(); + + sender.initialize(); + + return sender; + } + + private EventSender instantiateConfiguredSender() + { + if (isSenderNameConfigured()) + { + return instantiateSender(getConfiguredSenderName()); + } + return isLegacySkipQueueConfigured() ? instantiateDirectSender() : instantiateAsyncSender(); + } + + protected EventSender instantiateSender(String senderName) + { + if (DIRECT_EVENT_SENDER_NAME.equalsIgnoreCase(senderName)) + { + return instantiateDirectSender(); + } + + if (ASYNC_EVENT_SENDER_NAME.equalsIgnoreCase(senderName)) + { + return instantiateAsyncSender(); + } + + throw new IllegalStateException("Failed to instantiate sender: " + senderName); + } + + private DirectEventSender instantiateDirectSender() + { + return new DirectEventSender(getEvent2MessageProducer()); + } + + private EnqueuingEventSender instantiateAsyncSender() + { + return new EnqueuingEventSender(getEvent2MessageProducer(), enqueueThreadPoolExecutor, dequeueThreadPoolExecutor); + } + + private boolean isSenderNameConfigured() + { + return !Optional.ofNullable(getConfiguredSenderName()) + .map(String::isBlank) + .orElse(true); + } + + private boolean isLegacySkipQueueConfigured() + { + return Optional.ofNullable(resolveProperty(LEGACY_SKIP_QUEUE_PROPERTY, Boolean.class)) + .orElse(legacySkipQueueConfig); + } + + private String getConfiguredSenderName() + { + return Optional.ofNullable(resolveProperty(EVENT_SEND_STRATEGY_PROPERTY, String.class)) + .orElse(configuredSenderName); + } + + protected T resolveProperty(String key, Class targetType) + { + return propertyResolver.getProperty(key, targetType); + } + + protected Event2MessageProducer getEvent2MessageProducer() + { + return event2MessageProducer; + } +} diff --git a/repository/src/main/java/org/alfresco/repo/quickshare/QuickShareServiceImpl.java b/repository/src/main/java/org/alfresco/repo/quickshare/QuickShareServiceImpl.java index 2102a2bf93..9757bc622a 100644 --- a/repository/src/main/java/org/alfresco/repo/quickshare/QuickShareServiceImpl.java +++ b/repository/src/main/java/org/alfresco/repo/quickshare/QuickShareServiceImpl.java @@ -36,7 +36,10 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.UUID; +import com.fasterxml.uuid.Generators; +import com.fasterxml.uuid.impl.UUIDUtil; import org.alfresco.sync.events.types.ActivityEvent; import org.alfresco.sync.events.types.Event; import org.alfresco.model.ContentModel; @@ -108,8 +111,6 @@ import org.apache.commons.logging.LogFactory; import org.joda.time.DateTime; import org.joda.time.Interval; import org.joda.time.PeriodType; -import org.safehaus.uuid.UUID; -import org.safehaus.uuid.UUIDGenerator; /** * QuickShare Service implementation. @@ -435,8 +436,8 @@ public class QuickShareServiceImpl implements QuickShareService, // If it is retura dto built from the existing properties. if (! nodeService.getAspects(nodeRef).contains(QuickShareModel.ASPECT_QSHARE)) { - UUID uuid = UUIDGenerator.getInstance().generateRandomBasedUUID(); - sharedId = Base64.encodeBase64URLSafeString(uuid.toByteArray()); // => 22 chars (eg. q3bEKPeDQvmJYgt4hJxOjw) + UUID uuid = Generators.randomBasedGenerator().generate(); + sharedId = Base64.encodeBase64URLSafeString(UUIDUtil.asByteArray(uuid)); // => 22 chars (eg. q3bEKPeDQvmJYgt4hJxOjw) final Map props = new HashMap(2); props.put(QuickShareModel.PROP_QSHARE_SHAREDID, sharedId); @@ -1246,7 +1247,7 @@ public class QuickShareServiceImpl implements QuickShareService, } // Create the expiry action - final QuickShareLinkExpiryAction expiryAction = new QuickShareLinkExpiryActionImpl(java.util.UUID.randomUUID().toString(), sharedId, + final QuickShareLinkExpiryAction expiryAction = new QuickShareLinkExpiryActionImpl(UUID.randomUUID().toString(), sharedId, "QuickShare link expiry action"); // Create the persisted schedule final ScheduledPersistedAction schedule = scheduledPersistedActionService.createSchedule(expiryAction); diff --git a/repository/src/main/java/org/alfresco/repo/security/authentication/AuthenticationContextImpl.java b/repository/src/main/java/org/alfresco/repo/security/authentication/AuthenticationContextImpl.java index c9c4a84bb5..39b57d155f 100644 --- a/repository/src/main/java/org/alfresco/repo/security/authentication/AuthenticationContextImpl.java +++ b/repository/src/main/java/org/alfresco/repo/security/authentication/AuthenticationContextImpl.java @@ -36,7 +36,11 @@ import net.sf.acegisecurity.UserDetails; import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken; import net.sf.acegisecurity.providers.dao.User; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.tenant.TenantService; +import org.alfresco.service.cmr.security.AuthenticationService; +import org.alfresco.service.cmr.security.MutableAuthenticationService; +import org.alfresco.service.cmr.security.PersonService; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -49,12 +53,30 @@ public class AuthenticationContextImpl implements AuthenticationContext private final Log logger = LogFactory.getLog(getClass()); private TenantService tenantService; + private PersonService personService; + private AuthenticationService authenticationService; + private Boolean allowImmutableEnabledUpdate; public void setTenantService(TenantService tenantService) { this.tenantService = tenantService; } + public void setPersonService(PersonService personService) + { + this.personService = personService; + } + + public void setAuthenticationService(AuthenticationService authenticationService) + { + this.authenticationService = authenticationService; + } + + public void setAllowImmutableEnabledUpdate(Boolean allowImmutableEnabledUpdate) + { + this.allowImmutableEnabledUpdate = allowImmutableEnabledUpdate; + } + /** * Explicitly set the given validated user details to be authenticated. * @@ -70,7 +92,7 @@ public class AuthenticationContextImpl implements AuthenticationContext { // Apply the same validation that ACEGI would have to the user details - we may be going through a 'back // door'. - if (!ud.isEnabled()) + if (isDisabled(userId, ud)) { throw new DisabledException("User is disabled"); } @@ -114,6 +136,43 @@ public class AuthenticationContextImpl implements AuthenticationContext } } + private boolean isDisabled(String userId, UserDetails ud) + { + boolean isDisabled = !ud.isEnabled(); + boolean isSystemUser = isSystemUserName(userId); + + if (allowImmutableEnabledUpdate && !isSystemUser) + { + try + { + boolean isImmutable = isImmutableAuthority(userId); + boolean isPersonEnabled = personService.isEnabled(userId); + isDisabled = isDisabled || (isImmutable && !isPersonEnabled); + } + catch (Exception e) + { + if (logger.isWarnEnabled()) + { + logger.warn("Failed to determine if person is enabled: " + userId + ", using user details status: " + isDisabled); + } + } + } + + return isDisabled; + } + + private boolean isImmutableAuthority(String authorityName) + { + return AuthenticationUtil.runAsSystem(new RunAsWork() + { + @Override public Boolean doWork() throws Exception + { + MutableAuthenticationService mutableAuthenticationService = (MutableAuthenticationService) authenticationService; + return !mutableAuthenticationService.isAuthenticationMutable(authorityName); + } + }); + } + public Authentication setSystemUserAsCurrentUser() { return setSystemUserAsCurrentUser(TenantService.DEFAULT_DOMAIN); diff --git a/repository/src/main/java/org/alfresco/repo/security/person/SplitPersonCleanupBootstrapBean.java b/repository/src/main/java/org/alfresco/repo/security/person/SplitPersonCleanupBootstrapBean.java index 777ef52afb..c87679cc2e 100644 --- a/repository/src/main/java/org/alfresco/repo/security/person/SplitPersonCleanupBootstrapBean.java +++ b/repository/src/main/java/org/alfresco/repo/security/person/SplitPersonCleanupBootstrapBean.java @@ -1,32 +1,33 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2016 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 . - * #L% - */ +/* + * #%L + * Alfresco Repository + * %% + * 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 . + * #L% + */ package org.alfresco.repo.security.person; import java.util.Set; import java.util.TreeSet; +import java.util.UUID; import org.alfresco.model.ContentModel; import org.alfresco.repo.batch.BatchProcessor; @@ -41,7 +42,6 @@ import org.springframework.extensions.surf.util.AbstractLifecycleBean; import org.alfresco.util.GUID; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.safehaus.uuid.UUID; import org.springframework.context.ApplicationEvent; /** @@ -168,7 +168,7 @@ public class SplitPersonCleanupBootstrapBean extends AbstractLifecycleBean try { @SuppressWarnings("unused") - UUID id = new UUID(guidString); + UUID id = UUID.fromString(guidString); // We have a valid guid. return true; } @@ -188,7 +188,7 @@ public class SplitPersonCleanupBootstrapBean extends AbstractLifecycleBean try { @SuppressWarnings("unused") - UUID id = new UUID(guidString); + UUID id = UUID.fromString(guidString); // We have a valid guid. return true; } diff --git a/repository/src/main/java/org/alfresco/service/cmr/audit/AuditService.java b/repository/src/main/java/org/alfresco/service/cmr/audit/AuditService.java index 4a7bcd0091..bbb2f886ac 100644 --- a/repository/src/main/java/org/alfresco/service/cmr/audit/AuditService.java +++ b/repository/src/main/java/org/alfresco/service/cmr/audit/AuditService.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * 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 @@ -256,11 +256,10 @@ public interface AuditService /** * Issue an audit query to retrieve min / max audit record id for a given application and properties * - * @param applicationName the name of the application * @param parameters audit parameters provided by the where clause on the ReST API * @return a map containing min/max and the associated value */ - default int getAuditEntriesCountByAppAndProperties(String applicationName, AuditQueryParameters parameters) + default int getAuditEntriesCountByAppAndProperties(AuditQueryParameters parameters) { return -1; } diff --git a/repository/src/main/resources/alfresco/authentication-services-context.xml b/repository/src/main/resources/alfresco/authentication-services-context.xml index 1925c02d35..7d4f6d9666 100644 --- a/repository/src/main/resources/alfresco/authentication-services-context.xml +++ b/repository/src/main/resources/alfresco/authentication-services-context.xml @@ -274,6 +274,15 @@ + + + + + + + + ${allow.immutable.user.enabled.status.update} + diff --git a/repository/src/main/resources/alfresco/events2-context.xml b/repository/src/main/resources/alfresco/events2-context.xml index 0fb6f3ef43..aa5d78d1ba 100644 --- a/repository/src/main/resources/alfresco/events2-context.xml +++ b/repository/src/main/resources/alfresco/events2-context.xml @@ -41,7 +41,7 @@ - + @@ -55,18 +55,17 @@ + + + + + + - - - - - - - - + diff --git a/repository/src/main/resources/alfresco/ibatis/org.alfresco.repo.domain.dialect.Dialect/node-common-SqlMap.xml b/repository/src/main/resources/alfresco/ibatis/org.alfresco.repo.domain.dialect.Dialect/node-common-SqlMap.xml index 1bd8e4844d..9c1a6a0bd8 100644 --- a/repository/src/main/resources/alfresco/ibatis/org.alfresco.repo.domain.dialect.Dialect/node-common-SqlMap.xml +++ b/repository/src/main/resources/alfresco/ibatis/org.alfresco.repo.domain.dialect.Dialect/node-common-SqlMap.xml @@ -782,6 +782,25 @@ order by node.id ASC + + select diff --git a/repository/src/main/resources/alfresco/public-services-security-context.xml b/repository/src/main/resources/alfresco/public-services-security-context.xml index 8f8769493e..6b7806df0a 100644 --- a/repository/src/main/resources/alfresco/public-services-security-context.xml +++ b/repository/src/main/resources/alfresco/public-services-security-context.xml @@ -121,6 +121,8 @@ + + diff --git a/repository/src/main/resources/alfresco/repository.properties b/repository/src/main/resources/alfresco/repository.properties index 91f678dee9..a429143f61 100644 --- a/repository/src/main/resources/alfresco/repository.properties +++ b/repository/src/main/resources/alfresco/repository.properties @@ -435,6 +435,9 @@ repo.remote.endpoint=/service # persisted. create.missing.people=${server.transaction.allow-writes} +# Allow an immutable user to have its enabled status changed +allow.immutable.user.enabled.status.update=false + # Create home folders (unless disabled, see next property) as people are created (true) or create them lazily (false) home.folder.creation.eager=true # Disable home folder creation - if true then home folders are not created (neither eagerly nor lazily) @@ -1104,7 +1107,11 @@ system.fixedACLsUpdater.numThreads=4 # fixedACLsUpdater - Force shared ACL to propagate through children even if there is an unexpected ACL system.fixedACLsUpdater.forceSharedACL=false # fixedACLsUpdater cron expression - fire at midnight every day -system.fixedACLsUpdater.cronExpression=0 0 0 * * ? +system.fixedACLsUpdater.cronExpression=0 0 0 * * ? +# fixedACLsUpdater - maximum number of pending ACLs to process overall +system.fixedACLsUpdater.maxItems=-1 +# fixedACLsUpdater - Impose the order by in the query. If false, it may not process all the results but should do the queries faster +system.fixedACLsUpdater.orderNodes=true cmis.disable.hidden.leading.period.files=false @@ -1229,7 +1236,10 @@ repo.event2.filter.childAssocTypes=rn:rendition repo.event2.filter.users= # Topic name repo.event2.topic.endpoint=amqp:topic:alfresco.repo.event2 +# Specifies the strategy for sending the events +repo.event2.send.strategy= # Specifies if messages should be enqueued in in-memory queue or sent directly to the topic +# Deprecated. Please use repo.event2.send.strategy repo.event2.queue.skip=false #repo.event2.topic.endpoint=amqp:topic:VirtualTopic.alfresco.repo.event2 # Thread pool for async enqueue of repo events diff --git a/repository/src/test/java/org/alfresco/AppContext06TestSuite.java b/repository/src/test/java/org/alfresco/AppContext06TestSuite.java index 49bede2566..1c30218387 100644 --- a/repository/src/test/java/org/alfresco/AppContext06TestSuite.java +++ b/repository/src/test/java/org/alfresco/AppContext06TestSuite.java @@ -44,11 +44,6 @@ import org.junit.runners.Suite; @RunWith(Categories.class) @Categories.ExcludeCategory({DBTests.class, NonBuildTests.class}) @Suite.SuiteClasses({ - // Requires a running ActiveMQ - org.alfresco.repo.rawevents.EventBehaviourTest.class, - org.alfresco.repo.rawevents.TransactionAwareEventProducerTest.class, - org.alfresco.repo.event2.RepoEvent2ITSuite.class, - // Requires running transformers org.alfresco.transform.registry.LocalTransformServiceRegistryConfigTest.class, org.alfresco.repo.rendition2.RenditionService2IntegrationTest.class, @@ -71,7 +66,12 @@ import org.junit.runners.Suite; org.alfresco.repo.blog.BlogServiceImplTest.class, org.alfresco.repo.action.scheduled.ScheduledPersistedActionServiceTest.class, - org.alfresco.repo.rendition2.RenditionDefinitionTest.class + org.alfresco.repo.rendition2.RenditionDefinitionTest.class, + + // Requires a running ActiveMQ + org.alfresco.repo.rawevents.EventBehaviourTest.class, + org.alfresco.repo.rawevents.TransactionAwareEventProducerTest.class, + org.alfresco.repo.event2.RepoEvent2ITSuite.class, }) public class AppContext06TestSuite { diff --git a/repository/src/test/java/org/alfresco/repo/domain/permissions/FixedAclUpdaterTest.java b/repository/src/test/java/org/alfresco/repo/domain/permissions/FixedAclUpdaterTest.java index ad0dabfaf3..f66c3003a5 100644 --- a/repository/src/test/java/org/alfresco/repo/domain/permissions/FixedAclUpdaterTest.java +++ b/repository/src/test/java/org/alfresco/repo/domain/permissions/FixedAclUpdaterTest.java @@ -104,9 +104,9 @@ public class FixedAclUpdaterTest private ContentService contentService; private AuthorityService authorityService; private static final long MAX_TRANSACTION_TIME_DEFAULT = 10; + private static final int LARGE_TRANSACTION_TIME = 86_400_000; private static final int[] filesPerLevelMoreFolders = { 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; private static final int[] filesPerLevelMoreFiles = { 5, 100 }; - private long maxTransactionTime; private static HashMap> errors; private static String TEST_GROUP_NAME = "FixedACLUpdaterTest"; private static String TEST_GROUP_NAME_FULL = PermissionService.GROUP_PREFIX + TEST_GROUP_NAME; @@ -134,8 +134,11 @@ public class FixedAclUpdaterTest AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); homeFolderNodeRef = repository.getCompanyHome(); - maxTransactionTime = MAX_TRANSACTION_TIME_DEFAULT; - setFixedAclMaxTransactionTime(permissionsDaoComponent, homeFolderNodeRef, maxTransactionTime); + setFixedAclMaxTransactionTime(permissionsDaoComponent, homeFolderNodeRef, MAX_TRANSACTION_TIME_DEFAULT); + + fixedAclUpdater.setForceSharedACL(false); + fixedAclUpdater.setMaxItems(-1); + fixedAclUpdater.setOrderNodes(true); } @After @@ -155,8 +158,7 @@ public class FixedAclUpdaterTest try { - maxTransactionTime = 86400000; - setFixedAclMaxTransactionTime(permissionsDaoComponent, homeFolderNodeRef, maxTransactionTime); + setFixedAclMaxTransactionTime(permissionsDaoComponent, homeFolderNodeRef, LARGE_TRANSACTION_TIME); setPermissionsOnTree(folderRef, false, false); aclComparator.compareACLs(); @@ -164,6 +166,7 @@ public class FixedAclUpdaterTest } finally { + setFixedAclMaxTransactionTime(permissionsDaoComponent, homeFolderNodeRef, MAX_TRANSACTION_TIME_DEFAULT); deleteNodes(folderRef); } } @@ -344,8 +347,7 @@ public class FixedAclUpdaterTest try { - maxTransactionTime = 86400000; - setFixedAclMaxTransactionTime(permissionsDaoComponent, homeFolderNodeRef, maxTransactionTime); + setFixedAclMaxTransactionTime(permissionsDaoComponent, homeFolderNodeRef, LARGE_TRANSACTION_TIME); // Set permissions on target folder txnHelper.doInTransaction((RetryingTransactionCallback) () -> { @@ -386,6 +388,7 @@ public class FixedAclUpdaterTest } finally { + setFixedAclMaxTransactionTime(permissionsDaoComponent, homeFolderNodeRef, MAX_TRANSACTION_TIME_DEFAULT); deleteNodes(originalRef); deleteNodes(targetRefBase); } @@ -1438,6 +1441,79 @@ public class FixedAclUpdaterTest } } + /* + * Test with maxItems limit + */ + @Test + @RetryAtMost(3) + public void testWithLimits() + { + NodeRef folderRef = createFolderHierarchyInRootForFileTests("testWithLimitsFolder"); + + try + { + int maxItems = 200; + setPermissionsOnTree(folderRef, true, true); + + // Get the current amount of pending ACls + int initialPendingAcls = getNodesCountWithPendingFixedAclAspect(); + + // We need at least maxItems+1 pending ACLs + while (initialPendingAcls <= maxItems && initialPendingAcls > 0) + { + // Trigger the job a single round each time to create new pendings until we have enough + triggerFixedACLJob(false,true,maxItems,1); + initialPendingAcls = getNodesCountWithPendingFixedAclAspect(); + } + + assertTrue("We don't have enough pending acls to test", initialPendingAcls > 0); + + // Increase transaction time to not create new pending ACLs + setFixedAclMaxTransactionTime(permissionsDaoComponent, homeFolderNodeRef, LARGE_TRANSACTION_TIME); + + // Trigger job in single round without timeout + triggerFixedACLJob(false,true,maxItems,1); + + int finalPendingAcls = getNodesCountWithPendingFixedAclAspect(); + + assertTrue("Processed ACLs should not have exceeded 200", (initialPendingAcls - finalPendingAcls) <= maxItems); + } + finally + { + setFixedAclMaxTransactionTime(permissionsDaoComponent, homeFolderNodeRef, MAX_TRANSACTION_TIME_DEFAULT); + deleteNodes(folderRef); + } + } + + /* + * Test without imposing the order by + */ + @Test + @RetryAtMost(3) + public void testUnordered() + { + NodeRef folderRef = createFolderHierarchyInRootForFileTests("testWithLimitsFolder"); + + try + { + setPermissionsOnTree(folderRef, true, true); + + int initialPendingAcls = getNodesCountWithPendingFixedAclAspect(); + assertTrue("We don't have enough pending acls to test", initialPendingAcls > 0); + + triggerFixedACLJob(false,true,-1,30); + + int finalPendingAcls = getNodesCountWithPendingFixedAclAspect(); + + assertEquals("Not all ACls were processed",0, finalPendingAcls); + } + finally + { + setFixedAclMaxTransactionTime(permissionsDaoComponent, homeFolderNodeRef, MAX_TRANSACTION_TIME_DEFAULT); + deleteNodes(folderRef); + } + } + private Long getChild(Long parentId) { List children = fileFolderService.list(nodeDAO.getNodePair(parentId).getSecond()); @@ -1601,13 +1677,18 @@ public class FixedAclUpdaterTest private void triggerFixedACLJob() { - triggerFixedACLJob(false); + // Trigger job 30 times max to process all nodes + triggerFixedACLJob(false, true, -1, 30); } private void triggerFixedACLJob(boolean forceSharedACL) + { + triggerFixedACLJob(forceSharedACL, true, -1, 30); + } + + private void triggerFixedACLJob(boolean forceSharedACL, boolean orderNodes, int maxItems, int rounds) { LOG.debug("Fixing ACL"); - final int rounds = 30; final int enoughZeros = 3; int numberOfConsecutiveZeros = 0; @@ -1615,6 +1696,8 @@ public class FixedAclUpdaterTest { int count = txnHelper.doInTransaction(() -> { fixedAclUpdater.setForceSharedACL(forceSharedACL); + fixedAclUpdater.setMaxItems(maxItems); + fixedAclUpdater.setOrderNodes(orderNodes); return fixedAclUpdater.execute(); }, false, true); numberOfConsecutiveZeros = count == 0 ? numberOfConsecutiveZeros + 1 : 0; diff --git a/repository/src/test/java/org/alfresco/repo/event2/AbstractContextAwareRepoEvent.java b/repository/src/test/java/org/alfresco/repo/event2/AbstractContextAwareRepoEvent.java index d7af0a25e7..6edf471720 100644 --- a/repository/src/test/java/org/alfresco/repo/event2/AbstractContextAwareRepoEvent.java +++ b/repository/src/test/java/org/alfresco/repo/event2/AbstractContextAwareRepoEvent.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2023 Alfresco Software Limited + * 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 @@ -71,14 +71,14 @@ import org.junit.Before; import org.junit.BeforeClass; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.beans.factory.annotation.Value; import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.test.context.TestPropertySource; /** * @author Iulian Aftene */ - +@TestPropertySource(properties = {"repo.event2.queue.skip=false"}) public abstract class AbstractContextAwareRepoEvent extends BaseSpringTest { protected static final boolean DEBUG = false; @@ -123,9 +123,6 @@ public abstract class AbstractContextAwareRepoEvent extends BaseSpringTest @Autowired private NamespaceDAO namespaceDAO; - @Value("${repo.event2.queue.skip}") - protected boolean skipEventQueue; - protected NodeRef rootNodeRef; @BeforeClass diff --git a/repository/src/test/java/org/alfresco/repo/event2/DirectEventGeneratorTest.java b/repository/src/test/java/org/alfresco/repo/event2/DirectEventGeneratorTest.java index 1298ded98d..94349cfbbc 100644 --- a/repository/src/test/java/org/alfresco/repo/event2/DirectEventGeneratorTest.java +++ b/repository/src/test/java/org/alfresco/repo/event2/DirectEventGeneratorTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2023 Alfresco Software Limited + * 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 @@ -25,78 +25,32 @@ */ package org.alfresco.repo.event2; -import java.util.HashSet; -import java.util.Set; - -import org.junit.BeforeClass; import org.junit.Test; -import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.config.BeanPostProcessor; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.ContextHierarchy; +import org.springframework.test.context.TestPropertySource; -@ContextHierarchy({ - // Context hierarchy inherits context config from parent classes and extends it with TestConfig from this class - @ContextConfiguration(classes = DirectEventGeneratorTest.TestConfig.class) -}) -@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS) +import java.util.Collection; + +@TestPropertySource(properties = {"repo.event2.queue.skip=true"}) public class DirectEventGeneratorTest extends EventGeneratorTest { @Autowired - private InstantiatedBeansRegistry instantiatedBeansRegistry; - + private EventSender eventSender; @Autowired - private EventSender directEventSender; - - @BeforeClass - public static void beforeClass() - { - System.setProperty("repo.event2.queue.skip", "true"); - } + private Collection allEventSenderBeans; @Test - public void testIfEnqueuingEventSenderIsNotInstantiated() + public void testIfOnlyRequiredEventSenderIsInstantiated() { - final Set instantiatedBeans = this.instantiatedBeansRegistry.getBeans(); - - assertTrue(skipEventQueue); - assertFalse(instantiatedBeans.contains("enqueuingEventSender")); + assertEquals(1, allEventSenderBeans.size()); + assertTrue(allEventSenderBeans.contains(eventSender)); } + @Test public void testIfDirectSenderIsSetInEventGenerator() { - assertTrue(skipEventQueue); - assertEquals(directEventSender, eventGenerator.getEventSender()); - } - - @Configuration - public static class TestConfig - { - @Bean - public BeanPostProcessor instantiatedBeansRegistry() - { - return new InstantiatedBeansRegistry(); - } - } - - protected static class InstantiatedBeansRegistry implements BeanPostProcessor - { - private final Set registeredBeans = new HashSet<>(); - - @Override - public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException - { - registeredBeans.add(beanName); - return bean; - } - - public Set getBeans() { - return registeredBeans; - } + assertEquals(DirectEventSender.class, eventSender.getClass()); + assertEquals(eventSender, eventGenerator.getEventSender()); } } diff --git a/repository/src/test/java/org/alfresco/repo/event2/EnqueuingEventGeneratorTest.java b/repository/src/test/java/org/alfresco/repo/event2/EnqueuingEventGeneratorTest.java index 06e0643ec6..244f89ed23 100644 --- a/repository/src/test/java/org/alfresco/repo/event2/EnqueuingEventGeneratorTest.java +++ b/repository/src/test/java/org/alfresco/repo/event2/EnqueuingEventGeneratorTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2023 Alfresco Software Limited + * 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 @@ -27,9 +27,9 @@ package org.alfresco.repo.event2; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.TestPropertySource; -@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS) +@TestPropertySource(properties = {"repo.event2.queue.skip=false"}) public class EnqueuingEventGeneratorTest extends EventGeneratorTest { @Autowired @@ -38,7 +38,7 @@ public class EnqueuingEventGeneratorTest extends EventGeneratorTest @Test public void testIfEnqueuingSenderIsSetInEventGenerator() { - assertFalse(skipEventQueue); + assertEquals(EnqueuingEventSender.class, enqueuingEventSender.getClass()); assertEquals(enqueuingEventSender, eventGenerator.getEventSender()); } } diff --git a/repository/src/test/java/org/alfresco/repo/event2/EnqueuingEventSenderUnitTest.java b/repository/src/test/java/org/alfresco/repo/event2/EnqueuingEventSenderUnitTest.java index ce8604f326..162c99b7dd 100644 --- a/repository/src/test/java/org/alfresco/repo/event2/EnqueuingEventSenderUnitTest.java +++ b/repository/src/test/java/org/alfresco/repo/event2/EnqueuingEventSenderUnitTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2023 Alfresco Software Limited + * 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 @@ -65,15 +65,11 @@ public class EnqueuingEventSenderUnitTest @Before public void setup() { - eventSender = new EnqueuingEventSender(); - - enqueuePool = newThreadPool(); - eventSender.setEnqueueThreadPoolExecutor(enqueuePool); - dequeuePool = newThreadPool(); - eventSender.setDequeueThreadPoolExecutor(dequeuePool); - bus = mock(Event2MessageProducer.class); - eventSender.setEvent2MessageProducer(bus); + enqueuePool = newThreadPool(); + dequeuePool = newThreadPool(); + eventSender = new EnqueuingEventSender(bus, enqueuePool, dequeuePool); + eventSender.initialize(); events = new HashMap<>(); diff --git a/repository/src/test/java/org/alfresco/repo/event2/EventGeneratorTest.java b/repository/src/test/java/org/alfresco/repo/event2/EventGeneratorTest.java index 561cb77ae1..25c6f96cc2 100644 --- a/repository/src/test/java/org/alfresco/repo/event2/EventGeneratorTest.java +++ b/repository/src/test/java/org/alfresco/repo/event2/EventGeneratorTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2023 Alfresco Software Limited + * 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 @@ -49,7 +49,6 @@ import org.apache.activemq.command.ActiveMQTopic; import org.awaitility.Awaitility; import org.junit.After; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; public abstract class EventGeneratorTest extends AbstractContextAwareRepoEvent @@ -60,12 +59,6 @@ public abstract class EventGeneratorTest extends AbstractContextAwareRepoEvent private ActiveMQConnection connection; protected List> receivedEvents; - @BeforeClass - public static void beforeClass() - { - System.setProperty("repo.event2.queue.skip", "false"); - } - @Before public void startupTopicListener() throws Exception { diff --git a/repository/src/test/java/org/alfresco/repo/quickshare/QuickShareServiceIntegrationTest.java b/repository/src/test/java/org/alfresco/repo/quickshare/QuickShareServiceIntegrationTest.java index 1c50d23fd6..fbba0bdba2 100644 --- a/repository/src/test/java/org/alfresco/repo/quickshare/QuickShareServiceIntegrationTest.java +++ b/repository/src/test/java/org/alfresco/repo/quickshare/QuickShareServiceIntegrationTest.java @@ -37,7 +37,10 @@ import java.io.Serializable; import java.util.Date; import java.util.Map; import java.util.Properties; +import java.util.UUID; +import com.fasterxml.uuid.Generators; +import com.fasterxml.uuid.impl.UUIDUtil; import org.alfresco.model.ContentModel; import org.alfresco.model.QuickShareModel; import org.alfresco.repo.model.Repository; @@ -83,8 +86,6 @@ import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.rules.RuleChain; -import org.safehaus.uuid.UUID; -import org.safehaus.uuid.UUIDGenerator; import org.springframework.context.ApplicationContext; /** @@ -420,8 +421,8 @@ public class QuickShareServiceIntegrationTest @Test(expected=InvalidSharedIdException.class) public void getMetadataFromShareIdWithInvalidId() { - UUID uuid = UUIDGenerator.getInstance().generateRandomBasedUUID(); - String sharedId = Base64.encodeBase64URLSafeString(uuid.toByteArray()); // => 22 chars (eg. q3bEKPeDQvmJYgt4hJxOjw) + UUID uuid = Generators.randomBasedGenerator().generate(); + String sharedId = Base64.encodeBase64URLSafeString(UUIDUtil.asByteArray(uuid)); // => 22 chars (eg. q3bEKPeDQvmJYgt4hJxOjw) Map metadata = quickShareService.getMetaData(sharedId); } @@ -884,7 +885,7 @@ public class QuickShareServiceIntegrationTest { // Create a private site AuthenticationUtil.setFullyAuthenticatedUser(user1.getUsername()); - String randomUUID = UUIDGenerator.getInstance().generateRandomBasedUUID().toString(); + String randomUUID = Generators.randomBasedGenerator().generate().toString(); String siteName = "testSite" + randomUUID; siteService.createSite("site-dashboard", siteName, "Title for " + siteName, "Description for " + siteName, SiteVisibility.PRIVATE);