From d878fb5033ffae662f6b2bdd7a303a788922a8fa Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Fri, 7 Oct 2022 12:51:00 +0200 Subject: [PATCH 01/46] making sure pr's are updated for LTS --- .github/workflows/pr.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 6fa61be27..610e2fec3 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -6,9 +6,11 @@ on: - "main" - "master" - "development" + - "releases/**" pull_request: branches: - development + - "releases/**" jobs: tests: @@ -30,4 +32,4 @@ jobs: - name: Commit Format Changes uses: stefanzweifel/git-auto-commit-action@v4 with: - commit_message: Apply cfformat changes \ No newline at end of file + commit_message: Apply cfformat changes From 9dde373c56df51f2a95d62845cecfb9ed13c72e6 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Fri, 28 Apr 2023 16:52:37 -0500 Subject: [PATCH 02/46] trying out lts releases --- .github/CODE_OF_CONDUCT.MD | 3 + .github/ISSUE_TEMPLATE/BUG_REPORT.md | 33 ++++ .github/ISSUE_TEMPLATE/FEATURE_REQUEST.md | 18 ++ .github/PULL_REQUEST_TEMPLATE.md | 32 ++++ .github/SECURITY.md | 3 + .github/SUPPORT.md | 3 + .github/workflows/gh-release.yml | 19 -- .github/workflows/lts.yml | 34 ++++ .github/workflows/pr.yml | 20 +-- .github/workflows/release.yml | 141 ++++++++++++--- .github/workflows/snapshot.yml | 36 ++-- .github/workflows/tests.yml | 57 ++++-- changelog.md | 209 +++++++++++----------- readme.md | 15 +- 14 files changed, 431 insertions(+), 192 deletions(-) create mode 100644 .github/CODE_OF_CONDUCT.MD create mode 100644 .github/ISSUE_TEMPLATE/BUG_REPORT.md create mode 100644 .github/ISSUE_TEMPLATE/FEATURE_REQUEST.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/SECURITY.md create mode 100644 .github/SUPPORT.md delete mode 100644 .github/workflows/gh-release.yml create mode 100644 .github/workflows/lts.yml diff --git a/.github/CODE_OF_CONDUCT.MD b/.github/CODE_OF_CONDUCT.MD new file mode 100644 index 000000000..12507abcb --- /dev/null +++ b/.github/CODE_OF_CONDUCT.MD @@ -0,0 +1,3 @@ +# Code of Conduct + +Please see it in our [Contributing Guidelines](../CONTRIBUTING.md#code-of-conduct). diff --git a/.github/ISSUE_TEMPLATE/BUG_REPORT.md b/.github/ISSUE_TEMPLATE/BUG_REPORT.md new file mode 100644 index 000000000..300232e8d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/BUG_REPORT.md @@ -0,0 +1,33 @@ +--- +name: Bug report +about: Create a report to help us improve +--- + + + +## What are the steps to reproduce this issue? + +1. … +2. … +3. … + +## What happens? + +… + +## What were you expecting to happen? + +… + +## Any logs, error output, etc? + +… + +## Any other comments? + +… + +## What versions are you using? + +**Operating System:** … +**Package Version:** … diff --git a/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md b/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md new file mode 100644 index 000000000..c10946f8c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md @@ -0,0 +1,18 @@ +--- +name: Feature Request +about: Request a new feature or enhancement +--- + + + +## Summary + + + +## Detailed Description + + + +## Possible Implementation Ideas + + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..2d2353e40 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,32 @@ +# Description + +Please include a summary of the changes and which issue(s) is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. + +**Please note that all PRs must have tests attached to them** + +IMPORTANT: Please review the [CONTRIBUTING.md](../CONTRIBUTING.md) file for detailed contributing guidelines. + +## Jira Issues + +All PRs must have an accompanied Jira issue. Please make sure you created it and linked it here. + +> Bug Tracker: https://ortussolutions.atlassian.net/jira/software/c/projects/COLDBOX/issues + + +## Type of change + +Please delete options that are not relevant. + +- [ ] Bug Fix +- [ ] Improvement +- [ ] New Feature +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] This change requires a documentation update + +## Checklist + +- [ ] My code follows the style guidelines of this project [cfformat](../.cfformat.json) +- [ ] I have commented my code, particularly in hard-to-understand areas +- [ ] I have made corresponding changes to the documentation +- [ ] I have added tests that prove my fix is effective or that my feature works +- [ ] New and existing unit tests pass locally with my changes diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 000000000..f05709993 --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,3 @@ +# Security Policy + +Please see it in our [Contributing Guidelines](../CONTRIBUTING.md#security-vulnerabilities). diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md new file mode 100644 index 000000000..3bb8adb0e --- /dev/null +++ b/.github/SUPPORT.md @@ -0,0 +1,3 @@ +# Support & Help + +Please see it in our [Contributing Guidelines](../CONTRIBUTING.md#support-questions). diff --git a/.github/workflows/gh-release.yml b/.github/workflows/gh-release.yml deleted file mode 100644 index 5193ae57c..000000000 --- a/.github/workflows/gh-release.yml +++ /dev/null @@ -1,19 +0,0 @@ -# Publish Github Release -name: Github Release - -on: - push: - tags: - - v[0-9]+.* - -jobs: - create-release: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: taiki-e/create-gh-release-action@v1.5.0 - with: - # Produced by the build/Build.cfc - changelog: changelog.md - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/lts.yml b/.github/workflows/lts.yml new file mode 100644 index 000000000..bb4c27beb --- /dev/null +++ b/.github/workflows/lts.yml @@ -0,0 +1,34 @@ +name: ColdBox LTS Flow + +on: + push: + branches: + - "releases/**" + +jobs: + ############################################# + # Tests First baby! We fail, no build :( + ############################################# + tests: + uses: ./.github/workflows/tests.yml + secrets: inherit + + ########################################################################################## + # Format Source Code + ########################################################################################## + format: + name: Code Auto-Formatting + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v3 + + - name: Auto-format + uses: Ortus-Solutions/commandbox-action@v1.0.2 + with: + cmd: run-script format + + - name: Commit Format Changes + uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: Apply cfformat changes + push_options: --force diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 610e2fec3..540017d95 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -1,4 +1,4 @@ -name: Coldbox Pull Requests +name: Pull Requests on: push: @@ -14,22 +14,16 @@ on: jobs: tests: - uses: ColdBox/coldbox-platform/.github/workflows/tests.yml@development - secrets: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + uses: ./.github/workflows/tests.yml + secrets: inherit - format: - name: Format + format_check: + name: Checks Source Code Formatting runs-on: ubuntu-20.04 steps: - name: Checkout Repository - uses: actions/checkout@v2 + uses: actions/checkout@v3.2.0 - uses: Ortus-Solutions/commandbox-action@v1.0.2 with: - cmd: run-script format - - - name: Commit Format Changes - uses: stefanzweifel/git-auto-commit-action@v4 - with: - commit_message: Apply cfformat changes + cmd: run-script format:check diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e8a7ff9ea..62b52c0d9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,24 +1,35 @@ -# This workflow is used to release master releases and also by the snapshot workflow -# to release snapshots. +# This workflow is used to build releases +# It can also be called by other workflows to reuse the release flow. name: ColdBox Release on: + # If you push to master|main this will trigger a stable release push: branches: - master + - main + + # Reusable workflow : Usually called by a `snapshot` workflow workflow_call: - secrets: - SLACK_WEBHOOK_URL: - required: true - FORGEBOX_API_TOKEN: - required: true - AWS_ACCESS_KEY: - required: true - AWS_ACCESS_SECRET: - required: true + inputs: + snapshot: + description: 'Is this a snapshot build?' + required: false + default: false + type: boolean + + # Manual Trigger for LTS Releases + workflow_dispatch: + inputs: + lts: + description: 'The LTS marker' + required: false + default: true + type: boolean env: - COLDBOX_PRERELEASE: false + SNAPSHOT: ${{ inputs.snapshot || false }} + LTS: ${{ inputs.lts || false }} jobs: ############################################# @@ -29,30 +40,18 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Checkout Repository - uses: actions/checkout@v2 - with: - fetch-depth: 0 + uses: actions/checkout@v3 - name: Setup Java - uses: actions/setup-java@v2 + uses: actions/setup-java@v3 with: - distribution: "adopt" + distribution: "temurin" java-version: "11" - - name: Cache CommandBox Dependencies - uses: actions/cache@v1 - if: ${{ true }} - with: - path: ~/.CommandBox/artifacts - key: ${{ runner.OS }}-commandbox-cache-${{ hashFiles( 'box.json' ) }} - restore-keys: | - ${{ runner.OS }}-commandbox-cache-${{ hashFiles( 'box.json' ) }} - - name: Setup CommandBox - uses: Ortus-Solutions/setup-commandbox@v2.0.0 + uses: Ortus-Solutions/setup-commandbox@v2.0.1 with: forgeboxAPIKey: ${{ secrets.FORGEBOX_API_TOKEN }} - version: 5.4.2 - name: Setup Environment Variables For Build Process id: current_version @@ -60,6 +59,7 @@ jobs: echo "COLDBOX_VERSION=`cat box.json | jq '.version' -r`" >> $GITHUB_ENV box package set version=@build.version@+@build.number@ # master or snapshot + echo "Github Ref is $GITHUB_REF" echo "BRANCH=master" >> $GITHUB_ENV if [ $GITHUB_REF == 'refs/heads/development' ] then @@ -70,11 +70,37 @@ jobs: run: | cd apidocs && box install + - name: Update changelog [unreleased] with latest version + uses: thomaseizinger/keep-a-changelog-new-release@1.3.0 + if: env.SNAPSHOT == 'false' + with: + changelogPath: ./changelog.md + tag: v${{ env.COLDBOX_VERSION }} + - name: Build ColdBox Variants for ${{ env.BRANCH }} v${{ env.COLDBOX_VERSION }} run: | + npm install -g markdownlint-cli + markdownlint changelog.md --fix box server start serverConfigFile="server-lucee@5.json" --debug ant -DisPreRelease=${{ env.COLDBOX_PRERELEASE }} -Dcoldbox.version=${{ env.COLDBOX_VERSION }} -Dbuild.branch=${{ env.BRANCH }} -Dbuild.number=${{ github.run_number }} -f build/build.xml + - name: Commit Changelog [unreleased] with latest version + uses: EndBug/add-and-commit@v9.1.1 + if: env.SNAPSHOT == 'false' + with: + author_name: Github Actions + author_email: info@ortussolutions.com + message: 'Finalized changelog for v${{ env.COLDBOX_VERSION }}' + add: changelog.md + + - name: Tag Version + uses: rickstaa/action-create-tag@v1.6.1 + if: env.SNAPSHOT == 'false' + with: + tag: "v${{ env.COLDBOX_VERSION }}" + force_push_tag: true + message: "Latest Release v${{ env.COLDBOX_VERSION }}" + - name: Upload Build Artifacts if: success() uses: actions/upload-artifact@v2 @@ -82,6 +108,7 @@ jobs: name: coldbox-variants path: | artifacts/**/* + changelog.md - name: Upload Binaries to S3 uses: jakejarvis/s3-sync-action@master @@ -113,6 +140,16 @@ jobs: cd $ROOT_DIR/artifacts/wirebox/${{ env.COLDBOX_VERSION }} && box forgebox publish cd $ROOT_DIR/artifacts/logbox/${{ env.COLDBOX_VERSION }} && box forgebox publish + - name: Create Github Release + uses: taiki-e/create-gh-release-action@v1.6.2 + continue-on-error: true + if: env.SNAPSHOT == 'false' + with: + title: ${{ env.COLDBOX_VERSION }} + changelog: changelog.md + token: ${{ secrets.GITHUB_TOKEN }} + ref: refs/tags/v${{ env.COLDBOX_VERSION }} + - name: Inform Slack if: ${{ always() }} uses: rtCamp/action-slack-notify@v2 @@ -124,3 +161,51 @@ jobs: SLACK_TITLE: "ColdBox Build" SLACK_USERNAME: CI SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }} + + ########################################################################################## + # Prep Next Release + ########################################################################################## + prep_next_release: + name: Prep Next Release + if: github.ref != 'refs/heads/development' + runs-on: ubuntu-20.04 + needs: [ build ] + steps: + - name: Checkout Development Repository + uses: actions/checkout@v3 + if: env.LTS == 'false' + with: + ref: development + + - name: Checkout LTS Repository + uses: actions/checkout@v3 + if: env.LTS == 'true' + + - name: Setup CommandBox + uses: Ortus-Solutions/setup-commandbox@v2.0.1 + with: + forgeboxAPIKey: ${{ secrets.FORGEBOX_TOKEN }} + + - name: Download build artifacts + uses: actions/download-artifact@v2 + with: + name: coldbox-variants + path: .tmp + + - name: Copy Changelog + run: | + cp .tmp/changelog.md changelog.md + + - name: Bump Version + run: | + box bump --minor --!TagVersion + + - name: Commit Version Bump + uses: EndBug/add-and-commit@v9.1.1 + with: + author_name: Github Actions + author_email: info@ortussolutions.com + message: 'Version bump' + add: | + box.json + changelog.md diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml index 04f1a3d91..947b2b144 100644 --- a/.github/workflows/snapshot.yml +++ b/.github/workflows/snapshot.yml @@ -7,26 +7,40 @@ on: branches: - development -env: - COLDBOX_PRERELEASE: false - jobs: ############################################# # Tests First baby! We fail, no build :( ############################################# tests: uses: ./.github/workflows/tests.yml - secrets: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + secrets: inherit + + ########################################################################################## + # Format Source Code + ########################################################################################## + format: + name: Code Auto-Formatting + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v3 + + - name: Auto-format + uses: Ortus-Solutions/commandbox-action@v1.0.2 + with: + cmd: run-script format + + - name: Commit Format Changes + uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: Apply cfformat changes + push_options: --force ############################################# # Build Snapshot Release ############################################# build: - needs: tests uses: ./.github/workflows/release.yml - secrets: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} - FORGEBOX_API_TOKEN: ${{ secrets.FORGEBOX_API_TOKEN }} - AWS_ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY }} - AWS_ACCESS_SECRET: ${{ secrets.AWS_ACCESS_SECRET }} + needs: [ tests, format ] + secrets: inherit + with: + snapshot: true diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e58e41270..092a7b573 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -15,11 +15,35 @@ jobs: env: DB_USER: root DB_PASSWORD: root + continue-on-error: ${{ matrix.experimental }} strategy: fail-fast: false matrix: - cfengine: [ "lucee@5", "adobe@2016", "adobe@2018", "adobe@2021" ] - fullNull: [ "true", "false" ] + commandbox_version: [ "5.8.0" ] + cfengine: [ "lucee@5", "adobe@2018", "adobe@2021" ] + jdkVersion: [ "11" ] + experimental: [false] + include: + - cfengine: "lucee@5rc" + commandbox_version: "6.0.0-alpha" + jdkVersion: "11" + experimental: true + - cfengine: "lucee@6" + commandbox_version: "6.0.0-alpha" + jdkVersion: "11" + experimental: true + - cfengine: "lucee@6" + commandbox_version: "6.0.0-alpha" + jdkVersion: "17" + experimental: true + - cfengine: "adobe@2023" + commandbox_version: "6.0.0-alpha" + jdkVersion: "11" + experimental: true + - cfengine: "adobe@2023" + commandbox_version: "6.0.0-alpha" + jdkVersion: "17" + experimental: true steps: - name: Checkout Repository uses: actions/checkout@v2 @@ -31,15 +55,15 @@ jobs: mysql -u${{ env.DB_USER }} -p${{ env.DB_PASSWORD }} < tests/resources/coolblog.sql - name: Setup Java - uses: actions/setup-java@v2 + uses: actions/setup-java@v3 with: - distribution: "adopt" - java-version: "11" + distribution: "temurin" + java-version: ${{ matrix.jdkVersion }} - name: Setup CommandBox CLI - uses: Ortus-Solutions/setup-commandbox@v2.0.0 + uses: Ortus-Solutions/setup-commandbox@v2.0.1 with: - version: 5.4.2 + version: ${{ matrix.commandbox_version }} - name: Setup .env For Runner run: | @@ -54,7 +78,7 @@ jobs: printf "DB_BUNDLENAME=com.mysql.cj\n" >> .env - name: Cache CommandBox Dependencies - uses: actions/cache@v1 + uses: actions/cache@v3 if: ${{ true }} with: path: ~/.CommandBox/artifacts @@ -66,31 +90,26 @@ jobs: run: | # Core dependencies box install - # API Docs dependencies - cd apidocs && box install - name: Start ${{ matrix.cfengine }} Server run: | box server start serverConfigFile="server-${{ matrix.cfengine }}.json" --noSaveSettings --debug - # Install Adobe 2021 cfpm modules - if [[ "${{ matrix.cfengine }}" == "adobe@2021" ]] ; then - box run-script install:2021 - fi # Test the harness curl http://127.0.0.1:8599/test-harness - - name: Run Tests Full Null (${{ matrix.fullNull }}) - env: - FULL_NULL: ${{ matrix.fullNull }} + - name: Run Tests run: | ant -f build/build.xml run-tests + - name: Set cfengine version env + run: echo "CFENGINE_VERSION=$(box echo ${serverInfo.engineName@coldbox-${{ matrix.cfengine }}}@${serverInfo.engineVersion@coldbox-${{ matrix.cfengine }}})" >> $GITHUB_ENV + - name: Publish Test Results - uses: EnricoMi/publish-unit-test-result-action@v1 + uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: files: tests/results/**/*.xml - check_name: "${{ matrix.cfengine }} Test Results" + check_name: "${{ env.CFENGINE_VERSION }} Test Results" - name: Upload Test Results Artifacts if: always() diff --git a/changelog.md b/changelog.md index 60d824592..1197bc8f0 100644 --- a/changelog.md +++ b/changelog.md @@ -7,14 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ---- +## [Unreleased] + +### Added + +- Github actions for LTS Releases +- LTS Updates + ## [6.8.1] => 2022-AUG-11 ### ColdBox HMVC Core #### Bug -* [COLDBOX-1139](https://ortussolutions.atlassian.net/browse/COLDBOX-1139) make event caching cache keys lower cased to avoid case issues when clearing keys -* [COLDBOX-1138](https://ortussolutions.atlassian.net/browse/COLDBOX-1138) Event Cache Response Has Status Code of 0 or `null` +- [COLDBOX-1139](https://ortussolutions.atlassian.net/browse/COLDBOX-1139) make event caching cache keys lower cased to avoid case issues when clearing keys +- [COLDBOX-1138](https://ortussolutions.atlassian.net/browse/COLDBOX-1138) Event Cache Response Has Status Code of 0 or `null` ---- @@ -24,22 +31,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 #### Bug -* [COLDBOX-1134](https://ortussolutions.atlassian.net/browse/COLDBOX-1134) Router closure responses not marshalling complex content to json -* [COLDBOX-1132](https://ortussolutions.atlassian.net/browse/COLDBOX-1132) New virtual app was always starting up the virtual coldbox app instead of checking if it was running already +- [COLDBOX-1134](https://ortussolutions.atlassian.net/browse/COLDBOX-1134) Router closure responses not marshalling complex content to json +- [COLDBOX-1132](https://ortussolutions.atlassian.net/browse/COLDBOX-1132) New virtual app was always starting up the virtual coldbox app instead of checking if it was running already #### Improvement -* [COLDBOX-1131](https://ortussolutions.atlassian.net/browse/COLDBOX-1131) Updated Missing Action Response Code to 404 instead of 405 -* [COLDBOX-1127](https://ortussolutions.atlassian.net/browse/COLDBOX-1127) All core async proxies should send exceptions to the error log +- [COLDBOX-1131](https://ortussolutions.atlassian.net/browse/COLDBOX-1131) Updated Missing Action Response Code to 404 instead of 405 +- [COLDBOX-1127](https://ortussolutions.atlassian.net/browse/COLDBOX-1127) All core async proxies should send exceptions to the error log #### New Feature -* [COLDBOX-1130](https://ortussolutions.atlassian.net/browse/COLDBOX-1130) New config/ColdBox.cfc global injections: webMapping, coldboxVersion -* [COLDBOX-1126](https://ortussolutions.atlassian.net/browse/COLDBOX-1126) Funnel all out and err logging on a ColdBox Scheduled Task to LogBox +- [COLDBOX-1130](https://ortussolutions.atlassian.net/browse/COLDBOX-1130) New config/ColdBox.cfc global injections: webMapping, coldboxVersion +- [COLDBOX-1126](https://ortussolutions.atlassian.net/browse/COLDBOX-1126) Funnel all out and err logging on a ColdBox Scheduled Task to LogBox #### Task -* [COLDBOX-1135](https://ortussolutions.atlassian.net/browse/COLDBOX-1135) Remove HandlerTestCase as it is no longer in usage. +- [COLDBOX-1135](https://ortussolutions.atlassian.net/browse/COLDBOX-1135) Remove HandlerTestCase as it is no longer in usage. ---- @@ -49,81 +56,81 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 #### Bug -* [COLDBOX-1114](https://ortussolutions.atlassian.net/browse/COLDBOX-1114) Persistance of variables failing due to null support -* [COLDBOX-1110](https://ortussolutions.atlassian.net/browse/COLDBOX-1110) Renderer is causing coldbox RestHandler to render convention view -* [COLDBOX-1109](https://ortussolutions.atlassian.net/browse/COLDBOX-1109) Exceptions in async interceptors are missing onException announcement -* [COLDBOX-1105](https://ortussolutions.atlassian.net/browse/COLDBOX-1105) Interception with `async` annotation causes InterceptorState Exception on Reinit -* [COLDBOX-1104](https://ortussolutions.atlassian.net/browse/COLDBOX-1104) A view not set exception is thrown when trying to execution handler ColdBox methods that are not concrete actions when they should be invalid events. -* [COLDBOX-1103](https://ortussolutions.atlassian.net/browse/COLDBOX-1103) Update getServerIP\(\) so it avoids looking at the cgi scope as it can cause issues on ACF -* [COLDBOX-1100](https://ortussolutions.atlassian.net/browse/COLDBOX-1100) Event Caching Does Not Preserve HTTP Response Codes -* [COLDBOX-1099](https://ortussolutions.atlassian.net/browse/COLDBOX-1099) Regression on ColdBox v6.6.1 around usage of statusCode = 0 on relocates -* [COLDBOX-1098](https://ortussolutions.atlassian.net/browse/COLDBOX-1098) RequestService context creation not thread safe -* [COLDBOX-1097](https://ortussolutions.atlassian.net/browse/COLDBOX-1097) Missing scopes on isNull\(\) checks -* [COLDBOX-1092](https://ortussolutions.atlassian.net/browse/COLDBOX-1092) RestHandler Try/Catches Break In Testbox When RunEvent\(\) is Called -* [COLDBOX-1045](https://ortussolutions.atlassian.net/browse/COLDBOX-1045) Scheduled tasks have no default error handling -* [COLDBOX-1043](https://ortussolutions.atlassian.net/browse/COLDBOX-1043) Creating scheduled task with unrecognized timeUnit throws null pointer -* [COLDBOX-1042](https://ortussolutions.atlassian.net/browse/COLDBOX-1042) afterAnyTask\(\) and task.after\(\) don't run after failing task -* [COLDBOX-1040](https://ortussolutions.atlassian.net/browse/COLDBOX-1040) Error in onAnyTaskError\(\) or after\(\) tasks not handled and executor dies. -* [COLDBOX-966](https://ortussolutions.atlassian.net/browse/COLDBOX-966) Coldbox Renderer.RenderLayout\(\) Overwrites Event's Current View +- [COLDBOX-1114](https://ortussolutions.atlassian.net/browse/COLDBOX-1114) Persistance of variables failing due to null support +- [COLDBOX-1110](https://ortussolutions.atlassian.net/browse/COLDBOX-1110) Renderer is causing coldbox RestHandler to render convention view +- [COLDBOX-1109](https://ortussolutions.atlassian.net/browse/COLDBOX-1109) Exceptions in async interceptors are missing onException announcement +- [COLDBOX-1105](https://ortussolutions.atlassian.net/browse/COLDBOX-1105) Interception with `async` annotation causes InterceptorState Exception on Reinit +- [COLDBOX-1104](https://ortussolutions.atlassian.net/browse/COLDBOX-1104) A view not set exception is thrown when trying to execution handler ColdBox methods that are not concrete actions when they should be invalid events. +- [COLDBOX-1103](https://ortussolutions.atlassian.net/browse/COLDBOX-1103) Update getServerIP\(\) so it avoids looking at the cgi scope as it can cause issues on ACF +- [COLDBOX-1100](https://ortussolutions.atlassian.net/browse/COLDBOX-1100) Event Caching Does Not Preserve HTTP Response Codes +- [COLDBOX-1099](https://ortussolutions.atlassian.net/browse/COLDBOX-1099) Regression on ColdBox v6.6.1 around usage of statusCode = 0 on relocates +- [COLDBOX-1098](https://ortussolutions.atlassian.net/browse/COLDBOX-1098) RequestService context creation not thread safe +- [COLDBOX-1097](https://ortussolutions.atlassian.net/browse/COLDBOX-1097) Missing scopes on isNull\(\) checks +- [COLDBOX-1092](https://ortussolutions.atlassian.net/browse/COLDBOX-1092) RestHandler Try/Catches Break In Testbox When RunEvent\(\) is Called +- [COLDBOX-1045](https://ortussolutions.atlassian.net/browse/COLDBOX-1045) Scheduled tasks have no default error handling +- [COLDBOX-1043](https://ortussolutions.atlassian.net/browse/COLDBOX-1043) Creating scheduled task with unrecognized timeUnit throws null pointer +- [COLDBOX-1042](https://ortussolutions.atlassian.net/browse/COLDBOX-1042) afterAnyTask\(\) and task.after\(\) don't run after failing task +- [COLDBOX-1040](https://ortussolutions.atlassian.net/browse/COLDBOX-1040) Error in onAnyTaskError\(\) or after\(\) tasks not handled and executor dies. +- [COLDBOX-966](https://ortussolutions.atlassian.net/browse/COLDBOX-966) Coldbox Renderer.RenderLayout\(\) Overwrites Event's Current View #### Improvement -* [COLDBOX-1124](https://ortussolutions.atlassian.net/browse/COLDBOX-1124) Convert mixer util to script and utilize only the necessary mixins by deprecating older mixins -* [COLDBOX-1116](https://ortussolutions.atlassian.net/browse/COLDBOX-1116) Enhance EntityNotFound Exception Messages for rest handlers -* [COLDBOX-1096](https://ortussolutions.atlassian.net/browse/COLDBOX-1096) SES is always disabled on RequestContext until RoutingService request capture : SES is the new default for ColdBox Apps -* [COLDBOX-1094](https://ortussolutions.atlassian.net/browse/COLDBOX-1094) coldbox 6.5 and 6.6 break ORM event handling in cborm -* [COLDBOX-1067](https://ortussolutions.atlassian.net/browse/COLDBOX-1067) Scheduled Tasks: Inject module context variables to module schedulers and inject global context into global scheduler -* [COLDBOX-1044](https://ortussolutions.atlassian.net/browse/COLDBOX-1044) Create singular aliases for timeunits +- [COLDBOX-1124](https://ortussolutions.atlassian.net/browse/COLDBOX-1124) Convert mixer util to script and utilize only the necessary mixins by deprecating older mixins +- [COLDBOX-1116](https://ortussolutions.atlassian.net/browse/COLDBOX-1116) Enhance EntityNotFound Exception Messages for rest handlers +- [COLDBOX-1096](https://ortussolutions.atlassian.net/browse/COLDBOX-1096) SES is always disabled on RequestContext until RoutingService request capture : SES is the new default for ColdBox Apps +- [COLDBOX-1094](https://ortussolutions.atlassian.net/browse/COLDBOX-1094) coldbox 6.5 and 6.6 break ORM event handling in cborm +- [COLDBOX-1067](https://ortussolutions.atlassian.net/browse/COLDBOX-1067) Scheduled Tasks: Inject module context variables to module schedulers and inject global context into global scheduler +- [COLDBOX-1044](https://ortussolutions.atlassian.net/browse/COLDBOX-1044) Create singular aliases for timeunits #### New Feature -* [COLDBOX-1123](https://ortussolutions.atlassian.net/browse/COLDBOX-1123) New xTask\(\) method in the schedulers that will automatically disable the task but still register it. Great for debugging! -* [COLDBOX-1121](https://ortussolutions.atlassian.net/browse/COLDBOX-1121) Log schedule task failures to console so errors are not ignored -* [COLDBOX-1120](https://ortussolutions.atlassian.net/browse/COLDBOX-1120) Scheduler's onShutdown\(\) callback now receives the boolean force and numeric timeout arguments -* [COLDBOX-1119](https://ortussolutions.atlassian.net/browse/COLDBOX-1119) The Scheduler's shutdown method now has two arguments: boolean force, numeric timeout -* [COLDBOX-1118](https://ortussolutions.atlassian.net/browse/COLDBOX-1118) All schedulers have a new property: shutdownTimeout which defaults to 30 that can be used to control how long to wait for tasks to gracefully complete when shutting down. -* [COLDBOX-1113](https://ortussolutions.atlassian.net/browse/COLDBOX-1113) New coldobx.system.testing.VirtualApp object that can startup,restart and shutdown Virtual Testing Applications -* [COLDBOX-1108](https://ortussolutions.atlassian.net/browse/COLDBOX-1108) Async interceptos can now discover their announced data without duplicating it via cfthread -* [COLDBOX-1107](https://ortussolutions.atlassian.net/browse/COLDBOX-1107) Interception Event pools are now using synchronized linked maps to provide concurrency -* [COLDBOX-1106](https://ortussolutions.atlassian.net/browse/COLDBOX-1106) New super type function "forAttribute" to help us serialize simple/complex data and encoded for usage in html attributes -* [COLDBOX-1101](https://ortussolutions.atlassian.net/browse/COLDBOX-1101) announce `onException` interception from RESTHandler, when exceptions are detected -* [COLDBOX-1053](https://ortussolutions.atlassian.net/browse/COLDBOX-1053) Async schedulers and executors can now have a graceful shutdown and await for task termination with a configurable timeout. -* [COLDBOX-1052](https://ortussolutions.atlassian.net/browse/COLDBOX-1052) Scheduled tasks add start and end date/times +- [COLDBOX-1123](https://ortussolutions.atlassian.net/browse/COLDBOX-1123) New xTask\(\) method in the schedulers that will automatically disable the task but still register it. Great for debugging! +- [COLDBOX-1121](https://ortussolutions.atlassian.net/browse/COLDBOX-1121) Log schedule task failures to console so errors are not ignored +- [COLDBOX-1120](https://ortussolutions.atlassian.net/browse/COLDBOX-1120) Scheduler's onShutdown\(\) callback now receives the boolean force and numeric timeout arguments +- [COLDBOX-1119](https://ortussolutions.atlassian.net/browse/COLDBOX-1119) The Scheduler's shutdown method now has two arguments: boolean force, numeric timeout +- [COLDBOX-1118](https://ortussolutions.atlassian.net/browse/COLDBOX-1118) All schedulers have a new property: shutdownTimeout which defaults to 30 that can be used to control how long to wait for tasks to gracefully complete when shutting down. +- [COLDBOX-1113](https://ortussolutions.atlassian.net/browse/COLDBOX-1113) New coldobx.system.testing.VirtualApp object that can startup,restart and shutdown Virtual Testing Applications +- [COLDBOX-1108](https://ortussolutions.atlassian.net/browse/COLDBOX-1108) Async interceptos can now discover their announced data without duplicating it via cfthread +- [COLDBOX-1107](https://ortussolutions.atlassian.net/browse/COLDBOX-1107) Interception Event pools are now using synchronized linked maps to provide concurrency +- [COLDBOX-1106](https://ortussolutions.atlassian.net/browse/COLDBOX-1106) New super type function "forAttribute" to help us serialize simple/complex data and encoded for usage in html attributes +- [COLDBOX-1101](https://ortussolutions.atlassian.net/browse/COLDBOX-1101) announce `onException` interception from RESTHandler, when exceptions are detected +- [COLDBOX-1053](https://ortussolutions.atlassian.net/browse/COLDBOX-1053) Async schedulers and executors can now have a graceful shutdown and await for task termination with a configurable timeout. +- [COLDBOX-1052](https://ortussolutions.atlassian.net/browse/COLDBOX-1052) Scheduled tasks add start and end date/times #### Task -* [COLDBOX-1122](https://ortussolutions.atlassian.net/browse/COLDBOX-1122) lucee async tests where being skipped due to missing engine check -* [COLDBOX-1117](https://ortussolutions.atlassian.net/browse/COLDBOX-1117) Remove nextRun stat from scheduled task, it was never implemented +- [COLDBOX-1122](https://ortussolutions.atlassian.net/browse/COLDBOX-1122) lucee async tests where being skipped due to missing engine check +- [COLDBOX-1117](https://ortussolutions.atlassian.net/browse/COLDBOX-1117) Remove nextRun stat from scheduled task, it was never implemented ### CacheBox #### Bug -* [CACHEBOX-66](https://ortussolutions.atlassian.net/browse/CACHEBOX-66) Cachebox concurrent store meta index not thread safe during reaping +- [CACHEBOX-66](https://ortussolutions.atlassian.net/browse/CACHEBOX-66) Cachebox concurrent store meta index not thread safe during reaping #### Improvement -* [CACHEBOX-82](https://ortussolutions.atlassian.net/browse/CACHEBOX-82) Remove the usage of identity hash codes, they are no longer relevant and can cause contention under load +- [CACHEBOX-82](https://ortussolutions.atlassian.net/browse/CACHEBOX-82) Remove the usage of identity hash codes, they are no longer relevant and can cause contention under load ### LogBox #### Improvement -* [LOGBOX-68](https://ortussolutions.atlassian.net/browse/LOGBOX-68) Remove the usage of identity hash codes, they are no longer relevant and can cause contention under load -* [LOGBOX-65](https://ortussolutions.atlassian.net/browse/LOGBOX-65) File Appender missing text "ExtraInfo: " +- [LOGBOX-68](https://ortussolutions.atlassian.net/browse/LOGBOX-68) Remove the usage of identity hash codes, they are no longer relevant and can cause contention under load +- [LOGBOX-65](https://ortussolutions.atlassian.net/browse/LOGBOX-65) File Appender missing text "ExtraInfo: " ### WireBox #### Bug -* [WIREBOX-126](https://ortussolutions.atlassian.net/browse/WIREBOX-126) Inherited Metadata Usage - Singleton attribute evaluated before Scopes +- [WIREBOX-126](https://ortussolutions.atlassian.net/browse/WIREBOX-126) Inherited Metadata Usage - Singleton attribute evaluated before Scopes #### Improvement -* [WIREBOX-129](https://ortussolutions.atlassian.net/browse/WIREBOX-129) Massive refactor to improve object creation and injection wiring -* [WIREBOX-128](https://ortussolutions.atlassian.net/browse/WIREBOX-128) Injector now caches all object contains lookups to increase performance across hierarchy lookups -* [WIREBOX-127](https://ortussolutions.atlassian.net/browse/WIREBOX-127) Lazy load all constructs on the Injector to improve performance -* [WIREBOX-125](https://ortussolutions.atlassian.net/browse/WIREBOX-125) Remove the usage of identity hash codes, they are no longer relevant and can cause contention under load +- [WIREBOX-129](https://ortussolutions.atlassian.net/browse/WIREBOX-129) Massive refactor to improve object creation and injection wiring +- [WIREBOX-128](https://ortussolutions.atlassian.net/browse/WIREBOX-128) Injector now caches all object contains lookups to increase performance across hierarchy lookups +- [WIREBOX-127](https://ortussolutions.atlassian.net/browse/WIREBOX-127) Lazy load all constructs on the Injector to improve performance +- [WIREBOX-125](https://ortussolutions.atlassian.net/browse/WIREBOX-125) Remove the usage of identity hash codes, they are no longer relevant and can cause contention under load ---- @@ -133,25 +140,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 #### Bug -* [COLDBOX-1093](https://ortussolutions.atlassian.net/browse/COLDBOX-1093) Remove debug writedumps left over from previous testing -* [COLDBOX-1085](https://ortussolutions.atlassian.net/browse/COLDBOX-1085) Fix instance of bad route merging the routes but loosing the handler +- [COLDBOX-1093](https://ortussolutions.atlassian.net/browse/COLDBOX-1093) Remove debug writedumps left over from previous testing +- [COLDBOX-1085](https://ortussolutions.atlassian.net/browse/COLDBOX-1085) Fix instance of bad route merging the routes but loosing the handler #### Improvement -* [COLDBOX-1095](https://ortussolutions.atlassian.net/browse/COLDBOX-1095) Update Response Pagination Properties for Case-Sensitive Engines -* [COLDBOX-1091](https://ortussolutions.atlassian.net/browse/COLDBOX-1091) default status code to 302 in the internal relocate\(\) just like CFML does instead of 0 and eliminate source -* [COLDBOX-1089](https://ortussolutions.atlassian.net/browse/COLDBOX-1089) Update the internal cfml engine checker to have more engine based feature checkers -* [COLDBOX-1088](https://ortussolutions.atlassian.net/browse/COLDBOX-1088) Switch isInstance check on renderdata in controller to secondary of $renderdata check to optimize speed +- [COLDBOX-1095](https://ortussolutions.atlassian.net/browse/COLDBOX-1095) Update Response Pagination Properties for Case-Sensitive Engines +- [COLDBOX-1091](https://ortussolutions.atlassian.net/browse/COLDBOX-1091) default status code to 302 in the internal relocate\(\) just like CFML does instead of 0 and eliminate source +- [COLDBOX-1089](https://ortussolutions.atlassian.net/browse/COLDBOX-1089) Update the internal cfml engine checker to have more engine based feature checkers +- [COLDBOX-1088](https://ortussolutions.atlassian.net/browse/COLDBOX-1088) Switch isInstance check on renderdata in controller to secondary of $renderdata check to optimize speed ### CacheBox #### Bug -* [CACHEBOX-80](https://ortussolutions.atlassian.net/browse/CACHEBOX-80) Bug in JDBCMetadataIndexer sortedKeys\(\) using non-existent variable `arguments.objectKey` +- [CACHEBOX-80](https://ortussolutions.atlassian.net/browse/CACHEBOX-80) Bug in JDBCMetadataIndexer sortedKeys\(\) using non-existent variable `arguments.objectKey` #### Improvement -* [CACHEBOX-81](https://ortussolutions.atlassian.net/browse/CACHEBOX-81) JDBCStore Dynamically generate queryExecute options \+ new config to always include DSN due to ACF issues +- [CACHEBOX-81](https://ortussolutions.atlassian.net/browse/CACHEBOX-81) JDBCStore Dynamically generate queryExecute options \+ new config to always include DSN due to ACF issues ---- @@ -161,38 +168,38 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 #### Bug -* [COLDBOX-1072](https://ortussolutions.atlassian.net/browse/COLDBOX-1072) Non config apps fails since the core Settings.cfc had the configure() method removed -* [COLDBOX-1069](https://ortussolutions.atlassian.net/browse/COLDBOX-1069) Framework Initialization Fails in @be on AutoWire of App Scheduler -* [COLDBOX-1066](https://ortussolutions.atlassian.net/browse/COLDBOX-1066) Scheduled tasks not accessing application scope on Adobe Engines -* [COLDBOX-1063](https://ortussolutions.atlassian.net/browse/COLDBOX-1063) ColdBox schedulers starting before the application is ready to serve requests -* [COLDBOX-1062](https://ortussolutions.atlassian.net/browse/COLDBOX-1062) Scheduler service not registering schedulers with the appropriate name -* [COLDBOX-1051](https://ortussolutions.atlassian.net/browse/COLDBOX-1051) scheduler names can only be used once - executor needs to be removed -* [COLDBOX-1036](https://ortussolutions.atlassian.net/browse/COLDBOX-1036) Scheduled tasks fail after upgrading to coldbox 6.5. Downgrading to 6.4.0 works. -* [COLDBOX-1027](https://ortussolutions.atlassian.net/browse/COLDBOX-1027) actions for a specific pattern cannot point to different handlers +- [COLDBOX-1072](https://ortussolutions.atlassian.net/browse/COLDBOX-1072) Non config apps fails since the core Settings.cfc had the configure() method removed +- [COLDBOX-1069](https://ortussolutions.atlassian.net/browse/COLDBOX-1069) Framework Initialization Fails in @be on AutoWire of App Scheduler +- [COLDBOX-1066](https://ortussolutions.atlassian.net/browse/COLDBOX-1066) Scheduled tasks not accessing application scope on Adobe Engines +- [COLDBOX-1063](https://ortussolutions.atlassian.net/browse/COLDBOX-1063) ColdBox schedulers starting before the application is ready to serve requests +- [COLDBOX-1062](https://ortussolutions.atlassian.net/browse/COLDBOX-1062) Scheduler service not registering schedulers with the appropriate name +- [COLDBOX-1051](https://ortussolutions.atlassian.net/browse/COLDBOX-1051) scheduler names can only be used once - executor needs to be removed +- [COLDBOX-1036](https://ortussolutions.atlassian.net/browse/COLDBOX-1036) Scheduled tasks fail after upgrading to coldbox 6.5. Downgrading to 6.4.0 works. +- [COLDBOX-1027](https://ortussolutions.atlassian.net/browse/COLDBOX-1027) actions for a specific pattern cannot point to different handlers #### Improvement -* [COLDBOX-1074](https://ortussolutions.atlassian.net/browse/COLDBOX-1074) Improvements to module loading/activation log messages -* [COLDBOX-1071](https://ortussolutions.atlassian.net/browse/COLDBOX-1071) Make unloadAll() in ModuleService more resilient by verifying loaded modules exist -* [COLDBOX-1061](https://ortussolutions.atlassian.net/browse/COLDBOX-1061) Change default template cache from concurrentSoftReference to ConcurrentReference to avoid auto cleanups -* [COLDBOX-1056](https://ortussolutions.atlassian.net/browse/COLDBOX-1056) Default route names to pattern when using route() -* [COLDBOX-1050](https://ortussolutions.atlassian.net/browse/COLDBOX-1050) New router method: `apiResources()` to allow you to define resources without the new and edit actions -* [COLDBOX-1049](https://ortussolutions.atlassian.net/browse/COLDBOX-1049) Update elixirPath to allow for many permutations of filenames and arguments to avoid cache collisions -* [COLDBOX-1048](https://ortussolutions.atlassian.net/browse/COLDBOX-1048) Ability for the response `setPagination()` to use any incoming argument for storage -* [COLDBOX-1037](https://ortussolutions.atlassian.net/browse/COLDBOX-1037) Move `onRequestCapture` after default event capture to allow for consistency on the capture -* [COLDBOX-980](https://ortussolutions.atlassian.net/browse/COLDBOX-980) Deprecate declaration of multiple resources on a single `resources()` call -* [COLDBOX-676](https://ortussolutions.atlassian.net/browse/COLDBOX-676) Improve routing DSL to allow for different HTTP verbs on the the same route to point to different events or actions +- [COLDBOX-1074](https://ortussolutions.atlassian.net/browse/COLDBOX-1074) Improvements to module loading/activation log messages +- [COLDBOX-1071](https://ortussolutions.atlassian.net/browse/COLDBOX-1071) Make unloadAll() in ModuleService more resilient by verifying loaded modules exist +- [COLDBOX-1061](https://ortussolutions.atlassian.net/browse/COLDBOX-1061) Change default template cache from concurrentSoftReference to ConcurrentReference to avoid auto cleanups +- [COLDBOX-1056](https://ortussolutions.atlassian.net/browse/COLDBOX-1056) Default route names to pattern when using route() +- [COLDBOX-1050](https://ortussolutions.atlassian.net/browse/COLDBOX-1050) New router method: `apiResources()` to allow you to define resources without the new and edit actions +- [COLDBOX-1049](https://ortussolutions.atlassian.net/browse/COLDBOX-1049) Update elixirPath to allow for many permutations of filenames and arguments to avoid cache collisions +- [COLDBOX-1048](https://ortussolutions.atlassian.net/browse/COLDBOX-1048) Ability for the response `setPagination()` to use any incoming argument for storage +- [COLDBOX-1037](https://ortussolutions.atlassian.net/browse/COLDBOX-1037) Move `onRequestCapture` after default event capture to allow for consistency on the capture +- [COLDBOX-980](https://ortussolutions.atlassian.net/browse/COLDBOX-980) Deprecate declaration of multiple resources on a single `resources()` call +- [COLDBOX-676](https://ortussolutions.atlassian.net/browse/COLDBOX-676) Improve routing DSL to allow for different HTTP verbs on the the same route to point to different events or actions #### New Feature -* [COLDBOX-1082](https://ortussolutions.atlassian.net/browse/COLDBOX-1082) Announce `onException` interception points for async interceptors -* [COLDBOX-1080](https://ortussolutions.atlassian.net/browse/COLDBOX-1080) experimental web mapping support to allow for modern app templates with assets outside of the webroot -* [COLDBOX-1076](https://ortussolutions.atlassian.net/browse/COLDBOX-1076) Ability to pass in the domain to test executions in via integration testing -* [COLDBOX-1073](https://ortussolutions.atlassian.net/browse/COLDBOX-1073) Enable automated full null support via github actions -* [COLDBOX-1065](https://ortussolutions.atlassian.net/browse/COLDBOX-1065) ScheduledTask new `getMemento`() to get the state of the task -* [COLDBOX-1064](https://ortussolutions.atlassian.net/browse/COLDBOX-1064) Schedulers can now get the current thread and thread name: `getCurrentThread(), getThreadName()` as private helpers -* [COLDBOX-1033](https://ortussolutions.atlassian.net/browse/COLDBOX-1033) New controller method: `getUserSessionIdentifier`() which gives you the unique request tracking identifier according to our algorithms -* [COLDBOX-1032](https://ortussolutions.atlassian.net/browse/COLDBOX-1032) New coldbox setting `identifierProvider` which can be a closure/udf/lambda that provides a unique tracking identifier for user requests +- [COLDBOX-1082](https://ortussolutions.atlassian.net/browse/COLDBOX-1082) Announce `onException` interception points for async interceptors +- [COLDBOX-1080](https://ortussolutions.atlassian.net/browse/COLDBOX-1080) experimental web mapping support to allow for modern app templates with assets outside of the webroot +- [COLDBOX-1076](https://ortussolutions.atlassian.net/browse/COLDBOX-1076) Ability to pass in the domain to test executions in via integration testing +- [COLDBOX-1073](https://ortussolutions.atlassian.net/browse/COLDBOX-1073) Enable automated full null support via github actions +- [COLDBOX-1065](https://ortussolutions.atlassian.net/browse/COLDBOX-1065) ScheduledTask new `getMemento`() to get the state of the task +- [COLDBOX-1064](https://ortussolutions.atlassian.net/browse/COLDBOX-1064) Schedulers can now get the current thread and thread name: `getCurrentThread(), getThreadName()` as private helpers +- [COLDBOX-1033](https://ortussolutions.atlassian.net/browse/COLDBOX-1033) New controller method: `getUserSessionIdentifier`() which gives you the unique request tracking identifier according to our algorithms +- [COLDBOX-1032](https://ortussolutions.atlassian.net/browse/COLDBOX-1032) New coldbox setting `identifierProvider` which can be a closure/udf/lambda that provides a unique tracking identifier for user requests ---- @@ -200,14 +207,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 #### Bug -* [CACHEBOX-76](https://ortussolutions.atlassian.net/browse/CACHEBOX-76) Fixed method return value + SQL compatibility on jdbc metadata indexer thanks to @homestar9 -* [CACHEBOX-75](https://ortussolutions.atlassian.net/browse/CACHEBOX-75) reap operation was not ignoring 0 values for last access timeouts -* [CACHEBOX-74](https://ortussolutions.atlassian.net/browse/CACHEBOX-74) Typo in queryExecute Attribute "datasource" in the JDBCStore.cfc +- [CACHEBOX-76](https://ortussolutions.atlassian.net/browse/CACHEBOX-76) Fixed method return value + SQL compatibility on jdbc metadata indexer thanks to @homestar9 +- [CACHEBOX-75](https://ortussolutions.atlassian.net/browse/CACHEBOX-75) reap operation was not ignoring 0 values for last access timeouts +- [CACHEBOX-74](https://ortussolutions.atlassian.net/browse/CACHEBOX-74) Typo in queryExecute Attribute "datasource" in the JDBCStore.cfc #### Improvement -* [CACHEBOX-73](https://ortussolutions.atlassian.net/browse/CACHEBOX-73) Replace IIF and urlEncodedFormat on cache content reports -* [CACHEBOX-79](https://ortussolutions.atlassian.net/browse/CACHEBOX-79) Lower logging verbosity of cache reaping from info to debug messages +- [CACHEBOX-73](https://ortussolutions.atlassian.net/browse/CACHEBOX-73) Replace IIF and urlEncodedFormat on cache content reports +- [CACHEBOX-79](https://ortussolutions.atlassian.net/browse/CACHEBOX-79) Lower logging verbosity of cache reaping from info to debug messages ---- @@ -215,20 +222,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 #### Bug -* [WIREBOX-124](https://ortussolutions.atlassian.net/browse/WIREBOX-124) Killing `IInjector` interface usages due to many issues across cfml engines, leaving them for docs only -* [WIREBOX-118](https://ortussolutions.atlassian.net/browse/WIREBOX-118) Never override an existing variables key with virtual inheritance +- [WIREBOX-124](https://ortussolutions.atlassian.net/browse/WIREBOX-124) Killing `IInjector` interface usages due to many issues across cfml engines, leaving them for docs only +- [WIREBOX-118](https://ortussolutions.atlassian.net/browse/WIREBOX-118) Never override an existing variables key with virtual inheritance #### Improvement -* [WIREBOX-120](https://ortussolutions.atlassian.net/browse/WIREBOX-120) DSLs process method now receives the caller `targetID` alongside the `targetObject` and the `target` definition +- [WIREBOX-120](https://ortussolutions.atlassian.net/browse/WIREBOX-120) DSLs process method now receives the caller `targetID` alongside the `targetObject` and the `target` definition #### New Feature -* [WIREBOX-122](https://ortussolutions.atlassian.net/browse/WIREBOX-122) New wirebox DSL to inject the target's metadata that's cached in the target's binder: `wirebox:objectMetadata` -* [WIREBOX-121](https://ortussolutions.atlassian.net/browse/WIREBOX-121) New WireBoxDSL: `wirebox:targetID` to give you back the target ID used when injecting the object -* [WIREBOX-119](https://ortussolutions.atlassian.net/browse/WIREBOX-119) Missing `coldbox:schedulerService` DSL -* [WIREBOX-117](https://ortussolutions.atlassian.net/browse/WIREBOX-117) HDI - Ability for injectors to have a collection of child injectors to delegate lookups to, basically Hierarchical DI +- [WIREBOX-122](https://ortussolutions.atlassian.net/browse/WIREBOX-122) New wirebox DSL to inject the target's metadata that's cached in the target's binder: `wirebox:objectMetadata` +- [WIREBOX-121](https://ortussolutions.atlassian.net/browse/WIREBOX-121) New WireBoxDSL: `wirebox:targetID` to give you back the target ID used when injecting the object +- [WIREBOX-119](https://ortussolutions.atlassian.net/browse/WIREBOX-119) Missing `coldbox:schedulerService` DSL +- [WIREBOX-117](https://ortussolutions.atlassian.net/browse/WIREBOX-117) HDI - Ability for injectors to have a collection of child injectors to delegate lookups to, basically Hierarchical DI #### Task -* [WIREBOX-123](https://ortussolutions.atlassian.net/browse/WIREBOX-123) Removal of usage of Injector dsl interface due to so many issues with multiple engines +- [WIREBOX-123](https://ortussolutions.atlassian.net/browse/WIREBOX-123) Removal of usage of Injector dsl interface due to so many issues with multiple engines diff --git a/readme.md b/readme.md index 547fe507f..084c9db57 100755 --- a/readme.md +++ b/readme.md @@ -35,7 +35,7 @@ Holy Ghost which is given unto us. ." Romans 5:5 ---- -# Welcome to ColdBox +# Welcome to ColdBox LTS Release (`v6.x`) ColdBox *Hierarchical* MVC is the de-facto enterprise-level [HMVC](https://en.wikipedia.org/wiki/Hierarchical_model%E2%80%93view%E2%80%93controller) framework for ColdFusion (CFML) developers. It's professionally backed, conventions-based, modular, highly extensible, and productive. Getting started with ColdBox is quick and painless. ColdBox takes the pain out of development by giving you a standardized methodology for modern ColdFusion (CFML) development with features such as: @@ -52,6 +52,19 @@ ColdBox *Hierarchical* MVC is the de-facto enterprise-level [HMVC](https://en.wi * [An extensive eco-system](https://forgebox.io) * Much More +## LTS Support + +For all ColdBox releases, updates are provided for 12 months and security fixes are provided for 2 years after the next major release. + +**ColdBox 6.x will receive bug fixes until 2024 and security fixes until 2025.** + +| Version | Release | Updates | Security Fixes | +| ------- | ------- | --------- | -------------- | +| 6.x | 2022 | 2023 | 2025 | +| 7.x | 2023 | 2024 | 2026 | +| 8.x | 2024 | 2025 | 2027 | +| 9.x | 2025 | 2026 | 2028 | + ## License Apache License, Version 2.0. From b7ae0d063d524b4e9f9697cf2215720598e26c78 Mon Sep 17 00:00:00 2001 From: lmajano Date: Fri, 28 Apr 2023 21:54:24 +0000 Subject: [PATCH 03/46] Apply cfformat changes --- system/Bootstrap.cfc | 4 +++- system/async/proxies/Consumer.cfc | 6 ++---- system/async/proxies/Supplier.cfc | 6 ++---- system/async/tasks/ScheduledTask.cfc | 4 +++- system/cache/util/EventURLFacade.cfc | 4 +++- system/testing/VirtualApp.cfc | 4 ++-- 6 files changed, 15 insertions(+), 13 deletions(-) diff --git a/system/Bootstrap.cfc b/system/Bootstrap.cfc index 90f78457d..3916360a3 100644 --- a/system/Bootstrap.cfc +++ b/system/Bootstrap.cfc @@ -266,7 +266,9 @@ component serializable="false" accessors="true" { } ); // Cached Status Code - if ( isNumeric( local.refResults.eventCaching.statusCode ) && local.refResults.eventCaching.statusCode > 0 ) { + if ( + isNumeric( local.refResults.eventCaching.statusCode ) && local.refResults.eventCaching.statusCode > 0 + ) { event.setHTTPHeader( statusCode = local.refResults.eventCaching.statusCode ); } diff --git a/system/async/proxies/Consumer.cfc b/system/async/proxies/Consumer.cfc index 4239c782c..6de71d87d 100644 --- a/system/async/proxies/Consumer.cfc +++ b/system/async/proxies/Consumer.cfc @@ -24,14 +24,12 @@ component extends="BaseProxy" { lock name="#getConcurrentEngineLockName()#" type="exclusive" timeout="60" { variables.target( arguments.t ); } - } - catch( any e ){ + } catch ( any e ) { // Log it, so it doesn't go to ether err( "Error running Consumer: #e.message & e.detail#" ); err( "Stacktrace for Consumer: #e.stackTrace#" ); rethrow; - } - finally { + } finally { unLoadContext(); } } diff --git a/system/async/proxies/Supplier.cfc b/system/async/proxies/Supplier.cfc index 4d7fb02c5..768b69219 100644 --- a/system/async/proxies/Supplier.cfc +++ b/system/async/proxies/Supplier.cfc @@ -37,14 +37,12 @@ component extends="BaseProxy" { return invoke( variables.target, variables.method ); } } - } - catch( any e ){ + } catch ( any e ) { // Log it, so it doesn't go to ether err( "Error running Supplier: #e.message & e.detail#" ); err( "Stacktrace for Supplier: #e.stackTrace#" ); rethrow; - } - finally { + } finally { unLoadContext(); } } diff --git a/system/async/tasks/ScheduledTask.cfc b/system/async/tasks/ScheduledTask.cfc index 134a75a2b..05e5a24ba 100644 --- a/system/async/tasks/ScheduledTask.cfc +++ b/system/async/tasks/ScheduledTask.cfc @@ -493,7 +493,9 @@ component accessors="true" { } } catch ( any afterException ) { // Log it, so it doesn't go to ether and executor doesn't die. - err( "Error running task (#getname()#) after/error handlers : #afterException.message & afterException.detail#" ); + err( + "Error running task (#getname()#) after/error handlers : #afterException.message & afterException.detail#" + ); err( "Stacktrace for task (#getname()#) after/error handlers : #afterException.stackTrace#" ); } } finally { diff --git a/system/cache/util/EventURLFacade.cfc b/system/cache/util/EventURLFacade.cfc index 1ce53f6d5..1cc807d96 100644 --- a/system/cache/util/EventURLFacade.cfc +++ b/system/cache/util/EventURLFacade.cfc @@ -135,7 +135,9 @@ component accessors="true" { * @targetEvent The targeted ColdBox event string */ string function buildBasicCacheKey( required keySuffix, required targetEvent ){ - return lCase( variables.cacheProvider.getEventCacheKeyPrefix() & arguments.targetEvent & "-" & arguments.keySuffix & "-" ); + return lCase( + variables.cacheProvider.getEventCacheKeyPrefix() & arguments.targetEvent & "-" & arguments.keySuffix & "-" + ); } } diff --git a/system/testing/VirtualApp.cfc b/system/testing/VirtualApp.cfc index 89ce33e2b..c811116be 100644 --- a/system/testing/VirtualApp.cfc +++ b/system/testing/VirtualApp.cfc @@ -55,7 +55,7 @@ component accessors="true" { */ function startup( boolean force = false ){ // Return back if it's already running and not forcing - if( isRunning() && !arguments.force ){ + if ( isRunning() && !arguments.force ) { return application.cbController; } @@ -99,7 +99,7 @@ component accessors="true" { * */ function getController(){ - return isRunning() ? application.cbController : javaCast( "null", "" ); + return isRunning() ? application.cbController : javacast( "null", "" ); } /** From a7c43c735983bfc91faf02c89f0dced29c43cb9f Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Fri, 28 Apr 2023 16:58:44 -0500 Subject: [PATCH 04/46] fixing server updates --- server-adobe@2021.json | 3 +++ server-adobe@2023.json | 30 ++++++++++++++++++++++++++++ server.json => server-lucee@5rc.json | 15 +++++++------- 3 files changed, 41 insertions(+), 7 deletions(-) create mode 100644 server-adobe@2023.json rename server.json => server-lucee@5rc.json (60%) diff --git a/server-adobe@2021.json b/server-adobe@2021.json index c7e2e3c96..e0722c017 100644 --- a/server-adobe@2021.json +++ b/server-adobe@2021.json @@ -23,5 +23,8 @@ }, "cfconfig": { "file" : ".cfconfig.json" + }, + "scripts" : { + "onServerInstall":"cfpm install caching,zip,orm,mysql,postgresql,sqlserver,document,feed,mail,debugger" } } diff --git a/server-adobe@2023.json b/server-adobe@2023.json new file mode 100644 index 000000000..b3b04092e --- /dev/null +++ b/server-adobe@2023.json @@ -0,0 +1,30 @@ +{ + "app":{ + "cfengine":"adobe@2023.0.0-beta.1", + "serverHomeDirectory":".engine/adobe2023" + }, + "name":"coldbox-adobe@2023", + "force":true, + "openBrowser":false, + "web":{ + "directoryBrowsing":true, + "http":{ + "port":"8599" + }, + "rewrites":{ + "enable":true + }, + "aliases":{ + "/coldbox":"./" + } + }, + "JVM":{ + "heapSize":"1024" + }, + "cfconfig": { + "file" : ".cfconfig.json" + }, + "scripts" : { + "onServerInstall":"cfpm install caching,zip,orm,mysql,postgresql,sqlserver,document,feed,mail,debugger" + } +} diff --git a/server.json b/server-lucee@5rc.json similarity index 60% rename from server.json rename to server-lucee@5rc.json index b3d2c6532..e3852f415 100644 --- a/server.json +++ b/server-lucee@5rc.json @@ -1,8 +1,9 @@ { "app":{ - "cfengine":"lucee@5" + "cfengine":"lucee@5.3-rc", + "serverHomeDirectory":".engine/luceerc" }, - "name":"coldbox-lucee@5", + "name":"coldbox-lucee@5rc", "force":true, "openBrowser":false, "web":{ @@ -12,15 +13,15 @@ }, "rewrites":{ "enable":true - }, - "aliases":{ + }, + "aliases":{ "/coldbox":"./" } }, "JVM":{ "heapSize":"1024" }, - "cfconfig": { - "file" : ".cfconfig.json" - } + "cfconfig":{ + "file":".cfconfig.json" + } } From 1cfed9b9bffb4bfc96476a723d3186d8cb8eeba1 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Fri, 28 Apr 2023 16:59:50 -0500 Subject: [PATCH 05/46] setting up for LTS --- box.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/box.json b/box.json index f4411841b..3d524c971 100644 --- a/box.json +++ b/box.json @@ -1,6 +1,6 @@ { "name":"ColdBox Platform", - "version":"6.8.1", + "version":"6.8.2", "location":"https://downloads.ortussolutions.com/ortussolutions/coldbox/@build.version@/coldbox-@build.version@.zip", "author":"Ortus Solutions ", "slug":"coldbox", @@ -51,15 +51,17 @@ "release":"recipe build/release.boxr", "format":"cfformat run system/**/*.cfc,tests/specs/**/*.cfc --overwrite", "format:watch":"cfformat watch system/**/*.cfc,tests/specs/**/*.cfc ./.cfformat.json", - "format:check":"cfformat check system/**/*.cfc,tests/specs/**/*.cfc", + "format:check":"cfformat check system/**/*.cfc,tests/specs/**/*.cfc ./.cfformat.json", "start:lucee":"server start serverConfigFile='server-lucee@5.json' --force", "start:2016":"server start serverConfigFile='server-adobe@2016.json' --force", "start:2018":"server start serverConfigFile='server-adobe@2018.json' --force", "start:2021":"server start serverConfigFile='server-adobe@2021.json' --force", + "start:2023":"server start serverConfigFile='server-adobe@2023.json' --force", "log:lucee":"server log coldbox-lucee@5 --follow", "log:2016":"server log coldbox-adobe@2016 --follow", "log:2018":"server log coldbox-adobe@2018 --follow", "log:2021":"server log coldbox-adobe@2021 --follow", + "log:2023":"server log coldbox-adobe@2023 --follow", "cfpm":"echo '\".engine/acf2021/WEB-INF/cfusion/bin/cfpm.sh\"' | run", "cfpm:install":"echo '\".engine/adobe2021/WEB-INF/cfusion/bin/cfpm.sh\" install ${1}' | run", "install:2021":"run-script cfpm:install zip,orm,mysql,postgresql,sqlserver,document,feed,mail,debugger" From 847db91610ad7c07ffecdadac0bd4efe1a4b2c1b Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Mon, 13 Mar 2023 22:48:18 +0100 Subject: [PATCH 06/46] more debugging --- tests/specs/async/AsyncManagerSpec.cfc | 2 +- tests/specs/async/ExecutorServicesSpec.cfc | 4 ++-- tests/specs/async/tasks/SchedulerSpec.cfc | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/specs/async/AsyncManagerSpec.cfc b/tests/specs/async/AsyncManagerSpec.cfc index d9610bae3..218af94ac 100644 --- a/tests/specs/async/AsyncManagerSpec.cfc +++ b/tests/specs/async/AsyncManagerSpec.cfc @@ -361,7 +361,7 @@ component extends="BaseAsyncSpec" { return arguments.result.value.getMemento(); } ); - debug( results ); + //debug( results ); } ); } ); } diff --git a/tests/specs/async/ExecutorServicesSpec.cfc b/tests/specs/async/ExecutorServicesSpec.cfc index 278cf16b4..1941eeabe 100644 --- a/tests/specs/async/ExecutorServicesSpec.cfc +++ b/tests/specs/async/ExecutorServicesSpec.cfc @@ -109,7 +109,7 @@ component extends="BaseAsyncSpec" { var statusMap = asyncManager.getExecutorStatusMap(); - debug( statusMap ); + //debug( statusMap ); expect( statusMap ).toHaveKey( "unitTest1" ).toHaveKey( "unitTest2" ); } ); @@ -117,7 +117,7 @@ component extends="BaseAsyncSpec" { var executor1 = asyncManager.newExecutor( "unitTest1" ); var statusMap = asyncManager.getExecutorStatusMap( "unitTest1" ); - debug( statusMap ); + //debug( statusMap ); expect( statusMap.isShutdown ).toBeFalse(); } ); } ); diff --git a/tests/specs/async/tasks/SchedulerSpec.cfc b/tests/specs/async/tasks/SchedulerSpec.cfc index 74f6ec88c..6e5d87644 100644 --- a/tests/specs/async/tasks/SchedulerSpec.cfc +++ b/tests/specs/async/tasks/SchedulerSpec.cfc @@ -113,8 +113,8 @@ component extends="tests.specs.async.BaseAsyncSpec" { sleep( 1000 ); var stats = scheduler.getTaskStats(); - debug( scheduler.getTasks() ); - debug( stats ); + //debug( scheduler.getTasks() ); + //debug( stats ); expect( stats.test1.neverRun ).toBeFalse( "test 1 neverRun" ); expect( stats.test2.neverRun ).toBeFalse( "test 2 neverRun" ); From d213829cff774e849fe85c4e6cf6e57bf8a3f283 Mon Sep 17 00:00:00 2001 From: jclausen Date: Sat, 29 Apr 2023 14:00:18 +0000 Subject: [PATCH 07/46] Apply cfformat changes --- tests/specs/async/AsyncManagerSpec.cfc | 2 +- tests/specs/async/ExecutorServicesSpec.cfc | 4 ++-- tests/specs/async/tasks/SchedulerSpec.cfc | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/specs/async/AsyncManagerSpec.cfc b/tests/specs/async/AsyncManagerSpec.cfc index 218af94ac..fe6fc4a8c 100644 --- a/tests/specs/async/AsyncManagerSpec.cfc +++ b/tests/specs/async/AsyncManagerSpec.cfc @@ -361,7 +361,7 @@ component extends="BaseAsyncSpec" { return arguments.result.value.getMemento(); } ); - //debug( results ); + // debug( results ); } ); } ); } diff --git a/tests/specs/async/ExecutorServicesSpec.cfc b/tests/specs/async/ExecutorServicesSpec.cfc index 1941eeabe..5bbb53eac 100644 --- a/tests/specs/async/ExecutorServicesSpec.cfc +++ b/tests/specs/async/ExecutorServicesSpec.cfc @@ -109,7 +109,7 @@ component extends="BaseAsyncSpec" { var statusMap = asyncManager.getExecutorStatusMap(); - //debug( statusMap ); + // debug( statusMap ); expect( statusMap ).toHaveKey( "unitTest1" ).toHaveKey( "unitTest2" ); } ); @@ -117,7 +117,7 @@ component extends="BaseAsyncSpec" { var executor1 = asyncManager.newExecutor( "unitTest1" ); var statusMap = asyncManager.getExecutorStatusMap( "unitTest1" ); - //debug( statusMap ); + // debug( statusMap ); expect( statusMap.isShutdown ).toBeFalse(); } ); } ); diff --git a/tests/specs/async/tasks/SchedulerSpec.cfc b/tests/specs/async/tasks/SchedulerSpec.cfc index 6e5d87644..1c96eb398 100644 --- a/tests/specs/async/tasks/SchedulerSpec.cfc +++ b/tests/specs/async/tasks/SchedulerSpec.cfc @@ -113,8 +113,8 @@ component extends="tests.specs.async.BaseAsyncSpec" { sleep( 1000 ); var stats = scheduler.getTaskStats(); - //debug( scheduler.getTasks() ); - //debug( stats ); + // debug( scheduler.getTasks() ); + // debug( stats ); expect( stats.test1.neverRun ).toBeFalse( "test 1 neverRun" ); expect( stats.test2.neverRun ).toBeFalse( "test 2 neverRun" ); From 4ce59c4814a7fbd50a8e74448059c23a5b7baa19 Mon Sep 17 00:00:00 2001 From: jclausen Date: Fri, 28 Apr 2023 11:04:10 -0400 Subject: [PATCH 08/46] add ACF2023 to cfProvider modern servers --- server-adobe@2023.json | 12 ++++++------ system/cache/providers/CFProvider.cfc | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/server-adobe@2023.json b/server-adobe@2023.json index b3b04092e..12ac330f0 100644 --- a/server-adobe@2023.json +++ b/server-adobe@2023.json @@ -21,10 +21,10 @@ "JVM":{ "heapSize":"1024" }, - "cfconfig": { - "file" : ".cfconfig.json" - }, - "scripts" : { - "onServerInstall":"cfpm install caching,zip,orm,mysql,postgresql,sqlserver,document,feed,mail,debugger" - } + "cfconfig":{ + "file":".cfconfig.json" + }, + "scripts":{ + "onServerInstall":"cfpm install caching,zip,orm,mysql,postgresql,sqlserver,document,feed,mail,debugger" + } } diff --git a/system/cache/providers/CFProvider.cfc b/system/cache/providers/CFProvider.cfc index 50369a405..4eb4a8e14 100644 --- a/system/cache/providers/CFProvider.cfc +++ b/system/cache/providers/CFProvider.cfc @@ -299,7 +299,7 @@ component */ function getQuiet( required objectKey ){ // Don't touch the casing on 2018+ - if ( listFind( "2018,2021", server.coldfusion.productVersion.listFirst() ) ) { + if ( listFind( "2018,2021,2023", server.coldfusion.productVersion.listFirst() ) ) { var element = getObjectStore().getQuiet( arguments.objectKey ); } else { var element = getObjectStore().getQuiet( uCase( arguments.objectKey ) ); @@ -492,7 +492,7 @@ component * @objectKey The object cache key */ boolean function clearQuiet( required objectKey ){ - if ( listFind( "2018,2021", server.coldfusion.productVersion.listFirst() ) ) { + if ( listFind( "2018,2021,2023", server.coldfusion.productVersion.listFirst() ) ) { return getObjectStore().removeQuiet( arguments.objectKey ); } else { return getObjectStore().removeQuiet( uCase( arguments.objectKey ) ); From a8eae963935c5c93cd4743be7a152a8fc3b40cc1 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Mon, 1 May 2023 19:40:54 +0200 Subject: [PATCH 09/46] COLDBOX-1219 CFProvider ACF versions are Hard-Coded --- system/cache/providers/CFProvider.cfc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/system/cache/providers/CFProvider.cfc b/system/cache/providers/CFProvider.cfc index 4eb4a8e14..f036539b2 100644 --- a/system/cache/providers/CFProvider.cfc +++ b/system/cache/providers/CFProvider.cfc @@ -299,7 +299,7 @@ component */ function getQuiet( required objectKey ){ // Don't touch the casing on 2018+ - if ( listFind( "2018,2021,2023", server.coldfusion.productVersion.listFirst() ) ) { + if ( server.coldfusion.productVersion.listFirst() >= 2018 ) { var element = getObjectStore().getQuiet( arguments.objectKey ); } else { var element = getObjectStore().getQuiet( uCase( arguments.objectKey ) ); @@ -547,4 +547,3 @@ component } } - From 5af9b745ffff831dbe39a69cacd4eb665b09c766 Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Mon, 1 May 2023 11:43:46 -0600 Subject: [PATCH 10/46] 6.8.2 bug fixes (#565) WIREBOX-132 WireBox caches Singletons even if their autowired dependencies throw exceptions. --- system/ioc/scopes/CFScopes.cfc | 9 ++++++-- system/ioc/scopes/CacheBox.cfc | 9 ++++++-- system/ioc/scopes/RequestScope.cfc | 9 ++++++-- system/ioc/scopes/Singleton.cfc | 9 ++++++-- tests/specs/ioc/scopes/CFScopesTest.cfc | 21 +++++++++++++++++++ tests/specs/ioc/scopes/CacheBoxTest.cfc | 23 +++++++++++++++++++++ tests/specs/ioc/scopes/RequestScopeTest.cfc | 14 +++++++++++++ tests/specs/ioc/scopes/SingletonTest.cfc | 13 +++++++++++- 8 files changed, 98 insertions(+), 9 deletions(-) diff --git a/system/ioc/scopes/CFScopes.cfc b/system/ioc/scopes/CFScopes.cfc index c7affa733..eb896bfb1 100644 --- a/system/ioc/scopes/CFScopes.cfc +++ b/system/ioc/scopes/CFScopes.cfc @@ -67,8 +67,13 @@ component accessors="true" { variables.injector.getScopeStorage().put( cacheKey, target, CFScope ); } - // wire it - variables.injector.autowire( target = target, mapping = arguments.mapping ); + try { + // wire it + variables.injector.autowire( target = target, mapping = arguments.mapping ); + } catch ( any e ) { + variables.injector.getScopeStorage().delete( cacheKey, CFScope ); + rethrow; + } // If thread safe, then now store it in the scope, as all dependencies are now safely wired if ( arguments.mapping.getThreadSafe() ) { diff --git a/system/ioc/scopes/CacheBox.cfc b/system/ioc/scopes/CacheBox.cfc index 286494bdc..038533940 100644 --- a/system/ioc/scopes/CacheBox.cfc +++ b/system/ioc/scopes/CacheBox.cfc @@ -91,8 +91,13 @@ component accessors="true" { ); } - // wire up dependencies on the object - variables.injector.autowire( target = local.refLocal.target, mapping = arguments.mapping ); + try { + // wire up dependencies on the object + variables.injector.autowire( target = local.refLocal.target, mapping = arguments.mapping ); + } catch ( any e ) { + cacheProvider.clear( cacheKey ); + rethrow; + } // If thread safe, then now store it in the cache, as all dependencies are now safely wired if ( arguments.mapping.getThreadSafe() ) { diff --git a/system/ioc/scopes/RequestScope.cfc b/system/ioc/scopes/RequestScope.cfc index 092a1a6f5..263dbd8ac 100644 --- a/system/ioc/scopes/RequestScope.cfc +++ b/system/ioc/scopes/RequestScope.cfc @@ -55,8 +55,13 @@ component accessors="true" { var target = variables.injector.buildInstance( arguments.mapping, arguments.initArguments ); request[ cacheKey ] = target; - // wire it - variables.injector.autowire( target = target, mapping = arguments.mapping ); + try { + // wire it + variables.injector.autowire( target = target, mapping = arguments.mapping ); + } catch ( any e ) { + structDelete( request, cacheKey ); + rethrow; + } // log it if ( variables.log.canDebug() ) { diff --git a/system/ioc/scopes/Singleton.cfc b/system/ioc/scopes/Singleton.cfc index d88db39b9..b159b09c4 100644 --- a/system/ioc/scopes/Singleton.cfc +++ b/system/ioc/scopes/Singleton.cfc @@ -76,8 +76,13 @@ component accessors="true" { variables.singletons.put( cacheKey, tmpSingleton ); } - // wire up dependencies on the singleton object - variables.injector.autowire( target = tmpSingleton, mapping = arguments.mapping ); + try { + // wire up dependencies on the singleton object + variables.injector.autowire( target = tmpSingleton, mapping = arguments.mapping ); + } catch ( any e ) { + variables.singletons.remove( cacheKey ); + rethrow; + } // If thread safe, then now store it in the singleton cache, as all dependencies are now safely wired if ( arguments.mapping.getThreadSafe() ) { diff --git a/tests/specs/ioc/scopes/CFScopesTest.cfc b/tests/specs/ioc/scopes/CFScopesTest.cfc index f4cee97a2..41b2f61f8 100755 --- a/tests/specs/ioc/scopes/CFScopesTest.cfc +++ b/tests/specs/ioc/scopes/CFScopesTest.cfc @@ -66,6 +66,27 @@ expect( o ).toBe( mockStub ); } ); } ); + + given( "An object that fails in autowiring", function(){ + then( "it should not be stored in the scope", function(){ + var mapping = createMock( "coldbox.system.ioc.config.Mapping" ).init( + name = "CFScopeTest" + ); + mapping.setScope( "session" ); + mapping.setThreadSafe( false ); + mockInjector.$( "buildInstance", mockStub ); + mockInjector + .$( "autowire" ) + .$throws( type = "CustomAutowireError", message = "Error in autowire" ); + structClear( session ); + + expect( function(){ + scope.getFromScope( mapping, {} ); + } ).toThrow( "CustomAutowireError" ); + + expect( session ).toBeEmpty(); + } ); + } ); } ); } ); } diff --git a/tests/specs/ioc/scopes/CacheBoxTest.cfc b/tests/specs/ioc/scopes/CacheBoxTest.cfc index 61043350c..fb37f26b8 100755 --- a/tests/specs/ioc/scopes/CacheBoxTest.cfc +++ b/tests/specs/ioc/scopes/CacheBoxTest.cfc @@ -58,5 +58,28 @@ mockInjector.$( "buildInstance", mockStub ).$( "autowire", mockStub ); o = scope.getFromScope( mapping, {} ); } + + function testInstancesThatErrorInAutowireAreRemoved(){ + var mapping = createMock( "coldbox.system.ioc.config.Mapping" ).init( name = "CacheTest" ); + + mapping.setCacheProperties( + key = "CacheTest", + timeout = "", + provider = "default" + ); + mapping.setThreadSafe( false ); + mockCache + .$( "get" ) + .$( "set", true ) + .$( "clear", true ); + mockInjector.$( "buildInstance", mockStub ); + mockInjector.$( "autowire" ).$throws( type = "CustomAutowireError", message = "Error in autowire" ); + + expect( function(){ + scope.getFromScope( mapping, {} ); + } ).toThrow( "CustomAutowireError" ); + + expect( mockCache.$once( "clear" ) ).toBeTrue(); + } diff --git a/tests/specs/ioc/scopes/RequestScopeTest.cfc b/tests/specs/ioc/scopes/RequestScopeTest.cfc index 022b9b136..37b0c252c 100755 --- a/tests/specs/ioc/scopes/RequestScopeTest.cfc +++ b/tests/specs/ioc/scopes/RequestScopeTest.cfc @@ -31,5 +31,19 @@ var o = scope.getFromScope( mapping, {} ); assertEquals( request[ "wirebox:RequestTest" ], o ); } + + function testInstancesThatErrorInAutowireAreRemoved(){ + var mapping = createMock( "coldbox.system.ioc.config.Mapping" ).init( name = "RequestTest" ); + mapping.setThreadSafe( true ); + mockInjector.$( "buildInstance", mockStub ); + mockInjector.$( "autowire" ).$throws( type = "CustomAutowireError", message = "Error in autowire" ); + structDelete( request, "wirebox:RequestTest" ); + + expect( function(){ + scope.getFromScope( mapping, {} ); + } ).toThrow( "CustomAutowireError" ); + + expect( request ).notToHaveKey( "wirebox:RequestTest" ); + } diff --git a/tests/specs/ioc/scopes/SingletonTest.cfc b/tests/specs/ioc/scopes/SingletonTest.cfc index 4b7b4c1b0..03949b3f2 100755 --- a/tests/specs/ioc/scopes/SingletonTest.cfc +++ b/tests/specs/ioc/scopes/SingletonTest.cfc @@ -28,7 +28,7 @@ function testGetFromScope(){ // 1: Default construction var mapping = createMock( "coldbox.system.ioc.config.Mapping" ).init( name = "singletontest" ); - mapping.setThreadSafe( true ); + mapping.setThreadSafe( false ); mockInjector.$( "buildInstance", mockStub ).$( "autowire", mockStub ); var o = scope.getFromScope( mapping, {} ); @@ -52,5 +52,16 @@ scope.clear(); assertTrue( structCount( scope.getSingletons() ) eq 0 ); } + + function testInstancesThatErrorInAutowireAreRemoved(){ + var mapping = createMock( "coldbox.system.ioc.config.Mapping" ).init( name = "singletontest" ); + mapping.setThreadSafe( false ); + mockInjector.$( "buildInstance", mockStub ); + mockInjector.$( "autowire" ).$throws( type = "CustomAutowireError", message = "Error in autowire" ); + expect( function(){ + scope.getFromScope( mapping, {} ); + } ).toThrow( "CustomAutowireError" ); + expect( scope.getSingletons() ).toBeEmpty(); + } From d56b92cea8e53321a38c906f987915f63c77818a Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Mon, 1 May 2023 19:51:01 +0200 Subject: [PATCH 11/46] finalizing lts build --- .github/workflows/release.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 62b52c0d9..b06dd3b59 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -198,7 +198,12 @@ jobs: - name: Bump Version run: | - box bump --minor --!TagVersion + if [ $LTS == 'true' ] + then + box bump --patch --!TagVersion + else + box bump --minor --!TagVersion + fi - name: Commit Version Bump uses: EndBug/add-and-commit@v9.1.1 From e0aa96ff743adb860834194715729198ecb051bd Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Mon, 1 May 2023 19:54:28 +0200 Subject: [PATCH 12/46] changelog prep --- changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/changelog.md b/changelog.md index 1197bc8f0..07db10d69 100644 --- a/changelog.md +++ b/changelog.md @@ -14,6 +14,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Github actions for LTS Releases - LTS Updates +### Bugs + +- [COLDBOX-1219](https://ortussolutions.atlassian.net/browse/COLDBOX-1219) CFProvider ACF versions are Hard-Coded +- [WIREBOX-132](https://ortussolutions.atlassian.net/browse/WIREBOX-132) WireBox caches Singletons even if their autowired dependencies throw exceptions. + ## [6.8.1] => 2022-AUG-11 ### ColdBox HMVC Core From c3eb065b184ddc4b462b6fcbc14881f4377f79ad Mon Sep 17 00:00:00 2001 From: Github Actions Date: Mon, 1 May 2023 17:59:06 +0000 Subject: [PATCH 13/46] Finalized changelog for v6.8.2 --- changelog.md | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/changelog.md b/changelog.md index 07db10d69..2e20784eb 100644 --- a/changelog.md +++ b/changelog.md @@ -1,14 +1,16 @@ # Changelog -All notable changes to this project will be documented here: https://coldbox.ortusbooks.com/intro/release-history and summarized in this file. +All notable changes to this project will be documented here: and summarized in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ----- +* * * ## [Unreleased] +## [6.8.2] - 2023-05-01 + ### Added - Github actions for LTS Releases @@ -28,7 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [COLDBOX-1139](https://ortussolutions.atlassian.net/browse/COLDBOX-1139) make event caching cache keys lower cased to avoid case issues when clearing keys - [COLDBOX-1138](https://ortussolutions.atlassian.net/browse/COLDBOX-1138) Event Cache Response Has Status Code of 0 or `null` ----- +* * * ## [6.8.0] => 2022-JUL-23 @@ -53,7 +55,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [COLDBOX-1135](https://ortussolutions.atlassian.net/browse/COLDBOX-1135) Remove HandlerTestCase as it is no longer in usage. ----- +* * * ## [6.7.0] => 2022-JUN-22 @@ -66,17 +68,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [COLDBOX-1109](https://ortussolutions.atlassian.net/browse/COLDBOX-1109) Exceptions in async interceptors are missing onException announcement - [COLDBOX-1105](https://ortussolutions.atlassian.net/browse/COLDBOX-1105) Interception with `async` annotation causes InterceptorState Exception on Reinit - [COLDBOX-1104](https://ortussolutions.atlassian.net/browse/COLDBOX-1104) A view not set exception is thrown when trying to execution handler ColdBox methods that are not concrete actions when they should be invalid events. -- [COLDBOX-1103](https://ortussolutions.atlassian.net/browse/COLDBOX-1103) Update getServerIP\(\) so it avoids looking at the cgi scope as it can cause issues on ACF +- [COLDBOX-1103](https://ortussolutions.atlassian.net/browse/COLDBOX-1103) Update getServerIP() so it avoids looking at the cgi scope as it can cause issues on ACF - [COLDBOX-1100](https://ortussolutions.atlassian.net/browse/COLDBOX-1100) Event Caching Does Not Preserve HTTP Response Codes - [COLDBOX-1099](https://ortussolutions.atlassian.net/browse/COLDBOX-1099) Regression on ColdBox v6.6.1 around usage of statusCode = 0 on relocates - [COLDBOX-1098](https://ortussolutions.atlassian.net/browse/COLDBOX-1098) RequestService context creation not thread safe -- [COLDBOX-1097](https://ortussolutions.atlassian.net/browse/COLDBOX-1097) Missing scopes on isNull\(\) checks -- [COLDBOX-1092](https://ortussolutions.atlassian.net/browse/COLDBOX-1092) RestHandler Try/Catches Break In Testbox When RunEvent\(\) is Called +- [COLDBOX-1097](https://ortussolutions.atlassian.net/browse/COLDBOX-1097) Missing scopes on isNull() checks +- [COLDBOX-1092](https://ortussolutions.atlassian.net/browse/COLDBOX-1092) RestHandler Try/Catches Break In Testbox When RunEvent() is Called - [COLDBOX-1045](https://ortussolutions.atlassian.net/browse/COLDBOX-1045) Scheduled tasks have no default error handling - [COLDBOX-1043](https://ortussolutions.atlassian.net/browse/COLDBOX-1043) Creating scheduled task with unrecognized timeUnit throws null pointer -- [COLDBOX-1042](https://ortussolutions.atlassian.net/browse/COLDBOX-1042) afterAnyTask\(\) and task.after\(\) don't run after failing task -- [COLDBOX-1040](https://ortussolutions.atlassian.net/browse/COLDBOX-1040) Error in onAnyTaskError\(\) or after\(\) tasks not handled and executor dies. -- [COLDBOX-966](https://ortussolutions.atlassian.net/browse/COLDBOX-966) Coldbox Renderer.RenderLayout\(\) Overwrites Event's Current View +- [COLDBOX-1042](https://ortussolutions.atlassian.net/browse/COLDBOX-1042) afterAnyTask() and task.after() don't run after failing task +- [COLDBOX-1040](https://ortussolutions.atlassian.net/browse/COLDBOX-1040) Error in onAnyTaskError() or after() tasks not handled and executor dies. +- [COLDBOX-966](https://ortussolutions.atlassian.net/browse/COLDBOX-966) Coldbox Renderer.RenderLayout() Overwrites Event's Current View #### Improvement @@ -89,9 +91,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 #### New Feature -- [COLDBOX-1123](https://ortussolutions.atlassian.net/browse/COLDBOX-1123) New xTask\(\) method in the schedulers that will automatically disable the task but still register it. Great for debugging! +- [COLDBOX-1123](https://ortussolutions.atlassian.net/browse/COLDBOX-1123) New xTask() method in the schedulers that will automatically disable the task but still register it. Great for debugging! - [COLDBOX-1121](https://ortussolutions.atlassian.net/browse/COLDBOX-1121) Log schedule task failures to console so errors are not ignored -- [COLDBOX-1120](https://ortussolutions.atlassian.net/browse/COLDBOX-1120) Scheduler's onShutdown\(\) callback now receives the boolean force and numeric timeout arguments +- [COLDBOX-1120](https://ortussolutions.atlassian.net/browse/COLDBOX-1120) Scheduler's onShutdown() callback now receives the boolean force and numeric timeout arguments - [COLDBOX-1119](https://ortussolutions.atlassian.net/browse/COLDBOX-1119) The Scheduler's shutdown method now has two arguments: boolean force, numeric timeout - [COLDBOX-1118](https://ortussolutions.atlassian.net/browse/COLDBOX-1118) All schedulers have a new property: shutdownTimeout which defaults to 30 that can be used to control how long to wait for tasks to gracefully complete when shutting down. - [COLDBOX-1113](https://ortussolutions.atlassian.net/browse/COLDBOX-1113) New coldobx.system.testing.VirtualApp object that can startup,restart and shutdown Virtual Testing Applications @@ -137,7 +139,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [WIREBOX-127](https://ortussolutions.atlassian.net/browse/WIREBOX-127) Lazy load all constructs on the Injector to improve performance - [WIREBOX-125](https://ortussolutions.atlassian.net/browse/WIREBOX-125) Remove the usage of identity hash codes, they are no longer relevant and can cause contention under load ----- +* * * ## [6.6.1] => 2022-FEB-17 @@ -151,7 +153,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 #### Improvement - [COLDBOX-1095](https://ortussolutions.atlassian.net/browse/COLDBOX-1095) Update Response Pagination Properties for Case-Sensitive Engines -- [COLDBOX-1091](https://ortussolutions.atlassian.net/browse/COLDBOX-1091) default status code to 302 in the internal relocate\(\) just like CFML does instead of 0 and eliminate source +- [COLDBOX-1091](https://ortussolutions.atlassian.net/browse/COLDBOX-1091) default status code to 302 in the internal relocate() just like CFML does instead of 0 and eliminate source - [COLDBOX-1089](https://ortussolutions.atlassian.net/browse/COLDBOX-1089) Update the internal cfml engine checker to have more engine based feature checkers - [COLDBOX-1088](https://ortussolutions.atlassian.net/browse/COLDBOX-1088) Switch isInstance check on renderdata in controller to secondary of $renderdata check to optimize speed @@ -159,13 +161,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 #### Bug -- [CACHEBOX-80](https://ortussolutions.atlassian.net/browse/CACHEBOX-80) Bug in JDBCMetadataIndexer sortedKeys\(\) using non-existent variable `arguments.objectKey` +- [CACHEBOX-80](https://ortussolutions.atlassian.net/browse/CACHEBOX-80) Bug in JDBCMetadataIndexer sortedKeys() using non-existent variable `arguments.objectKey` #### Improvement -- [CACHEBOX-81](https://ortussolutions.atlassian.net/browse/CACHEBOX-81) JDBCStore Dynamically generate queryExecute options \+ new config to always include DSN due to ACF issues +- [CACHEBOX-81](https://ortussolutions.atlassian.net/browse/CACHEBOX-81) JDBCStore Dynamically generate queryExecute options + new config to always include DSN due to ACF issues ----- +* * * ## [6.6.0] => 2022-JAN-31 @@ -206,7 +208,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [COLDBOX-1033](https://ortussolutions.atlassian.net/browse/COLDBOX-1033) New controller method: `getUserSessionIdentifier`() which gives you the unique request tracking identifier according to our algorithms - [COLDBOX-1032](https://ortussolutions.atlassian.net/browse/COLDBOX-1032) New coldbox setting `identifierProvider` which can be a closure/udf/lambda that provides a unique tracking identifier for user requests ----- +* * * ### CacheBox @@ -221,7 +223,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [CACHEBOX-73](https://ortussolutions.atlassian.net/browse/CACHEBOX-73) Replace IIF and urlEncodedFormat on cache content reports - [CACHEBOX-79](https://ortussolutions.atlassian.net/browse/CACHEBOX-79) Lower logging verbosity of cache reaping from info to debug messages ----- +* * * ### WireBox @@ -244,3 +246,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 #### Task - [WIREBOX-123](https://ortussolutions.atlassian.net/browse/WIREBOX-123) Removal of usage of Injector dsl interface due to so many issues with multiple engines + +[Unreleased]: https://github.com/ColdBox/coldbox-platform/compare/v6.8.2...HEAD + +[6.8.2]: https://github.com/ColdBox/coldbox-platform/compare/e0aa96ff743adb860834194715729198ecb051bd...v6.8.2 From 9a4bf2563e5b7e80d0d74f84ff6d706d671fddb6 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Mon, 1 May 2023 20:10:30 +0200 Subject: [PATCH 14/46] some minor issues on the LTS flow --- .github/workflows/release.yml | 1 + box.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b06dd3b59..b1da74fca 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -204,6 +204,7 @@ jobs: else box bump --minor --!TagVersion fi + git pull - name: Commit Version Bump uses: EndBug/add-and-commit@v9.1.1 diff --git a/box.json b/box.json index 3d524c971..af634215c 100644 --- a/box.json +++ b/box.json @@ -1,6 +1,6 @@ { "name":"ColdBox Platform", - "version":"6.8.2", + "version":"6.8.3", "location":"https://downloads.ortussolutions.com/ortussolutions/coldbox/@build.version@/coldbox-@build.version@.zip", "author":"Ortus Solutions ", "slug":"coldbox", From ee3dae34c6637d066bb17ac6d358beef5a808939 Mon Sep 17 00:00:00 2001 From: Giancarlo Gomez Date: Mon, 15 May 2023 21:35:20 -0400 Subject: [PATCH 15/46] Feature/scheduled tasks updates for 6.8 (#573) * Scheduled Tasks Updates for 6.8 Updates to match improvements added to 7. COLDBOX-1226 * Updated Comments on isConstrained() Comments now include new constrains checked and matches the order of execution within the function * Added timestamp to debugLog * executed cfformat after updates * Property Types, Comments and minor refactor Added types to all properties Minor formatting updates Log output updates ( clean up and additional output where helpful ) Comment updates Updates to minimize repeated code validateTime() add the minutes if missing and return the proper time value if succesful setInitialDelayPeriodAndTimeUnit() - NEW FUNCTION Handles the repetative steps of many of the scheduling methods * Fixed invalid argument name in new function * Enhanced ValidateTime() Updated the regex check to check for values from 00:00 - 23:59 The function now parses the value entered by the user and uses internal date and formatting functions to create the required HH:mm format while protecting the user / engine from itself ( Lucee sees hour 24 as valid while Java does not ) and still throwing an error if the user simply enters a value that can not be converted at all. --- system/async/tasks/ScheduledTask.cfc | 1287 +++++++++++++++++-------- system/web/tasks/ColdBoxScheduler.cfc | 6 +- 2 files changed, 872 insertions(+), 421 deletions(-) diff --git a/system/async/tasks/ScheduledTask.cfc b/system/async/tasks/ScheduledTask.cfc index 05e5a24ba..6d98e1be6 100644 --- a/system/async/tasks/ScheduledTask.cfc +++ b/system/async/tasks/ScheduledTask.cfc @@ -6,11 +6,31 @@ */ component accessors="true" { + /** + * The human name of this task + */ + property name="name" type="string"; + + /** + * The task closure or CFC to execute in the task + */ + property name="task" type="any"; + + /** + * The method to execute if the task is a CFC + */ + property name="method" type="string"; + /** * The delay or time to wait before we execute the task in the scheduler */ property name="delay" type="numeric"; + /** + * The time unit string used when there is a delay requested for the task + */ + property name="delayTimeUnit" type="string"; + /** * A fixed time period of execution of the tasks in this schedule. It does not wait for tasks to finish, * tasks are fired exactly at that time period. @@ -25,22 +45,17 @@ component accessors="true" { /** * The time unit string used to schedule the task */ - property name="timeunit"; - - /** - * The task closure or CFC to execute in the task - */ - property name="task"; + property name="timeUnit" type="string"; /** - * The method to execute if the task is a CFC + * A handy boolean that is set when the task is annually scheduled */ - property name="method"; + property name="annually" type="boolean"; /** - * The human name of this task + * The boolean value is used for debugging */ - property name="name"; + property name="debug" type="boolean"; /** * A handy boolean that disables the scheduling of this task @@ -54,81 +69,113 @@ component accessors="true" { property name="whenClosure" type="any"; /** - * The timezone this task runs under, by default we use the timezone defined in the schedulers + * Constraint of what day of the month we need to run on: 1-31 */ - property name="timezone"; + property name="dayOfTheMonth" type="numeric"; /** - * This task can be assigned to a task scheduler or be executed on its own at runtime + * Constraint of what day of the week this runs on: 1-7 */ - property name="scheduler"; + property name="dayOfTheWeek" type="numeric"; /** - * The collection of stats for the task: { created, lastRun, totalRuns, totalFailures, totalSuccess, lastResult, neverRun, lastExecutionTime } + * Constraint to run only on weekends */ - property name="stats" type="struct"; + property name="weekends" type="boolean"; /** - * The before task closure + * Constraint to run only on weekdays */ - property name="beforeTask"; + property name="weekdays" type="boolean"; /** - * The after task closure + * Constraint to run only on the first business day of the month */ - property name="afterTask"; + property name="firstBusinessDay" type="boolean"; /** - * The task success closure + * Constraint to run only on the last business day of the month */ - property name="onTaskSuccess"; + property name="lastBusinessDay" type="boolean"; /** - * The task failure closure + * By default tasks execute in an interval frequency which can cause tasks to + * stack if they take longer than their periods ( fire immediately after completion ). + * With this boolean flag turned on, the schedulers don't kick off the + * intervals until the tasks finish executing. Meaning no stacking. */ - property name="onTaskFailure"; + property name="noOverlaps" type="boolean"; /** - * The constraint of what day of the month we need to run on: 1-31 + * Used by first and last business day constraints to + * log the time of day for use in setNextRunTime() */ - property name="dayOfTheMonth" type="numeric"; + property name="taskTime" type="string"; /** - * The constraint of what day of the week this runs on: 1-7 + * Constraint of when the task can start execution. */ - property name="dayOfTheWeek" type="numeric"; + property name="startOnDateTime" type="date"; /** - * Constraint to run only on weekends + * Constraint of when the task must not continue to execute */ - property name="weekends" type="boolean"; + property name="endOnDateTime" type="date"; /** - * Constraint to run only on weekdays + * Constraint to limit the task to run after a specified time of day. */ - property name="weekdays" type="boolean"; + property name="startTime" type="string"; /** - * Constraint to run only on the last business day of the month + * Constraint to limit the task to run before a specified time of day. */ - property name="lastBusinessDay" type="boolean"; + property name="endTime" type="string"; /** - * By default tasks execute in an interval frequency which can cause overlaps if tasks - * take longer than their periods. With this boolean flag turned on, the schedulers - * don't kick off the intervals until the tasks finish executing. Meaning no overlaps. + * The boolean value that lets us know if this task has been scheduled */ - property name="noOverlaps" type="boolean"; + property name="scheduled" type="boolean"; + + /** + * This task can be assigned to a task scheduler or be executed on its own at runtime + */ + property name="scheduler" type="any"; + + /** + * A struct for the task that can be used to store any metadata + */ + property name="meta" type="struct"; + + /** + * The collection of stats for the task: { name, created, lastRun, nextRun, totalRuns, totalFailures, totalSuccess, lastResult, neverRun, lastExecutionTime } + */ + property name="stats" type="struct"; + + /** + * The timezone this task runs under, by default we use the timezone defined in the schedulers + */ + property name="timezone" type="string"; + + /** + * The before task closure + */ + property name="beforeTask" type="any"; + + /** + * The after task closure + */ + property name="afterTask" type="any"; /** - * The constraint of when the task can start execution. + * The task success closure */ - property name="startOnDateTime"; + property name="onTaskSuccess" type="any"; /** - * The constraint of when the task must not continue to execute + * The task failure closure */ - property name="endOnDateTime"; + property name="onTaskFailure" type="any"; /** @@ -138,12 +185,14 @@ component accessors="true" { * @executor The executor this task will run under and be linked to * @task The closure or cfc that represents the task (optional) * @method The method on the cfc to call, defaults to "run" (optional) + * @debug Add debugging logs to System out, disabled by default */ ScheduledTask function init( required name, required executor, any task = "", - method = "run" + method = "run", + debug = false ){ // Utility class variables.util = new coldbox.system.core.util.Util(); @@ -157,22 +206,29 @@ component accessors="true" { variables.task = arguments.task; variables.method = arguments.method; // Default Frequencies - variables.period = 0; variables.delay = 0; + variables.delayTimeUnit = ""; + variables.period = 0; variables.spacedDelay = 0; variables.timeUnit = "milliseconds"; - variables.noOverlap = false; + variables.noOverlaps = false; // Constraints + variables.annually = false; + variables.debug = arguments.debug; variables.disabled = false; variables.whenClosure = ""; variables.dayOfTheMonth = 0; variables.dayOfTheWeek = 0; variables.weekends = false; variables.weekdays = false; + variables.firstBusinessDay = false; variables.lastBusinessDay = false; - variables.noOverlaps = false; + variables.taskTime = ""; variables.startOnDateTime = ""; variables.endOnDateTime = ""; + variables.startTime = ""; + variables.endTime = ""; + variables.scheduled = false; // Probable Scheduler or not variables.scheduler = ""; // Prepare execution tracking stats @@ -183,6 +239,8 @@ component accessors="true" { "created" : now(), // The last execution run timestamp "lastRun" : "", + // The next execution run timestamp + "nextRun" : "", // Total runs "totalRuns" : 0, // Total faiulres @@ -200,12 +258,16 @@ component accessors="true" { // Server IP "localIp" : variables.util.getServerIp() }; + // Prepare for the user to store metadata + variables.meta = {}; // Life cycle methods variables.beforeTask = ""; variables.afterTask = ""; variables.onTaskSuccess = ""; variables.onTaskFailure = ""; + debugLog( "init" ); + return this; } @@ -221,6 +283,8 @@ component accessors="true" { * @throws UserInterruptException - When the thread has been interrupted */ function checkInterrupted(){ + debugLog( "checkInterrupted" ); + var thisThread = createObject( "java", "java.lang.Thread" ).currentThread(); // Has the user/system tried to interrupt this thread? if ( thisThread.isInterrupted() ) { @@ -261,6 +325,8 @@ component accessors="true" { * @timezone The timezone string identifier */ ScheduledTask function setTimezone( required timezone ){ + debugLog( "setTimezone", arguments ); + variables.timezone = createObject( "java", "java.time.ZoneId" ).of( arguments.timezone ); return this; } @@ -269,6 +335,8 @@ component accessors="true" { * Has this task been assigned to a scheduler or not? */ boolean function hasScheduler(){ + debugLog( "hasScheduler" ); + return isObject( variables.scheduler ); } @@ -281,11 +349,53 @@ component accessors="true" { * @return The schedule with the task/method registered on it */ ScheduledTask function call( required task, method = "run" ){ + debugLog( "call" ); + variables.task = arguments.task; variables.method = arguments.method; return this; } + /** + * Update the debug setting for this task! + */ + ScheduledTask function debug( required boolean value ){ + debugLog( "debug" ); + + variables.debug = arguments.value; + return this; + } + + /** + * Set the meta data for this task! + */ + ScheduledTask function setMeta( required struct meta ){ + debugLog( "setMeta" ); + + variables.meta = arguments.meta; + return this; + } + + /** + * Set a specific meta data key for this task! + */ + ScheduledTask function setMetaKey( required string key, required any value ){ + debugLog( "setMetaKey" ); + + variables.meta[ arguments.key ] = arguments.value; + return this; + } + + /** + * Delete a specific meta data key from this task! + */ + ScheduledTask function deleteMetaKey( required string key ){ + debugLog( "deleteMetaKey" ); + + variables.meta.delete( arguments.key ); + return this; + } + /** * -------------------------------------------------------------------------- * Restrictions @@ -297,6 +407,8 @@ component accessors="true" { * If the closure returns true we schedule, else we disable it. */ ScheduledTask function when( target ){ + debugLog( "when" ); + variables.whenClosure = arguments.target; return this; } @@ -305,6 +417,8 @@ component accessors="true" { * Disable the task when scheduled, meaning, don't run this sucker! */ ScheduledTask function disable(){ + debugLog( "disable" ); + variables.disabled = true; return this; } @@ -313,6 +427,8 @@ component accessors="true" { * Enable the task when disabled so we can run again */ ScheduledTask function enable(){ + debugLog( "enable" ); + variables.disabled = false; return this; } @@ -324,9 +440,52 @@ component accessors="true" { * - when closure */ boolean function isDisabled(){ + debugLog( "isDisabled" ); + return variables.disabled; } + /** + * + * @startTime The specific time using 24 hour format => HH:mm + * @endTime The specific time using 24 hour format => HH:mm + */ + ScheduledTask function between( required string startTime, required string endTime ){ + debugLog( "between" ); + + startOnTime( arguments.startTime ); + endOnTime( arguments.endTime ); + return this; + } + + /** + * + * @time The specific time using 24 hour format => HH:mm + */ + ScheduledTask function startOnTime( required string time ){ + debugLog( "startOnTime" ); + + // Validate time format + arguments.time = validateTime( arguments.time ); + + variables.startTime = arguments.time; + return this; + } + + /** + * + * @time The specific time using 24 hour format => HH:mm + */ + ScheduledTask function endOnTime( required string time ){ + debugLog( "endOnTime" ); + + // Validate time format + arguments.time = validateTime( arguments.time ); + + variables.endTime = arguments.time; + return this; + } + /** * -------------------------------------------------------------------------- * Startup and Runnable Proxy @@ -339,13 +498,19 @@ component accessors="true" { * - when * - dayOfTheMonth * - dayOfTheWeek + * - firstBusinessDay * - lastBusinessDay - * - weekends * - weekdays + * - weekends + * - startOnDateTime + * - endOnDateTime + * - startTime and/or endTime * * This method is called by the `run()` method at runtime to determine if the task can be ran at that point in time */ boolean function isConstrained(){ + debugLog( "isConstrained" ); + var now = getJavaNow(); // When Closure that dictates if the task can be scheduled/ran: true => yes, false => no @@ -358,25 +523,36 @@ component accessors="true" { } // Do we have a day of the month constraint? and the same as the running date/time? Else skip it + // If the day day assigned is greater than the days in the month, then we let it thru + // as the user intended to run it at the end of the month if ( variables.dayOfTheMonth > 0 && - now.getDayOfMonth() != variables.dayOfTheMonth + now.getDayOfMonth() != variables.dayOfTheMonth && + daysInMonth( now.toString() ) > variables.dayOfTheMonth ) { return true; } - // Do we have a last business day constraint + // Do we have day of the week? if ( - variables.lastBusinessDay && - now.getDayOfMonth() != getLastDayOfTheMonth().getDayOfMonth() + variables.dayOfTheWeek > 0 && + now.getDayOfWeek().getValue() != variables.dayOfTheWeek ) { return true; } - // Do we have weekends? + // Do we have a first business day constraint if ( - variables.weekends && - now.getDayOfWeek().getValue() <= 5 + variables.firstBusinessDay && + now.getDayOfMonth() != getFirstBusinessDayOfTheMonth().getDayOfMonth() + ) { + return true; + } + + // Do we have a last business day constraint + if ( + variables.lastBusinessDay && + now.getDayOfMonth() != getLastBusinessDayOfTheMonth().getDayOfMonth() ) { return true; } @@ -389,10 +565,10 @@ component accessors="true" { return true; } - // Do we have day of the week? + // Do we have weekends? if ( - variables.dayOfTheWeek > 0 && - now.getDayOfWeek().getValue() != variables.dayOfTheWeek + variables.weekends && + now.getDayOfWeek().getValue() <= 5 ) { return true; } @@ -405,7 +581,7 @@ component accessors="true" { return true; } - // Do we have a end on constraint + // Do we have an end on constraint if ( len( variables.endOnDateTime ) && now.isAfter( variables.endOnDateTime ) @@ -413,26 +589,48 @@ component accessors="true" { return true; } + // Do we have we have a start time and / or end time constraint + if ( + len( variables.startTime ) || + len( variables.endTime ) + ) { + var _startTime = variables.chronoUnitHelper.parse( + dateFormat( now(), "yyyy-mm-dd" ) & "T" & ( + len( variables.startTime ) ? variables.startTime : "00:00:00" + ) + ); + var _endTime = variables.chronoUnitHelper.parse( + dateFormat( now(), "yyyy-mm-dd" ) & "T" & ( len( variables.endTime ) ? variables.endTime : "23:59:59" ) + ); + if ( now.isBefore( _startTime ) || now.isAfter( _endTime ) ) { + return true; + } + } + return false; } /** * This is the runnable proxy method that executes your code by the executors */ - function run(){ + function run( boolean force = false ){ + debugLog( "run( #arguments.force# )" ); + var sTime = getTickCount(); // If disabled or paused - if ( isDisabled() ) { + if ( !arguments.force && isDisabled() ) { + setNextRunTime(); return; } // Check for constraints of execution - if ( isConstrained() ) { + if ( !arguments.force && isConstrained() ) { + setNextRunTime(); return; } - // Mark the task as it wil run now for the first time + // Mark the task as it will run now for the first time variables.stats.neverRun = false; try { @@ -471,8 +669,8 @@ component accessors="true" { // store failures variables.stats.totalFailures = variables.stats.totalFailures + 1; // Log it, so it doesn't go to ether - err( "Error running task (#getname()#) : #e.message & e.detail#" ); - err( "Stacktrace for task (#getname()#) : #e.stackTrace#" ); + err( "Error running task (#getName()#) : #e.message & e.detail#" ); + err( "Stacktrace for task (#geNname()#) : #e.stackTrace#" ); // Try to execute the error handlers. Try try try just in case. try { @@ -493,10 +691,8 @@ component accessors="true" { } } catch ( any afterException ) { // Log it, so it doesn't go to ether and executor doesn't die. - err( - "Error running task (#getname()#) after/error handlers : #afterException.message & afterException.detail#" - ); - err( "Stacktrace for task (#getname()#) after/error handlers : #afterException.stackTrace#" ); + err( "Error running task (#getName()#) after/error handlers : #afterException.message & afterException.detail#" ); + err( "Stacktrace for task (#getName()#) after/error handlers : #afterException.stackTrace#" ); } } finally { // Store finalization stats @@ -505,6 +701,8 @@ component accessors="true" { variables.stats.lastExecutionTime = getTickCount() - sTime; // Call internal cleanups event cleanupTaskRun(); + // set next run time based on timeUnit and period + setNextRunTime(); } } @@ -513,6 +711,7 @@ component accessors="true" { * any type of cleanups */ function cleanupTaskRun(){ + debugLog( "cleanupTaskRun" ); // no cleanups for now } @@ -524,10 +723,65 @@ component accessors="true" { */ ScheduledFuture function start(){ // If we have overlaps and the spaced delay is 0 then grab it from the period - if ( variables.noOverlaps and variables.spacedDelay eq 0 ) { + if ( variables.noOverlaps && variables.spacedDelay == 0 ) { variables.spacedDelay = variables.period; } + // If we have a delay and a delayTimeUnit, then we need to compare to our + // current timeUnit and convert to support the delay + // ( only if our time unit is seconds , if not we disable the delay ) + // + // TODO: We need to support other time units - this is a temporary fix + // for the previous issue where the delay and/or setting would replace + // the time setting of the task based on the order presented + if ( + variables.delay > 0 && + len( variables.delayTimeUnit ) && + compare( variables.delayTimeUnit, variables.timeUnit ) + ) { + if ( variables.timeUnit != "seconds" ) { + variables.delay = 0; + // reset the initial nextRunTime + variables.stats.nextRun = ""; + } else { + // transform all to seconds + switch ( variables.delayTimeUnit ) { + case "days": + variables.delay = javacast( "int", variables.delay * 60 * 60 * 24 ); + break; + case "hours": + variables.delay = javacast( "int", variables.delay * 60 * 60 ); + break; + case "minutes": + variables.delay = javacast( "int", variables.delay * 60 ); + break; + case "milliseconds": + variables.delay = javacast( "int", variables.delay / 1000 ); + break; + case "microseconds": + variables.delay = javacast( "int", variables.delay / 1000000 ); + break; + case "nanoseconds": + variables.delay = javacast( "int", variables.delay / 1000000000 ); + break; + } + } + } + + debugLog( + "start", + { + delay : variables.delay, + delayTimeUnit : variables.delayTimeUnit, + period : variables.period, + spacedDelay : variables.spacedDelay, + timeUnit : variables.timeUnit, + type : variables.spacedDelay > 0 ? "scheduleWithFixedDelay" : variables.period > 0 ? "scheduleAtFixedRate" : "runOnce" + } + ); + + variables.scheduled = true; + // Startup a spaced frequency task: no overlaps if ( variables.spacedDelay > 0 ) { return variables.executor.scheduleWithFixedDelay( @@ -571,6 +825,8 @@ component accessors="true" { * @target The closure to execute */ ScheduledTask function before( required target ){ + debugLog( "before" ); + variables.beforeTask = arguments.target; return this; } @@ -581,6 +837,8 @@ component accessors="true" { * @target The closure to execute */ ScheduledTask function after( required target ){ + debugLog( "after" ); + variables.afterTask = arguments.target; return this; } @@ -591,6 +849,8 @@ component accessors="true" { * @target The closure to execute */ ScheduledTask function onSuccess( required target ){ + debugLog( "onSuccess" ); + variables.onTaskSuccess = arguments.target; return this; } @@ -601,6 +861,8 @@ component accessors="true" { * @target The closure to execute */ ScheduledTask function onFailure( required target ){ + debugLog( "onFailure" ); + variables.onTaskFailure = arguments.target; return this; } @@ -614,22 +876,39 @@ component accessors="true" { /** * Set a delay in the running of the task that will be registered with this schedule * - * @delay The delay that will be used before executing the task - * @timeUnit The time unit to use, available units are: days, hours, microseconds, milliseconds, minutes, nanoseconds, and seconds. The default is milliseconds - */ - ScheduledTask function delay( numeric delay, timeUnit = "milliseconds" ){ - variables.delay = arguments.delay; - variables.timeUnit = arguments.timeUnit; + * @delay The delay that will be used before executing the task + * @timeUnit The time unit to use, available units are: days, hours, microseconds, milliseconds, minutes, nanoseconds, and seconds. The default is milliseconds + * @overwrites Boolean to overwrite delay and delayTimeUnit even if value is already set, this is helpful if the delay is set later in the chain when creating the task - defaults to false + * @setNextRunTime Boolean to execute setInitialNextRunTime() - defaults to true + */ + ScheduledTask function delay( + numeric delay, + timeUnit = "milliseconds", + boolean overwrites = false, + boolean setNextRunTime = true + ){ + debugLog( "delay", arguments ); + + if ( arguments.overwrites || !variables.delay ) { + variables.delay = arguments.delay; + variables.delayTimeUnit = arguments.timeUnit; + } + + if ( arguments.setNextRunTime ) + setInitialNextRunTime( delay: arguments.delay, timeUnit: arguments.timeUnit ); + return this; } /** * Run the task every custom spaced delay of execution, meaning no overlaps * - * @delay The delay that will be used before executing the task - * @timeUnit The time unit to use, available units are: days, hours, microseconds, milliseconds, minutes, nanoseconds, and seconds. The default is milliseconds + * @spacedDelay The delay that will be used before executing the task with no overlaps + * @timeUnit The time unit to use, available units are: days, hours, microseconds, milliseconds, minutes, nanoseconds, and seconds. The default is milliseconds */ ScheduledTask function spacedDelay( numeric spacedDelay, timeUnit = "milliseconds" ){ + debugLog( "spacedDelay", arguments ); + variables.spacedDelay = arguments.spacedDelay; variables.timeUnit = arguments.timeUnit; return this; @@ -637,12 +916,11 @@ component accessors="true" { /** * Calling this method prevents task frequencies to overlap. By default all tasks are executed with an - * interval but ccould potentially overlap if they take longer to execute than the period. - * - * @period - * @timeUnit + * interval but could potentially overlap if they take longer to execute than the period. */ ScheduledTask function withNoOverlaps(){ + debugLog( "withNoOverlaps" ); + variables.noOverlaps = true; return this; } @@ -654,8 +932,13 @@ component accessors="true" { * @timeUnit The time unit to use, available units are: days, hours, microseconds, milliseconds, minutes, nanoseconds, and seconds. The default is milliseconds */ ScheduledTask function every( numeric period, timeUnit = "milliseconds" ){ + debugLog( "every", arguments ); + variables.period = arguments.period; variables.timeUnit = arguments.timeUnit; + + setInitialNextRunTime(); + return this; } @@ -663,18 +946,18 @@ component accessors="true" { * Run the task every minute from the time it get's scheduled */ ScheduledTask function everyMinute(){ - variables.period = 1; - variables.timeUnit = "minutes"; - return this; + debugLog( "everyMinute" ); + + return this.every( 1, "minutes" ); } /** * Run the task every hour from the time it get's scheduled */ ScheduledTask function everyHour(){ - variables.period = 1; - variables.timeUnit = "hours"; - return this; + debugLog( "everyHour" ); + + return this.every( 1, "hours" ); } /** @@ -683,24 +966,17 @@ component accessors="true" { * @minutes The minutes past the hour mark */ ScheduledTask function everyHourAt( required numeric minutes ){ + debugLog( "everyHourAt", arguments ); + + // Get times var now = getJavaNow(); var nextRun = now.withMinute( javacast( "int", arguments.minutes ) ).withSecond( javacast( "int", 0 ) ); - // If we passed it, then move the hour by 1 + // If we passed it, then move to the next hour if ( now.compareTo( nextRun ) > 0 ) { nextRun = nextRun.plusHours( javacast( "int", 1 ) ); } - // Get the duration time for the next run and delay accordingly - this.delay( - variables.chronoUnitHelper - .duration() - .getNative() - .between( now, nextRun ) - .getSeconds(), - "seconds" - ); - // Set the period to be every hour - variables.period = variables.timeUnitHelper.get( "hours" ).toSeconds( 1 ); - variables.timeUnit = "seconds"; + // Set the initial delay, period, and time unit + setInitialDelayPeriodAndTimeUnit( now, nextRun, "hours" ); return this; } @@ -709,30 +985,9 @@ component accessors="true" { * Run the task every day at midnight */ ScheduledTask function everyDay(){ - var now = getJavaNow(); - // Set at midnight - var nextRun = now - .withHour( javacast( "int", 0 ) ) - .withMinute( javacast( "int", 0 ) ) - .withSecond( javacast( "int", 0 ) ); - // If we passed it, then move to the next day - if ( now.compareTo( nextRun ) > 0 ) { - nextRun = nextRun.plusDays( javacast( "int", 1 ) ); - } - // Get the duration time for the next run and delay accordingly - this.delay( - variables.chronoUnitHelper - .duration() - .getNative() - .between( now, nextRun ) - .getSeconds(), - "seconds" - ); - // Set the period to every day in seconds - variables.period = variables.timeUnitHelper.get( "days" ).toSeconds( 1 ); - variables.timeUnit = "seconds"; + debugLog( "everyDay" ); - return this; + return this.everyDayAt( "00:00" ); } /** @@ -742,12 +997,11 @@ component accessors="true" { * @time The specific time using 24 hour format => HH:mm */ ScheduledTask function everyDayAt( required string time ){ - // Check for mintues else add them - if ( !find( ":", arguments.time ) ) { - arguments.time &= ":00"; - } + debugLog( "everyDayAt", arguments ); + // Validate time format - validateTime( arguments.time ); + arguments.time = validateTime( arguments.time ); + // Get times var now = getJavaNow(); var nextRun = now @@ -758,18 +1012,8 @@ component accessors="true" { if ( now.compareTo( nextRun ) > 0 ) { nextRun = nextRun.plusDays( javacast( "int", 1 ) ); } - // Get the duration time for the next run and delay accordingly - this.delay( - variables.chronoUnitHelper - .duration() - .getNative() - .between( now, nextRun ) - .getSeconds(), - "seconds" - ); - // Set the period to every day in seconds - variables.period = variables.timeUnitHelper.get( "DAYS" ).toSeconds( 1 ); - variables.timeUnit = "seconds"; + // Set the initial delay, period, and time unit + setInitialDelayPeriodAndTimeUnit( now, nextRun ); return this; } @@ -778,49 +1022,25 @@ component accessors="true" { * Run the task every Sunday at midnight */ ScheduledTask function everyWeek(){ + debugLog( "everyWeek" ); + + return this.everyWeekOn( 7 ); + } + + /** + * Run the task weekly on the given day of the week and time + * + * @dayOfWeek The day of the week from 1 (Monday) -> 7 (Sunday) + * @time The specific time using 24 hour format => HH:mm, defaults to midnight + */ + ScheduledTask function everyWeekOn( required numeric dayOfWeek, string time = "00:00" ){ + debugLog( "everyWeekOn", arguments ); + + // Validate time format + arguments.time = validateTime( arguments.time ); + + // Get times var now = getJavaNow(); - // Set at midnight - var nextRun = now - // Sunday - .with( variables.chronoUnitHelper.ChronoField.DAY_OF_WEEK, javacast( "int", 7 ) ) - // Midnight - .withHour( javacast( "int", 0 ) ) - .withMinute( javacast( "int", 0 ) ) - .withSecond( javacast( "int", 0 ) ); - // If we passed it, then move to the next week - if ( now.compareTo( nextRun ) > 0 ) { - nextRun = nextRun.plusWeeks( javacast( "int", 1 ) ); - } - // Get the duration time for the next run and delay accordingly - this.delay( - variables.chronoUnitHelper - .duration() - .getNative() - .between( now, nextRun ) - .getSeconds(), - "seconds" - ); - // Set the period to every week in seconds - variables.period = variables.timeUnitHelper.get( "days" ).toSeconds( 7 ); - variables.timeUnit = "seconds"; - variables.dayOfTheWeek = 7; - return this; - } - - /** - * Run the task weekly on the given day of the week and time - * - * @dayOfWeek The day of the week from 1 (Monday) -> 7 (Sunday) - * @time The specific time using 24 hour format => HH:mm, defaults to midnight - */ - ScheduledTask function everyWeekOn( required numeric dayOfWeek, string time = "00:00" ){ - var now = getJavaNow(); - // Check for mintues else add them - if ( !find( ":", arguments.time ) ) { - arguments.time &= ":00"; - } - // Validate time format - validateTime( arguments.time ); var nextRun = now // Given day .with( variables.chronoUnitHelper.ChronoField.DAY_OF_WEEK, javacast( "int", arguments.dayOfWeek ) ) @@ -832,19 +1052,11 @@ component accessors="true" { if ( now.compareTo( nextRun ) > 0 ) { nextRun = nextRun.plusWeeks( javacast( "int", 1 ) ); } - // Get the duration time for the next run and delay accordingly - this.delay( - variables.chronoUnitHelper - .duration() - .getNative() - .between( now, nextRun ) - .getSeconds(), - "seconds" - ); - // Set the period to every week in seconds - variables.period = variables.timeUnitHelper.get( "days" ).toSeconds( 7 ); - variables.timeUnit = "seconds"; + // Set the initial delay, period, and time unit + setInitialDelayPeriodAndTimeUnit( now, nextRun, "days", 7 ); + // set constraints variables.dayOfTheWeek = arguments.dayOfWeek; + return this; } @@ -852,34 +1064,9 @@ component accessors="true" { * Run the task on the first day of every month at midnight */ ScheduledTask function everyMonth(){ - var now = getJavaNow(); - // Set at midnight - var nextRun = now - // First day of the month - .with( variables.chronoUnitHelper.ChronoField.DAY_OF_MONTH, javacast( "int", 1 ) ) - // Midnight - .withHour( javacast( "int", 0 ) ) - .withMinute( javacast( "int", 0 ) ) - .withSecond( javacast( "int", 0 ) ); + debugLog( "everyMonth" ); - if ( now.compareTo( nextRun ) > 0 ) { - nextRun = nextRun.plusMonths( javacast( "int", 1 ) ); - } - // Get the duration time for the next run and delay accordingly - this.delay( - variables.chronoUnitHelper - .duration() - .getNative() - .between( now, nextRun ) - .getSeconds(), - "seconds" - ); - // Set the period to one day. And make sure we add a constraint for it - // Mostly because every month is different - variables.period = variables.timeUnitHelper.get( "days" ).toSeconds( 1 ); - variables.timeUnit = "seconds"; - variables.dayOfTheMonth = 1; - return this; + return this.everyMonthOn( 1 ); } /** @@ -889,14 +1076,13 @@ component accessors="true" { * @time The specific time using 24 hour format => HH:mm, defaults to midnight */ ScheduledTask function everyMonthOn( required numeric day, string time = "00:00" ){ - var now = getJavaNow(); - // Check for mintues else add them - if ( !find( ":", arguments.time ) ) { - arguments.time &= ":00"; - } + debugLog( "everyMonthOn", arguments ); + // Validate time format - validateTime( arguments.time ); - // Get new time + arguments.time = validateTime( arguments.time ); + + // Get times + var now = getJavaNow(); var nextRun = now // First day of the month .with( variables.chronoUnitHelper.ChronoField.DAY_OF_MONTH, javacast( "int", arguments.day ) ) @@ -904,24 +1090,15 @@ component accessors="true" { .withHour( javacast( "int", getToken( arguments.time, 1, ":" ) ) ) .withMinute( javacast( "int", getToken( arguments.time, 2, ":" ) ) ) .withSecond( javacast( "int", 0 ) ); - // Have we passed it + // If we passed it, then move to the next month if ( now.compareTo( nextRun ) > 0 ) { nextRun = nextRun.plusMonths( javacast( "int", 1 ) ); } - // Get the duration time for the next run and delay accordingly - this.delay( - variables.chronoUnitHelper - .duration() - .getNative() - .between( now, nextRun ) - .getSeconds(), - "seconds" - ); - // Set the period to one day. And make sure we add a constraint for it - // Mostly because every month is different - variables.period = variables.timeUnitHelper.get( "days" ).toSeconds( 1 ); - variables.timeUnit = "seconds"; + // Set the initial delay, period, and time unit + setInitialDelayPeriodAndTimeUnit( now, nextRun ); + // Set constraints variables.dayOfTheMonth = arguments.day; + return this; } @@ -931,69 +1108,25 @@ component accessors="true" { * @time The specific time using 24 hour format => HH:mm, defaults to midnight */ ScheduledTask function onFirstBusinessDayOfTheMonth( string time = "00:00" ){ - var now = getJavaNow(); - // Check for mintues else add them - if ( !find( ":", arguments.time ) ) { - arguments.time &= ":00"; - } + debugLog( "onFirstBusinessDayOfTheMonth", arguments ); + // Validate time format - validateTime( arguments.time ); - // Get new time - var nextRun = now - // First business day of the month - .with( - createObject( "java", "java.time.temporal.TemporalAdjusters" ).firstInMonth( - createObject( "java", "java.time.DayOfWeek" ).MONDAY - ) - ) - // Specific Time - .withHour( javacast( "int", getToken( arguments.time, 1, ":" ) ) ) - .withMinute( javacast( "int", getToken( arguments.time, 2, ":" ) ) ) - .withSecond( javacast( "int", 0 ) ); - // Have we passed it - if ( now.compareTo( nextRun ) > 0 ) { - nextRun = nextRun.plusMonths( javacast( "int", 1 ) ); - } - // Get the duration time for the next run and delay accordingly - this.delay( - variables.chronoUnitHelper - .duration() - .getNative() - .between( now, nextRun ) - .getSeconds(), - "seconds" - ); - // Set the period to one day. And make sure we add a constraint for it - // Mostly because every month is different - variables.period = variables.timeUnitHelper.get( "days" ).toSeconds( 1 ); - variables.timeUnit = "seconds"; - variables.dayOfTheMonth = 1; - return this; - } + arguments.time = validateTime( arguments.time ); - /** - * This utility method gives us the last day of the month in Java format - */ - private function getLastDayOfTheMonth(){ - // Get the last day of the month - var lastDay = variables.chronoUnitHelper - .toLocalDateTime( now(), this.getTimezone() ) - .with( createObject( "java", "java.time.temporal.TemporalAdjusters" ).lastDayOfMonth() ); - // Verify if on weekend - switch ( lastDay.getDayOfWeek().getValue() ) { - // Sunday - 2 days - case 7: { - lastDay = lastDay.minusDays( 2 ); - break; - } - // Saturday - 1 day - case 6: { - lastDay = lastDay.minusDays( 1 ); - break; - } + // Get times + var now = getJavaNow(); + var nextRun = getFirstBusinessDayOfTheMonth( arguments.time ); + // If we passed it, then move to the first business day of next month + if ( now.compareTo( nextRun ) > 0 ) { + nextRun = getFirstBusinessDayOfTheMonth( arguments.time, true ); } + // Set the initial delay, period, and time unit + setInitialDelayPeriodAndTimeUnit( now, nextRun ); + // Set constraints + variables.firstBusinessDay = true; + variables.taskTime = arguments.time; - return lastDay; + return this; } /** @@ -1002,37 +1135,24 @@ component accessors="true" { * @time The specific time using 24 hour format => HH:mm, defaults to midnight */ ScheduledTask function onLastBusinessDayOfTheMonth( string time = "00:00" ){ - var now = getJavaNow(); - // Check for mintues else add them - if ( !find( ":", arguments.time ) ) { - arguments.time &= ":00"; - } + debugLog( "onLastBusinessDayOfTheMonth", arguments ); + // Validate time format - validateTime( arguments.time ); - // Get the last day of the month - var nextRun = getLastDayOfTheMonth() - // Specific Time - .withHour( javacast( "int", getToken( arguments.time, 1, ":" ) ) ) - .withMinute( javacast( "int", getToken( arguments.time, 2, ":" ) ) ) - .withSecond( javacast( "int", 0 ) ); - // Have we passed it + arguments.time = validateTime( arguments.time ); + + // Get times + var now = getJavaNow(); + var nextRun = getLastBusinessDayOfTheMonth( arguments.time ); + // If we passed it, then move to the last business day of next month if ( now.compareTo( nextRun ) > 0 ) { - nextRun = nextRun.plusMonths( javacast( "int", 1 ) ); + nextRun = getLastBusinessDayOfTheMonth( arguments.time, true ); } - // Get the duration time for the next run and delay accordingly - this.delay( - variables.chronoUnitHelper - .duration() - .getNative() - .between( now, nextRun ) - .getSeconds(), - "seconds" - ); - // Set the period to one day. And make sure we add a constraint for it - // Mostly because every month is different - variables.period = variables.timeUnitHelper.get( "days" ).toSeconds( 1 ); - variables.timeUnit = "seconds"; + // Set the initial delay, period, and time unit + setInitialDelayPeriodAndTimeUnit( now, nextRun ); + // Set constraints variables.lastBusinessDay = true; + variables.taskTime = arguments.time; + return this; } @@ -1040,32 +1160,9 @@ component accessors="true" { * Run the task on the first day of the year at midnight */ ScheduledTask function everyYear(){ - var now = getJavaNow(); - // Set at midnight - var nextRun = now - // First day of the month - .with( variables.chronoUnitHelper.ChronoField.DAY_OF_YEAR, javacast( "int", 1 ) ) - // Midnight - .withHour( javacast( "int", 0 ) ) - .withMinute( javacast( "int", 0 ) ) - .withSecond( javacast( "int", 0 ) ); + debugLog( "everyYear" ); - if ( now.compareTo( nextRun ) > 0 ) { - nextRun = nextRun.plusYears( javacast( "int", 1 ) ); - } - // Get the duration time for the next run and delay accordingly - this.delay( - variables.chronoUnitHelper - .duration() - .getNative() - .between( now, nextRun ) - .getSeconds(), - "seconds" - ); - // Set the period to - variables.period = variables.timeUnitHelper.get( "days" ).toSeconds( 365 ); - variables.timeUnit = "seconds"; - return this; + return this.everyYearOn( 1, 1 ); } /** @@ -1080,13 +1177,13 @@ component accessors="true" { required numeric day, required string time = "00:00" ){ - var now = getJavaNow(); - // Check for mintues else add them - if ( !find( ":", arguments.time ) ) { - arguments.time &= ":00"; - } + debugLog( "everyYearOn", arguments ); + // Validate time format - validateTime( arguments.time ); + arguments.time = validateTime( arguments.time ); + + // Get times + var now = getJavaNow(); var nextRun = now // Specific month .with( variables.chronoUnitHelper.ChronoField.MONTH_OF_YEAR, javacast( "int", arguments.month ) ) @@ -1096,22 +1193,15 @@ component accessors="true" { .withHour( javacast( "int", getToken( arguments.time, 1, ":" ) ) ) .withMinute( javacast( "int", getToken( arguments.time, 2, ":" ) ) ) .withSecond( javacast( "int", 0 ) ); - // Have we passed it? + // If we passed it, then move to the next year if ( now.compareTo( nextRun ) > 0 ) { nextRun = nextRun.plusYears( javacast( "int", 1 ) ); } - // Get the duration time for the next run and delay accordingly - this.delay( - variables.chronoUnitHelper - .duration() - .getNative() - .between( now, nextRun ) - .getSeconds(), - "seconds" - ); - // Set the period to - variables.period = variables.timeUnitHelper.get( "days" ).toSeconds( 365 ); - variables.timeUnit = "seconds"; + // Set the initial delay, period, and time unit + setInitialDelayPeriodAndTimeUnit( now, nextRun, "days", 365 ); + // Set constraints + variables.annually = true; + return this; } @@ -1121,12 +1211,11 @@ component accessors="true" { * @time The specific time using 24 hour format => HH:mm, defaults to 00:00 */ ScheduledTask function onWeekends( string time = "00:00" ){ - // Check for mintues else add them - if ( !find( ":", arguments.time ) ) { - arguments.time &= ":00"; - } + debugLog( "onWeekends", arguments ); + // Validate time format - validateTime( arguments.time ); + arguments.time = validateTime( arguments.time ); + // Get times var now = getJavaNow(); var nextRun = now @@ -1137,19 +1226,9 @@ component accessors="true" { if ( now.compareTo( nextRun ) > 0 ) { nextRun = nextRun.plusDays( javacast( "int", 1 ) ); } - // Get the duration time for the next run and delay accordingly - this.delay( - variables.chronoUnitHelper - .duration() - .getNative() - .between( now, nextRun ) - .getSeconds(), - "seconds" - ); - // Set the period to every day in seconds - variables.period = variables.timeUnitHelper.get( "DAYS" ).toSeconds( 1 ); - variables.timeUnit = "seconds"; - // Constraint to only run on weekends + // Set the initial delay, period, and time unit + setInitialDelayPeriodAndTimeUnit( now, nextRun ); + // Set constraints variables.weekends = true; variables.weekdays = false; @@ -1162,12 +1241,11 @@ component accessors="true" { * @time The specific time using 24 hour format => HH:mm, defaults to 00:00 */ ScheduledTask function onWeekdays( string time = "00:00" ){ - // Check for mintues else add them - if ( !find( ":", arguments.time ) ) { - arguments.time &= ":00"; - } + debugLog( "onWeekdays", arguments ); + // Validate time format - validateTime( arguments.time ); + arguments.time = validateTime( arguments.time ); + // Get times var now = getJavaNow(); var nextRun = now @@ -1178,21 +1256,12 @@ component accessors="true" { if ( now.compareTo( nextRun ) > 0 ) { nextRun = nextRun.plusDays( javacast( "int", 1 ) ); } - // Get the duration time for the next run and delay accordingly - this.delay( - variables.chronoUnitHelper - .duration() - .getNative() - .between( now, nextRun ) - .getSeconds(), - "seconds" - ); - // Set the period to every day in seconds - variables.period = variables.timeUnitHelper.get( "DAYS" ).toSeconds( 1 ); - variables.timeUnit = "seconds"; - // Constraint to only run on weekdays + // Set the initial delay, period, and time unit + setInitialDelayPeriodAndTimeUnit( now, nextRun ); + // Set constraints variables.weekdays = true; variables.weekends = false; + return this; } @@ -1202,6 +1271,8 @@ component accessors="true" { * @time The specific time using 24 hour format => HH:mm, defaults to 00:00 */ ScheduledTask function onMondays( string time = "00:00" ){ + debugLog( "onMondays", arguments ); + return this.everyWeekOn( 1, arguments.time ); } @@ -1211,6 +1282,8 @@ component accessors="true" { * @time The specific time using 24 hour format => HH:mm, defaults to 00:00 */ ScheduledTask function onTuesdays( string time = "00:00" ){ + debugLog( "onTuesdays", arguments ); + return this.everyWeekOn( 2, arguments.time ); } @@ -1220,6 +1293,8 @@ component accessors="true" { * @time The specific time using 24 hour format => HH:mm, defaults to 00:00 */ ScheduledTask function onWednesdays( string time = "00:00" ){ + debugLog( "onWednesdays", arguments ); + return this.everyWeekOn( 3, arguments.time ); } @@ -1229,6 +1304,8 @@ component accessors="true" { * @time The specific time using 24 hour format => HH:mm, defaults to 00:00 */ ScheduledTask function onThursdays( string time = "00:00" ){ + debugLog( "onThursdays", arguments ); + return this.everyWeekOn( 4, arguments.time ); } @@ -1238,6 +1315,8 @@ component accessors="true" { * @time The specific time using 24 hour format => HH:mm, defaults to 00:00 */ ScheduledTask function onFridays( string time = "00:00" ){ + debugLog( "onFridays", arguments ); + return this.everyWeekOn( 5, arguments.time ); } @@ -1247,6 +1326,8 @@ component accessors="true" { * @time The specific time using 24 hour format => HH:mm, defaults to 00:00 */ ScheduledTask function onSaturdays( string time = "00:00" ){ + debugLog( "onSaturdays", arguments ); + return this.everyWeekOn( 6, arguments.time ); } @@ -1256,6 +1337,8 @@ component accessors="true" { * @time The specific time using 24 hour format => HH:mm, defaults to 00:00 */ ScheduledTask function onSundays( string time = "00:00" ){ + debugLog( "onSundays", arguments ); + return this.everyWeekOn( 7, arguments.time ); } @@ -1266,6 +1349,8 @@ component accessors="true" { * @time The specific time using 24 hour format => HH:mm, defaults to 00:00 */ ScheduledTask function startOn( required date, string time = "00:00" ){ + debugLog( "startOn", arguments ); + variables.startOnDateTime = variables.chronoUnitHelper.parse( "#dateFormat( arguments.date, "yyyy-mm-dd" )#T#arguments.time#" ); @@ -1279,6 +1364,8 @@ component accessors="true" { * @time The specific time using 24 hour format => HH:mm, defaults to 00:00 */ ScheduledTask function endOn( required date, string time = "00:00" ){ + debugLog( "endOn", arguments ); + variables.endOnDateTime = variables.chronoUnitHelper.parse( "#dateFormat( arguments.date, "yyyy-mm-dd" )#T#arguments.time#" ); @@ -1297,6 +1384,8 @@ component accessors="true" { * Set the time unit in days */ ScheduledTask function inDays(){ + debugLog( "inDays" ); + variables.timeUnit = "days"; return this; } @@ -1305,6 +1394,8 @@ component accessors="true" { * Set the time unit in hours */ ScheduledTask function inHours(){ + debugLog( "inHours" ); + variables.timeUnit = "hours"; return this; } @@ -1313,6 +1404,8 @@ component accessors="true" { * Set the time unit in microseconds */ ScheduledTask function inMicroseconds(){ + debugLog( "inMicroseconds" ); + variables.timeUnit = "microseconds"; return this; } @@ -1321,6 +1414,8 @@ component accessors="true" { * Set the time unit in milliseconds */ ScheduledTask function inMilliseconds(){ + debugLog( "inMilliseconds" ); + variables.timeUnit = "milliseconds"; return this; } @@ -1329,6 +1424,8 @@ component accessors="true" { * Set the time unit in minutes */ ScheduledTask function inMinutes(){ + debugLog( "inMinutes" ); + variables.timeUnit = "minutes"; return this; } @@ -1337,6 +1434,8 @@ component accessors="true" { * Set the time unit in nanoseconds */ ScheduledTask function inNanoseconds(){ + debugLog( "inNanoseconds" ); + variables.timeUnit = "nanoseconds"; return this; } @@ -1345,32 +1444,61 @@ component accessors="true" { * Set the time unit in seconds */ ScheduledTask function inSeconds(){ + debugLog( "inSeconds" ); + variables.timeUnit = "seconds"; return this; } /** - * Validates an incoming string to adhere to either: HH:mm + * Validates an incoming string to adhere to HH:mm while allowing a user to simply enter an hour value * * @time The time to check * - * @throws InvalidTimeException - If the time is invalid, else it just continues operation + * @throws InvalidTimeException - If the time is invalid, else it returns the time value */ - function validateTime( required time ){ - // Regex check - if ( !reFind( "^[0-2][0-9]\:[0-5][0-9]$", arguments.time ) ) { - throw( - message = "Invalid time representation (#arguments.time#). Time is represented in 24 hour minute format => HH:mm", - type = "InvalidTimeException" - ); + string function validateTime( required string time ){ + debugLog( "validateTime", arguments ); + + if ( !reFind( "^([0-1][0-9]|[2][0-3])\:[0-5][0-9]$", arguments.time ) ) { + debugLog( "validateTime( parsing )" ); + // To allow users to simply enter an hour we will + // parse the string and use time functions to create + // a valid time string + var parsedTime = listToArray( arguments.time, ":" ); + try { + arguments.time = timeFormat( + createTime( + // protect the user if they entered an + // hour value more than 23 + parsedTime[ 1 ] > 23 ? 0 : parsedTime[ 1 ], + // protect the user if they entered a + // minute value more than 59 or set to 0 + // if they did not enter any + arrayLen( parsedTime ) > 1 ? ( parsedTime[ 2 ] > 59 ? 59 : parsedTime[ 2 ] ) + : 0, + 0 + ), + "HH:mm" + ); + } catch ( any e ) { + throw( + message = "Invalid time representation (#arguments.time#). Time is represented in 24 hour minute format => HH:mm", + type = "InvalidTimeException" + ); + } } + + return arguments.time; } /** * Get a Java localDateTime object using the current date/time and timezone + * + * @now The date to use as the starting point, defaults to now() - modifications are helpful for testing */ - function getJavaNow(){ - return variables.chronoUnitHelper.toLocalDateTime( now(), this.getTimezone() ); + function getJavaNow( date now = now() ){ + return variables.chronoUnitHelper.toLocalDateTime( arguments.now, this.getTimezone() ); } /** @@ -1382,4 +1510,325 @@ component accessors="true" { } ); } + /** + * -------------------------------------------------------------------------- + * Private Methods + * -------------------------------------------------------------------------- + */ + + /** + * This utility method gives us the first business day of the month in Java format + * + * @time The specific time using 24 hour format => HH:mm, defaults to midnight + * @addMonth Boolean to specify adding a month to today's date + * @now The date to use as the starting point, defaults to now() + */ + private function getFirstBusinessDayOfTheMonth( + string time = "00:00", + boolean addMonth = false, + date now = now() + ){ + // Get the last day of the month + return variables.chronoUnitHelper + .toLocalDateTime( + arguments.addMonth ? dateAdd( "m", 1, arguments.now ) : arguments.now, + this.getTimezone() + ) + // First business day of the month + .with( + createObject( "java", "java.time.temporal.TemporalAdjusters" ).firstInMonth( + createObject( "java", "java.time.DayOfWeek" ).MONDAY + ) + ) + // Specific Time + .withHour( javacast( "int", getToken( arguments.time, 1, ":" ) ) ) + .withMinute( javacast( "int", getToken( arguments.time, 2, ":" ) ) ) + .withSecond( javacast( "int", 0 ) ); + } + + /** + * This utility method gives us the last business day of the month in Java format + * + * @time The specific time using 24 hour format => HH:mm, defaults to midnight + * @addMonth Boolean to specify adding a month to today's date + * @now The date to use as the starting point, defaults to now() + */ + private function getLastBusinessDayOfTheMonth( + string time = "00:00", + boolean addMonth = false, + date now = now() + ){ + debugLog( "getLastBusinessDayOfTheMonth" ); + + // Get the last day of the month + var lastDay = variables.chronoUnitHelper + .toLocalDateTime( + arguments.addMonth ? dateAdd( "m", 1, arguments.now ) : arguments.now, + this.getTimezone() + ) + .with( createObject( "java", "java.time.temporal.TemporalAdjusters" ).lastDayOfMonth() ) + // Specific Time + .withHour( javacast( "int", getToken( arguments.time, 1, ":" ) ) ) + .withMinute( javacast( "int", getToken( arguments.time, 2, ":" ) ) ) + .withSecond( javacast( "int", 0 ) ); + // Verify if on weekend + switch ( lastDay.getDayOfWeek().getValue() ) { + // Sunday - 2 days + case 7: { + lastDay = lastDay.minusDays( 2 ); + break; + } + // Saturday - 1 day + case 6: { + lastDay = lastDay.minusDays( 1 ); + break; + } + } + + return lastDay; + } + + /** + * This method is called to set the initial next run time of the task + * if none exists it sets it to now or it can be also passed in as an argument + * + * If a delay is set, it will set the next run time based on the delay and timeUnit + * + * @nextRun An instance of java.time.LocalDateTime to set the next run time to + * @delay The delay to set the next run time to + * @timeUnit The time unit to use for the delay + */ + private function setInitialNextRunTime( any nextRun, numeric delay, string timeUnit ){ + var amount = structKeyExists( arguments, "delay" ) ? arguments.delay : variables.delay; + var unit = structKeyExists( arguments, "timeUnit" ) ? arguments.timeUnit : variables.timeUnit; + + debugLog( + "setInitialNextRunTime", + { + delay : amount, + timeUnit : unit, + nextRunSet : isValid( "date", variables.stats.nextRun ) ? true : false, + nextRunInArgs : !isNull( arguments.nextRun ) + } + ); + + if ( !isValid( "date", variables.stats.nextRun ) ) { + if ( !isNull( arguments.nextRun ) && isInstanceOf( arguments.nextRun, "java.time.LocalDateTime" ) ) + variables.stats.nextRun = arguments.nextRun; + else if ( !isInstanceOf( variables.stats.nextRun, "java.time.LocalDateTime" ) ) + variables.stats.nextRun = getJavaNow(); + + if ( amount ) { + switch ( unit ) { + case "days": + variables.stats.nextRun = variables.stats.nextRun.plusDays( javacast( "int", amount ) ); + break; + case "hours": + variables.stats.nextRun = variables.stats.nextRun.plusHours( javacast( "int", amount ) ); + break; + case "minutes": + variables.stats.nextRun = variables.stats.nextRun.plusMinutes( javacast( "int", amount ) ); + break; + case "milliseconds": + variables.stats.nextRun = variables.stats.nextRun.plusSeconds( + javacast( "int", amount / 1000 ) + ); + break; + case "microseconds": + variables.stats.nextRun = variables.stats.nextRun.plusNanos( + javacast( "int", amount * 1000 ) + ); + break; + case "nanoseconds": + variables.stats.nextRun = variables.stats.nextRun.plusNanos( javacast( "int", amount ) ); + break; + default: + variables.stats.nextRun = variables.stats.nextRun.plusSeconds( javacast( "int", amount ) ); + break; + } + } + + var now = getJavaNow(); + var startTime = len( variables.startTime ) ? now + .withHour( javacast( "int", getToken( variables.startTime, 1, ":" ) ) ) + .withMinute( javacast( "int", getToken( variables.startTime, 2, ":" ) ) ) + .withSecond( javacast( "int", 0 ) ) : now + .withHour( javacast( "int", 0 ) ) + .withMinute( javacast( "int", 0 ) ) + .withSecond( javacast( "int", 0 ) ); + var endTime = len( variables.endTime ) ? now + .withHour( javacast( "int", getToken( variables.endTime, 1, ":" ) ) ) + .withMinute( javacast( "int", getToken( variables.endTime, 2, ":" ) ) ) + .withSecond( javacast( "int", 0 ) ) : now + .withHour( javacast( "int", 23 ) ) + .withMinute( javacast( "int", 59 ) ) + .withSecond( javacast( "int", 59 ) ); + + + debugLog( + "startTime", + { + startTime : startTime.toString(), + comp : now.compareTo( startTime ) + } + ); + debugLog( + "endTime", + { + endTime : endTime.toString(), + comp : now.compareTo( endTime ) + } + ); + + if ( now.compareTo( startTime ) < 0 ) { + variables.stats.nextRun = startTime + }; + + if ( now.compareTo( endTime ) > 0 ) { + variables.stats.nextRun = startTime.plusDays( javacast( "int", 1 ) ) + }; + + variables.stats.nextRun = variables.stats.nextRun.toString(); + } + } + + /** + * This method is called to set the initial delay period which + * calls setInitialNextRunTime, then sets the timeUnit to seconds + * and the period based on a value to convert to seconds. + * + * @now The current time to use for calculating the initial delay + * @nextRun The first run time to use for calculating the initial delay + * @periodValue The value to use when calculating the period to seconds + * @periodMultiplier The multiplier to use when calculating the period to seconds + */ + private function setInitialDelayPeriodAndTimeUnit( + required now, + required nextRun, + string periodValue = "days", + numeric periodMultiplier = 1 + ){ + debugLog( + "setInitialDelayPeriodAndTimeUnit", + { + now : arguments.now.toString(), + nextRun : arguments.nextRun.toString(), + periodValue : arguments.periodValue, + periodMultiplier : arguments.periodMultiplier + } + ); + + // Get the duration time for the next run and delay accordingly + this.delay( + variables.chronoUnitHelper + .duration() + .getNative() + .between( arguments.now, arguments.nextRun ) + .getSeconds(), + "seconds", + true + ); + // Set the period to be every hour in seconds + variables.period = variables.timeUnitHelper + .get( arguments.periodValue ) + .toSeconds( arguments.periodMultiplier ); + variables.timeUnit = "seconds"; + } + + /** + * This method is called to set the next run time of the task based on the timeUnit and period. + */ + private function setNextRunTime(){ + debugLog( "setNextRunTime" ); + + var now = getJavaNow(); + var amount = variables.spacedDelay != 0 ? variables.spacedDelay : variables.period; + + // if overlaps are allowed task is immediately scheduled + if ( variables.spacedDelay == 0 && variables.stats.lastExecutionTime / 1000 > variables.period ) { + amount = 0; + } + + // reset nextRun to empty string to continue with process of setting + // next run time + variables.stats.nextRun = ""; + + // check if we are a first or last business day of month entry + if ( variables.firstBusinessDay ) { + variables.stats.nextRun = getFirstBusinessDayOfTheMonth( variables.taskTime, true ); + } else if ( variables.lastBusinessDay ) { + variables.stats.nextRun = getLastBusinessDayOfTheMonth( variables.taskTime, true ); + } + // check if we have a daily start or end time + else if ( len( variables.startTime ) || len( variables.endTime ) ) { + var startTime = len( variables.startTime ) ? now + .withHour( javacast( "int", getToken( variables.startTime, 1, ":" ) ) ) + .withMinute( javacast( "int", getToken( variables.startTime, 2, ":" ) ) ) + .withSecond( javacast( "int", 0 ) ) : now + .withHour( javacast( "int", 0 ) ) + .withMinute( javacast( "int", 0 ) ) + .withSecond( javacast( "int", 0 ) ); + var endTime = len( variables.endTime ) ? now + .withHour( javacast( "int", getToken( variables.endTime, 1, ":" ) ) ) + .withMinute( javacast( "int", getToken( variables.endTime, 2, ":" ) ) ) + .withSecond( javacast( "int", 0 ) ) : now + .withHour( javacast( "int", 23 ) ) + .withMinute( javacast( "int", 59 ) ) + .withSecond( javacast( "int", 59 ) ); + + if ( now.compareTo( startTime ) < 0 ) { + variables.stats.nextRun = startTime; + } else if ( now.compareTo( endTime ) > 0 ) { + variables.stats.nextRun = startTime.plusDays( javacast( "int", 1 ) ); + } + } + + if ( !len( variables.stats.nextRun ) ) { + switch ( variables.timeUnit ) { + case "days": + variables.stats.nextRun = now.plusDays( javacast( "int", amount ) ); + break; + case "hours": + variables.stats.nextRun = now.plusHours( javacast( "int", amount ) ); + break; + case "minutes": + variables.stats.nextRun = now.plusMinutes( javacast( "int", amount ) ); + break; + case "milliseconds": + variables.stats.nextRun = now.plusSeconds( javacast( "int", amount / 1000 ) ); + break; + case "microseconds": + variables.stats.nextRun = now.plusNanos( javacast( "int", amount * 1000 ) ); + break; + case "nanoseconds": + variables.stats.nextRun = now.plusNanos( javacast( "int", amount ) ); + break; + default: + variables.stats.nextRun = now.plusSeconds( javacast( "int", amount ) ); + break; + } + } + + variables.stats.nextRun = variables.stats.nextRun.toString(); + } + + /** + * Debug output method + */ + function debugLog( required string caller, struct args = {} ){ + if ( variables.debug ) { + var message = dateTimeFormat( now(), "yyyy-mm-dd hh:nn:ss" ) & + " : ScheduledTask : " & + variables.name & " : " & + arguments.caller & + ( !arguments.caller.find( "(" ) ? "()" : "" ) & + ( + structIsEmpty( arguments.args ) ? "" : chr( 10 ) & repeatString( " ", 8 ) & serializeJSON( + arguments.args + ) + ); + variables.executor.out( message ); + } + } + } diff --git a/system/web/tasks/ColdBoxScheduler.cfc b/system/web/tasks/ColdBoxScheduler.cfc index 140ce7fda..b4d731b4a 100644 --- a/system/web/tasks/ColdBoxScheduler.cfc +++ b/system/web/tasks/ColdBoxScheduler.cfc @@ -91,14 +91,16 @@ component * Register a new task in this scheduler that will be executed once the `startup()` is fired or manually * via the run() method of the task. * + * @name The name of this task + * @debug Add debugging logs to System out, disabled by default in coldbox.system.web.tasks.ColdBoxScheduledTask * @return a ScheduledTask object so you can work on the registration of the task */ - ColdBoxScheduledTask function task( required name ){ + ColdBoxScheduledTask function task( required name, boolean debug = false ){ // Create task with custom name var oColdBoxTask = variables.wirebox .getInstance( "coldbox.system.web.tasks.ColdBoxScheduledTask", - { name : arguments.name, executor : variables.executor } + { name : arguments.name, executor : variables.executor, debug: arguments.debug } ) // Set ourselves into the task .setScheduler( this ) From a7535e7732124d828da9e6e08aed2d8f2151632a Mon Sep 17 00:00:00 2001 From: lmajano Date: Tue, 16 May 2023 01:36:52 +0000 Subject: [PATCH 16/46] Apply cfformat changes --- system/async/tasks/ScheduledTask.cfc | 4 +++- system/web/tasks/ColdBoxScheduler.cfc | 11 ++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/system/async/tasks/ScheduledTask.cfc b/system/async/tasks/ScheduledTask.cfc index 6d98e1be6..5e97e8bcb 100644 --- a/system/async/tasks/ScheduledTask.cfc +++ b/system/async/tasks/ScheduledTask.cfc @@ -691,7 +691,9 @@ component accessors="true" { } } catch ( any afterException ) { // Log it, so it doesn't go to ether and executor doesn't die. - err( "Error running task (#getName()#) after/error handlers : #afterException.message & afterException.detail#" ); + err( + "Error running task (#getName()#) after/error handlers : #afterException.message & afterException.detail#" + ); err( "Stacktrace for task (#getName()#) after/error handlers : #afterException.stackTrace#" ); } } finally { diff --git a/system/web/tasks/ColdBoxScheduler.cfc b/system/web/tasks/ColdBoxScheduler.cfc index b4d731b4a..ce73fa321 100644 --- a/system/web/tasks/ColdBoxScheduler.cfc +++ b/system/web/tasks/ColdBoxScheduler.cfc @@ -91,8 +91,9 @@ component * Register a new task in this scheduler that will be executed once the `startup()` is fired or manually * via the run() method of the task. * - * @name The name of this task - * @debug Add debugging logs to System out, disabled by default in coldbox.system.web.tasks.ColdBoxScheduledTask + * @name The name of this task + * @debug Add debugging logs to System out, disabled by default in coldbox.system.web.tasks.ColdBoxScheduledTask + * * @return a ScheduledTask object so you can work on the registration of the task */ ColdBoxScheduledTask function task( required name, boolean debug = false ){ @@ -100,7 +101,11 @@ component var oColdBoxTask = variables.wirebox .getInstance( "coldbox.system.web.tasks.ColdBoxScheduledTask", - { name : arguments.name, executor : variables.executor, debug: arguments.debug } + { + name : arguments.name, + executor : variables.executor, + debug : arguments.debug + } ) // Set ourselves into the task .setScheduler( this ) From 406006d02f091b3b9586b8c3bbb8f0ecf4f35a84 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Mon, 15 May 2023 20:37:25 -0500 Subject: [PATCH 17/46] fixing tests --- tests/specs/async/tasks/ScheduledTaskSpec.cfc | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/specs/async/tasks/ScheduledTaskSpec.cfc b/tests/specs/async/tasks/ScheduledTaskSpec.cfc index 04b5885c6..df5ff5b3a 100644 --- a/tests/specs/async/tasks/ScheduledTaskSpec.cfc +++ b/tests/specs/async/tasks/ScheduledTaskSpec.cfc @@ -175,7 +175,8 @@ component extends="tests.specs.async.BaseAsyncSpec" { var t = scheduler.task( "test" ).onFirstBusinessDayOfTheMonth( "09:00" ); expect( t.getPeriod() ).toBe( 86400 ); expect( t.getTimeUnit() ).toBe( "seconds" ); - expect( t.getDayOfTheMonth() ).toBe( 1 ); + expect( t.getTaskTime() ).toBe( "09:00" ); + expect( t.getFirstBusinessDay() ).toBeTrue(); } ); it( "can register to fire onLastBusinessDayOfTheMonth()", function(){ @@ -266,10 +267,10 @@ component extends="tests.specs.async.BaseAsyncSpec" { var t = prepareMock( scheduler.task( "test" ) ).setLastBusinessDay( true ); - makePublic( t, "getLastDayOfTheMonth" ); + makePublic( t, "getLastBusinessDayOfTheMonth" ); // If we are at the last day, increase it - if ( nowDate.getDayOfMonth() == t.getLastDayOfTheMonth().getDayOfMonth() ) { + if ( nowDate.getDayOfMonth() == t.getLastBusinessDayOfTheMonth().getDayOfMonth() ) { nowDate = nowDate.plusDays( javacast( "int", -1 ) ); } @@ -277,7 +278,7 @@ component extends="tests.specs.async.BaseAsyncSpec" { expect( t.isConstrained() ).toBeTrue(); var mockNow = t.getJavaNow(); - prepareMock( t ).$( "getLastDayOfTheMonth", mockNow ); + prepareMock( t ).$( "getLastBusinessDayOfTheMonth", mockNow ); expect( t.isConstrained() ).toBeFalse(); } ); @@ -375,7 +376,7 @@ component extends="tests.specs.async.BaseAsyncSpec" { } ); } ); - fstory( "can restrict tasks via a end on constraint", function(){ + story( "can restrict tasks via a end on constraint", function(){ given( "a valid end on constraint", function(){ then( "it should run the task", function(){ var targetDate = dateFormat( dateAdd( "d", 5, now() ), "yyyy-mm-dd" ); From 427d85d3f2c8ecb2f31e785da3eb10f642fbebd2 Mon Sep 17 00:00:00 2001 From: Giancarlo Gomez Date: Thu, 8 Jun 2023 09:17:03 -0500 Subject: [PATCH 18/46] Debug update for Core ScheduledTasks and Code Re-org (#575) COLDBOX-1230 #resolve Reorganized ScheduledTasks functions within the CFC into code groups and comments COLDBOX-1229 #resolve Added debug argument to ScheduleExecutor and Scheduler when creating tasks for consistency --- system/async/executors/ScheduledExecutor.cfc | 4 +- system/async/tasks/ScheduledTask.cfc | 105 ++++++++++--------- system/async/tasks/Scheduler.cfc | 12 ++- 3 files changed, 66 insertions(+), 55 deletions(-) diff --git a/system/async/executors/ScheduledExecutor.cfc b/system/async/executors/ScheduledExecutor.cfc index d27113625..852bf1e76 100644 --- a/system/async/executors/ScheduledExecutor.cfc +++ b/system/async/executors/ScheduledExecutor.cfc @@ -163,11 +163,13 @@ component extends="Executor" accessors="true" singleton { * Build out a new scheduled task representation. Calling this method does not mean that the task is executed. * * @name The name of the task + * @debug Add debugging logs to System out, disabled by default * @task The closure or cfc that represents the task (optional) * @method The method on the cfc to call, defaults to "run" (optional) */ ScheduledTask function newTask( - name = "task-#getName()#-#createUUID()#", + name = "task-#getName()#-#createUUID()#", + debug = false, task, method = "run" ){ diff --git a/system/async/tasks/ScheduledTask.cfc b/system/async/tasks/ScheduledTask.cfc index 5e97e8bcb..3a68a3b94 100644 --- a/system/async/tasks/ScheduledTask.cfc +++ b/system/async/tasks/ScheduledTask.cfc @@ -366,6 +366,38 @@ component accessors="true" { return this; } + /** + * Disable the task when scheduled, meaning, don't run this sucker! + */ + ScheduledTask function disable(){ + debugLog( "disable" ); + + variables.disabled = true; + return this; + } + + /** + * Enable the task when disabled so we can run again + */ + ScheduledTask function enable(){ + debugLog( "enable" ); + + variables.disabled = false; + return this; + } + + /** + * Verifies if we can schedule this task or not by looking at the following constraints: + * + * - disabled + * - when closure + */ + boolean function isDisabled(){ + debugLog( "isDisabled" ); + + return variables.disabled; + } + /** * Set the meta data for this task! */ @@ -414,38 +446,37 @@ component accessors="true" { } /** - * Disable the task when scheduled, meaning, don't run this sucker! - */ - ScheduledTask function disable(){ - debugLog( "disable" ); - - variables.disabled = true; - return this; - } - - /** - * Enable the task when disabled so we can run again + * Set when this task should start execution on. By default it starts automatically. + * + * @date The date when this task should start execution on => yyyy-mm-dd format is preferred. + * @time The specific time using 24 hour format => HH:mm, defaults to 00:00 */ - ScheduledTask function enable(){ - debugLog( "enable" ); + ScheduledTask function startOn( required date, string time = "00:00" ){ + debugLog( "startOn", arguments ); - variables.disabled = false; + variables.startOnDateTime = variables.chronoUnitHelper.parse( + "#dateFormat( arguments.date, "yyyy-mm-dd" )#T#arguments.time#" + ); return this; } /** - * Verifies if we can schedule this task or not by looking at the following constraints: + * Set when this task should stop execution on. By default it never ends * - * - disabled - * - when closure + * @date The date when this task should stop execution on => yyyy-mm-dd format is preferred. + * @time The specific time using 24 hour format => HH:mm, defaults to 00:00 */ - boolean function isDisabled(){ - debugLog( "isDisabled" ); + ScheduledTask function endOn( required date, string time = "00:00" ){ + debugLog( "endOn", arguments ); - return variables.disabled; + variables.endOnDateTime = variables.chronoUnitHelper.parse( + "#dateFormat( arguments.date, "yyyy-mm-dd" )#T#arguments.time#" + ); + return this; } /** + * Sets a daily time range restriction where this task can run on. * * @startTime The specific time using 24 hour format => HH:mm * @endTime The specific time using 24 hour format => HH:mm @@ -459,6 +490,7 @@ component accessors="true" { } /** + * Sets a daily start time restriction for this task. * * @time The specific time using 24 hour format => HH:mm */ @@ -473,6 +505,7 @@ component accessors="true" { } /** + * Sets a daily end time restriction for this task. * * @time The specific time using 24 hour format => HH:mm */ @@ -670,7 +703,7 @@ component accessors="true" { variables.stats.totalFailures = variables.stats.totalFailures + 1; // Log it, so it doesn't go to ether err( "Error running task (#getName()#) : #e.message & e.detail#" ); - err( "Stacktrace for task (#geNname()#) : #e.stackTrace#" ); + err( "Stacktrace for task (#getName()#) : #e.stackTrace#" ); // Try to execute the error handlers. Try try try just in case. try { @@ -1344,36 +1377,6 @@ component accessors="true" { return this.everyWeekOn( 7, arguments.time ); } - /** - * Set when this task should start execution on. By default it starts automatically. - * - * @date The date when this task should start execution on => yyyy-mm-dd format is preferred. - * @time The specific time using 24 hour format => HH:mm, defaults to 00:00 - */ - ScheduledTask function startOn( required date, string time = "00:00" ){ - debugLog( "startOn", arguments ); - - variables.startOnDateTime = variables.chronoUnitHelper.parse( - "#dateFormat( arguments.date, "yyyy-mm-dd" )#T#arguments.time#" - ); - return this; - } - - /** - * Set when this task should stop execution on. By default it never ends - * - * @date The date when this task should stop execution on => yyyy-mm-dd format is preferred. - * @time The specific time using 24 hour format => HH:mm, defaults to 00:00 - */ - ScheduledTask function endOn( required date, string time = "00:00" ){ - debugLog( "endOn", arguments ); - - variables.endOnDateTime = variables.chronoUnitHelper.parse( - "#dateFormat( arguments.date, "yyyy-mm-dd" )#T#arguments.time#" - ); - return this; - } - /** * -------------------------------------------------------------------------- * TimeUnit Methods diff --git a/system/async/tasks/Scheduler.cfc b/system/async/tasks/Scheduler.cfc index 54ce09e6c..5c2eec07d 100644 --- a/system/async/tasks/Scheduler.cfc +++ b/system/async/tasks/Scheduler.cfc @@ -97,9 +97,12 @@ component accessors="true" singleton { * Register a new task in this scheduler but disable it immediately. This is useful * when debugging tasks and have the easy ability to disable them. * + * @name The name of this task + * @debug Add debugging logs to System out, disabled by default in coldbox.system.async.tasks.ScheduledTask + * * @return The registered and disabled Scheduled Task */ - ScheduledTask function xtask( required name ){ + ScheduledTask function xtask( required name, boolean debug = false ){ return task( argumentCollection = arguments ).disable(); } @@ -107,13 +110,16 @@ component accessors="true" singleton { * Register a new task in this scheduler that will be executed once the `startup()` is fired or manually * via the run() method of the task. * + * @name The name of this task + * @debug Add debugging logs to System out, disabled by default in coldbox.system.async.tasks.ScheduledTask + * * @return a ScheduledTask object so you can work on the registration of the task */ - ScheduledTask function task( required name ){ + ScheduledTask function task( required name, boolean debug = false ){ // Create task with custom name var oTask = variables.executor // Give me the task broda! - .newTask( arguments.name ) + .newTask( argumentCollection = arguments ) // Register ourselves in the task .setScheduler( this ) // Set default timezone into the task From be940ea133009760df4605df7bfd3e1012e546ec Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Thu, 8 Jun 2023 16:22:10 +0200 Subject: [PATCH 19/46] fix: :zap: COLDBOX-1145 #resolve RestHandler OnError() Exception not checking for empty `exception` blocks which would cause another exception on development ONLY --- system/RestHandler.cfc | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/system/RestHandler.cfc b/system/RestHandler.cfc index 0cbebbf3e..bd21f1c47 100644 --- a/system/RestHandler.cfc +++ b/system/RestHandler.cfc @@ -1,10 +1,10 @@ /** - * ******************************************************************************** - * Copyright 2005-2007 ColdBox Framework by Luis Majano and Ortus Solutions, Corp + * Copyright Since 2005 ColdBox Framework by Luis Majano and Ortus Solutions, Corp * www.ortussolutions.com - * ******************************************************************************** - * This specialized handler is to be used for Restful applications. - * It wraps around functions to provide consistency and an opinionated approach to RESTing! + * --- + * Base class for all RESTFul event handlers + * + * @author Luis Majano */ component extends="EventHandler" { @@ -59,10 +59,12 @@ component extends="EventHandler" { } // Auth Issues catch ( "InvalidCredentials" e ) { + arguments.exception = e; this.onAuthenticationFailure( argumentCollection = arguments ); } // Token Decoding Issues catch ( "TokenInvalidException" e ) { + arguments.exception = e; this.onAuthenticationFailure( argumentCollection = arguments ); } // Validation Exceptions @@ -84,7 +86,9 @@ component extends="EventHandler" { catch ( "RecordNotFound" e ) { arguments.exception = e; this.onEntityNotFoundException( argumentCollection = arguments ); - } catch ( Any e ) { + } + // Global Catch + catch ( Any e ) { arguments.exception = e; this.onAnyOtherException( argumentCollection = arguments ); // If in development, let's show the error template @@ -122,15 +126,14 @@ component extends="EventHandler" { // Magical renderings event.renderData( - type = arguments.prc.response.getFormat(), - data = responseData, - contentType = arguments.prc.response.getContentType(), - statusCode = arguments.prc.response.getStatusCode(), - statusText = arguments.prc.response.getStatusText(), - location = arguments.prc.response.getLocation(), - isBinary = arguments.prc.response.getBinary(), - jsonCallback = arguments.prc.response.getJsonCallback(), - jsonQueryFormat = arguments.prc.response.getJsonQueryFormat() + type = arguments.prc.response.getFormat(), + data = responseData, + contentType = arguments.prc.response.getContentType(), + statusCode = arguments.prc.response.getStatusCode(), + statusText = arguments.prc.response.getStatusText(), + location = arguments.prc.response.getLocation(), + isBinary = arguments.prc.response.getBinary(), + jsonCallback = arguments.prc.response.getJsonCallback() ); } @@ -167,7 +170,9 @@ component extends="EventHandler" { eventArguments = {} ){ // Try to discover exception, if not, hard error - if ( isNull( arguments.exception ) && !isNull( arguments.prc.exception ) ) { + if ( + !isNull( arguments.prc.exception ) && ( isNull( arguments.exception ) || isEmpty( arguments.exception ) ) + ) { arguments.exception = arguments.prc.exception.getExceptionStruct(); } From f3ea2ed251a86ee5ba920f4841576f66a0472880 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Thu, 8 Jun 2023 16:28:17 +0200 Subject: [PATCH 20/46] ci: :fire: added adobe 2023 to supported engines list and more testing ci --- .github/workflows/tests.yml | 8 ++------ server-adobe@2023.json | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 092a7b573..b683d9e75 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -19,8 +19,8 @@ jobs: strategy: fail-fast: false matrix: - commandbox_version: [ "5.8.0" ] - cfengine: [ "lucee@5", "adobe@2018", "adobe@2021" ] + commandbox_version: [ "5.9.0" ] + cfengine: [ "lucee@5", "adobe@2018", "adobe@2021", "adobe@2023" ] jdkVersion: [ "11" ] experimental: [false] include: @@ -36,10 +36,6 @@ jobs: commandbox_version: "6.0.0-alpha" jdkVersion: "17" experimental: true - - cfengine: "adobe@2023" - commandbox_version: "6.0.0-alpha" - jdkVersion: "11" - experimental: true - cfengine: "adobe@2023" commandbox_version: "6.0.0-alpha" jdkVersion: "17" diff --git a/server-adobe@2023.json b/server-adobe@2023.json index 12ac330f0..cb99cc3dc 100644 --- a/server-adobe@2023.json +++ b/server-adobe@2023.json @@ -1,6 +1,6 @@ { "app":{ - "cfengine":"adobe@2023.0.0-beta.1", + "cfengine":"adobe@2023", "serverHomeDirectory":".engine/adobe2023" }, "name":"coldbox-adobe@2023", From 72975dd2eda86c6582e8e54beb84ef062d928906 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Fri, 9 Jun 2023 13:55:49 +0200 Subject: [PATCH 21/46] changelog and lts 6.9 version --- box.json | 2 +- changelog.md | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/box.json b/box.json index af634215c..5f31d8834 100644 --- a/box.json +++ b/box.json @@ -1,6 +1,6 @@ { "name":"ColdBox Platform", - "version":"6.8.3", + "version":"6.9.0", "location":"https://downloads.ortussolutions.com/ortussolutions/coldbox/@build.version@/coldbox-@build.version@.zip", "author":"Ortus Solutions ", "slug":"coldbox", diff --git a/changelog.md b/changelog.md index 2e20784eb..7748775e4 100644 --- a/changelog.md +++ b/changelog.md @@ -9,6 +9,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- [COLDBOX-1229](https://ortussolutions.atlassian.net/browse/COLDBOX-1229) Added debug argument to ScheduleExecutor and Scheduler when creating tasks for consistency +- [COLDBOX-1230](https://ortussolutions.atlassian.net/browse/COLDBOX-1230) Reorganized ScheduledTasks functions within the CFC into code groups and comments + +### Improvements + +- [COLDBOX-1226](https://ortussolutions.atlassian.net/browse/COLDBOX-1226) Scheduled Tasks Updates + +### Fixed + +- [COLDBOX-1145](https://ortussolutions.atlassian.net/browse/COLDBOX-1145) RestHandler OnError\(\) Exception not checking for empty \`exception\` blocks which would cause another exception on development ONLY + ## [6.8.2] - 2023-05-01 ### Added From 55b88a7ecba1de9dcfd973fa13aa98cc52c67359 Mon Sep 17 00:00:00 2001 From: Github Actions Date: Fri, 9 Jun 2023 12:02:20 +0000 Subject: [PATCH 22/46] Finalized changelog for v6.9.0 --- changelog.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 7748775e4..cb2b78711 100644 --- a/changelog.md +++ b/changelog.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [6.9.0] - 2023-06-09 + ### Added - [COLDBOX-1229](https://ortussolutions.atlassian.net/browse/COLDBOX-1229) Added debug argument to ScheduleExecutor and Scheduler when creating tasks for consistency @@ -20,7 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed -- [COLDBOX-1145](https://ortussolutions.atlassian.net/browse/COLDBOX-1145) RestHandler OnError\(\) Exception not checking for empty \`exception\` blocks which would cause another exception on development ONLY +- [COLDBOX-1145](https://ortussolutions.atlassian.net/browse/COLDBOX-1145) RestHandler OnError() Exception not checking for empty \`exception\` blocks which would cause another exception on development ONLY ## [6.8.2] - 2023-05-01 @@ -260,6 +262,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [WIREBOX-123](https://ortussolutions.atlassian.net/browse/WIREBOX-123) Removal of usage of Injector dsl interface due to so many issues with multiple engines -[Unreleased]: https://github.com/ColdBox/coldbox-platform/compare/v6.8.2...HEAD +[Unreleased]: https://github.com/ColdBox/coldbox-platform/compare/v6.9.0...HEAD + +[6.9.0]: https://github.com/ColdBox/coldbox-platform/compare/v6.8.2...v6.9.0 [6.8.2]: https://github.com/ColdBox/coldbox-platform/compare/e0aa96ff743adb860834194715729198ecb051bd...v6.8.2 From 05d83d87d1e487696800e4cf7dbc10046fd5f85f Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Fri, 9 Jun 2023 15:21:30 +0200 Subject: [PATCH 23/46] testing ci --- .github/workflows/release.yml | 44 +++++++++++++++++------------------ 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b1da74fca..939abcf74 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -70,12 +70,12 @@ jobs: run: | cd apidocs && box install - - name: Update changelog [unreleased] with latest version - uses: thomaseizinger/keep-a-changelog-new-release@1.3.0 - if: env.SNAPSHOT == 'false' - with: - changelogPath: ./changelog.md - tag: v${{ env.COLDBOX_VERSION }} + # - name: Update changelog [unreleased] with latest version + # uses: thomaseizinger/keep-a-changelog-new-release@1.3.0 + # if: env.SNAPSHOT == 'false' + # with: + # changelogPath: ./changelog.md + # tag: v${{ env.COLDBOX_VERSION }} - name: Build ColdBox Variants for ${{ env.BRANCH }} v${{ env.COLDBOX_VERSION }} run: | @@ -84,22 +84,22 @@ jobs: box server start serverConfigFile="server-lucee@5.json" --debug ant -DisPreRelease=${{ env.COLDBOX_PRERELEASE }} -Dcoldbox.version=${{ env.COLDBOX_VERSION }} -Dbuild.branch=${{ env.BRANCH }} -Dbuild.number=${{ github.run_number }} -f build/build.xml - - name: Commit Changelog [unreleased] with latest version - uses: EndBug/add-and-commit@v9.1.1 - if: env.SNAPSHOT == 'false' - with: - author_name: Github Actions - author_email: info@ortussolutions.com - message: 'Finalized changelog for v${{ env.COLDBOX_VERSION }}' - add: changelog.md - - - name: Tag Version - uses: rickstaa/action-create-tag@v1.6.1 - if: env.SNAPSHOT == 'false' - with: - tag: "v${{ env.COLDBOX_VERSION }}" - force_push_tag: true - message: "Latest Release v${{ env.COLDBOX_VERSION }}" + # - name: Commit Changelog [unreleased] with latest version + # uses: EndBug/add-and-commit@v9.1.1 + # if: env.SNAPSHOT == 'false' + # with: + # author_name: Github Actions + # author_email: info@ortussolutions.com + # message: 'Finalized changelog for v${{ env.COLDBOX_VERSION }}' + # add: changelog.md + + # - name: Tag Version + # uses: rickstaa/action-create-tag@v1.6.1 + # if: env.SNAPSHOT == 'false' + # with: + # tag: "v${{ env.COLDBOX_VERSION }}" + # force_push_tag: true + # message: "Latest Release v${{ env.COLDBOX_VERSION }}" - name: Upload Build Artifacts if: success() From 2246b3a7d7303f404b784b39c178e72ddb6ad118 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Fri, 9 Jun 2023 15:26:18 +0200 Subject: [PATCH 24/46] adding tag input for when failures ocur in CI --- .github/workflows/release.yml | 55 +++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 939abcf74..e5389d874 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,6 +17,11 @@ on: required: false default: false type: boolean + tag: + description: 'Tag changelogs, source and commit' + required: false + default: false + type: boolean # Manual Trigger for LTS Releases workflow_dispatch: @@ -26,10 +31,16 @@ on: required: false default: true type: boolean + tag: + description: 'Tag changelogs, source and commit' + required: false + default: true + type: boolean env: SNAPSHOT: ${{ inputs.snapshot || false }} LTS: ${{ inputs.lts || false }} + TAG: ${{ inputs.tag || true }} jobs: ############################################# @@ -70,12 +81,12 @@ jobs: run: | cd apidocs && box install - # - name: Update changelog [unreleased] with latest version - # uses: thomaseizinger/keep-a-changelog-new-release@1.3.0 - # if: env.SNAPSHOT == 'false' - # with: - # changelogPath: ./changelog.md - # tag: v${{ env.COLDBOX_VERSION }} + - name: Update changelog [unreleased] with latest version + uses: thomaseizinger/keep-a-changelog-new-release@1.3.0 + if: env.SNAPSHOT == 'false' && env.TAG == 'true' + with: + changelogPath: ./changelog.md + tag: v${{ env.COLDBOX_VERSION }} - name: Build ColdBox Variants for ${{ env.BRANCH }} v${{ env.COLDBOX_VERSION }} run: | @@ -84,22 +95,22 @@ jobs: box server start serverConfigFile="server-lucee@5.json" --debug ant -DisPreRelease=${{ env.COLDBOX_PRERELEASE }} -Dcoldbox.version=${{ env.COLDBOX_VERSION }} -Dbuild.branch=${{ env.BRANCH }} -Dbuild.number=${{ github.run_number }} -f build/build.xml - # - name: Commit Changelog [unreleased] with latest version - # uses: EndBug/add-and-commit@v9.1.1 - # if: env.SNAPSHOT == 'false' - # with: - # author_name: Github Actions - # author_email: info@ortussolutions.com - # message: 'Finalized changelog for v${{ env.COLDBOX_VERSION }}' - # add: changelog.md - - # - name: Tag Version - # uses: rickstaa/action-create-tag@v1.6.1 - # if: env.SNAPSHOT == 'false' - # with: - # tag: "v${{ env.COLDBOX_VERSION }}" - # force_push_tag: true - # message: "Latest Release v${{ env.COLDBOX_VERSION }}" + - name: Commit Changelog [unreleased] with latest version + uses: EndBug/add-and-commit@v9.1.1 + if: env.SNAPSHOT == 'false' && env.TAG == 'true' + with: + author_name: Github Actions + author_email: info@ortussolutions.com + message: 'Finalized changelog for v${{ env.COLDBOX_VERSION }}' + add: changelog.md + + - name: Tag Version + uses: rickstaa/action-create-tag@v1.6.1 + if: env.SNAPSHOT == 'false' && env.TAG == 'true' + with: + tag: "v${{ env.COLDBOX_VERSION }}" + force_push_tag: true + message: "Latest Release v${{ env.COLDBOX_VERSION }}" - name: Upload Build Artifacts if: success() From 8ab77f4e8ee15568b47535ba8cd830822dc2fce1 Mon Sep 17 00:00:00 2001 From: Github Actions Date: Fri, 9 Jun 2023 13:27:47 +0000 Subject: [PATCH 25/46] Version bump --- box.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/box.json b/box.json index 5f31d8834..f2e48742f 100644 --- a/box.json +++ b/box.json @@ -1,6 +1,6 @@ { "name":"ColdBox Platform", - "version":"6.9.0", + "version":"6.9.1", "location":"https://downloads.ortussolutions.com/ortussolutions/coldbox/@build.version@/coldbox-@build.version@.zip", "author":"Ortus Solutions ", "slug":"coldbox", @@ -56,12 +56,12 @@ "start:2016":"server start serverConfigFile='server-adobe@2016.json' --force", "start:2018":"server start serverConfigFile='server-adobe@2018.json' --force", "start:2021":"server start serverConfigFile='server-adobe@2021.json' --force", - "start:2023":"server start serverConfigFile='server-adobe@2023.json' --force", + "start:2023":"server start serverConfigFile='server-adobe@2023.json' --force", "log:lucee":"server log coldbox-lucee@5 --follow", "log:2016":"server log coldbox-adobe@2016 --follow", "log:2018":"server log coldbox-adobe@2018 --follow", "log:2021":"server log coldbox-adobe@2021 --follow", - "log:2023":"server log coldbox-adobe@2023 --follow", + "log:2023":"server log coldbox-adobe@2023 --follow", "cfpm":"echo '\".engine/acf2021/WEB-INF/cfusion/bin/cfpm.sh\"' | run", "cfpm:install":"echo '\".engine/adobe2021/WEB-INF/cfusion/bin/cfpm.sh\" install ${1}' | run", "install:2021":"run-script cfpm:install zip,orm,mysql,postgresql,sqlserver,document,feed,mail,debugger" From ec1c50a9358fed6a8fc7273506415e00f7a3cfa7 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Thu, 22 Jun 2023 14:07:40 +0200 Subject: [PATCH 26/46] WIREBOX-147 #resolve Improve debug logging to not send the full memento on several debug operations --- system/ioc/Injector.cfc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/system/ioc/Injector.cfc b/system/ioc/Injector.cfc index 28a3d441f..ca78f378f 100644 --- a/system/ioc/Injector.cfc +++ b/system/ioc/Injector.cfc @@ -872,8 +872,7 @@ component serializable="false" accessors="true" { // Debug Data if ( variables.log.canDebug() ) { variables.log.debug( - "Finalized Autowire for: #arguments.targetID#", - arguments.mapping.getMemento().toString() + "Finalized Autowire for: #arguments.targetID#:#arguments.mapping.getName()#:#arguments.mapping.getPath().toString()#" ); } } From f221465d1ec4c42ae79eade2f3180ec6299dd025 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Mon, 8 Apr 2024 06:17:01 +0200 Subject: [PATCH 27/46] adding artifacts ignores --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 14a770b21..651342cfe 100644 --- a/.gitignore +++ b/.gitignore @@ -17,7 +17,7 @@ tests/suites/loadtests/settings.xml logs/* # Build artifacts -artifacts/* +.artifacts/* apidocs/*-APIDocs build/build.number build-coldbox From e2db3fb15340cd0eecfa09279c7ac84df2727b6e Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Mon, 8 Apr 2024 06:17:28 +0200 Subject: [PATCH 28/46] COLDBOX-1273 #resolve Removal of deprecated CFML functions in core --- system/web/config/ApplicationLoader.cfc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/web/config/ApplicationLoader.cfc b/system/web/config/ApplicationLoader.cfc index 6175de522..1c6c41903 100644 --- a/system/web/config/ApplicationLoader.cfc +++ b/system/web/config/ApplicationLoader.cfc @@ -176,7 +176,7 @@ component accessors="true" { "" ); // Cleanup the template path - var localPath = getDirectoryFromPath( replaceNoCase( getTemplatePath(), "\", "/", "all" ) ); + var localPath = getDirectoryFromPath( replaceNoCase( getBaseTemplatePath(), "\", "/", "all" ) ); // Verify Path Location var pathLocation = findNoCase( webPath, localPath ); From e19a636f831ec5c1d41cfa3cc30f8bdfbb56f546 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Mon, 8 Apr 2024 08:59:59 +0200 Subject: [PATCH 29/46] orm extension now via ortus --- .github/workflows/tests.yml | 12 +++------ server-adobe@2016.json | 27 -------------------- server-lucee@5.json | 3 +++ server-lucee@5rc.json => server-lucee@6.json | 9 ++++--- server-lucee@be.json | 5 +++- server-lucee@light.json | 5 +++- 6 files changed, 21 insertions(+), 40 deletions(-) delete mode 100644 server-adobe@2016.json rename server-lucee@5rc.json => server-lucee@6.json (67%) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b683d9e75..8b0079b25 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -19,25 +19,21 @@ jobs: strategy: fail-fast: false matrix: - commandbox_version: [ "5.9.0" ] + commandbox_version: [ "6.0.0" ] cfengine: [ "lucee@5", "adobe@2018", "adobe@2021", "adobe@2023" ] jdkVersion: [ "11" ] experimental: [false] include: - - cfengine: "lucee@5rc" - commandbox_version: "6.0.0-alpha" - jdkVersion: "11" - experimental: true - cfengine: "lucee@6" - commandbox_version: "6.0.0-alpha" + commandbox_version: "6.0.0" jdkVersion: "11" experimental: true - cfengine: "lucee@6" - commandbox_version: "6.0.0-alpha" + commandbox_version: "6.0.0" jdkVersion: "17" experimental: true - cfengine: "adobe@2023" - commandbox_version: "6.0.0-alpha" + commandbox_version: "6.0.0" jdkVersion: "17" experimental: true steps: diff --git a/server-adobe@2016.json b/server-adobe@2016.json deleted file mode 100644 index 2d0f10188..000000000 --- a/server-adobe@2016.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "app":{ - "cfengine":"adobe@2016", - "serverHomeDirectory":".engine/adobe2016" - }, - "name":"coldbox-adobe@2016", - "force":true, - "openBrowser":false, - "web":{ - "directoryBrowsing":true, - "http":{ - "port":"8599" - }, - "rewrites":{ - "enable":true - }, - "aliases":{ - "/coldbox":"./" - } - }, - "JVM":{ - "heapSize":"1024" - }, - "cfconfig": { - "file" : ".cfconfig.json" - } -} diff --git a/server-lucee@5.json b/server-lucee@5.json index 0d8a008e2..cf3eb1b8c 100644 --- a/server-lucee@5.json +++ b/server-lucee@5.json @@ -23,5 +23,8 @@ }, "cfconfig":{ "file":".cfconfig.json" + }, + "env":{ + "LUCEE_EXTENSIONS":"D062D72F-F8A2-46F0-8CBC91325B2F067B" } } diff --git a/server-lucee@5rc.json b/server-lucee@6.json similarity index 67% rename from server-lucee@5rc.json rename to server-lucee@6.json index e3852f415..a7ce77216 100644 --- a/server-lucee@5rc.json +++ b/server-lucee@6.json @@ -1,9 +1,9 @@ { "app":{ - "cfengine":"lucee@5.3-rc", - "serverHomeDirectory":".engine/luceerc" + "cfengine":"lucee@6", + "serverHomeDirectory":".engine/lucee6" }, - "name":"coldbox-lucee@5rc", + "name":"coldbox-lucee@6", "force":true, "openBrowser":false, "web":{ @@ -23,5 +23,8 @@ }, "cfconfig":{ "file":".cfconfig.json" + }, + "env":{ + "LUCEE_EXTENSIONS":"D062D72F-F8A2-46F0-8CBC91325B2F067B" } } diff --git a/server-lucee@be.json b/server-lucee@be.json index d82aa548a..979584681 100644 --- a/server-lucee@be.json +++ b/server-lucee@be.json @@ -23,5 +23,8 @@ }, "cfconfig": { "file" : ".cfconfig.json" - } + }, + "env":{ + "LUCEE_EXTENSIONS":"D062D72F-F8A2-46F0-8CBC91325B2F067B" + } } diff --git a/server-lucee@light.json b/server-lucee@light.json index 2d301c65e..2c7725a0b 100644 --- a/server-lucee@light.json +++ b/server-lucee@light.json @@ -23,5 +23,8 @@ }, "cfconfig": { "file" : ".cfconfig.json" - } + }, + "env":{ + "LUCEE_EXTENSIONS":"D062D72F-F8A2-46F0-8CBC91325B2F067B" + } } From c3b6a497aab30b7d12b05ed5d2e19572f0dfe5e1 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Mon, 8 Apr 2024 09:06:24 +0200 Subject: [PATCH 30/46] removal of webservices tests --- tests/specs/ioc/BuilderTest.cfc | 7 ------- tests/specs/ioc/InjectorCreationTest.cfc | 19 ------------------- tests/specs/ioc/config/BinderTest.cfc | 7 ------- .../samples/InjectorCreationTestsBinder.cfc | 2 -- 4 files changed, 35 deletions(-) diff --git a/tests/specs/ioc/BuilderTest.cfc b/tests/specs/ioc/BuilderTest.cfc index 1417689d8..51fb1956f 100755 --- a/tests/specs/ioc/BuilderTest.cfc +++ b/tests/specs/ioc/BuilderTest.cfc @@ -60,13 +60,6 @@ // debug(r); } - function testbuildWebservice() skip="isAdobe"{ - mapping = createMock( "coldbox.system.ioc.config.Mapping" ).init( "Buffer" ); - mapping.setPath( "http://localhost:8599/test-harness/remote/Echo.cfc?wsdl" ); - r = builder.buildwebservice( mapping ); - // debug(r); - } - function testbuildcfc(){ // simple cfc var mapping = createMock( "coldbox.system.ioc.config.Mapping" ).init( "MyCFC" ); diff --git a/tests/specs/ioc/InjectorCreationTest.cfc b/tests/specs/ioc/InjectorCreationTest.cfc index fdd21aa71..42f32dcd8 100755 --- a/tests/specs/ioc/InjectorCreationTest.cfc +++ b/tests/specs/ioc/InjectorCreationTest.cfc @@ -70,12 +70,6 @@ val = injector.buildInstance( mapping ); assertEquals( mockStub, val ); - // Webservice - mapping.setType( "webservice" ); - mockBuilder.$( "buildWebService", mockStub ); - val = injector.buildInstance( mapping ); - assertEquals( mockStub, val ); - // Feed mapping.setType( "rss" ); mockBuilder.$( "buildFeed", mockStub ); @@ -139,19 +133,6 @@ assertTrue( len( prop ) ); } - function testWebService() skip="isAdobe"{ - ws = injector.getInstance( "coldboxWS" ); - - // - if ( listFindNoCase( "Lucee", server.coldfusion.productname ) ) { - expect( getMetadata( ws ).name ).toMatch( "rpc" ); - } - // adobe - else { - expect( getMetadata( ws ).name ).toMatch( "ServiceProxy" ); - } - } - function testDSL(){ dslobject = injector.getInstance( "coolDSL" ); diff --git a/tests/specs/ioc/config/BinderTest.cfc b/tests/specs/ioc/config/BinderTest.cfc index e6fc4d393..e12ac2ed5 100755 --- a/tests/specs/ioc/config/BinderTest.cfc +++ b/tests/specs/ioc/config/BinderTest.cfc @@ -201,13 +201,6 @@ assertEquals( "java.lang.StringBuilder", mapping.getPath() ); } - function testToWebservice(){ - config.map( "Test" ).toWebservice( "http://localhost:8599/test-harness/remote/Echo.cfc?wsdl" ); - mapping = config.getMapping( "Test" ); - assertEquals( this.TYPES.WEBSERVICE, mapping.getType() ); - assertEquals( "http://localhost:8599/test-harness/remote/Echo.cfc?wsdl", mapping.getPath() ); - } - function testToRSS(){ config.map( "Test" ).toRSS( "http://www.coldbox.org/rss" ); mapping = config.getMapping( "Test" ); diff --git a/tests/specs/ioc/config/samples/InjectorCreationTestsBinder.cfc b/tests/specs/ioc/config/samples/InjectorCreationTestsBinder.cfc index eb0ee8d14..843cf0902 100755 --- a/tests/specs/ioc/config/samples/InjectorCreationTestsBinder.cfc +++ b/tests/specs/ioc/config/samples/InjectorCreationTestsBinder.cfc @@ -23,8 +23,6 @@ // map to constant value, no need for scope map( "jsonProperty" ).toValue( "[{name:'luis'},{name:'Jose'}]" ); - // map to ws - map( "coldboxWS" ).toWebservice( "http://localhost:8599/test-harness/remote/Echo.cfc?wsdl" ); // map to rss feed map( "googleNews" ) .toRSS( "http://news.google.com/news?pz=1&cf=all&ned=us&hl=en&output=rss" ) From ec2f0f5b887af10027d29afffe1fdaaa07b37e84 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Mon, 8 Apr 2024 09:07:34 +0200 Subject: [PATCH 31/46] concurrency support --- .github/workflows/snapshot.yml | 5 +++++ .github/workflows/tests.yml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml index 947b2b144..8cc4d5a33 100644 --- a/.github/workflows/snapshot.yml +++ b/.github/workflows/snapshot.yml @@ -7,6 +7,11 @@ on: branches: - development +#Cancel running builds if another push to branch is made while this build is running +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: ############################################# # Tests First baby! We fail, no build :( diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8b0079b25..e41b50d93 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -6,7 +6,7 @@ on: workflow_call: secrets: SLACK_WEBHOOK_URL: - required: true + required: false jobs: tests: From e144ecf2967e09f55e22d34576e5e75fd8ed929a Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Mon, 8 Apr 2024 08:14:05 +0100 Subject: [PATCH 32/46] COLDBOX-1274 #resolve javacasting to long for new Java LocalDateTime instead of int, Adobe not doing type promotion --- system/async/tasks/ScheduledTask.cfc | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/system/async/tasks/ScheduledTask.cfc b/system/async/tasks/ScheduledTask.cfc index 3a68a3b94..603e82b18 100644 --- a/system/async/tasks/ScheduledTask.cfc +++ b/system/async/tasks/ScheduledTask.cfc @@ -1626,29 +1626,29 @@ component accessors="true" { if ( amount ) { switch ( unit ) { case "days": - variables.stats.nextRun = variables.stats.nextRun.plusDays( javacast( "int", amount ) ); + variables.stats.nextRun = variables.stats.nextRun.plusDays( javacast( "long", amount ) ); break; case "hours": - variables.stats.nextRun = variables.stats.nextRun.plusHours( javacast( "int", amount ) ); + variables.stats.nextRun = variables.stats.nextRun.plusHours( javacast( "long", amount ) ); break; case "minutes": - variables.stats.nextRun = variables.stats.nextRun.plusMinutes( javacast( "int", amount ) ); + variables.stats.nextRun = variables.stats.nextRun.plusMinutes( javacast( "long", amount ) ); break; case "milliseconds": variables.stats.nextRun = variables.stats.nextRun.plusSeconds( - javacast( "int", amount / 1000 ) + javacast( "long", amount / 1000 ) ); break; case "microseconds": variables.stats.nextRun = variables.stats.nextRun.plusNanos( - javacast( "int", amount * 1000 ) + javacast( "long", amount * 1000 ) ); break; case "nanoseconds": - variables.stats.nextRun = variables.stats.nextRun.plusNanos( javacast( "int", amount ) ); + variables.stats.nextRun = variables.stats.nextRun.plusNanos( javacast( "long", amount ) ); break; default: - variables.stats.nextRun = variables.stats.nextRun.plusSeconds( javacast( "int", amount ) ); + variables.stats.nextRun = variables.stats.nextRun.plusSeconds( javacast( "long", amount ) ); break; } } @@ -1791,25 +1791,25 @@ component accessors="true" { if ( !len( variables.stats.nextRun ) ) { switch ( variables.timeUnit ) { case "days": - variables.stats.nextRun = now.plusDays( javacast( "int", amount ) ); + variables.stats.nextRun = now.plusDays( javacast( "long", amount ) ); break; case "hours": - variables.stats.nextRun = now.plusHours( javacast( "int", amount ) ); + variables.stats.nextRun = now.plusHours( javacast( "long", amount ) ); break; case "minutes": - variables.stats.nextRun = now.plusMinutes( javacast( "int", amount ) ); + variables.stats.nextRun = now.plusMinutes( javacast( "long", amount ) ); break; case "milliseconds": - variables.stats.nextRun = now.plusSeconds( javacast( "int", amount / 1000 ) ); + variables.stats.nextRun = now.plusSeconds( javacast( "long", amount / 1000 ) ); break; case "microseconds": - variables.stats.nextRun = now.plusNanos( javacast( "int", amount * 1000 ) ); + variables.stats.nextRun = now.plusNanos( javacast( "long", amount * 1000 ) ); break; case "nanoseconds": - variables.stats.nextRun = now.plusNanos( javacast( "int", amount ) ); + variables.stats.nextRun = now.plusNanos( javacast( "long", amount ) ); break; default: - variables.stats.nextRun = now.plusSeconds( javacast( "int", amount ) ); + variables.stats.nextRun = now.plusSeconds( javacast( "long", amount ) ); break; } } From 9e46a0950b8791edbda2b5384413b947794040b9 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Wed, 10 Apr 2024 10:01:51 -0500 Subject: [PATCH 33/46] COLDBOX-1275 #resolve Improved engine detection by the CFMLEngine feature class --- system/core/util/CFMLEngine.cfc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/system/core/util/CFMLEngine.cfc b/system/core/util/CFMLEngine.cfc index 2cda87e39..89e5393ef 100644 --- a/system/core/util/CFMLEngine.cfc +++ b/system/core/util/CFMLEngine.cfc @@ -22,6 +22,7 @@ component { adobe2016 : { invokeArray : false }, adobe2018 : { invokeArray : false }, adobe2021 : { invokeArray : false }, + adobe2023 : { invokeArray : false }, lucee : { invokeArray : true } }; variables.productVersion = listFirst( server.coldfusion.productversion ); @@ -54,7 +55,7 @@ component { * Verify if this is an adobe server */ boolean function isAdobe(){ - return !isLucee(); + return server.keyExists( "coldfusion" ) && server.coldfusion.productName.findNoCase( "ColdFusion" ); } /** From 607653c9aae66b13ac0fed39c743291796288064 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Wed, 10 Apr 2024 10:12:57 -0500 Subject: [PATCH 34/46] update supporting github actions --- .github/workflows/lts.yml | 4 ++-- .github/workflows/pr.yml | 4 ++-- .github/workflows/release.yml | 8 ++++---- .github/workflows/snapshot.yml | 4 ++-- .markdownlint.json | 6 ++++-- 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/.github/workflows/lts.yml b/.github/workflows/lts.yml index bb4c27beb..17c8337ea 100644 --- a/.github/workflows/lts.yml +++ b/.github/workflows/lts.yml @@ -20,10 +20,10 @@ jobs: name: Code Auto-Formatting runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Auto-format - uses: Ortus-Solutions/commandbox-action@v1.0.2 + uses: Ortus-Solutions/commandbox-action@v1.0.3 with: cmd: run-script format diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 540017d95..6166327ba 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -22,8 +22,8 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Checkout Repository - uses: actions/checkout@v3.2.0 + uses: actions/checkout@v4 - - uses: Ortus-Solutions/commandbox-action@v1.0.2 + - uses: Ortus-Solutions/commandbox-action@v1.0.3 with: cmd: run-script format:check diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e5389d874..e6fb94fc0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -51,7 +51,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Checkout Repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Java uses: actions/setup-java@v3 @@ -183,13 +183,13 @@ jobs: needs: [ build ] steps: - name: Checkout Development Repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 if: env.LTS == 'false' with: ref: development - name: Checkout LTS Repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 if: env.LTS == 'true' - name: Setup CommandBox @@ -198,7 +198,7 @@ jobs: forgeboxAPIKey: ${{ secrets.FORGEBOX_TOKEN }} - name: Download build artifacts - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: coldbox-variants path: .tmp diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml index 8cc4d5a33..a040ed9fa 100644 --- a/.github/workflows/snapshot.yml +++ b/.github/workflows/snapshot.yml @@ -27,10 +27,10 @@ jobs: name: Code Auto-Formatting runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Auto-format - uses: Ortus-Solutions/commandbox-action@v1.0.2 + uses: Ortus-Solutions/commandbox-action@v1.0.3 with: cmd: run-script format diff --git a/.markdownlint.json b/.markdownlint.json index 31705fa38..21bc84336 100644 --- a/.markdownlint.json +++ b/.markdownlint.json @@ -10,5 +10,7 @@ }, "no-duplicate-header" : { "siblings_only" : true - } -} \ No newline at end of file + }, + "no-duplicate-heading" : false, + "no-inline-html" : false +} From 7c006a3ee045fa6e02eafe977307fca7b7f465bc Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Wed, 10 Apr 2024 10:16:00 -0500 Subject: [PATCH 35/46] upgrades to github actions --- .github/workflows/lts.yml | 2 +- .github/workflows/snapshot.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lts.yml b/.github/workflows/lts.yml index 17c8337ea..57637d4fa 100644 --- a/.github/workflows/lts.yml +++ b/.github/workflows/lts.yml @@ -28,7 +28,7 @@ jobs: cmd: run-script format - name: Commit Format Changes - uses: stefanzweifel/git-auto-commit-action@v4 + uses: stefanzweifel/git-auto-commit-action@v5 with: commit_message: Apply cfformat changes push_options: --force diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml index a040ed9fa..c10cd19af 100644 --- a/.github/workflows/snapshot.yml +++ b/.github/workflows/snapshot.yml @@ -35,7 +35,7 @@ jobs: cmd: run-script format - name: Commit Format Changes - uses: stefanzweifel/git-auto-commit-action@v4 + uses: stefanzweifel/git-auto-commit-action@v5 with: commit_message: Apply cfformat changes push_options: --force From 42e7706cc985a9dd46792aaf5428f5114203db29 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Wed, 10 Apr 2024 10:18:54 -0500 Subject: [PATCH 36/46] COLDBOX-1275 Improved engine detection by the CFMLEngine feature class --- system/async/proxies/BaseProxy.cfc | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/system/async/proxies/BaseProxy.cfc b/system/async/proxies/BaseProxy.cfc index 8778f5d6e..d4cfcfe0f 100644 --- a/system/async/proxies/BaseProxy.cfc +++ b/system/async/proxies/BaseProxy.cfc @@ -47,12 +47,15 @@ component accessors="true" { variables.UUID = createUUID(); variables.loadAppContext = arguments.loadAppContext; + variables.isLucee = server.keyExists( "lucee" ); + variables.isAdobe = server.keyExists( "coldfusion" ) && server.coldfusion.productName.findNocase( "ColdFusion" ) > 0; + // If loading App context or not if ( arguments.loadAppContext ) { - if ( server.keyExists( "lucee" ) ) { + if ( variables.isLucee ) { variables.cfContext = getCFMLContext().getApplicationContext(); variables.pageContext = getCFMLContext(); - } else { + } else if ( variables.isAdobe ) { variables.DataSrcImplStatic = createObject( "java", "coldfusion.sql.DataSrcImpl" ); variables.fusionContextStatic = createObject( "java", "coldfusion.filter.FusionContext" ); variables.originalFusionContext = fusionContextStatic.getCurrent().clone(); @@ -102,9 +105,9 @@ component accessors="true" { try { // Lucee vs Adobe Implementations - if ( server.keyExists( "lucee" ) ) { + if ( variables.isLucee ) { getCFMLContext().setApplicationContext( variables.cfContext ); - } else { + } else if ( variables.isAdobe ) { // Set the current thread's class loader from the CF space to avoid // No class defined issues in thread land. getCurrentThread().setContextClassLoader( @@ -162,8 +165,7 @@ component accessors="true" { try { // Lucee vs Adobe Implementations - if ( server.keyExists( "lucee" ) ) { - } else { + if ( variables.isAdobe ) { // Ensure any DB connections used get returned to the connection pool. Without clearSqlProxy an executor will hold onto any connections it touched while running and they will not timeout/close, and no other code can use the connection except for the executor that last touched it. Credit to Brad Wood for finding this! variables.DataSrcImplStatic.clearSqlProxy(); variables.fusionContextStatic.setCurrent( javacast( "null", "" ) ); @@ -197,10 +199,10 @@ component accessors="true" { * Amend this check once Adobe fixes this in a later update */ function getConcurrentEngineLockName(){ - if ( server.keyExists( "lucee" ) ) { - return createUUID(); - } else { + if ( variables.isAdobe ) { return variables.UUID; + } else { + return createUUID(); } } From 715224a603f32a60ef5d6b606db30b298bd06f4f Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Mon, 22 Apr 2024 10:32:40 +0200 Subject: [PATCH 37/46] COLDBOX-1278 #resolve Remove unsafe evaluate function usage --- system/core/dynamic/MixerUtil.cfc | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/system/core/dynamic/MixerUtil.cfc b/system/core/dynamic/MixerUtil.cfc index cc792dcc4..75b830897 100644 --- a/system/core/dynamic/MixerUtil.cfc +++ b/system/core/dynamic/MixerUtil.cfc @@ -161,7 +161,16 @@ component { * Removes a method in a CFC */ function removePropertyMixin( required propertyName, scope = "variables" ){ - structDelete( evaluate( arguments.scope ), arguments.propertyName ); + switch( arguments.scope ){ + case "variables": + return structDelete( variables, arguments.propertyName ); + break; + case "this": + structDelete( this, arguments.propertyName ); + break; + default: + throw( "Invalid scope" ); + } return this; } From df1b1e0983341a1b3d8477962132da3179f62bfe Mon Sep 17 00:00:00 2001 From: lmajano Date: Mon, 22 Apr 2024 08:33:46 +0000 Subject: [PATCH 38/46] Apply cfformat changes --- system/core/dynamic/MixerUtil.cfc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/core/dynamic/MixerUtil.cfc b/system/core/dynamic/MixerUtil.cfc index 75b830897..4ad8d8e44 100644 --- a/system/core/dynamic/MixerUtil.cfc +++ b/system/core/dynamic/MixerUtil.cfc @@ -161,7 +161,7 @@ component { * Removes a method in a CFC */ function removePropertyMixin( required propertyName, scope = "variables" ){ - switch( arguments.scope ){ + switch ( arguments.scope ) { case "variables": return structDelete( variables, arguments.propertyName ); break; From 77a5881ba3be3daf9082946e002570dabe33e7ed Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Mon, 22 Apr 2024 10:39:59 +0200 Subject: [PATCH 39/46] COLDBOX-1278 #resolve Remove unsafe evaluate function usage --- tests/specs/web/context/eventhandlerBeanTest.cfc | 2 +- tests/specs/web/context/exceptionBeanTest.cfc | 9 --------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/tests/specs/web/context/eventhandlerBeanTest.cfc b/tests/specs/web/context/eventhandlerBeanTest.cfc index bf6f95984..348b17cdf 100755 --- a/tests/specs/web/context/eventhandlerBeanTest.cfc +++ b/tests/specs/web/context/eventhandlerBeanTest.cfc @@ -15,7 +15,7 @@ this.ehbean.init( this.instance.invocationPath ); for ( var thisKey in this.instance ) { - evaluate( "this.ehBean.set#thisKey#( this.instance[ thisKey ] )" ); + invoke( this.ehBean, "set#thisKey#", [ this.instance[ thisKey ] ] ); } diff --git a/tests/specs/web/context/exceptionBeanTest.cfc b/tests/specs/web/context/exceptionBeanTest.cfc index 20c9cc9cc..3184125fe 100755 --- a/tests/specs/web/context/exceptionBeanTest.cfc +++ b/tests/specs/web/context/exceptionBeanTest.cfc @@ -58,14 +58,6 @@ - - - for ( key in this.instance ) { - evaluate( "this.e.get#key#()" ); - } - - - assertTrue( isStruct( this.e.getMemento() ) ); @@ -169,4 +161,3 @@ - From f24a3e73dd83b9a91a751dd867c5fdbba7c9a6e5 Mon Sep 17 00:00:00 2001 From: lmajano Date: Mon, 22 Apr 2024 08:42:24 +0000 Subject: [PATCH 40/46] Apply cfformat changes --- tests/specs/web/context/eventhandlerBeanTest.cfc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/specs/web/context/eventhandlerBeanTest.cfc b/tests/specs/web/context/eventhandlerBeanTest.cfc index 348b17cdf..15e67cf6c 100755 --- a/tests/specs/web/context/eventhandlerBeanTest.cfc +++ b/tests/specs/web/context/eventhandlerBeanTest.cfc @@ -15,7 +15,11 @@ this.ehbean.init( this.instance.invocationPath ); for ( var thisKey in this.instance ) { - invoke( this.ehBean, "set#thisKey#", [ this.instance[ thisKey ] ] ); + invoke( + this.ehBean, + "set#thisKey#", + [ this.instance[ thisKey ] ] + ); } From 7a360db1463f248f392cc56fef52d11c565abe15 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Mon, 22 Apr 2024 10:45:17 +0200 Subject: [PATCH 41/46] COLDBOX-1279 #resolve Render encapsulator bleed of this scope by engines, workaround. --- system/web/RendererEncapsulator.cfm | 1 + 1 file changed, 1 insertion(+) diff --git a/system/web/RendererEncapsulator.cfm b/system/web/RendererEncapsulator.cfm index 26b240512..20aac27a0 100644 --- a/system/web/RendererEncapsulator.cfm +++ b/system/web/RendererEncapsulator.cfm @@ -24,6 +24,7 @@ variables.rc = attributes.rc; variables.prc = attributes.prc; variables.args = attributes.args; + variables.this = variables; // Spoof the arguments scope for backwards compat. i.e. arguments.args, arguments.view variables.arguments = { From 5fe203bc0e7d5ff1b43cdff829aa472ab83f2529 Mon Sep 17 00:00:00 2001 From: John Whish Date: Wed, 1 May 2024 16:34:44 +0100 Subject: [PATCH 42/46] Cache policies LRU error (#590) COLDBOX-1280 https://ortussolutions.atlassian.net/browse/COLDBOX-1280 #Resolve * check if md keys exist before referencing Fixes error: Error sorting via store indexer Element TIMEOUT is undefined in MD. coldfusion.runtime.UndefinedElementException: Element TIMEOUT is undefined in MD. * Add default value if missing Fixes error: Error sorting via store indexer Invalid property requested: isSoftReference Valid properties are: coldfusion.runtime.CustomException: Invalid property requested: isSoftReference --- system/cache/policies/AbstractEvictionPolicy.cfc | 3 +++ system/cache/store/ConcurrentSoftReferenceStore.cfc | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/system/cache/policies/AbstractEvictionPolicy.cfc b/system/cache/policies/AbstractEvictionPolicy.cfc index e9569f60f..0da5c7ae9 100644 --- a/system/cache/policies/AbstractEvictionPolicy.cfc +++ b/system/cache/policies/AbstractEvictionPolicy.cfc @@ -83,6 +83,9 @@ component continue; } var md = indexer.getObjectMetadata( item ); + if ( NOT md.keyExists( "timeout" ) || NOT md.keyExists( "isExpired" ) ) { + continue; + } // Evict if not already marked for eviction or an eternal object. if ( md.timeout GT 0 AND NOT md.isExpired ) { diff --git a/system/cache/store/ConcurrentSoftReferenceStore.cfc b/system/cache/store/ConcurrentSoftReferenceStore.cfc index 70e973368..908b210c7 100644 --- a/system/cache/store/ConcurrentSoftReferenceStore.cfc +++ b/system/cache/store/ConcurrentSoftReferenceStore.cfc @@ -189,7 +189,8 @@ component extends="coldbox.system.cache.store.ConcurrentStore" accessors=true { if ( !isNull( local.softRef ) && variables.indexer.getObjectMetadataProperty( arguments.objectKey, - "isSoftReference" + "isSoftReference", + false ) ) { variables.softRefKeyMap.remove( softRef.hashCode() ); From d0d9f7e11a4ae6b6bd695e4088e1a330b70c812d Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Mon, 13 May 2024 17:25:26 -0400 Subject: [PATCH 43/46] updates for testing --- .github/workflows/tests.yml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e41b50d93..93f8988d3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -19,21 +19,17 @@ jobs: strategy: fail-fast: false matrix: - commandbox_version: [ "6.0.0" ] + commandbox_version: [ "6.0.1" ] cfengine: [ "lucee@5", "adobe@2018", "adobe@2021", "adobe@2023" ] jdkVersion: [ "11" ] experimental: [false] include: - cfengine: "lucee@6" - commandbox_version: "6.0.0" - jdkVersion: "11" - experimental: true - - cfengine: "lucee@6" - commandbox_version: "6.0.0" + commandbox_version: "6.0.1" jdkVersion: "17" experimental: true - cfengine: "adobe@2023" - commandbox_version: "6.0.0" + commandbox_version: "6.0.1" jdkVersion: "17" experimental: true steps: From 98fe5d481722649eab483b90b570e54079fc6042 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Mon, 13 May 2024 17:26:38 -0400 Subject: [PATCH 44/46] changelog updates --- box.json | 2 +- changelog.md | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/box.json b/box.json index f2e48742f..0e5e1c0ff 100644 --- a/box.json +++ b/box.json @@ -1,6 +1,6 @@ { "name":"ColdBox Platform", - "version":"6.9.1", + "version":"6.10.0", "location":"https://downloads.ortussolutions.com/ortussolutions/coldbox/@build.version@/coldbox-@build.version@.zip", "author":"Ortus Solutions ", "slug":"coldbox", diff --git a/changelog.md b/changelog.md index cb2b78711..4995fa418 100644 --- a/changelog.md +++ b/changelog.md @@ -9,6 +9,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Bug + +- [COLDBOX-1274](https://ortussolutions.atlassian.net/browse/COLDBOX-1274) javacasting to long for new Java LocalDateTime instead of int, Adobe not doing type promotion +- [COLDBOX-1279](https://ortussolutions.atlassian.net/browse/COLDBOX-1279) Render encapsulator bleed of this scope by engines + +### Improvement + +- [COLDBOX-1273](https://ortussolutions.atlassian.net/browse/COLDBOX-1273) Removal of deprecated CFML functions in core +- [COLDBOX-1275](https://ortussolutions.atlassian.net/browse/COLDBOX-1275) Improved engine detection by the CFMLEngine feature class +- [COLDBOX-1278](https://ortussolutions.atlassian.net/browse/COLDBOX-1278) Remove unsafe evaluate function usage + ## [6.9.0] - 2023-06-09 ### Added From b125839f1c50105375f4d41134bbc618cd3e59b3 Mon Sep 17 00:00:00 2001 From: Github Actions Date: Mon, 13 May 2024 22:28:35 +0000 Subject: [PATCH 45/46] Finalized changelog for v6.10.0 --- changelog.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 4995fa418..660fe108e 100644 --- a/changelog.md +++ b/changelog.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [6.10.0] - 2024-05-13 + ### Bug - [COLDBOX-1274](https://ortussolutions.atlassian.net/browse/COLDBOX-1274) javacasting to long for new Java LocalDateTime instead of int, Adobe not doing type promotion @@ -273,7 +275,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [WIREBOX-123](https://ortussolutions.atlassian.net/browse/WIREBOX-123) Removal of usage of Injector dsl interface due to so many issues with multiple engines -[Unreleased]: https://github.com/ColdBox/coldbox-platform/compare/v6.9.0...HEAD +[Unreleased]: https://github.com/ColdBox/coldbox-platform/compare/v6.10.0...HEAD + +[6.10.0]: https://github.com/ColdBox/coldbox-platform/compare/v6.9.0...v6.10.0 [6.9.0]: https://github.com/ColdBox/coldbox-platform/compare/v6.8.2...v6.9.0 From d1fb20a8bf5d3432d953a77278cf1b1f0c494fd8 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Mon, 13 May 2024 18:34:38 -0400 Subject: [PATCH 46/46] version bump --- box.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/box.json b/box.json index 0e5e1c0ff..ad6e85dff 100644 --- a/box.json +++ b/box.json @@ -1,6 +1,6 @@ { "name":"ColdBox Platform", - "version":"6.10.0", + "version":"6.10.1", "location":"https://downloads.ortussolutions.com/ortussolutions/coldbox/@build.version@/coldbox-@build.version@.zip", "author":"Ortus Solutions ", "slug":"coldbox",