From 9f740c27574b149eb8fc8c8eaa2df9adf8e3373e Mon Sep 17 00:00:00 2001 From: Mattias Reichel Date: Fri, 17 Nov 2023 17:13:12 +0100 Subject: [PATCH 1/9] Update to Grails 6 Compatible with Groovy 3.0.19 Updated gradle build to use version catalogs Added renovate and dependabot Documentation generation has been disabled. Maybe create a separate project for documentation like grails/grails-data-mapping -> grails/gorm-docs --- .github/dependabot.yml | 13 + .github/release-drafter.yml | 41 +++ .github/renovate.json | 61 ++++ .github/workflows/gradle.yml | 56 ++++ .github/workflows/release-notes.yml | 48 +++ .github/workflows/release.yml | 69 ++++ .travis.yml | 34 -- build.gradle | 87 +++-- buildSrc/build.gradle | 20 ++ buildSrc/settings.gradle | 9 + examples/pubsub-demo/build.gradle | 87 ++--- examples/pubsub-demo/gradle.properties | 1 - .../grails-app/conf/application.yml | 6 +- .../grails-app/conf/logback.groovy | 36 --- .../pubsub-demo/grails-app/conf/logback.xml | 19 ++ .../groovy/pubsub/demo/PubSubSpec.groovy | 10 +- .../groovy/pubsub/demo/BookSubscriber.groovy | 3 +- gradle.properties | 37 +-- gradle/documentation.gradle | 137 ++++++++ gradle/java.gradle | 5 + gradle/libs.versions.toml | 46 +++ gradle/publishing.gradle | 94 ++++++ gradle/wrapper/gradle-wrapper.jar | Bin 56177 -> 63721 bytes gradle/wrapper/gradle-wrapper.properties | 4 +- gradlew | 301 +++++++++++------- gradlew.bat | 56 ++-- {grails-async => grails-async-core}/README.md | 0 grails-async-core/build.gradle | 26 ++ .../groovy/grails/async/DelegateAsync.groovy | 0 .../main/groovy/grails/async/Promise.groovy | 0 .../groovy/grails/async/PromiseFactory.groovy | 0 .../groovy/grails/async/PromiseList.groovy | 0 .../groovy/grails/async/PromiseMap.groovy | 0 .../main/groovy/grails/async/Promises.groovy | 0 .../async/decorator/PromiseDecorator.groovy | 0 .../PromiseDecoratorLookupStrategy.groovy | 0 .../decorator/PromiseDecoratorProvider.groovy | 0 .../factory/AbstractPromiseFactory.groovy | 0 .../grails/async/factory/BoundPromise.groovy | 0 .../factory/PromiseFactoryBuilder.groovy | 0 .../async/factory/SynchronousPromise.groovy | 0 .../factory/SynchronousPromiseFactory.groovy | 0 .../CachedThreadPoolPromiseFactory.groovy | 39 +-- .../future/ExecutorPromiseFactory.groovy | 0 .../future/FutureTaskChildPromise.groovy | 0 .../factory/future/FutureTaskPromise.groovy | 0 ...teAsyncTransactionalMethodTransformer.java | 0 .../internal/DelegateAsyncTransformation.java | 21 +- .../internal/DelegateAsyncUtils.groovy | 0 .../grails/async/DelegateAsyncSpec.groovy | 0 .../async/FutureTaskPromiseFactorySpec.groovy | 0 .../grails/async/PromiseListSpec.groovy | 0 .../groovy/grails/async/PromiseMapSpec.groovy | 0 .../groovy/grails/async/PromiseSpec.groovy | 0 .../SynchronousPromiseFactorySpec.groovy | 0 grails-async-gpars/build.gradle | 32 +- grails-async-rxjava/build.gradle | 26 +- .../factory/rxjava/RxPromiseFactory.groovy | 28 +- grails-async-rxjava2/build.gradle | 28 +- .../factory/rxjava2/RxPromiseFactory.groovy | 30 +- grails-events-compat/build.gradle | 27 +- .../main/groovy/grails/events/Events.groovy | 2 +- .../README.md | 0 grails-events-core/build.gradle | 26 ++ .../main/groovy/grails/events/Event.groovy | 2 +- .../grails/events/EventPublisher.groovy | 0 .../groovy/grails/events/bus/EventBus.groovy | 0 .../grails/events/bus/EventBusAware.groovy | 0 .../grails/events/bus/EventBusBuilder.groovy | 0 .../grails/events/emitter/EventEmitter.groovy | 0 .../events/subscriber/EventSubscriber.groovy | 0 .../subscriber/MethodEventSubscriber.groovy | 0 .../events/subscriber/MethodSubscriber.groovy | 0 .../grails/events/subscriber/Subjects.groovy | 0 .../events/subscriber/Subscriber.groovy | 0 .../events/subscriber/Subscription.groovy | 0 .../grails/events/trigger/EventTrigger.groovy | 0 .../grails/events/ClosureEventTrigger.groovy | 0 .../events/EventSubscriberTrigger.groovy | 0 .../grails/events/bus/AbstractEventBus.groovy | 42 +-- .../grails/events/bus/ExecutorEventBus.groovy | 0 .../events/bus/SynchronousEventBus.groovy | 0 .../bus/spring/EventBusFactoryBean.groovy | 0 .../registry/AbstractSubscription.groovy | 0 .../registry/ClosureSubscription.groovy | 0 .../EventSubscriberSubscription.groovy | 5 +- .../events/SynchronousEventBusSpec.groovy | 0 .../events/TaskExecuterEventBusSpec.groovy | 0 .../events/TransactionAwareEventSpec.groovy | 0 .../MethodEventSubscriberSpec.groovy | 0 grails-events-gpars/build.gradle | 34 +- grails-events-rxjava/build.gradle | 35 +- .../grails/events/rxjava/RxEventBus.groovy | 37 ++- grails-events-rxjava2/build.gradle | 30 +- .../grails/events/rxjava2/RxEventBus.groovy | 35 +- grails-events-spring/build.gradle | 30 +- grails-events-transform/build.gradle | 36 ++- grails-events/build.gradle | 4 - grails-plugin-async/build.gradle | 47 ++- grails-plugin-events/build.gradle | 39 ++- .../events/EventBusGrailsPlugin.groovy | 5 +- settings.gradle | 19 +- travis-build.sh | 74 ----- 103 files changed, 1465 insertions(+), 574 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/release-drafter.yml create mode 100644 .github/renovate.json create mode 100644 .github/workflows/gradle.yml create mode 100644 .github/workflows/release-notes.yml create mode 100644 .github/workflows/release.yml delete mode 100644 .travis.yml create mode 100644 buildSrc/build.gradle create mode 100644 buildSrc/settings.gradle delete mode 100644 examples/pubsub-demo/grails-app/conf/logback.groovy create mode 100644 examples/pubsub-demo/grails-app/conf/logback.xml create mode 100644 gradle/documentation.gradle create mode 100644 gradle/java.gradle create mode 100644 gradle/libs.versions.toml create mode 100644 gradle/publishing.gradle rename {grails-async => grails-async-core}/README.md (100%) create mode 100644 grails-async-core/build.gradle rename {grails-async => grails-async-core}/src/main/groovy/grails/async/DelegateAsync.groovy (100%) rename {grails-async => grails-async-core}/src/main/groovy/grails/async/Promise.groovy (100%) rename {grails-async => grails-async-core}/src/main/groovy/grails/async/PromiseFactory.groovy (100%) rename {grails-async => grails-async-core}/src/main/groovy/grails/async/PromiseList.groovy (100%) rename {grails-async => grails-async-core}/src/main/groovy/grails/async/PromiseMap.groovy (100%) rename {grails-async => grails-async-core}/src/main/groovy/grails/async/Promises.groovy (100%) rename {grails-async => grails-async-core}/src/main/groovy/grails/async/decorator/PromiseDecorator.groovy (100%) rename {grails-async => grails-async-core}/src/main/groovy/grails/async/decorator/PromiseDecoratorLookupStrategy.groovy (100%) rename {grails-async => grails-async-core}/src/main/groovy/grails/async/decorator/PromiseDecoratorProvider.groovy (100%) rename {grails-async => grails-async-core}/src/main/groovy/grails/async/factory/AbstractPromiseFactory.groovy (100%) rename {grails-async => grails-async-core}/src/main/groovy/org/grails/async/factory/BoundPromise.groovy (100%) rename {grails-async => grails-async-core}/src/main/groovy/org/grails/async/factory/PromiseFactoryBuilder.groovy (100%) rename {grails-async => grails-async-core}/src/main/groovy/org/grails/async/factory/SynchronousPromise.groovy (100%) rename {grails-async => grails-async-core}/src/main/groovy/org/grails/async/factory/SynchronousPromiseFactory.groovy (100%) rename {grails-async => grails-async-core}/src/main/groovy/org/grails/async/factory/future/CachedThreadPoolPromiseFactory.groovy (67%) rename {grails-async => grails-async-core}/src/main/groovy/org/grails/async/factory/future/ExecutorPromiseFactory.groovy (100%) rename {grails-async => grails-async-core}/src/main/groovy/org/grails/async/factory/future/FutureTaskChildPromise.groovy (100%) rename {grails-async => grails-async-core}/src/main/groovy/org/grails/async/factory/future/FutureTaskPromise.groovy (100%) rename {grails-async => grails-async-core}/src/main/groovy/org/grails/async/transform/internal/DelegateAsyncTransactionalMethodTransformer.java (100%) rename {grails-async => grails-async-core}/src/main/groovy/org/grails/async/transform/internal/DelegateAsyncTransformation.java (93%) rename {grails-async => grails-async-core}/src/main/groovy/org/grails/async/transform/internal/DelegateAsyncUtils.groovy (100%) rename {grails-async => grails-async-core}/src/test/groovy/grails/async/DelegateAsyncSpec.groovy (100%) rename {grails-async => grails-async-core}/src/test/groovy/grails/async/FutureTaskPromiseFactorySpec.groovy (100%) rename {grails-async => grails-async-core}/src/test/groovy/grails/async/PromiseListSpec.groovy (100%) rename {grails-async => grails-async-core}/src/test/groovy/grails/async/PromiseMapSpec.groovy (100%) rename {grails-async => grails-async-core}/src/test/groovy/grails/async/PromiseSpec.groovy (100%) rename {grails-async => grails-async-core}/src/test/groovy/grails/async/SynchronousPromiseFactorySpec.groovy (100%) rename {grails-events => grails-events-core}/README.md (100%) create mode 100644 grails-events-core/build.gradle rename {grails-events => grails-events-core}/src/main/groovy/grails/events/Event.groovy (95%) rename {grails-events => grails-events-core}/src/main/groovy/grails/events/EventPublisher.groovy (100%) rename {grails-events => grails-events-core}/src/main/groovy/grails/events/bus/EventBus.groovy (100%) rename {grails-events => grails-events-core}/src/main/groovy/grails/events/bus/EventBusAware.groovy (100%) rename {grails-events => grails-events-core}/src/main/groovy/grails/events/bus/EventBusBuilder.groovy (100%) rename {grails-events => grails-events-core}/src/main/groovy/grails/events/emitter/EventEmitter.groovy (100%) rename {grails-events => grails-events-core}/src/main/groovy/grails/events/subscriber/EventSubscriber.groovy (100%) rename {grails-events => grails-events-core}/src/main/groovy/grails/events/subscriber/MethodEventSubscriber.groovy (100%) rename {grails-events => grails-events-core}/src/main/groovy/grails/events/subscriber/MethodSubscriber.groovy (100%) rename {grails-events => grails-events-core}/src/main/groovy/grails/events/subscriber/Subjects.groovy (100%) rename {grails-events => grails-events-core}/src/main/groovy/grails/events/subscriber/Subscriber.groovy (100%) rename {grails-events => grails-events-core}/src/main/groovy/grails/events/subscriber/Subscription.groovy (100%) rename {grails-events => grails-events-core}/src/main/groovy/grails/events/trigger/EventTrigger.groovy (100%) rename {grails-events => grails-events-core}/src/main/groovy/org/grails/events/ClosureEventTrigger.groovy (100%) rename {grails-events => grails-events-core}/src/main/groovy/org/grails/events/EventSubscriberTrigger.groovy (100%) rename {grails-events => grails-events-core}/src/main/groovy/org/grails/events/bus/AbstractEventBus.groovy (88%) rename {grails-events => grails-events-core}/src/main/groovy/org/grails/events/bus/ExecutorEventBus.groovy (100%) rename {grails-events => grails-events-core}/src/main/groovy/org/grails/events/bus/SynchronousEventBus.groovy (100%) rename {grails-events => grails-events-core}/src/main/groovy/org/grails/events/bus/spring/EventBusFactoryBean.groovy (100%) rename {grails-events => grails-events-core}/src/main/groovy/org/grails/events/registry/AbstractSubscription.groovy (100%) rename {grails-events => grails-events-core}/src/main/groovy/org/grails/events/registry/ClosureSubscription.groovy (100%) rename {grails-events => grails-events-core}/src/main/groovy/org/grails/events/registry/EventSubscriberSubscription.groovy (87%) rename {grails-events => grails-events-core}/src/test/groovy/org/grails/events/SynchronousEventBusSpec.groovy (100%) rename {grails-events => grails-events-core}/src/test/groovy/org/grails/events/TaskExecuterEventBusSpec.groovy (100%) rename {grails-events => grails-events-core}/src/test/groovy/org/grails/events/TransactionAwareEventSpec.groovy (100%) rename {grails-events => grails-events-core}/src/test/groovy/org/grails/events/subscriber/MethodEventSubscriberSpec.groovy (100%) delete mode 100644 grails-events/build.gradle delete mode 100755 travis-build.sh diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..965ca9df --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,13 @@ +version: 2 +updates: + - package-ecosystem: gradle + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 + target-branch: 5.0.x + labels: + - "type: dependency upgrade" + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-patch", "version-update:semver-minor"] \ No newline at end of file diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 00000000..f5ba56fd --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,41 @@ +name-template: $RESOLVED_VERSION +tag-template: v$RESOLVED_VERSION +categories: + - title: ✨ Features + labels: + - "type: enhancement" + - "type: new feature" + - "type: major" + - title: 🐛 Bug Fixes/Improvements + labels: + - "type: improvement" + - "type: bug" + - "type: minor" + - title: 🛠 Dependency upgrades + labels: + - "type: dependency upgrade" + - "dependencies" + - title: ⚙️ Build/CI + labels: + - "type: ci" + - "type: build" +change-template: '- $TITLE @$AUTHOR (#$NUMBER)' +version-resolver: + major: + labels: + - 'type: major' + minor: + labels: + - 'type: minor' + patch: + labels: + - 'type: patch' + default: patch +template: | + ## What's Changed + + $CHANGES + + ## Contributors + + $CONTRIBUTORS \ No newline at end of file diff --git a/.github/renovate.json b/.github/renovate.json new file mode 100644 index 00000000..30740f84 --- /dev/null +++ b/.github/renovate.json @@ -0,0 +1,61 @@ +{ + "extends": [ + "config:base" + ], + "labels": ["type: dependency upgrade"], + "packageRules": [ + { + "matchUpdateTypes": ["major"], + "enabled": false + }, + { + "matchPackagePatterns": ["*"], + "allowedVersions": "!/SNAPSHOT$/" + }, + { + "matchPackagePatterns": [ + "^org\\.codehaus\\.groovy" + ], + "groupName": "groovy monorepo" + }, + { + "matchPackageNames": [ + "org.grails:grails-bom", + "org.grails:grails-bootstrap", + "org.grails:grails-codecs", + "org.grails:grails-console", + "org.grails:grails-core", + "org.grails:grails-databinding", + "org.grails:grails-dependencies", + "org.grails:grails-docs", + "org.grails:grails-encoder", + "org.grails:grails-gradle-model", + "org.grails:grails-logging", + "org.grails:grails-plugin-codecs", + "org.grails:grails-plugin-controllers", + "org.grails:grails-plugin-databinding", + "org.grails:grails-plugin-datasource", + "org.grails:grails-plugin-domain-class", + "org.grails:grails-plugin-i18n", + "org.grails:grails-plugin-interceptors", + "org.grails:grails-plugin-mimetypes", + "org.grails:grails-plugin-rest", + "org.grails:grails-plugin-services", + "org.grails:grails-plugin-url-mappings", + "org.grails:grails-plugin-url-validation", + "org.grails:grails-shell", + "org.grails:grails-spring", + "org.grails:grails-test", + "org.grails:grails-validation", + "org.grails:grails-web", + "org.grails:grails-web-boot", + "org.grails:grails-web-common", + "org.grails:grails-web-databinding", + "org.grails:grails-web-fileupload", + "org.grails:grails-web-mvc", + "org.grails:grails-web-url-mappings" + ], + "groupName": "grails monorepo" + } + ] +} \ No newline at end of file diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml new file mode 100644 index 00000000..ac3885c3 --- /dev/null +++ b/.github/workflows/gradle.yml @@ -0,0 +1,56 @@ +name: Java CI +on: + push: + branches: + - '[5-9]+.[0-9]+.x' + pull_request: + branches: + - '[5-9]+.[0-9]+.x' +jobs: + build: + name: build + runs-on: ubuntu-latest + strategy: + matrix: + java: ['11', '14'] + env: + WORKSPACE: ${{ github.workspace }} + GRADLE_OPTS: -Xmx1500m -Dfile.encoding=UTF-8 + steps: + - uses: actions/checkout@v4 + - name: Set up JDK + uses: actions/setup-java@v3 + with: + distribution: 'adopt' + java-version: ${{ matrix.java }} + - name: Run Build + id: build + uses: gradle/gradle-build-action@v2 + env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} + GRADLE_ENTERPRISE_BUILD_CACHE_NODE_USER: ${{ secrets.GRADLE_ENTERPRISE_BUILD_CACHE_NODE_USER }} + GRADLE_ENTERPRISE_BUILD_CACHE_NODE_KEY: ${{ secrets.GRADLE_ENTERPRISE_BUILD_CACHE_NODE_KEY }} + with: + arguments: build + publish: + name: Publish Snapshot release + if: github.event_name == 'push' + needs: build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up JDK + uses: actions/setup-java@v3 + with: + distribution: 'adopt' + java-version: '11' + - name: Publish to Artifactory (repo.grails.org) + uses: gradle/gradle-build-action@v2 + env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} + GRADLE_ENTERPRISE_BUILD_CACHE_NODE_USER: ${{ secrets.GRADLE_ENTERPRISE_BUILD_CACHE_NODE_USER }} + GRADLE_ENTERPRISE_BUILD_CACHE_NODE_KEY: ${{ secrets.GRADLE_ENTERPRISE_BUILD_CACHE_NODE_KEY }} + ARTIFACTORY_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }} + ARTIFACTORY_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }} + with: + arguments: -Dorg.gradle.internal.publish.checksums.insecure=true publish \ No newline at end of file diff --git a/.github/workflows/release-notes.yml b/.github/workflows/release-notes.yml new file mode 100644 index 00000000..2807f6a5 --- /dev/null +++ b/.github/workflows/release-notes.yml @@ -0,0 +1,48 @@ +name: Changelog +on: + issues: + types: [closed,reopened] + push: + branches: + - master + - '[5-9]+.[0-9]+.x' + workflow_dispatch: +jobs: + release_notes: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Check if it has release drafter config file + id: check_release_drafter + run: | + has_release_drafter=$([ -f .github/release-drafter.yml ] && echo "true" || echo "false") + echo "has_release_drafter=${has_release_drafter}" >> $GITHUB_OUTPUT + - name: Extract branch name + id: extract_branch + run: echo "value=${GITHUB_REF:11}" >> $GITHUB_OUTPUT + # If it has release drafter: + - uses: release-drafter/release-drafter@v5 + if: steps.check_release_drafter.outputs.has_release_drafter == 'true' + env: + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} + with: + commitish: ${{ steps.extract_branch.outputs.value }} + # Otherwise: + - name: Export Gradle Properties + if: steps.check_release_drafter.outputs.has_release_drafter == 'false' + uses: micronaut-projects/github-actions/export-gradle-properties@master + - uses: micronaut-projects/github-actions/release-notes@master + if: steps.check_release_drafter.outputs.has_release_drafter == 'false' + id: release_notes + with: + token: ${{ secrets.GH_TOKEN }} + - uses: ncipollo/release-action@v1 + if: steps.check_release_drafter.outputs.has_release_drafter == 'false' && steps.release_notes.outputs.generated_changelog == 'true' + with: + allowUpdates: true + commit: ${{ steps.release_notes.outputs.current_branch }} + draft: true + name: ${{ env.title }} ${{ steps.release_notes.outputs.next_version }} + tag: v${{ steps.release_notes.outputs.next_version }} + bodyFile: CHANGELOG.md + token: ${{ secrets.GH_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..4a058bd8 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,69 @@ +name: Release +on: + release: + types: [published] +jobs: + release: + runs-on: ubuntu-latest + strategy: + matrix: + java: ['11'] + env: + GIT_USER_NAME: puneetbehl + GIT_USER_EMAIL: behlp@objectcomputing.com + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + token: ${{ secrets.GH_TOKEN }} + - uses: gradle/wrapper-validation-action@v1 + - name: Set up JDK + uses: actions/setup-java@v3 + with: + distribution: 'adopt' + java-version: ${{ matrix.java }} + - name: Extract Target Branch + id: extract_branch + run: | + echo "Determining Target Branch" + TARGET_BRANCH=`cat $GITHUB_EVENT_PATH | jq '.release.target_commitish' | sed -e 's/^"\(.*\)"$/\1/g'` + echo $TARGET_BRANCH + echo "value=${TARGET_BRANCH}" >> $GITHUB_OUTPUT + - name: Set the current release version + id: release_version + run: echo "release_version=${GITHUB_REF:11}" >> $GITHUB_OUTPUT + - name: Run pre-release + uses: micronaut-projects/github-actions/pre-release@master + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: Generate secring file + env: + SECRING_FILE: ${{ secrets.SECRING_FILE }} + run: echo $SECRING_FILE | base64 -d > ${{ github.workspace }}/secring.gpg + - name: Publish to Sonatype OSSRH + id: publish + uses: gradle/gradle-build-action@v2 + env: + SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} + SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} + SONATYPE_STAGING_PROFILE_ID: ${{ secrets.SONATYPE_STAGING_PROFILE_ID }} + SIGNING_KEY: ${{ secrets.SIGNING_KEY }} + SIGNING_PASSPHRASE: ${{ secrets.SIGNING_PASSPHRASE }} + SECRING_FILE: ${{ secrets.SECRING_FILE }} + with: + arguments: -Psigning.secretKeyRingFile=${{ github.workspace }}/secring.gpg publishToSonatype closeAndReleaseSonatypeStagingRepository + - name: Run Assemble + if: steps.publish.outcome == 'success' + id: assemble + uses: gradle/gradle-build-action@v2 + with: + arguments: assemble + - name: Export Gradle Properties + uses: micronaut-projects/github-actions/export-gradle-properties@master + - name: Run post-release + if: steps.publish.outcome == 'success' + uses: micronaut-projects/github-actions/post-release@master + with: + token: ${{ secrets.GITHUB_TOKEN }} + env: + SNAPSHOT_SUFFIX: -SNAPSHOT \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index dedd2f23..00000000 --- a/.travis.yml +++ /dev/null @@ -1,34 +0,0 @@ -language: groovy -jdk: -- oraclejdk8 -- openjdk11 -dist: trusty -env: - global: - - TERM=dumb - - MALLOC_ARENA_MAX=1 - - GRADLE_OPTS="-Xmx1024m -Xms256m -XX:MaxPermSize=372m -XX:+CMSClassUnloadingEnabled" - - GIT_NAME="Graeme Rocher" - - GIT_EMAIL="graeme.rocher@gmail.com" - - secure: gr+BVcVJ6wkFpBBNM0vn03V0AgcxJ+RbE8kHem5XR7jMwskVaPsrPnN1G2C6xzc+8SuA6uliSMH65gokSybTYEU8lWlgt87tsY+wQoufHhvi5YsKSfUMAXK8PdA+Yl9JOEz7UiyDdAq6lyu0AxrSO0IL+pORqBkzKyJ5p5jZoyjqW0fq3NkLKnXnJM5nsG6Vno2eKwvy8rGZTs2i/h2kPMtadsFImH0L7giDvxjXh+loz8rp5a7NSMs4fB5M07542dd5MEYXnZVCBkYW2f0YImk6ExeIRXvY64txczliX4EbIp6brTNvDeKoxvNKI/q1VEohFBmYK7Dw09k8xO+V2hpQaWV9eKSNr21PBoCM5HcQYoFjpFGWljwMXOnQIuU3GxqtBDOttZQBWxxo0aTkRJnOSXFplG+Fr9ZqYVlbEpGcUabpZjKq2ceFztiFJ00A/fdzh2jOpKpprmtItYF5waySwT9U0g+w1IvIFbn6yPamdY+oeCBIrZZnW5LaeR4OGjDo0wjXJF0Rf0ggaZj/MXt9bRlRzcwZ73igMoYkQdIKowX1Yht3dqrCprd/NjQOPE1GaGPkoXNIYRvdsELiDF1uQd6u6uNdocbSJWiqws6vmCrEx5P5ZeTSp/Moi4RJen6AWAjA8yPiqf2WcqMN/akozv+4iQrgQYtnyTKIpns= - - secure: ZA1tIW9mKj7tPuyaYv9Z3gLRTkETbxEgmmjFA5beTrN1H4zBukURFcLAeIwmpyfVsOzQEaDBs/SL5BhJULOCed44EazduDTU0CMeGjGKwcTiyInGlxfzzZ15GwBZel2DK4EKb8dXfFjbpfrqBYB+8j7Y37yDJ1lgrdzfHqOlx1iHwB8bgUibUrUX7fNc0K/XJObMxaWIiDIuxqTmdD4t6FKcSnyFTJmuxE7lpD9MJA4YhB9K73fKxCrRAm81lnTkrNO9gXtwns6VHLpiHYn48lpryekPfq7FER0ycJe2kwQhpC2M30kURTPC+oaGLjaT+RWrjw3e6x7yZR7wnsDKBXeslqcI2uNaeP1tjIQ50S01tvCqSsvO+P0aflGIcULbJGBesjQGVk5kTK89Nzcfns/EQVGkzk0UUIjVVGorVw6Ogfo1JwwVKWBk2V1/LO/J4pZqK6HmqBr8RRkEU3DDgi1tY+870zYDB8RokPTy0vmjRC1oijRWUpziqS9s0yWPKtglzqqD9EvuMPVpWYJl0q/uMo0kXyEc2go1U9/bI8/wFcR1KneLFGq3LNzP8QHGq1aOMAbg2V5m6+rCw2jjtcQlseKvPCfgdCD2E5JHxAo/M5SpXKi+g/WIxKM01d/i2xOTtCmCJVDbVFvayFM+6QF7nrufM2q8jl3LCQuRrUM= - - secure: onYM79ebWVScrglufxpWQZwaOFbfgnKs3HFDauHniLc10/8AQeqB962vmwtKFiivmCQwVOMMbeYEBgWsWVbZtUAiVlAjwUPL1uiljcDmHYo8LqaLnK+zezkvfDyhGodVAM+UOmjWj4jLb3D0shEmiufHe96cuwRi8aafU2Cot6cKssg3oN3mw7ob+Nr6JX9RDCrqa1UYF1yD7S52wWMNW/WnvhN3t9vhAhxpb05Zvmj6KjwxgxBicGEU8El+Pz6H1hDZ1xUXmGa9k7DitXLZEp4HCwcAC+CP588NSsGygS/icvjQ6QLc8h3zVjru+P3EVgr/HQXkSBc7uQ4JJ3Ob6ODPiWiuW6ViDr+/lNN2F8qsHkpOlzA/cZjBRG6HXsNoxcMABj31XbkAV/s0BmDLUYNKb4LJ5MLtFnBHX656UhxInaWLgM+1Q/PB+SIPRXx1St8cZU3qPAoMr9UN7nDGjfb2tcuB2BRGn4t6kHj2Sg3EYx+7OTB4X183t/99Ihvb6iBYvtzluwEzL8YiJCKTZMfHUSdx9cZYHyWhi3+HQekMhSaDw/uHG6HD/rrB4Ys2qa9yeCQ+cRR+GVnikH10z44V81R58HLFaIYym+WfiJAqhlb8rx4YFS5WsTjY03t/4I1OB0MxU2TzVyWHHAsW2laHsdRCwaYRBs28p2AZVz4= - - secure: J4aJW/bbPFmeiwqxNahyi2zp3xsQpT0KxpnLRT+jE/h8LAS8p5G7UiP2PGqIDJbcp5uBSS25kvfNSQq5fIAOeDBz9wZgaCu/CiPqU5ZMnFkbaGL38HBYU4Y2JXpjFXkVYi60XemUuWOkTUlPAkweC6Z2UJZAH9R3lpZSGh3QPl3ahDSymapaRfhcDeSUpTun2QcmViGuf2ZbMj492WbkhpEyDBlqVPjRmO5/Rc1hqkg9A7Vj5srRCydSSkvFi1GYESKiqV1BI5aIpScbe5Jh5gd3BCIaiQZ1GqE/elNaS9Y7Qc9qfYm+Yu7PwIJh8FaxIRQV5352im2A0ZE20AvK30yUI6jrjDIMo1hdog6AFs8/i8aCcdERoX8CK0s3qcfprM2jh6gJLoIsR8Rd/wttr7bcEOd8fvuLXxIZca18hs7+VMq78p1YXfYF/lWvEi8hlmazAyB3rohUgxTEI7Gj0OBNeg1SRrBQZ2cxKorpLCUMPdLECVGVta89hX1EbSEgSCU2KTxrkg9TSocqqluSgzOnOuYN8KXJXD7inWZ+1oPde0lZ2du0EdTxts1lnQqU3kbmw/i7NTRICb2rbhyMGHrO1fG+Ck1QMfTkaq0aKiTvuD37Edtj8QGchXezFxIvnwUTVaJyt7oUXczeUBozZ83q95FAGuy5EUdrXmPTmyE= - - secure: ApWT4MJsLy/39njQtCs2TFMgPcWzkpbb82mTgyk43BgIRsXeq5NtilhfAJlDiG8U9GUi2KSIR1jZ1ZUOtJx2Hi8VhYRT526aLdeCeKBh/nfe3iRVpwMvFXzI/Kzqzj92Ns5Cr7/+UWGzu/Ib/c+EqF5CAPBeIq2oohBsXX8k8kSu9cQ7kY5n3un066rAKEyhtxsAn8LulJ1vd3QLWy6h7pNaPqNRlfJ9tr5KBaZR0b9pk30y/86VMjVW8nZOpTRE63BTs1vfUwmhQarjDXQadWIB3DuKoeYBJnK/G/A9Xc9w8Mt8uc4FCKIDXxOZSGM6uznGU4Etqs8euUAPeBvHWghhRa96ox13kH5NNx+CX2mb1tkB8VdsI9b0/OWplM6iya7MBW311d/8mGwEwj/5HhvgYJ2mnulrVPWCnOHsR4Nend6ih1UqmC55hJNqYTP0olHUfVuVUU+8upGxpqBdF27qFcCoXBFeKPurQ1kXEZmzFKur/LhwijevpCzh58ICoWdcChgceI9tOIX0pXHZuI88vI3D/NiB0g52rMMVT5nbT+kLmLPZXHbLTbQuP5myiIJyolYiYI8x3mswX4hNN4QzOzVUu6C39bNEZkfWTFi+J+ODC2aU51i+DLKSvhzU6er9cQJJCSYlV0fw7S1d7w0cJQQwYw4nXfr0Y+MlPpA= - - secure: Ptger8KnUxhaw9oP4P0YJh/0c23rVROiyXTX6czj0wuZzdyGiYhLNXWM+ZQZXhgQLK2jTxuSjvGPv/UJ7RbWsh+voveoCijBO65MpZsFiRb8ymjHKbLvV993SKOwmU64NL1KezmVdjh+OpnugjSVzPZPCLHnkTlEieHcdYtN00DVKi9Sehu55wguhxfzQ4huWmxmbCfKcAKkJc15TOYQrUKQkxnZTUYcHuIFG9WBz6zOedMbBWbk0DcwDlg5Ou32yKjcUGzsskOKw4ndeDz79EDEZ+++hER21hM020ItR1QCmLnYv5fRIEwEPTbzlCd4iu83c6xgMzqlMnv81lIq7rXrBF2FbAn5Zb+T2xuIop/HH5xtzuPe965DuS9q5o8kYftk/eBbl7xuue+KkYl25XN2d9V6Fd3/uXB+vZxfcX21n2F0Hr16fRAz7wjcQ7NqIE0Tpq3YxOvTaubdjvWDo8VqOt8IuHtiKI8HPAHEMJxqsWSfeSyRVgwZu7YtshCTxqd2psimly0Pp+WfaFaR2upwFtZx1gviU/zgHINjxD29y/uEen90WRWnAZD0+Abx4YVWFbjh1zXglk9YIF5eFec6qV/4RMEBCjopw+bpbJubqKrHGT33bOEYdpVHhMLlO2AfLvYPWth48f3TWri6dKJFlSxmAbssNL7kCh+Ikj4= - - secure: UAeyzCb7LUneQOty+/HiB+TTK+Nd9GnIpknDcedOzxTa35iQcjFVCjmn054pYuVlvVa4JZQwDP0KHq63eCyMT4TmlAnL4uGjw/nXUne+6bpEJZVsHvW+SGfe3NaEWrKoogZtR5hGV5dgJWqL4Qr/eWojihdwncR85JeYjXSV/jPKm6P5ASleb93yS+vc7/1/0wDWxDzs0uhhroYX0iEnsCY8CGz+Kuk+Kfz1wL8dp3FFuEOf9/uPhCL+6qQb3XuHSHmyoz6vM9PZLEZxlwnrpA1+d2D65mQVVQR0LpnbBd/cQRFuh403bWYzdzxgRcvvo7WH41GZaiwTNQ2QPs5p0C1yF4YrEW+hL0VRbTvKv2fwaZcLfe3ND5H3BxEzExDXdJhuHV0XY2bxZeFNdjoRUuXsrU7PQfSlS2T/R+QWt0eaVKDs3PrCQL7FG7f6ZTi7HoSkumBJIDYG+R3GxUSFFdGt31YYI/Xg8wVwfnUtMHt+8BaOLMPx/ezNqJ6/I6D8xKT4ZltmQuo4klsV1q51OrjIFEWLt55WxUtNsCIsL4Cs+mS0ij4mzjUB+AJ6phY1iEa3UpxRx+B+xhtsUGhPG+HNdp4FMoBBmvH8vQCseikJvOWk/UtTi7FB1+k1eSCOytgf5kTum1B99LYDBa8kIZiYvUhZMYd3mGLQY2yDUJ4= - - secure: ANoqm7NLJFRHq1Z9//PrWD+8xrgeUleHFRhiMm3vGLNDQU/0T37vebHvGVPW2r1+r7a1RavnIyX7b07TQn712nti1bovdyAV8GWMRwoUwbeOyHr3HbtoTrLWW79W71DsvmeYLkLsNWVwyymBG2sugMB/HsimRJBOifoZWNkqbcbOnzUpjAUxG0e88u0WUHUshJEUqPDQCr2ShRqI1aFORrsetPGFuB+2nZShPYutgC2AnO2dlQ0oo1QyNKilarG1Br+tAI0B+eseym+5dAcD4gkTp2hAq/xJZOx810HYuTjGSqENJeUI6Wt4ZCzAYawcPRXbncgDQUpv9k5XapnTZlLF55/WjWoFOrbuGZQJE01EuWRT4rpMm4Tixlajr07Y2L4Ad7NIP82DczLKpg2qbYZdDhkJNvor4zzZD+SFIFVn2yYkwAh9NY05vPYjZeOx/kLMggzsHpYWVznO9YI7gMtawhP56WWvCibvanFx8QnJ0g4dX3sqz5N6x4Ed1jdMJEGI0N2t/nLdfKimVCFKm7sJQA3RVPwnVVIhDLMPEBBVJwzltTTdeQFxtopwk9Rm6b1La9lmzcw3XcQXXrDm8WZoH1+POSld7zswSLLT4vXGv6YBAsgDUEukyEZtRCk1cPpjpsC/8eOXVsULJsn4RNS8amjDOQBSW9S/d9ol3gU= - - secure: Mh7gfuKjZAXqcgzFjT0mRzTYdTUcbb4eVchV8r/TH7F49bo9LgQBzcnyOj+FNFfCsxiR+D6XTbrGZx12WVpYzOYLeoWs1tYjWk/Qwq2UHx8BlsYoEbUdsnC3almWsN6ej1IY4ldsRZpmZEomZObHud8dagMb6O6MTbD0SKw5StkVJnQbbvBlFmdrt/YH9Gq7zKdYl4tS57MZXe+9YbGvPj1HwfSiI7ty03et2q05geSQ4OseAwHgPvwmYwZg3fUECqa+xdk3pPwPWJNG5C24wtb+N4HHKj6k5JEPCTCs7yEdvZ8KO5fQ/3VvcapSGUlhkrpS96FsSlOHp8oZ8cdqzVxP3Ii4/k0I4efX98krrmfy2pAkqTGR4FtbZRGG5LnFQFIiKPX6ZD8N6u8VVI0H3ZhOnFQ3KLSxnTZ/jB/I8Ed6sqR1sEZeWu4qaG6tBSiHM4jab/cstrAteJUs/Ag/uviXPY09m9cfpF7EEOrYbYtACywVXI0UvRrFKDtxAbl3iDNLGxj9nmVsDC7Ok4xKDaepKDk4VP+2Mb04cnia86iOEjYOFfDR+hSuvtlS+5pg5dFFVGrSseMjRXv2Dif1ESVMSjAlPVGUd71dEsg4hPMwP02s/ossMJ67ADgujI90gNwQ5sYurOZbb8Im6GJNjSV9sDW35e+MjjSaS8PzMj8= - - secure: D/6j7NTKNnQl0sZBnSPF4fdJG/SbQPtUPbSDbzWvFI4l6xrs5tKSi59SBcqrSiTujDUn0feMiy+o+bQcU0LdVYZh+RtJoWivGajkuzIfsdS67uTBKZI9A9T8bI/fHaaGusi/RlrZ9FKqb9qhEHjICy6+WKFY839oC6x/mYcqO9j/+TqSUev5zfV5rWhxD1Beae9PfMTykmuaLWEeIfTBbZm3gCLU3iwTb/1u1LMIT/BTuU07KyddOYYRrYdcNRL26S+vbJqPFUbCYIxqB8yDwBZM2XYTwhtmCfVkdS9Vjmlsrm6o+EVAHo1I0lBQiD1zR9EJWcZx1rxzvqcejjq6bgW3/TORzZkShQhYEEHAwGvZltsBM1Khmt8z55v8g7xomPWC04kqqaSUfIu9XAy2m8uzt2IYOLNhT6yxU1f4PGECeX9nbCsWqHMIM1n7B9lH+PHtTWF0F1HNttDstXGoQtExHPYWWK+abv4oNBfBdt5oakN6yhNmthMa+X72SySMBc651mu31wT9M8961iO1irxWtit3dxSE0WwOm1sAuqRgn5x0tZJZlUFq2MQrb1jrn5SaBwaQLk3ZoAFKRfrDgIqYVIOfHyPt2dYhgvkaLXPdmJzzNARaRQWB4OFZGt5VPWPFtKp5EHYQg/EK2zg/SOWqWhjxQom5+omUxW2pF6I= - - secure: OFWMeD6q08mFt4kWoXmuSFay58lDc4oqQ5L32HfzFX0KubCcEeQ1/k1KM55CYqa/yY0gD1E/1kgo+Znt7nKjAoJRhaSHI2UOPO49MHLWzhFA0loG6IMiAgbNRiN+6ETkRgBfg4hRc2rcwWMcZgYcHVOA5kRn+8nqF1RCPjbVejZENQoofVLgxRlNOx5wGqfTblq6/yUwOt9zGPjl3ZwOidtBqEKrOvnBH92TGS+Y+iOTwvJMbHKwhQ4HrMqY32FAQAEaQbhIu2vSlrD+s+Q6yzKYo2GJOrscFsijBflhSAFqxkw2hTHSYGOP72BsDWAwsJW58C9nPP0ESQt/WMXbHMO+lpeDW0tSSENtWsuLw3Yyk3yd+tPrPaFJNDBcAIRvXK0KgvNv11lwq4Bs9/10X7G78BYPBzc3Lc/TakfIWtLDMQRWxPV38ZA+J5AtkR0QN/XuMR/wqDBnGjSX2dK9WSHrhgUu9mSr2Hn70gzBHpPLNHNJ4ly2kNZiaw3h7xd5nbMwQBdGfnBD4aSjqGZkhfEtkUsioNw0g+S0XHhclULZQuD0pioIDIRTtjtGJNkj4ZyN86dToLQocuQ1KkQv2kttQ4dqtZMuXpmKnxOQ+b2hsQjfs0nK1IAUupx+SMKpVYVctiTbPJWJdgvwF7ZYdNKQH6oFC3FvmBWbrQYl+Ao= - - secure: JqEwNrJg2+T6SK1CU0RiI8otGJanIo5LTcFh6RHXB4VXMex99x8XNR+CP0zDDS7R0P2C4MSyrxo2LXhOYG1QMPglPyxQp8HkzthiUs7PqBQiPwdJU4V78zw/8K3o120Yi+W9hj6f1JPJkE+EHEcRWFzmNNO/5+i1UaUBR3MFjGGo2YTyFfswPdslZYomeoLFM4uCGcrBf1YSmW8QFLlHcvN4VIenkOUWgim5+rzMu9YWil62J/fe5Ys99XIDglu3vKrL3YvmR6ulObGhYAgU96Amt3yf4nZYRfk6iiICyZOR5ztkP0TbMCuzxMsLMMP7sHxWseu31caJyyrG87u/6ycFDM13GTKeJwf+n5HHPZvP1TcyIO4BWcOdu8Y2J5M563Ueh1VkENdjcOZuWYZE4d81JkujQK2RXEh3oZbiQP74aVEP3In4GevJNALGNcEPXBsNHWakagAisEQo+2wtc9PodKxmtHUUMTh3UzrVVEITyL8lOuAglX5BzSfWAG+lgbdqfnxrCVk1qL4I6r0Lj3a8slnIjQA/v1CcJOqm/DvqLP3AbFyTXcVny60ACZOmvQ6n5kDCMtFqzUpXLahTqRpt/i30eg6cKJfpDtiRzZ/0uaIMuRzTzH20lcXPC7aWlsuYWoUSr/HFz//Dp3LmYqZhbk36UjFNV5mPWwFyD6o= - - secure: Ewv2e92YVVCVkYjLXcyxwPBnbRTirDqB4Mac8s3dAFgWFGZ0my9lJJ7ToynQCPbBhvuDsSfg5eP29rp6HDqBhso5KxyqoSKJ4JGRAGs9oHvh5UMRkKx9T6dR3DLB0LGc1ie1M57prZBdSStYoKFkDaZc5kk6kyRK2QX8pAVqyR5IG6Q2R+8+tH95abdiKQ78Si3hoVbsR5voUGhHK7CDn4Yc+OUHrjD3B8voUwbDSR1jDSAcsrY9fI4sCj4JfeuVPwQ/H4BrhX2nKQWUwh2F4FQ/nDxNbk4pSeBkyLx5yhIwCljQ7yDUvarJiLTnsyj7/cvMBKi9YtBUzzSziI/Z5HGNAvuqClM4vUPFiZk9qvRIIwyaH1r7Q8cV6WhZ8IZZCqaNSNVDIo/rurJVTRF1Jrn4F6Jpuxz9E2bhFt4chVAGsWMjdlcYMzao4TEV1Eg0d3pYh0Cct3dGrc0KYKtlphumy1JDBkBIt3A2JlK4sbm8gAA2vpxYNhTg7998LO0czyA6LqS+xZ2C7NxHbX59sq+7CgmBl9Lquf46b/d99BN9q+G18wrSmYtJueS4bLqjfkS9hhj3wPlBA25gBXI4J6b1U0ge6OciapmsCj4NZKCaMXcSGqhsKNFHZOe3PgeA35NBeXLeD9hwNXPqQHIY7Hniajp9Uuab3wgJ1MVKulE= -install: true -script: "./travis-build.sh" -before_cache: -- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock -- rm -fr $HOME/.gradle/caches/*/plugin-resolution/ -cache: - directories: - - "$HOME/.gradle/caches/" - - "$HOME/.gradle/wrapper/" diff --git a/build.gradle b/build.gradle index c1445c24..6f1f1b8b 100644 --- a/build.gradle +++ b/build.gradle @@ -1,64 +1,57 @@ -buildscript { - repositories { - mavenLocal() - maven { url "https://repo.grails.org/grails/core" } - } - dependencies { - classpath "org.grails:grails-gradle-plugin:$grailsVersion" - classpath "org.grails:grails-docs:$grailsVersion" - classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4' - classpath 'com.bmuschko:gradle-nexus-plugin:2.3.1' - classpath 'io.github.groovylang.groovydoc:groovydoc-gradle-plugin:1.0.1' - } +plugins { + id 'groovy' + id 'io.github.gradle-nexus.publish-plugin' } +version = projectVersion + repositories { mavenLocal() - maven { url "https://repo.grails.org/grails/core" } + mavenCentral() + maven { url 'https://repo.grails.org/grails/core' } } -version project.projectVersion -ext { - commonBuild = 'https://raw.githubusercontent.com/grails/grails-common-build/1549696f8d1a6bce3d84db9c4c1bfde61eddf175' +ext['isSnapshot'] = projectVersion.endsWith('-SNAPSHOT') +ext['isReleaseVersion'] = !isSnapshot + +if(isReleaseVersion) { + nexusPublishing { + String nexusUser = System.getenv('SONATYPE_USERNAME') ?: project.findProperty('sonatypeOssUsername') ?: '' + String nexusPass = System.getenv('SONATYPE_PASSWORD') ?: project.findProperty('sonatypeOssPassword') ?: '' + String nexusStagingProfileId = System.getenv('SONATYPE_STAGING_PROFILE_ID') ?: project.findProperty('sonatypeOssStagingProfileId') ?: '' + repositories { + sonatype { + nexusUrl = uri 'https://s01.oss.sonatype.org/service/local/' + username = nexusUser + password = nexusPass + stagingProfileId = nexusStagingProfileId + } + } + transitionCheckOptions { + maxRetries = 40 + delayBetween = java.time.Duration.ofSeconds 2 + } + } } subprojects { - version project.projectVersion - - ext { - userOrg = "grails" - isGrailsPlugin = name.startsWith('grails-plugin') - isBuildSnapshot = version.toString().endsWith("-SNAPSHOT") - } - - if(isGrailsPlugin) { - group "org.grails.plugins" - } - else { - group "org.grails" - } + version = rootProject.version repositories { - mavenLocal() - maven { url "https://repo.grails.org/grails/core" } - } - - if(it.projectDir.path.endsWith("examples/${it.name}".toString())) { - apply plugin:"org.grails.grails-web" - } - else if(isGrailsPlugin) { - apply from:"${commonBuild}/common-project.gradle" - apply from:"${commonBuild}/common-publishing.gradle" - } - else { - apply from:"${commonBuild}/common-project.gradle" - apply from:"${commonBuild}/common-publishing.gradle" + mavenCentral() + maven { url 'https://repo.grails.org/grails/core' } } - dependencies { - compile 'javax.annotation:javax.annotation-api:1.3.2' + tasks.withType(Test).configureEach { + useJUnitPlatform() + testLogging { + events 'passed', 'skipped', 'failed', 'standardOut', 'standardError' + } } } -apply from:"${commonBuild}/common-docs.gradle" +//do not generate extra load on Nexus with new staging repository if signing fails +tasks.withType(io.github.gradlenexus.publishplugin.InitializeNexusStagingRepository).configureEach { + shouldRunAfter tasks.withType(Sign) +} \ No newline at end of file diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle new file mode 100644 index 00000000..449d446f --- /dev/null +++ b/buildSrc/build.gradle @@ -0,0 +1,20 @@ +plugins { + + id 'groovy-gradle-plugin' + id 'maven-publish' + +} + +repositories { + mavenCentral() + maven { url 'https://repo.grails.org/grails/core' } +} + +dependencies { + + implementation libs.nexus.publish.gradle.plugin + implementation libs.grails.gradle.plugin + implementation libs.grails.views.gradle.plugin + implementation libs.groovydoc.gradle.plugin + +} \ No newline at end of file diff --git a/buildSrc/settings.gradle b/buildSrc/settings.gradle new file mode 100644 index 00000000..4194f6d4 --- /dev/null +++ b/buildSrc/settings.gradle @@ -0,0 +1,9 @@ +rootProject.name = 'grails-async' + +dependencyResolutionManagement { + versionCatalogs { + libs { + from(files('../gradle/libs.versions.toml')) + } + } +} \ No newline at end of file diff --git a/examples/pubsub-demo/build.gradle b/examples/pubsub-demo/build.gradle index b3cc0ddc..843aaccb 100644 --- a/examples/pubsub-demo/build.gradle +++ b/examples/pubsub-demo/build.gradle @@ -1,41 +1,50 @@ -dependencies { - compile "org.springframework.boot:spring-boot-starter-logging" - compile "org.springframework.boot:spring-boot-autoconfigure" - compile "org.grails:grails-core" - compile "org.springframework.boot:spring-boot-starter-actuator" - compile "org.springframework.boot:spring-boot-starter-tomcat" - compile "org.grails:grails-plugin-url-mappings" - compile "org.grails:grails-plugin-rest" - compile "org.grails:grails-plugin-codecs" - compile "org.grails:grails-plugin-interceptors" - compile "org.grails:grails-plugin-services" - compile "org.grails:grails-plugin-datasource" - compile "org.grails:grails-plugin-databinding" - compile project(":grails-plugin-async") - runtime project(":grails-events-rxjava") -// runtime project(":grails-async-rxjava") - compile "org.grails:grails-web-boot" - compile "org.grails:grails-logging" - compile "org.grails.plugins:cache", { - exclude module:'asset-pipeline-grails' - } - compile "org.grails.plugins:hibernate5" - compile "org.hibernate:hibernate-core:$hibernateCoreVersion" - compile "org.hibernate:hibernate-ehcache:$hibernateCoreVersion" - compile "org.grails.plugins:views-json" - compile "org.grails.plugins:views-json-templates" - console "org.grails:grails-console" - profile "org.grails.profiles:rest-api" - runtime "com.h2database:h2" - runtime "org.apache.tomcat:tomcat-jdbc" - - testCompile "org.grails:grails-plugin-testing" - testCompile "org.grails:grails-gorm-testing-support" - testCompile "org.grails:grails-web-testing-support" - testCompile "io.micronaut:micronaut-http-client:$micronautVersion" -} +plugins { + + id 'application' + id 'groovy' + + id 'org.grails.grails-web' + id 'org.grails.plugins.views-json' -bootRun { - jvmArgs('-Dspring.output.ansi.enabled=always') - sourceResources sourceSets.main } + +group = 'pubsub.demo' + +dependencies { + + implementation project(':grails-plugin-async') + implementation project(':grails-plugin-events') + + implementation 'org.grails:grails-core' + implementation 'org.grails:grails-logging' + implementation 'org.grails:grails-plugin-databinding' + implementation 'org.grails:grails-plugin-interceptors' + implementation 'org.grails:grails-plugin-rest' + implementation 'org.grails:grails-plugin-services' + implementation 'org.grails:grails-plugin-url-mappings' + implementation 'org.grails:grails-web-boot' + + implementation 'org.grails.plugins:cache', { exclude module: 'asset-pipeline-grails' } + implementation 'org.grails.plugins:hibernate5' + implementation 'org.grails.plugins:views-json' + implementation 'org.grails.plugins:views-json-templates' + + implementation 'org.springframework.boot:spring-boot-autoconfigure' + implementation 'org.springframework.boot:spring-boot-starter' + implementation 'org.springframework.boot:spring-boot-starter-logging' + implementation 'org.springframework.boot:spring-boot-starter-tomcat' + + implementation 'org.hibernate:hibernate-core' + implementation 'org.hibernate:hibernate-ehcache' + + console 'org.grails:grails-console' + + runtimeOnly 'com.h2database:h2' + runtimeOnly 'org.apache.tomcat:tomcat-jdbc' + + testImplementation 'io.micronaut:micronaut-http-client' + testImplementation 'io.micronaut:micronaut-inject-groovy' + testImplementation 'org.grails:grails-gorm-testing-support' + testImplementation 'org.grails:grails-web-testing-support' + +} \ No newline at end of file diff --git a/examples/pubsub-demo/gradle.properties b/examples/pubsub-demo/gradle.properties index 545ab7eb..e69de29b 100644 --- a/examples/pubsub-demo/gradle.properties +++ b/examples/pubsub-demo/gradle.properties @@ -1 +0,0 @@ -grailsVersion=4.0.0.BUILD-SNAPSHOT \ No newline at end of file diff --git a/examples/pubsub-demo/grails-app/conf/application.yml b/examples/pubsub-demo/grails-app/conf/application.yml index 2492aa99..cbae1f92 100644 --- a/examples/pubsub-demo/grails-app/conf/application.yml +++ b/examples/pubsub-demo/grails-app/conf/application.yml @@ -87,15 +87,15 @@ environments: development: dataSource: dbCreate: create-drop - url: jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE + url: jdbc:h2:mem:devDb;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE test: dataSource: dbCreate: update - url: jdbc:h2:mem:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE + url: jdbc:h2:mem:testDb;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE production: dataSource: dbCreate: none - url: jdbc:h2:./prodDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE + url: jdbc:h2:./prodDb;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE properties: jmxEnabled: true initialSize: 5 diff --git a/examples/pubsub-demo/grails-app/conf/logback.groovy b/examples/pubsub-demo/grails-app/conf/logback.groovy deleted file mode 100644 index 20f85e19..00000000 --- a/examples/pubsub-demo/grails-app/conf/logback.groovy +++ /dev/null @@ -1,36 +0,0 @@ -import grails.util.BuildSettings -import grails.util.Environment -import org.springframework.boot.logging.logback.ColorConverter -import org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter - -import java.nio.charset.Charset - -conversionRule 'clr', ColorConverter -conversionRule 'wex', WhitespaceThrowableProxyConverter - -// See http://logback.qos.ch/manual/groovy.html for details on configuration -appender('STDOUT', ConsoleAppender) { - encoder(PatternLayoutEncoder) { - charset = Charset.forName('UTF-8') - - pattern = - '%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} ' + // Date - '%clr(%5p) ' + // Log level - '%clr(---){faint} %clr([%15.15t]){faint} ' + // Thread - '%clr(%-40.40logger{39}){cyan} %clr(:){faint} ' + // Logger - '%m%n%wex' // Message - } -} - -def targetDir = BuildSettings.TARGET_DIR -if (Environment.isDevelopmentMode() && targetDir != null) { - appender("FULL_STACKTRACE", FileAppender) { - file = "${targetDir}/stacktrace.log" - append = true - encoder(PatternLayoutEncoder) { - pattern = "%level %logger - %msg%n" - } - } - logger("StackTrace", ERROR, ['FULL_STACKTRACE'], false) -} -root(ERROR, ['STDOUT']) diff --git a/examples/pubsub-demo/grails-app/conf/logback.xml b/examples/pubsub-demo/grails-app/conf/logback.xml new file mode 100644 index 00000000..a163180e --- /dev/null +++ b/examples/pubsub-demo/grails-app/conf/logback.xml @@ -0,0 +1,19 @@ + + + + + + + + true + + UTF-8 + %clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%5p) %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n%wex + + + + + + + + \ No newline at end of file diff --git a/examples/pubsub-demo/src/integration-test/groovy/pubsub/demo/PubSubSpec.groovy b/examples/pubsub-demo/src/integration-test/groovy/pubsub/demo/PubSubSpec.groovy index 39210d9c..bb219102 100644 --- a/examples/pubsub-demo/src/integration-test/groovy/pubsub/demo/PubSubSpec.groovy +++ b/examples/pubsub-demo/src/integration-test/groovy/pubsub/demo/PubSubSpec.groovy @@ -2,7 +2,7 @@ package pubsub.demo import grails.gorm.transactions.Rollback import grails.testing.mixin.integration.Integration -import org.springframework.beans.factory.annotation.Autowired +import jakarta.inject.Inject import spock.lang.Specification /** @@ -11,10 +11,10 @@ import spock.lang.Specification @Integration class PubSubSpec extends Specification { - @Autowired SumService sumService - @Autowired TotalService totalService - @Autowired BookService bookService - @Autowired BookSubscriber bookSubscriber + @Inject SumService sumService + @Inject TotalService totalService + @Inject BookService bookService + @Inject BookSubscriber bookSubscriber void "test event bus within Grails"() { when: diff --git a/examples/pubsub-demo/src/main/groovy/pubsub/demo/BookSubscriber.groovy b/examples/pubsub-demo/src/main/groovy/pubsub/demo/BookSubscriber.groovy index 77e8418c..eadaa81a 100644 --- a/examples/pubsub-demo/src/main/groovy/pubsub/demo/BookSubscriber.groovy +++ b/examples/pubsub-demo/src/main/groovy/pubsub/demo/BookSubscriber.groovy @@ -9,7 +9,8 @@ import java.util.concurrent.ConcurrentLinkedDeque @Component class BookSubscriber { - List newBooks =[] + List newBooks = [] + @Subscriber("newBook") void withBook(Book book) { newBooks.add(book.title) diff --git a/gradle.properties b/gradle.properties index 7a8a4655..7b754a49 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,24 +1,25 @@ +projectVersion=5.0.0-SNAPSHOT + +# These versions have to be defined here for usage in settings.gradle +# (versions in settings.gradle cannot be set from the version catalog) +grailsVersion=6.1.0 +viewsVersion=3.1.0 + +# This prevents the Grails Gradle Plugin from unnecessarily excluding SLF4J in the generated POMs +# https://github.com/grails/grails-gradle-plugin/issues/222 +slf4jPreventExclusion=true + title=Grails Async Framework authors=Graeme Rocher -projectVersion=4.0.1.BUILD-SNAPSHOT projectDesc=Grails Async Libraries projectUrl=https://github.com/grails/grails-async githubSlug=grails/grails-async githubBranch=master -developers=Graeme Rocher -grailsVersion=4.0.0 -grailsDocsVersion=4.0.0.M1 -groovyVersion=2.5.6 -gormVersion=7.0.2.RELEASE -servletApiVersion=4.0.1 -spockVersion=1.2-groovy-2.5 -springVersion=5.1.8.RELEASE -slf4jVersion=1.7.22 -rxJavaVersion=1.3.0 -rxJava2Version=2.1.3 -rxjavadocs=http://reactivex.io/RxJava/javadoc -rxjava2docs=http://reactivex.io/RxJava/2.x/javadoc -gparsdocs=http://gpars.org/1.2.0/groovydoc/ -mavenCentralSync=false -micronautVersion=1.0.4 -hibernateCoreVersion=5.4.0.Final +developers=Graeme Rocher, Mattias Reichel + +#gpars docs is currently not available with https +gparsdocs=http://gpars.org/1.2.1/groovydoc +rxjavadocs=https://reactivex.io/RxJava/1.x/javadoc +rxjava2docs=https://reactivex.io/RxJava/2.x/javadoc + +org.gradle.caching=true \ No newline at end of file diff --git a/gradle/documentation.gradle b/gradle/documentation.gradle new file mode 100644 index 00000000..8f62ecf1 --- /dev/null +++ b/gradle/documentation.gradle @@ -0,0 +1,137 @@ +/* + README + ====== + This file is not used at the moment. Should Guide/API-docs generation be moved to its on project? + (Like the grails/gorm-docs project for grails/grails-data-mapping) +*/ + +def DOCUMENTATION_GROUP = 'Documentation' + +def commonGithubOrg = 'grails' +def commonGithubSlug = 'grails-common-build' +def commonBranch = 'master' +def docResourcesDir = "${buildDir}/resources/${commonGithubSlug}-${commonBranch}/src/main/resources" + +tasks.register('remoteDocResources', Copy) { + + group = DOCUMENTATION_GROUP + description = 'Downloads common documentation resources and unzips them to build folder' + + from zipTree("https://github.com/${commonGithubOrg}/${commonGithubSlug}/archive/${commonBranch}.zip") + into docResourcesDir +} + +tasks.register('localDocResources', Copy) { + + group = DOCUMENTATION_GROUP + description = 'Copy local resources to build folder' + + from 'src/main/docs/resources' + into docResourcesDir + onlyIf { project.file('src/main/docs/resources').exists() } +} + +configurations { + documentation +} + +dependencies { + if (configurations.find { it.name == 'documentation' }) { + documentation libs.picocli + documentation libs.jansi + documentation libs.jline2 + documentation libs.javaparser.core + documentation libs.groovy + documentation libs.groovy.ant + documentation libs.groovy.cli.picocli + } +} + +//groovydoc.classpath += configurations.documentation + +tasks.withType(Groovydoc).configureEach { + + group = DOCUMENTATION_GROUP + + docTitle = "${project.title} - ${project.version}" + destinationDir = project.file('build/docs/api') + + // Add the sources of all subprojects (except from the examples directory) + def files = [] + rootProject.subprojects + .findAll { it.projectDir.parentFile.name != 'examples' && it.file('src/main/groovy').exists() } + .each { sp -> source = sp.files('src/main/groovy') } + + classpath += configurations.documentation +} + +tasks.register('copyHtmlIndex', Copy) { + + group = DOCUMENTATION_GROUP + description = 'Copy index.html to build folder' + + from "$buildDir/docs/guide/single.html" + into "$buildDir/docs/guide/" + rename 'single.html', 'index.html' + //overwrite true + +/* + dependsOn tasks.named('prepareDocResources') + source file(project.buildDir, 'docs/guide/single.html') + destinationDirectory file(project.buildDir, 'docs/guide/') + include 'index.html' + rename 'index.html', 'single.html' +*/ +} + +tasks.register('guide', grails.doc.gradle.PublishGuide) { + + group = DOCUMENTATION_GROUP + description = 'Generate Guide' + + targetDir = project.file("${buildDir}/docs") + sourceRepo = "https://github.com/${githubSlug}/edit/${githubBranch}/src/main/docs" + sourceDir = new File(rootProject.projectDir, 'src/main/docs') + propertiesFiles = [ new File(rootProject.projectDir, 'gradle.properties') ] + asciidoc = true + resourcesDir = project.file(docResourcesDir) + properties = [ + 'safe': 'UNSAFE', + 'version': project.version, + 'subtitle': project.projectDesc, + 'api': '../api', + 'sourceDir': rootProject.projectDir.absolutePath, + 'sourcedir': rootProject.projectDir.absolutePath, + 'javaee': 'https://docs.oracle.com/javaee/7/api/', + 'javase': 'https://docs.oracle.com/en/java/javase/11/docs/api/', + 'groovyapi': "https://docs.groovy-lang.org/${libs.versions.groovy}/html/gapi/", + 'grailsapi': "https://docs.grails.org/${libs.versions.grails}/api/", + 'gormapi': "https://gorm.grails.org/${libs.versions.gorm}/api/", + 'springapi': "https://docs.spring.io/spring/docs/${libs.versions.spring}/javadoc-api/" + ] + doLast { + ant.move(file: "${project.buildDir}/docs/guide/single.html", + tofile: "${project.buildDir}/docs/guide/index.html", overwrite: true) + new File(project.buildDir, 'docs/index.html').text = '' + } +} + +/* +tasks.register('docs') { + + group = DOCUMENTATION_GROUP + from ['groovydoc', 'guide'] +} +*/ + +def assembleTask = project.tasks.findByName("assemble") +if(assembleTask == null) { + tasks.register('assemble', Zip) { + + from groovydoc + from guide + + archiveBaseName = "${project.name}-${project.version}" + destinationDirectory = new File(project.buildDir, 'distributions') + } +} \ No newline at end of file diff --git a/gradle/java.gradle b/gradle/java.gradle new file mode 100644 index 00000000..f5363192 --- /dev/null +++ b/gradle/java.gradle @@ -0,0 +1,5 @@ +java { + sourceCompatibility = JavaVersion.VERSION_11 + withSourcesJar() + withJavadocJar() +} \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 00000000..ee781d20 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,46 @@ +[versions] +gpars = '1.2.1' +grails = '6.1.0' +grails-gradle-plugin = '6.1.0' +grails-views = '3.1.0' +groovy = '3.0.19' +groovy-doc = '1.0.1' +gorm = '8.0.2' +jansi = '2.4.1' +javaparser = '3.25.6' +javax-annotation = '1.3.2' +jline2 = '2.14.6' +nexus-publish-gradle-plugin = '1.3.0' +picocli = '4.7.5' +rxjava = '1.3.8' +rxjava2 = '2.2.21' +slf4j = '1.7.36' +spock = '2.1-groovy-3.0' +spring = '5.3.30' + +[libraries] +gpars = { module = 'org.codehaus.gpars:gpars', version.ref = 'gpars' } +gorm = { module = 'org.grails:grails-datastore-gorm', version.ref = 'gorm' } +gorm-test = { module = 'org.grails:grails-datastore-gorm-test', version.ref = 'gorm' } +groovy = { module = 'org.codehaus.groovy:groovy', version.ref = 'groovy' } +#groovy-ant = { module = 'org.codehaus.groovy:groovy-ant', version.ref = 'groovy' } +#groovy-cli-picocli = { module = 'org.codehaus.groovy:groovy-cli-picocli', version.ref = 'groovy' } +jansi = { module = 'org.fusesource.jansi:jansi', version.ref = 'jansi' } +javaparser-core = { module = 'com.github.javaparser:javaparser-core', version.ref = 'javaparser' } +javax-annotation-api = { module = 'javax.annotation:javax.annotation-api', version.ref = 'javax-annotation' } +jline2 = { module = 'jline:jline', version.ref = 'jline2' } +picocli = { module = 'info.picocli:picocli', version.ref = 'picocli' } +rxjava = { module = 'io.reactivex:rxjava', version.ref = 'rxjava' } +rxjava2 = { module = 'io.reactivex.rxjava2:rxjava', version.ref = 'rxjava2' } +slf4j-api = { module = 'org.slf4j:slf4j-api', version.ref = 'slf4j' } +spock-core = { module = 'org.spockframework:spock-core', version.ref = 'spock' } +spring-aop = { module = 'org.springframework:spring-aop', version.ref = 'spring' } +spring-context = { module = 'org.springframework:spring-context', version.ref = 'spring' } +spring-expression = { module = 'org.springframework:spring-expression', version.ref = 'spring' } +spring-tx = { module = 'org.springframework:spring-tx', version.ref = 'spring' } + +#plugins +grails-gradle-plugin = { module = 'org.grails:grails-gradle-plugin', version.ref = 'grails-gradle-plugin' } +grails-views-gradle-plugin = { module = 'org.grails.plugins:views-gradle', version.ref = 'grails-views' } +nexus-publish-gradle-plugin = { module = 'io.github.gradle-nexus:publish-plugin', version.ref = 'nexus-publish-gradle-plugin' } +groovydoc-gradle-plugin = { module = 'io.github.groovylang.groovydoc:groovydoc-gradle-plugin', version.ref = 'groovy-doc' } diff --git a/gradle/publishing.gradle b/gradle/publishing.gradle new file mode 100644 index 00000000..a3046458 --- /dev/null +++ b/gradle/publishing.gradle @@ -0,0 +1,94 @@ +ext['signing.keyId'] = project.findProperty("signing.keyId") ?: System.getenv('SIGNING_KEY') +ext['signing.secretKeyRingFile'] = project.findProperty("signing.secretKeyRingFile") ?: "${System.properties['user.home']}${File.separator}.gnupg${File.separator}secring.gpg" +ext['signing.password'] = project.findProperty("signing.password") ?: System.getenv('SIGNING_PASSPHRASE') + +def isExample = project.projectDir.parentFile.name == 'examples' +def isGrailsPlugin = project.group = 'org.grails.plugins' +def isCoreModule = project.name.endsWith('-core') + +def pomInfo = { + delegate.name project.title + delegate.description project.projectDesc + delegate.url projectUrl + + delegate.licenses { + delegate.license { + delegate.name 'The Apache Software License, Version 2.0' + delegate.url 'http://www.apache.org/licenses/LICENSE-2.0.txt' + delegate.distribution 'repo' + } + } + + delegate.scm { + delegate.url "scm:git@github.com:${githubSlug}.git" + delegate.connection "scm:git@github.com:${githubSlug}.git" + delegate.developerConnection "scm:git@github.com:${githubSlug}.git" + } + + if (developers) { + delegate.developers { + for (dev in developers.split(',')) { + delegate.developer { + delegate.id dev.toLowerCase().replace(' ', '') + delegate.name dev + } + } + } + } +} + +if (!isExample) { // don't publish examples + + publishing { + + if (isSnapshot) { + repositories { + maven { + credentials { + username = System.getenv('ARTIFACTORY_USERNAME') ?: project.findProperty('artifactoryPublishUsername') ?: '' + password = System.getenv('ARTIFACTORY_PASSWORD') ?: project.findProperty('artifactoryPublishPassword') ?: '' + } + url = (project.group == 'org.grails.plugins') ? + uri('https://repo.grails.org/grails/plugins3-snapshots-local') : + uri('https://repo.grails.org/grails/libs-snapshots-local') + + } + } + } + + publications { + + maven(MavenPublication) { + + if (isGrailsPlugin) artifactId = project.name - 'grails-plugin-' + if (isCoreModule) artifactId = project.name - '-core' + + from components.java + + versionMapping { + usage('java-api') { fromResolutionOf('runtimeClasspath') } + usage('java-runtime') { fromResolutionResult() } + } + + pom.withXml { + def xml = asNode() + xml.children().last() + pomInfo + // dependency management shouldn't be included + def n = xml.get('dependencyManagement') + if (n) xml.remove(n) + } + } + } + } + + afterEvaluate { + signing { + required { isReleaseVersion } + sign publishing.publications.maven + } + } + + tasks.withType(Sign).configureEach { + onlyIf { isReleaseVersion } + } +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 29953ea141f55e3b8fc691d31b5ca8816d89fa87..7f93135c49b765f8051ef9d0a6055ff8e46073d8 100644 GIT binary patch delta 55376 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e0b3fb8d..878fe049 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index cccdd3d5..1aa94a42 100755 --- a/gradlew +++ b/gradlew @@ -1,78 +1,127 @@ -#!/usr/bin/env sh +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -81,92 +130,120 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=$((i+1)) + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" fi +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index e95643d6..6689b85b 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,4 +1,20 @@ -@if "%DEBUG%" == "" @echo off +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -9,19 +25,23 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -35,7 +55,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -45,38 +65,26 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/grails-async/README.md b/grails-async-core/README.md similarity index 100% rename from grails-async/README.md rename to grails-async-core/README.md diff --git a/grails-async-core/build.gradle b/grails-async-core/build.gradle new file mode 100644 index 00000000..6838f14c --- /dev/null +++ b/grails-async-core/build.gradle @@ -0,0 +1,26 @@ +plugins { + + id 'groovy' + id 'java-library' + + id 'maven-publish' + id 'signing' + +} + +group = 'org.grails' + +dependencies { + + implementation libs.groovy + + implementation libs.javax.annotation.api + implementation libs.slf4j.api + + testImplementation libs.spock.core + +} + +apply from: file("$rootProject.projectDir/gradle/java.gradle") +apply from: file("$rootProject.projectDir/gradle/publishing.gradle") + diff --git a/grails-async/src/main/groovy/grails/async/DelegateAsync.groovy b/grails-async-core/src/main/groovy/grails/async/DelegateAsync.groovy similarity index 100% rename from grails-async/src/main/groovy/grails/async/DelegateAsync.groovy rename to grails-async-core/src/main/groovy/grails/async/DelegateAsync.groovy diff --git a/grails-async/src/main/groovy/grails/async/Promise.groovy b/grails-async-core/src/main/groovy/grails/async/Promise.groovy similarity index 100% rename from grails-async/src/main/groovy/grails/async/Promise.groovy rename to grails-async-core/src/main/groovy/grails/async/Promise.groovy diff --git a/grails-async/src/main/groovy/grails/async/PromiseFactory.groovy b/grails-async-core/src/main/groovy/grails/async/PromiseFactory.groovy similarity index 100% rename from grails-async/src/main/groovy/grails/async/PromiseFactory.groovy rename to grails-async-core/src/main/groovy/grails/async/PromiseFactory.groovy diff --git a/grails-async/src/main/groovy/grails/async/PromiseList.groovy b/grails-async-core/src/main/groovy/grails/async/PromiseList.groovy similarity index 100% rename from grails-async/src/main/groovy/grails/async/PromiseList.groovy rename to grails-async-core/src/main/groovy/grails/async/PromiseList.groovy diff --git a/grails-async/src/main/groovy/grails/async/PromiseMap.groovy b/grails-async-core/src/main/groovy/grails/async/PromiseMap.groovy similarity index 100% rename from grails-async/src/main/groovy/grails/async/PromiseMap.groovy rename to grails-async-core/src/main/groovy/grails/async/PromiseMap.groovy diff --git a/grails-async/src/main/groovy/grails/async/Promises.groovy b/grails-async-core/src/main/groovy/grails/async/Promises.groovy similarity index 100% rename from grails-async/src/main/groovy/grails/async/Promises.groovy rename to grails-async-core/src/main/groovy/grails/async/Promises.groovy diff --git a/grails-async/src/main/groovy/grails/async/decorator/PromiseDecorator.groovy b/grails-async-core/src/main/groovy/grails/async/decorator/PromiseDecorator.groovy similarity index 100% rename from grails-async/src/main/groovy/grails/async/decorator/PromiseDecorator.groovy rename to grails-async-core/src/main/groovy/grails/async/decorator/PromiseDecorator.groovy diff --git a/grails-async/src/main/groovy/grails/async/decorator/PromiseDecoratorLookupStrategy.groovy b/grails-async-core/src/main/groovy/grails/async/decorator/PromiseDecoratorLookupStrategy.groovy similarity index 100% rename from grails-async/src/main/groovy/grails/async/decorator/PromiseDecoratorLookupStrategy.groovy rename to grails-async-core/src/main/groovy/grails/async/decorator/PromiseDecoratorLookupStrategy.groovy diff --git a/grails-async/src/main/groovy/grails/async/decorator/PromiseDecoratorProvider.groovy b/grails-async-core/src/main/groovy/grails/async/decorator/PromiseDecoratorProvider.groovy similarity index 100% rename from grails-async/src/main/groovy/grails/async/decorator/PromiseDecoratorProvider.groovy rename to grails-async-core/src/main/groovy/grails/async/decorator/PromiseDecoratorProvider.groovy diff --git a/grails-async/src/main/groovy/grails/async/factory/AbstractPromiseFactory.groovy b/grails-async-core/src/main/groovy/grails/async/factory/AbstractPromiseFactory.groovy similarity index 100% rename from grails-async/src/main/groovy/grails/async/factory/AbstractPromiseFactory.groovy rename to grails-async-core/src/main/groovy/grails/async/factory/AbstractPromiseFactory.groovy diff --git a/grails-async/src/main/groovy/org/grails/async/factory/BoundPromise.groovy b/grails-async-core/src/main/groovy/org/grails/async/factory/BoundPromise.groovy similarity index 100% rename from grails-async/src/main/groovy/org/grails/async/factory/BoundPromise.groovy rename to grails-async-core/src/main/groovy/org/grails/async/factory/BoundPromise.groovy diff --git a/grails-async/src/main/groovy/org/grails/async/factory/PromiseFactoryBuilder.groovy b/grails-async-core/src/main/groovy/org/grails/async/factory/PromiseFactoryBuilder.groovy similarity index 100% rename from grails-async/src/main/groovy/org/grails/async/factory/PromiseFactoryBuilder.groovy rename to grails-async-core/src/main/groovy/org/grails/async/factory/PromiseFactoryBuilder.groovy diff --git a/grails-async/src/main/groovy/org/grails/async/factory/SynchronousPromise.groovy b/grails-async-core/src/main/groovy/org/grails/async/factory/SynchronousPromise.groovy similarity index 100% rename from grails-async/src/main/groovy/org/grails/async/factory/SynchronousPromise.groovy rename to grails-async-core/src/main/groovy/org/grails/async/factory/SynchronousPromise.groovy diff --git a/grails-async/src/main/groovy/org/grails/async/factory/SynchronousPromiseFactory.groovy b/grails-async-core/src/main/groovy/org/grails/async/factory/SynchronousPromiseFactory.groovy similarity index 100% rename from grails-async/src/main/groovy/org/grails/async/factory/SynchronousPromiseFactory.groovy rename to grails-async-core/src/main/groovy/org/grails/async/factory/SynchronousPromiseFactory.groovy diff --git a/grails-async/src/main/groovy/org/grails/async/factory/future/CachedThreadPoolPromiseFactory.groovy b/grails-async-core/src/main/groovy/org/grails/async/factory/future/CachedThreadPoolPromiseFactory.groovy similarity index 67% rename from grails-async/src/main/groovy/org/grails/async/factory/future/CachedThreadPoolPromiseFactory.groovy rename to grails-async-core/src/main/groovy/org/grails/async/factory/future/CachedThreadPoolPromiseFactory.groovy index c8ad4dea..d9bffe76 100644 --- a/grails-async/src/main/groovy/org/grails/async/factory/future/CachedThreadPoolPromiseFactory.groovy +++ b/grails-async-core/src/main/groovy/org/grails/async/factory/future/CachedThreadPoolPromiseFactory.groovy @@ -7,13 +7,7 @@ import groovy.transform.CompileStatic import org.grails.async.factory.BoundPromise import javax.annotation.PreDestroy -import java.util.concurrent.Callable -import java.util.concurrent.Executor -import java.util.concurrent.ExecutorService -import java.util.concurrent.RunnableFuture -import java.util.concurrent.SynchronousQueue -import java.util.concurrent.ThreadPoolExecutor -import java.util.concurrent.TimeUnit +import java.util.concurrent.* /** * A promise factory that uses an ExecutorService by default @@ -31,18 +25,18 @@ class CachedThreadPoolPromiseFactory extends AbstractPromiseFactory implements C this.executorService = new ThreadPoolExecutor(0, maxPoolSize, timeout, unit, new SynchronousQueue()) { @Override protected RunnableFuture newTaskFor(Callable callable) { - return new FutureTaskPromise(pf,callable) + new FutureTaskPromise(pf,callable) } @Override protected RunnableFuture newTaskFor(Runnable runnable, T value) { - return new FutureTaskPromise(pf,runnable, value) + new FutureTaskPromise(pf,runnable, value) } } } @Override - def Promise createPromise(Class returnType) { + Promise createPromise(Class returnType) { return new BoundPromise(null) } @@ -52,45 +46,45 @@ class CachedThreadPoolPromiseFactory extends AbstractPromiseFactory implements C } @Override - def Promise createPromise(Closure... closures) { + Promise createPromise(Closure... closures) { if(closures.length == 1) { def callable = closures[0] applyDecorators(callable, null) (Promise)executorService.submit((Callable)callable) } else { - def list = new PromiseList<>() + PromiseList list = new PromiseList<>() for(c in closures) { list.add(c) } - return list + return list as Promise } } @Override - def List waitAll(List> promises) { + List waitAll(List> promises) { return promises.collect() { Promise p -> p.get() } } @Override - def List waitAll(List> promises, long timeout, TimeUnit units) { + List waitAll(List> promises, long timeout, TimeUnit units) { return promises.collect() { Promise p -> p.get(timeout, units) } } @Override - def Promise> onComplete(List> promises, Closure callable) { - return (Promise>)executorService.submit( (Callable) { + Promise> onComplete(List> promises, Closure callable) { + executorService.submit((Callable) { while(promises.every() { Promise p -> !p.isDone() }) { // wait } - def values = promises.collect() { Promise p -> p.get() } + List values = promises.collect { Promise p -> p.get() } callable.call(values) - }) + }) as Promise> } @Override - def Promise> onError(List> promises, Closure callable) { - return (Promise>)executorService.submit((Callable) { + Promise> onError(List> promises, Closure callable) { + executorService.submit((Callable) { while(promises.every() { Promise p -> !p.isDone() }) { // wait } @@ -100,7 +94,7 @@ class CachedThreadPoolPromiseFactory extends AbstractPromiseFactory implements C callable.call(e) return e } - }) + }) as Promise> } @Override @@ -110,5 +104,4 @@ class CachedThreadPoolPromiseFactory extends AbstractPromiseFactory implements C executorService.shutdown() } } - } diff --git a/grails-async/src/main/groovy/org/grails/async/factory/future/ExecutorPromiseFactory.groovy b/grails-async-core/src/main/groovy/org/grails/async/factory/future/ExecutorPromiseFactory.groovy similarity index 100% rename from grails-async/src/main/groovy/org/grails/async/factory/future/ExecutorPromiseFactory.groovy rename to grails-async-core/src/main/groovy/org/grails/async/factory/future/ExecutorPromiseFactory.groovy diff --git a/grails-async/src/main/groovy/org/grails/async/factory/future/FutureTaskChildPromise.groovy b/grails-async-core/src/main/groovy/org/grails/async/factory/future/FutureTaskChildPromise.groovy similarity index 100% rename from grails-async/src/main/groovy/org/grails/async/factory/future/FutureTaskChildPromise.groovy rename to grails-async-core/src/main/groovy/org/grails/async/factory/future/FutureTaskChildPromise.groovy diff --git a/grails-async/src/main/groovy/org/grails/async/factory/future/FutureTaskPromise.groovy b/grails-async-core/src/main/groovy/org/grails/async/factory/future/FutureTaskPromise.groovy similarity index 100% rename from grails-async/src/main/groovy/org/grails/async/factory/future/FutureTaskPromise.groovy rename to grails-async-core/src/main/groovy/org/grails/async/factory/future/FutureTaskPromise.groovy diff --git a/grails-async/src/main/groovy/org/grails/async/transform/internal/DelegateAsyncTransactionalMethodTransformer.java b/grails-async-core/src/main/groovy/org/grails/async/transform/internal/DelegateAsyncTransactionalMethodTransformer.java similarity index 100% rename from grails-async/src/main/groovy/org/grails/async/transform/internal/DelegateAsyncTransactionalMethodTransformer.java rename to grails-async-core/src/main/groovy/org/grails/async/transform/internal/DelegateAsyncTransactionalMethodTransformer.java diff --git a/grails-async/src/main/groovy/org/grails/async/transform/internal/DelegateAsyncTransformation.java b/grails-async-core/src/main/groovy/org/grails/async/transform/internal/DelegateAsyncTransformation.java similarity index 93% rename from grails-async/src/main/groovy/org/grails/async/transform/internal/DelegateAsyncTransformation.java rename to grails-async-core/src/main/groovy/org/grails/async/transform/internal/DelegateAsyncTransformation.java index 773e97b8..c3d3fcb5 100644 --- a/grails-async/src/main/groovy/org/grails/async/transform/internal/DelegateAsyncTransformation.java +++ b/grails-async-core/src/main/groovy/org/grails/async/transform/internal/DelegateAsyncTransformation.java @@ -49,7 +49,6 @@ import org.codehaus.groovy.ast.stmt.BlockStatement; import org.codehaus.groovy.ast.stmt.ExpressionStatement; import org.codehaus.groovy.ast.tools.GenericsUtils; -import org.codehaus.groovy.control.CompilePhase; import org.codehaus.groovy.control.SourceUnit; import org.codehaus.groovy.transform.ASTTransformation; import org.codehaus.groovy.transform.GroovyASTTransformation; @@ -61,7 +60,7 @@ * @author Graeme Rocher * @since 2.3 */ -@GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION) +@GroovyASTTransformation public class DelegateAsyncTransformation implements ASTTransformation { private static final ArgumentListExpression NO_ARGS = new ArgumentListExpression(); private static final String VOID = "void"; @@ -171,15 +170,14 @@ private void applyDelegateAsyncTransform(ClassNode classNode, ClassNode targetAp private static ClassNode alignReturnType(final ClassNode receiver, final ClassNode originalReturnType) { ClassNode copiedReturnType = originalReturnType.getPlainNodeReference(); - ClassNode actualReceiver = receiver; - List redirectTypes = new ArrayList(); - if (actualReceiver.redirect().getGenericsTypes()!=null) { - Collections.addAll(redirectTypes,actualReceiver.redirect().getGenericsTypes()); + List redirectTypes = new ArrayList<>(); + if (receiver.redirect().getGenericsTypes() != null) { + Collections.addAll(redirectTypes, receiver.redirect().getGenericsTypes()); } if (!redirectTypes.isEmpty()) { - GenericsType[] redirectReceiverTypes = redirectTypes.toArray(new GenericsType[redirectTypes.size()]); + GenericsType[] redirectReceiverTypes = redirectTypes.toArray(new GenericsType[0]); - GenericsType[] receiverParameterizedTypes = actualReceiver.getGenericsTypes(); + GenericsType[] receiverParameterizedTypes = receiver.getGenericsTypes(); if (receiverParameterizedTypes==null) { receiverParameterizedTypes = redirectReceiverTypes; } @@ -197,7 +195,7 @@ private static ClassNode alignReturnType(final ClassNode receiver, final ClassNo protected DelegateAsyncTransactionalMethodTransformer lookupAsyncTransactionalMethodTransformer() { try { Class transformerClass = getClass().getClassLoader().loadClass("org.grails.async.transform.internal.DefaultDelegateAsyncTransactionalMethodTransformer"); - return (DelegateAsyncTransactionalMethodTransformer) transformerClass.newInstance(); + return (DelegateAsyncTransactionalMethodTransformer) transformerClass.getDeclaredConstructor().newInstance(); } catch (Throwable e) { // ignore } @@ -205,12 +203,11 @@ protected DelegateAsyncTransactionalMethodTransformer lookupAsyncTransactionalMe } private static boolean isCandidateMethod(MethodNode declaredMethod) { - ClassNode groovyMethods = GROOVY_OBJECT_CLASS_NODE; String methodName = declaredMethod.getName(); return !declaredMethod.isSynthetic() && !methodName.contains("$") && Modifier.isPublic(declaredMethod.getModifiers()) && - !groovyMethods.hasMethod(declaredMethod.getName(), declaredMethod.getParameters()); + !GROOVY_OBJECT_CLASS_NODE.hasMethod(declaredMethod.getName(), declaredMethod.getParameters()); } private static Parameter[] copyParameters(Parameter[] parameterTypes) { @@ -229,7 +226,7 @@ private static Parameter[] copyParameters(Parameter[] parameterTypes) { return newParameterTypes; } - private class NoopDelegateAsyncTransactionalMethodTransformer implements DelegateAsyncTransactionalMethodTransformer { + private static class NoopDelegateAsyncTransactionalMethodTransformer implements DelegateAsyncTransactionalMethodTransformer { public void transformTransactionalMethod(ClassNode classNode,ClassNode delegateClassNode, MethodNode methodNode, ListExpression promiseDecoratorLookupArguments) { // noop } diff --git a/grails-async/src/main/groovy/org/grails/async/transform/internal/DelegateAsyncUtils.groovy b/grails-async-core/src/main/groovy/org/grails/async/transform/internal/DelegateAsyncUtils.groovy similarity index 100% rename from grails-async/src/main/groovy/org/grails/async/transform/internal/DelegateAsyncUtils.groovy rename to grails-async-core/src/main/groovy/org/grails/async/transform/internal/DelegateAsyncUtils.groovy diff --git a/grails-async/src/test/groovy/grails/async/DelegateAsyncSpec.groovy b/grails-async-core/src/test/groovy/grails/async/DelegateAsyncSpec.groovy similarity index 100% rename from grails-async/src/test/groovy/grails/async/DelegateAsyncSpec.groovy rename to grails-async-core/src/test/groovy/grails/async/DelegateAsyncSpec.groovy diff --git a/grails-async/src/test/groovy/grails/async/FutureTaskPromiseFactorySpec.groovy b/grails-async-core/src/test/groovy/grails/async/FutureTaskPromiseFactorySpec.groovy similarity index 100% rename from grails-async/src/test/groovy/grails/async/FutureTaskPromiseFactorySpec.groovy rename to grails-async-core/src/test/groovy/grails/async/FutureTaskPromiseFactorySpec.groovy diff --git a/grails-async/src/test/groovy/grails/async/PromiseListSpec.groovy b/grails-async-core/src/test/groovy/grails/async/PromiseListSpec.groovy similarity index 100% rename from grails-async/src/test/groovy/grails/async/PromiseListSpec.groovy rename to grails-async-core/src/test/groovy/grails/async/PromiseListSpec.groovy diff --git a/grails-async/src/test/groovy/grails/async/PromiseMapSpec.groovy b/grails-async-core/src/test/groovy/grails/async/PromiseMapSpec.groovy similarity index 100% rename from grails-async/src/test/groovy/grails/async/PromiseMapSpec.groovy rename to grails-async-core/src/test/groovy/grails/async/PromiseMapSpec.groovy diff --git a/grails-async/src/test/groovy/grails/async/PromiseSpec.groovy b/grails-async-core/src/test/groovy/grails/async/PromiseSpec.groovy similarity index 100% rename from grails-async/src/test/groovy/grails/async/PromiseSpec.groovy rename to grails-async-core/src/test/groovy/grails/async/PromiseSpec.groovy diff --git a/grails-async/src/test/groovy/grails/async/SynchronousPromiseFactorySpec.groovy b/grails-async-core/src/test/groovy/grails/async/SynchronousPromiseFactorySpec.groovy similarity index 100% rename from grails-async/src/test/groovy/grails/async/SynchronousPromiseFactorySpec.groovy rename to grails-async-core/src/test/groovy/grails/async/SynchronousPromiseFactorySpec.groovy diff --git a/grails-async-gpars/build.gradle b/grails-async-gpars/build.gradle index 0c73ce24..d728b731 100644 --- a/grails-async-gpars/build.gradle +++ b/grails-async-gpars/build.gradle @@ -1,10 +1,34 @@ +plugins { + + id 'groovy' + id 'java-library' + + id 'maven-publish' + id 'signing' + +} + +group = 'org.grails' + dependencies { - compile project(":grails-async") - compile 'org.codehaus.gpars:gpars:1.2.1', { - exclude group:'org.multiverse', module:'multiverse-core' - exclude group:'org.codehaus.groovy', module: 'groovy-all' + + implementation project(':grails-async-core') + + implementation libs.groovy + + implementation libs.gpars, { + exclude group: 'org.multiverse', module: 'multiverse-core' + exclude group: 'org.codehaus.groovy', module: 'groovy-all' } + implementation libs.slf4j.api + + testImplementation libs.spock.core + } +apply from: file("$rootProject.projectDir/gradle/java.gradle") +apply from: file("$rootProject.projectDir/gradle/publishing.gradle") + + diff --git a/grails-async-rxjava/build.gradle b/grails-async-rxjava/build.gradle index a3d5094a..20fa8db1 100644 --- a/grails-async-rxjava/build.gradle +++ b/grails-async-rxjava/build.gradle @@ -1,7 +1,29 @@ +plugins { + + id 'groovy' + id 'java-library' + + id 'maven-publish' + id 'signing' + +} + +group = 'org.grails' + dependencies { - compile project(":grails-async") - compile "io.reactivex:rxjava:$rxJavaVersion" + + implementation project(':grails-async-core') + + implementation libs.groovy + + implementation libs.rxjava + implementation libs.slf4j.api + + testImplementation libs.spock.core + } +apply from: file("$rootProject.projectDir/gradle/java.gradle") +apply from: file("$rootProject.projectDir/gradle/publishing.gradle") diff --git a/grails-async-rxjava/src/main/groovy/org/grails/async/factory/rxjava/RxPromiseFactory.groovy b/grails-async-rxjava/src/main/groovy/org/grails/async/factory/rxjava/RxPromiseFactory.groovy index 0cea266c..82d08401 100644 --- a/grails-async-rxjava/src/main/groovy/org/grails/async/factory/rxjava/RxPromiseFactory.groovy +++ b/grails-async-rxjava/src/main/groovy/org/grails/async/factory/rxjava/RxPromiseFactory.groovy @@ -20,17 +20,17 @@ import java.util.concurrent.TimeUnit @CompileStatic class RxPromiseFactory extends AbstractPromiseFactory { @Override - def Promise createPromise(Class returnType) { - return new RxPromise(this, Single.just(null)) + Promise createPromise(Class returnType) { + new RxPromise(this, Single.just(null)) } @Override Promise createPromise() { - return new RxPromise(this, Single.just(null)) + new RxPromise(this, Single.just(null)) } @Override - def Promise createPromise(Closure[] closures) { + Promise createPromise(Closure[] closures) { if(closures.length == 1) { return new RxPromise(this,closures[0], Schedulers.io()) } @@ -44,35 +44,35 @@ class RxPromiseFactory extends AbstractPromiseFactory { } @Override - def List waitAll(List> promises) { - return promises.collect() { Promise p -> p.get( )} + List waitAll(List> promises) { + promises.collect() { Promise p -> p.get() } } @Override - def List waitAll(List> promises, long timeout, TimeUnit units) { - return promises.collect() { Promise p -> p.get( timeout, units ) } + List waitAll(List> promises, long timeout, TimeUnit units) { + promises.collect() { Promise p -> p.get(timeout, units) } } @Override - def Promise> onComplete(List> promises, Closure callable) { + Promise> onComplete(List> promises, Closure callable) { new RxPromise<>(this, Observable.concat( promises.collect() { Promise p -> if(p instanceof BoundPromise) { return Observable.just(((BoundPromise)p).value) } else { - return ((RxPromise)p).subject + return ((RxPromise)p).subject as Observable } } ).toList()) - .onComplete(callable) + .onComplete(callable) as Promise> } @Override - def Promise> onError(List> promises, Closure callable) { + Promise> onError(List> promises, Closure callable) { new RxPromise<>(this, Observable.concat( - promises.collect() { Promise p-> ((RxPromise)p).subject } + promises.collect() { Promise p -> ((RxPromise)p).subject as Observable } ).toList()) - .onError(callable) + .onError(callable) as Promise> } } diff --git a/grails-async-rxjava2/build.gradle b/grails-async-rxjava2/build.gradle index 64922fe5..90864c19 100644 --- a/grails-async-rxjava2/build.gradle +++ b/grails-async-rxjava2/build.gradle @@ -1,4 +1,26 @@ +plugins { + + id 'groovy' + id 'java-library' + id 'maven-publish' + id 'signing' + +} + +group = 'org.grails' + dependencies { - compile project(":grails-async") - compile "io.reactivex.rxjava2:rxjava:$rxJava2Version" -} \ No newline at end of file + + implementation project(':grails-async-core') + + implementation libs.groovy + + implementation libs.rxjava2 + implementation libs.slf4j.api + + testImplementation libs.spock.core + +} + +apply from: file("$rootProject.projectDir/gradle/java.gradle") +apply from: file("$rootProject.projectDir/gradle/publishing.gradle") diff --git a/grails-async-rxjava2/src/main/groovy/org/grails/async/factory/rxjava2/RxPromiseFactory.groovy b/grails-async-rxjava2/src/main/groovy/org/grails/async/factory/rxjava2/RxPromiseFactory.groovy index 99598f8c..09443917 100644 --- a/grails-async-rxjava2/src/main/groovy/org/grails/async/factory/rxjava2/RxPromiseFactory.groovy +++ b/grails-async-rxjava2/src/main/groovy/org/grails/async/factory/rxjava2/RxPromiseFactory.groovy @@ -20,17 +20,17 @@ import java.util.concurrent.TimeUnit @CompileStatic class RxPromiseFactory extends AbstractPromiseFactory { @Override - def Promise createPromise(Class returnType) { - return new RxPromise(this, Single.just(null)) + Promise createPromise(Class returnType) { + new RxPromise(this, Single.just(null)) } @Override Promise createPromise() { - return new RxPromise(this, Single.just(null)) + new RxPromise(this, Single.just(null)) } @Override - def Promise createPromise(Closure[] closures) { + Promise createPromise(Closure[] closures) { if(closures.length == 1) { return new RxPromise(this, closures[0], Schedulers.io()) } @@ -44,35 +44,35 @@ class RxPromiseFactory extends AbstractPromiseFactory { } @Override - def List waitAll(List> promises) { - return promises.collect() { Promise p -> p.get( )} + List waitAll(List> promises) { + return promises.collect() { Promise p -> p.get() } } @Override - def List waitAll(List> promises, long timeout, TimeUnit units) { - return promises.collect() { Promise p -> p.get( timeout, units ) } + List waitAll(List> promises, long timeout, TimeUnit units) { + promises.collect() { Promise p -> p.get(timeout, units) } } @Override - def Promise> onComplete(List> promises, Closure callable) { + Promise> onComplete(List> promises, Closure callable) { new RxPromise<>(this, Observable.concat( promises.collect() { Promise p -> if(p instanceof BoundPromise) { - return Observable.just(((BoundPromise)p).value) + return Observable.just(((BoundPromise)p).value) as Observable } else { - return ((RxPromise)p).toObservable() + return ((RxPromise)p).toObservable() as Observable } } ).toList()) - .onComplete(callable) + .onComplete(callable) as Promise> } @Override - def Promise> onError(List> promises, Closure callable) { + Promise> onError(List> promises, Closure callable) { new RxPromise<>(this, Observable.concat( - promises.collect() { Promise p-> ((RxPromise)p).toObservable() } + promises.collect() { Promise p -> ((RxPromise)p).toObservable() as Observable } ).toList()) - .onError(callable) + .onError(callable) as Promise> } } diff --git a/grails-events-compat/build.gradle b/grails-events-compat/build.gradle index 9cdd4caa..690dd6e4 100644 --- a/grails-events-compat/build.gradle +++ b/grails-events-compat/build.gradle @@ -1,3 +1,26 @@ +plugins { + + id 'groovy' + id 'java-library' + + id 'maven-publish' + id 'signing' + +} + +group = 'org.grails' + dependencies { - compile project(":grails-events"), project(":grails-events-transform") -} \ No newline at end of file + + implementation project(':grails-events-core') + implementation project(':grails-events-transform') + + implementation libs.groovy + + implementation libs.slf4j.api + implementation libs.spring.tx + +} + +apply from: file("$rootProject.projectDir/gradle/java.gradle") +apply from: file("$rootProject.projectDir/gradle/publishing.gradle") diff --git a/grails-events-compat/src/main/groovy/grails/events/Events.groovy b/grails-events-compat/src/main/groovy/grails/events/Events.groovy index 46479fa0..b519b875 100644 --- a/grails-events-compat/src/main/groovy/grails/events/Events.groovy +++ b/grails-events-compat/src/main/groovy/grails/events/Events.groovy @@ -54,7 +54,7 @@ trait Events { key = key.toString() } on(key) { - consumer.accept(it) + consumer.accept(it as E) } } diff --git a/grails-events/README.md b/grails-events-core/README.md similarity index 100% rename from grails-events/README.md rename to grails-events-core/README.md diff --git a/grails-events-core/build.gradle b/grails-events-core/build.gradle new file mode 100644 index 00000000..c1f0c940 --- /dev/null +++ b/grails-events-core/build.gradle @@ -0,0 +1,26 @@ +plugins { + + id 'groovy' + id 'java-library' + + id 'maven-publish' + id 'signing' + +} + +group = 'org.grails' + +dependencies { + + implementation libs.groovy + + implementation libs.slf4j.api + implementation libs.spring.context + implementation libs.spring.tx + + testImplementation libs.spock.core + +} + +apply from: file("$rootProject.projectDir/gradle/java.gradle") +apply from: file("$rootProject.projectDir/gradle/publishing.gradle") diff --git a/grails-events/src/main/groovy/grails/events/Event.groovy b/grails-events-core/src/main/groovy/grails/events/Event.groovy similarity index 95% rename from grails-events/src/main/groovy/grails/events/Event.groovy rename to grails-events-core/src/main/groovy/grails/events/Event.groovy index f2fb2d97..df23a54c 100644 --- a/grails-events/src/main/groovy/grails/events/Event.groovy +++ b/grails-events-core/src/main/groovy/grails/events/Event.groovy @@ -28,7 +28,7 @@ class Event extends EventObject { final Map parameters Event(String id, T data) { - this(id, Collections.emptyMap(), data) + this(id, Collections.emptyMap() as Map, data) } Event(String id, Map parameters, T data) { diff --git a/grails-events/src/main/groovy/grails/events/EventPublisher.groovy b/grails-events-core/src/main/groovy/grails/events/EventPublisher.groovy similarity index 100% rename from grails-events/src/main/groovy/grails/events/EventPublisher.groovy rename to grails-events-core/src/main/groovy/grails/events/EventPublisher.groovy diff --git a/grails-events/src/main/groovy/grails/events/bus/EventBus.groovy b/grails-events-core/src/main/groovy/grails/events/bus/EventBus.groovy similarity index 100% rename from grails-events/src/main/groovy/grails/events/bus/EventBus.groovy rename to grails-events-core/src/main/groovy/grails/events/bus/EventBus.groovy diff --git a/grails-events/src/main/groovy/grails/events/bus/EventBusAware.groovy b/grails-events-core/src/main/groovy/grails/events/bus/EventBusAware.groovy similarity index 100% rename from grails-events/src/main/groovy/grails/events/bus/EventBusAware.groovy rename to grails-events-core/src/main/groovy/grails/events/bus/EventBusAware.groovy diff --git a/grails-events/src/main/groovy/grails/events/bus/EventBusBuilder.groovy b/grails-events-core/src/main/groovy/grails/events/bus/EventBusBuilder.groovy similarity index 100% rename from grails-events/src/main/groovy/grails/events/bus/EventBusBuilder.groovy rename to grails-events-core/src/main/groovy/grails/events/bus/EventBusBuilder.groovy diff --git a/grails-events/src/main/groovy/grails/events/emitter/EventEmitter.groovy b/grails-events-core/src/main/groovy/grails/events/emitter/EventEmitter.groovy similarity index 100% rename from grails-events/src/main/groovy/grails/events/emitter/EventEmitter.groovy rename to grails-events-core/src/main/groovy/grails/events/emitter/EventEmitter.groovy diff --git a/grails-events/src/main/groovy/grails/events/subscriber/EventSubscriber.groovy b/grails-events-core/src/main/groovy/grails/events/subscriber/EventSubscriber.groovy similarity index 100% rename from grails-events/src/main/groovy/grails/events/subscriber/EventSubscriber.groovy rename to grails-events-core/src/main/groovy/grails/events/subscriber/EventSubscriber.groovy diff --git a/grails-events/src/main/groovy/grails/events/subscriber/MethodEventSubscriber.groovy b/grails-events-core/src/main/groovy/grails/events/subscriber/MethodEventSubscriber.groovy similarity index 100% rename from grails-events/src/main/groovy/grails/events/subscriber/MethodEventSubscriber.groovy rename to grails-events-core/src/main/groovy/grails/events/subscriber/MethodEventSubscriber.groovy diff --git a/grails-events/src/main/groovy/grails/events/subscriber/MethodSubscriber.groovy b/grails-events-core/src/main/groovy/grails/events/subscriber/MethodSubscriber.groovy similarity index 100% rename from grails-events/src/main/groovy/grails/events/subscriber/MethodSubscriber.groovy rename to grails-events-core/src/main/groovy/grails/events/subscriber/MethodSubscriber.groovy diff --git a/grails-events/src/main/groovy/grails/events/subscriber/Subjects.groovy b/grails-events-core/src/main/groovy/grails/events/subscriber/Subjects.groovy similarity index 100% rename from grails-events/src/main/groovy/grails/events/subscriber/Subjects.groovy rename to grails-events-core/src/main/groovy/grails/events/subscriber/Subjects.groovy diff --git a/grails-events/src/main/groovy/grails/events/subscriber/Subscriber.groovy b/grails-events-core/src/main/groovy/grails/events/subscriber/Subscriber.groovy similarity index 100% rename from grails-events/src/main/groovy/grails/events/subscriber/Subscriber.groovy rename to grails-events-core/src/main/groovy/grails/events/subscriber/Subscriber.groovy diff --git a/grails-events/src/main/groovy/grails/events/subscriber/Subscription.groovy b/grails-events-core/src/main/groovy/grails/events/subscriber/Subscription.groovy similarity index 100% rename from grails-events/src/main/groovy/grails/events/subscriber/Subscription.groovy rename to grails-events-core/src/main/groovy/grails/events/subscriber/Subscription.groovy diff --git a/grails-events/src/main/groovy/grails/events/trigger/EventTrigger.groovy b/grails-events-core/src/main/groovy/grails/events/trigger/EventTrigger.groovy similarity index 100% rename from grails-events/src/main/groovy/grails/events/trigger/EventTrigger.groovy rename to grails-events-core/src/main/groovy/grails/events/trigger/EventTrigger.groovy diff --git a/grails-events/src/main/groovy/org/grails/events/ClosureEventTrigger.groovy b/grails-events-core/src/main/groovy/org/grails/events/ClosureEventTrigger.groovy similarity index 100% rename from grails-events/src/main/groovy/org/grails/events/ClosureEventTrigger.groovy rename to grails-events-core/src/main/groovy/org/grails/events/ClosureEventTrigger.groovy diff --git a/grails-events/src/main/groovy/org/grails/events/EventSubscriberTrigger.groovy b/grails-events-core/src/main/groovy/org/grails/events/EventSubscriberTrigger.groovy similarity index 100% rename from grails-events/src/main/groovy/org/grails/events/EventSubscriberTrigger.groovy rename to grails-events-core/src/main/groovy/org/grails/events/EventSubscriberTrigger.groovy diff --git a/grails-events/src/main/groovy/org/grails/events/bus/AbstractEventBus.groovy b/grails-events-core/src/main/groovy/org/grails/events/bus/AbstractEventBus.groovy similarity index 88% rename from grails-events/src/main/groovy/org/grails/events/bus/AbstractEventBus.groovy rename to grails-events-core/src/main/groovy/org/grails/events/bus/AbstractEventBus.groovy index 5a0b78e3..95138f1f 100644 --- a/grails-events/src/main/groovy/org/grails/events/bus/AbstractEventBus.groovy +++ b/grails-events-core/src/main/groovy/org/grails/events/bus/AbstractEventBus.groovy @@ -7,10 +7,10 @@ import grails.events.subscriber.Subjects import grails.events.subscriber.Subscriber import grails.events.subscriber.Subscription import groovy.transform.CompileStatic -import org.grails.events.registry.EventSubscriberSubscription import org.grails.events.registry.ClosureSubscription +import org.grails.events.registry.EventSubscriberSubscription import org.springframework.transaction.event.TransactionPhase -import org.springframework.transaction.support.TransactionSynchronizationAdapter +import org.springframework.transaction.support.TransactionSynchronization import org.springframework.transaction.support.TransactionSynchronizationManager import java.util.concurrent.Callable @@ -25,53 +25,54 @@ import java.util.concurrent.ConcurrentLinkedQueue */ @CompileStatic abstract class AbstractEventBus implements EventBus { + protected final Map> subscriptions = new ConcurrentHashMap>().withDefault { - new ConcurrentLinkedQueue() + new ConcurrentLinkedQueue() as Collection } @Override boolean isActive() { - return true + true } @Override final EventEmitter notify(CharSequence eventId, Object... data) { - return notify(new Event(eventId.toString(), data.length == 1 ? data[0] : data)) + notify(new Event(eventId.toString(), data.length == 1 ? data[0] : data)) } @Override final EventEmitter publish(CharSequence eventId, Object... data) { - return notify(eventId, data) + notify(eventId, data) } @Override final EventEmitter publish(Event event) { - return notify(event) + notify(event) } @Override final EventEmitter sendAndReceive(CharSequence eventId, Object data, Closure reply) { - return sendAndReceive(new Event(eventId.toString(), data), reply) + sendAndReceive(new Event(eventId.toString(), data), reply) } @Override final Subscription subscribe(CharSequence event, Closure subscriber) { - return on(event, subscriber) + on(event, subscriber) } @Override EventEmitter publish(Event event, TransactionPhase transactionPhase) { - return notify(event, transactionPhase) + notify(event, transactionPhase) } @Override Subscription on(CharSequence event, Closure subscriber) { - return buildClosureSubscription(event, subscriber) + buildClosureSubscription(event, subscriber) } @Override Subscription subscribe(CharSequence event, Subscriber subscriber) { - return buildSubscriberSubscription(event, subscriber) + buildSubscriberSubscription(event, subscriber) } @Override @@ -83,7 +84,7 @@ abstract class AbstractEventBus implements EventBus { } } subs.clear() - return this + this } EventEmitter notify(Event event) { @@ -100,7 +101,7 @@ abstract class AbstractEventBus implements EventBus { } } } - return this + this } @Override @@ -115,7 +116,7 @@ abstract class AbstractEventBus implements EventBus { .run() } } - return this + this } @Override @@ -144,8 +145,10 @@ abstract class AbstractEventBus implements EventBus { * @return */ protected final NotificationTrigger buildNotificationTrigger(Event event, Collection eventSubscriptions, Closure reply = null) { + final Callable callable = buildNotificationCallable(event, eventSubscriptions, reply) - return new AbstractEventBus.NotificationTrigger(event, eventSubscriptions, reply) { + + new NotificationTrigger(event, eventSubscriptions, reply) { @Override void run() { callable.call() @@ -164,6 +167,7 @@ abstract class AbstractEventBus implements EventBus { protected abstract Callable buildNotificationCallable(Event event, Collection eventSubscriptions, Closure reply = null) protected static abstract class NotificationTrigger implements Runnable { + final Event event final Collection subscriptions final Closure reply @@ -175,7 +179,7 @@ abstract class AbstractEventBus implements EventBus { } } - protected static class EventTriggerTransactionSynchronization extends TransactionSynchronizationAdapter{ + protected static class EventTriggerTransactionSynchronization implements TransactionSynchronization { final NotificationTrigger notificationTrigger final TransactionPhase transactionPhase @@ -212,10 +216,10 @@ abstract class AbstractEventBus implements EventBus { protected EventSubscriberSubscription buildSubscriberSubscription(CharSequence eventId, Subscriber subscriber) { - return new EventSubscriberSubscription(eventId, subscriptions, subscriber) + new EventSubscriberSubscription(eventId, subscriptions, subscriber) } protected ClosureSubscription buildClosureSubscription(CharSequence eventId, Closure subscriber) { - return new ClosureSubscription(eventId, subscriptions, subscriber) + new ClosureSubscription(eventId, subscriptions, subscriber) } } diff --git a/grails-events/src/main/groovy/org/grails/events/bus/ExecutorEventBus.groovy b/grails-events-core/src/main/groovy/org/grails/events/bus/ExecutorEventBus.groovy similarity index 100% rename from grails-events/src/main/groovy/org/grails/events/bus/ExecutorEventBus.groovy rename to grails-events-core/src/main/groovy/org/grails/events/bus/ExecutorEventBus.groovy diff --git a/grails-events/src/main/groovy/org/grails/events/bus/SynchronousEventBus.groovy b/grails-events-core/src/main/groovy/org/grails/events/bus/SynchronousEventBus.groovy similarity index 100% rename from grails-events/src/main/groovy/org/grails/events/bus/SynchronousEventBus.groovy rename to grails-events-core/src/main/groovy/org/grails/events/bus/SynchronousEventBus.groovy diff --git a/grails-events/src/main/groovy/org/grails/events/bus/spring/EventBusFactoryBean.groovy b/grails-events-core/src/main/groovy/org/grails/events/bus/spring/EventBusFactoryBean.groovy similarity index 100% rename from grails-events/src/main/groovy/org/grails/events/bus/spring/EventBusFactoryBean.groovy rename to grails-events-core/src/main/groovy/org/grails/events/bus/spring/EventBusFactoryBean.groovy diff --git a/grails-events/src/main/groovy/org/grails/events/registry/AbstractSubscription.groovy b/grails-events-core/src/main/groovy/org/grails/events/registry/AbstractSubscription.groovy similarity index 100% rename from grails-events/src/main/groovy/org/grails/events/registry/AbstractSubscription.groovy rename to grails-events-core/src/main/groovy/org/grails/events/registry/AbstractSubscription.groovy diff --git a/grails-events/src/main/groovy/org/grails/events/registry/ClosureSubscription.groovy b/grails-events-core/src/main/groovy/org/grails/events/registry/ClosureSubscription.groovy similarity index 100% rename from grails-events/src/main/groovy/org/grails/events/registry/ClosureSubscription.groovy rename to grails-events-core/src/main/groovy/org/grails/events/registry/ClosureSubscription.groovy diff --git a/grails-events/src/main/groovy/org/grails/events/registry/EventSubscriberSubscription.groovy b/grails-events-core/src/main/groovy/org/grails/events/registry/EventSubscriberSubscription.groovy similarity index 87% rename from grails-events/src/main/groovy/org/grails/events/registry/EventSubscriberSubscription.groovy rename to grails-events-core/src/main/groovy/org/grails/events/registry/EventSubscriberSubscription.groovy index 3128a379..36b14335 100644 --- a/grails-events/src/main/groovy/org/grails/events/registry/EventSubscriberSubscription.groovy +++ b/grails-events-core/src/main/groovy/org/grails/events/registry/EventSubscriberSubscription.groovy @@ -15,6 +15,7 @@ import org.grails.events.EventSubscriberTrigger */ @CompileStatic class EventSubscriberSubscription extends AbstractSubscription { + final Subscriber subscriber EventSubscriberSubscription(CharSequence eventKey, Map> subscriptions, Subscriber subscriber) { @@ -24,11 +25,11 @@ class EventSubscriberSubscription extends AbstractSubscription { @Override EventTrigger buildTrigger(Event event) { - return new EventSubscriberTrigger(event, subscriber) + new EventSubscriberTrigger(event, subscriber) } @Override EventTrigger buildTrigger(Event event, Closure reply) { - return new EventSubscriberTrigger(event, subscriber) + new EventSubscriberTrigger(event, subscriber) } } diff --git a/grails-events/src/test/groovy/org/grails/events/SynchronousEventBusSpec.groovy b/grails-events-core/src/test/groovy/org/grails/events/SynchronousEventBusSpec.groovy similarity index 100% rename from grails-events/src/test/groovy/org/grails/events/SynchronousEventBusSpec.groovy rename to grails-events-core/src/test/groovy/org/grails/events/SynchronousEventBusSpec.groovy diff --git a/grails-events/src/test/groovy/org/grails/events/TaskExecuterEventBusSpec.groovy b/grails-events-core/src/test/groovy/org/grails/events/TaskExecuterEventBusSpec.groovy similarity index 100% rename from grails-events/src/test/groovy/org/grails/events/TaskExecuterEventBusSpec.groovy rename to grails-events-core/src/test/groovy/org/grails/events/TaskExecuterEventBusSpec.groovy diff --git a/grails-events/src/test/groovy/org/grails/events/TransactionAwareEventSpec.groovy b/grails-events-core/src/test/groovy/org/grails/events/TransactionAwareEventSpec.groovy similarity index 100% rename from grails-events/src/test/groovy/org/grails/events/TransactionAwareEventSpec.groovy rename to grails-events-core/src/test/groovy/org/grails/events/TransactionAwareEventSpec.groovy diff --git a/grails-events/src/test/groovy/org/grails/events/subscriber/MethodEventSubscriberSpec.groovy b/grails-events-core/src/test/groovy/org/grails/events/subscriber/MethodEventSubscriberSpec.groovy similarity index 100% rename from grails-events/src/test/groovy/org/grails/events/subscriber/MethodEventSubscriberSpec.groovy rename to grails-events-core/src/test/groovy/org/grails/events/subscriber/MethodEventSubscriberSpec.groovy diff --git a/grails-events-gpars/build.gradle b/grails-events-gpars/build.gradle index 4c323e3f..010528e1 100644 --- a/grails-events-gpars/build.gradle +++ b/grails-events-gpars/build.gradle @@ -1,7 +1,31 @@ +plugins { + + id 'groovy' + id 'java-library' + + id 'maven-publish' + id 'signing' + +} + +group = 'org.grails' + dependencies { - compile project(":grails-events") - compile 'org.codehaus.gpars:gpars:1.2.1', { - exclude group:'org.multiverse', module:'multiverse-core' - exclude group:'org.codehaus.groovy', module: 'groovy-all' + + implementation project(':grails-events-core') + + implementation libs.groovy + + implementation libs.gpars, { + exclude group: 'org.multiverse', module: 'multiverse-core' + exclude group: 'org.codehaus.groovy', module: 'groovy-all' } -} \ No newline at end of file + implementation libs.slf4j.api + implementation libs.spring.tx + + testImplementation libs.spock.core + +} + +apply from: file("$rootProject.projectDir/gradle/java.gradle") +apply from: file("$rootProject.projectDir/gradle/publishing.gradle") diff --git a/grails-events-rxjava/build.gradle b/grails-events-rxjava/build.gradle index f2c0f096..8f903ac1 100644 --- a/grails-events-rxjava/build.gradle +++ b/grails-events-rxjava/build.gradle @@ -1,9 +1,32 @@ +plugins { + + id 'groovy' + id 'java-library' + + id 'maven-publish' + id 'signing' + +} + +group = 'org.grails' + dependencies { - compile project(":grails-events") - compile "io.reactivex:rxjava:$rxJavaVersion" - testCompile project(":grails-events-transform") - testCompile "org.grails:grails-datastore-gorm-test:$gormVersion", { - exclude group:'org.grails', module:'grails-datastore-gorm-validation' + implementation project(':grails-events-core') + testImplementation project(':grails-events-transform') + + implementation libs.groovy + + implementation libs.rxjava + implementation libs.slf4j.api + implementation libs.spring.tx + + testImplementation libs.gorm.test, { + exclude group:'org.grails', module: 'grails-datastore-gorm-validation' } -} \ No newline at end of file + testImplementation libs.spock.core + +} + +apply from: file("$rootProject.projectDir/gradle/java.gradle") +apply from: file("$rootProject.projectDir/gradle/publishing.gradle") diff --git a/grails-events-rxjava/src/main/groovy/org/grails/events/rxjava/RxEventBus.groovy b/grails-events-rxjava/src/main/groovy/org/grails/events/rxjava/RxEventBus.groovy index 8c6d3868..8921ab33 100644 --- a/grails-events-rxjava/src/main/groovy/org/grails/events/rxjava/RxEventBus.groovy +++ b/grails-events-rxjava/src/main/groovy/org/grails/events/rxjava/RxEventBus.groovy @@ -28,6 +28,7 @@ import java.util.concurrent.ConcurrentHashMap @CompileStatic @Slf4j class RxEventBus extends AbstractEventBus { + protected final Map subjects = new ConcurrentHashMap().withDefault { PublishSubject.create() } @@ -40,17 +41,20 @@ class RxEventBus extends AbstractEventBus { @Override protected EventSubscriberSubscription buildSubscriberSubscription(CharSequence eventId, Subscriber subscriber) { + String eventKey = eventId.toString() Subject subject = subjects.get(eventKey) - return new RxEventSubscriberSubscription(eventId, subscriptions, subscriber, subject, scheduler) + new RxEventSubscriberSubscription(eventId, subscriptions, subscriber, subject, scheduler) } @Override protected ClosureSubscription buildClosureSubscription(CharSequence eventId, Closure subscriber) { + String eventKey = eventId.toString() Subject subject = subjects.get(eventKey) - return new RxClosureSubscription(eventId, subscriptions, subscriber, subject, scheduler) + + new RxClosureSubscription(eventId, subscriptions, subscriber, subject, scheduler) } @Override @@ -69,13 +73,12 @@ class RxEventBus extends AbstractEventBus { } private static class RxClosureSubscription extends ClosureSubscription { + final rx.Subscription subscription RxClosureSubscription(CharSequence eventId, Map> subscriptions, Closure subscriber, Subject subject, Scheduler scheduler) { super(eventId, subscriptions, subscriber) - this.subscription = subject.observeOn(scheduler) - .subscribe( { eventObject -> - + this.subscription = subject.observeOn(scheduler).subscribe( { eventObject -> Event event Closure reply = null if(eventObject instanceof EventWithReply) { @@ -84,10 +87,10 @@ class RxEventBus extends AbstractEventBus { reply = eventWithReply.reply } else { - event = (Event)eventObject + event = (Event) eventObject } - EventTrigger trigger = buildTrigger(event, reply) + EventTrigger trigger = buildTrigger(event as Event, reply) trigger.proceed() } as Action1, { Throwable t -> log.error("Error occurred triggering event listener for event [$eventId]: ${t.message}", t) @@ -96,43 +99,43 @@ class RxEventBus extends AbstractEventBus { @Override Subscription cancel() { - if(!subscription.isUnsubscribed()) { + if(!subscription.unsubscribed) { subscription.unsubscribe() } - return super.cancel() + super.cancel() } @Override boolean isCancelled() { - return subscription.isUnsubscribed() + subscription.unsubscribed } } private static class RxEventSubscriberSubscription extends EventSubscriberSubscription { + final rx.Subscription subscription RxEventSubscriberSubscription(CharSequence eventId, Map> subscriptions, Subscriber subscriber, Subject subject, Scheduler scheduler) { super(eventId, subscriptions, subscriber) - this.subscription = subject.observeOn(scheduler) - .subscribe( { Event event -> - EventTrigger trigger = buildTrigger(event) + this.subscription = subject.observeOn(scheduler).subscribe({ event -> + EventTrigger trigger = buildTrigger(event as Event) trigger.proceed() - } as Action1, { Throwable t -> + } as Action1, { Throwable t -> log.error("Error occurred triggering event listener for event [$eventId]: ${t.message}", t) } as Action1) } @Override Subscription cancel() { - if(!subscription.isUnsubscribed()) { + if(!subscription.unsubscribed) { subscription.unsubscribe() } - return super.cancel() + super.cancel() } @Override boolean isCancelled() { - return subscription.isUnsubscribed() + subscription.unsubscribed } } } diff --git a/grails-events-rxjava2/build.gradle b/grails-events-rxjava2/build.gradle index 4db5cb36..b242dfa1 100644 --- a/grails-events-rxjava2/build.gradle +++ b/grails-events-rxjava2/build.gradle @@ -1,4 +1,28 @@ +plugins { + + id 'groovy' + id 'java-library' + + id 'maven-publish' + id 'signing' + +} + +group = 'org.grails' + dependencies { - compile project(":grails-events") - compile "io.reactivex.rxjava2:rxjava:$rxJava2Version" -} \ No newline at end of file + + implementation project(':grails-events-core') + + implementation libs.groovy + + implementation libs.rxjava2 + implementation libs.slf4j.api + implementation libs.spring.tx + + testImplementation libs.spock.core + +} + +apply from: file("$rootProject.projectDir/gradle/java.gradle") +apply from: file("$rootProject.projectDir/gradle/publishing.gradle") diff --git a/grails-events-rxjava2/src/main/groovy/org/grails/events/rxjava2/RxEventBus.groovy b/grails-events-rxjava2/src/main/groovy/org/grails/events/rxjava2/RxEventBus.groovy index 7a9f4cc9..dbd40dac 100644 --- a/grails-events-rxjava2/src/main/groovy/org/grails/events/rxjava2/RxEventBus.groovy +++ b/grails-events-rxjava2/src/main/groovy/org/grails/events/rxjava2/RxEventBus.groovy @@ -29,6 +29,7 @@ import java.util.concurrent.ConcurrentHashMap @CompileStatic @Slf4j class RxEventBus extends AbstractEventBus { + protected final Map subjects = new ConcurrentHashMap().withDefault { PublishSubject.create() } @@ -56,27 +57,29 @@ class RxEventBus extends AbstractEventBus { @Override protected EventSubscriberSubscription buildSubscriberSubscription(CharSequence eventId, Subscriber subscriber) { + String eventKey = eventId.toString() Subject subject = subjects.get(eventKey) - return new RxEventSubscriberSubscription(eventId, subscriptions, subscriber, subject, scheduler) + new RxEventSubscriberSubscription(eventId, subscriptions, subscriber, subject, scheduler) } @Override protected ClosureSubscription buildClosureSubscription(CharSequence eventId, Closure subscriber) { + String eventKey = eventId.toString() Subject subject = subjects.get(eventKey) - return new RxClosureSubscription(eventId, subscriptions, subscriber, subject, scheduler) + + new RxClosureSubscription(eventId, subscriptions, subscriber, subject, scheduler) } private static class RxClosureSubscription extends ClosureSubscription { + final Disposable subscription RxClosureSubscription(CharSequence eventId, Map> subscriptions, Closure subscriber, Subject subject, Scheduler scheduler) { super(eventId, subscriptions, subscriber) - this.subscription = subject.observeOn(scheduler) - .subscribe( { eventObject -> - + this.subscription = subject.observeOn(scheduler).subscribe( { eventObject -> Event event Closure reply = null if(eventObject instanceof EventWithReply) { @@ -85,10 +88,10 @@ class RxEventBus extends AbstractEventBus { reply = eventWithReply.reply } else { - event = (Event)eventObject + event = (Event) eventObject } - EventTrigger trigger = buildTrigger(event, reply) + EventTrigger trigger = buildTrigger(event as Event, reply) trigger.proceed() } as Consumer, { Throwable t -> log.error("Error occurred triggering event listener for event [$eventId]: ${t.message}", t) @@ -97,26 +100,26 @@ class RxEventBus extends AbstractEventBus { @Override Subscription cancel() { - if(!subscription.isDisposed()) { + if(!subscription.disposed) { subscription.dispose() } - return super.cancel() + super.cancel() } @Override boolean isCancelled() { - return subscription.isDisposed() + subscription.disposed } } private static class RxEventSubscriberSubscription extends EventSubscriberSubscription { + final Disposable subscription RxEventSubscriberSubscription(CharSequence eventId, Map> subscriptions, Subscriber subscriber, Subject subject, Scheduler scheduler) { super(eventId, subscriptions, subscriber) - this.subscription = subject.observeOn(scheduler) - .subscribe( { Event event -> - EventTrigger trigger = buildTrigger(event) + this.subscription = subject.observeOn(scheduler).subscribe( { event -> + EventTrigger trigger = buildTrigger(event as Event) trigger.proceed() } as Consumer, { Throwable t -> log.error("Error occurred triggering event listener for event [$eventId]: ${t.message}", t) @@ -125,15 +128,15 @@ class RxEventBus extends AbstractEventBus { @Override Subscription cancel() { - if(!subscription.isDisposed()) { + if(!subscription.disposed) { subscription.dispose() } - return super.cancel() + super.cancel() } @Override boolean isCancelled() { - return subscription.isDisposed() + subscription.disposed } } } diff --git a/grails-events-spring/build.gradle b/grails-events-spring/build.gradle index 8a39b969..4d3fcc26 100644 --- a/grails-events-spring/build.gradle +++ b/grails-events-spring/build.gradle @@ -1,4 +1,28 @@ +plugins { + + id 'groovy' + id 'java-library' + + id 'maven-publish' + id 'signing' + +} + +group = 'org.grails' + dependencies { - compile project(":grails-events") - compile "org.springframework:spring-context:$springVersion" -} \ No newline at end of file + + implementation project(':grails-events-core') + + implementation libs.groovy + + implementation libs.slf4j.api + implementation libs.spring.context + implementation libs.spring.tx + + testImplementation libs.spock.core + +} + +apply from: file("$rootProject.projectDir/gradle/java.gradle") +apply from: file("$rootProject.projectDir/gradle/publishing.gradle") diff --git a/grails-events-transform/build.gradle b/grails-events-transform/build.gradle index 9262325b..60d50a1c 100644 --- a/grails-events-transform/build.gradle +++ b/grails-events-transform/build.gradle @@ -1,12 +1,32 @@ +plugins { + + id 'groovy' + id 'java-library' + + id 'maven-publish' + id 'signing' + +} + +group = 'org.grails' + dependencies { - compile project(":grails-events") - compile "org.grails:grails-datastore-gorm:$gormVersion", { - exclude group:'org.grails', module:'grails-datastore-gorm-validation' + + implementation project(':grails-events-core') + + implementation libs.gorm, { + exclude group: 'org.grails', module: 'grails-datastore-gorm-validation' } - testRuntime "org.springframework:spring-aop:$springVersion" - testRuntime "org.springframework:spring-expression:$springVersion" - testCompile "org.grails:grails-datastore-gorm-test:$gormVersion", { - exclude group:'org.grails', module:'grails-datastore-gorm-validation' + testImplementation libs.spock.core + testImplementation libs.gorm.test, { + exclude group: 'org.grails', module: 'grails-datastore-gorm-validation' } -} \ No newline at end of file + + testRuntimeOnly libs.spring.aop + testRuntimeOnly libs.spring.expression + +} + +apply from: file("$rootProject.projectDir/gradle/java.gradle") +apply from: file("$rootProject.projectDir/gradle/publishing.gradle") diff --git a/grails-events/build.gradle b/grails-events/build.gradle deleted file mode 100644 index 9b19f623..00000000 --- a/grails-events/build.gradle +++ /dev/null @@ -1,4 +0,0 @@ -dependencies { - compile "org.springframework:spring-context:$springVersion" - compile "org.springframework:spring-tx:$springVersion" -} \ No newline at end of file diff --git a/grails-plugin-async/build.gradle b/grails-plugin-async/build.gradle index b0500cbf..71fc6727 100644 --- a/grails-plugin-async/build.gradle +++ b/grails-plugin-async/build.gradle @@ -1,10 +1,41 @@ +plugins { + + id 'groovy' + id 'java-library' + + id 'org.grails.grails-web' + id 'org.grails.grails-plugin' + + id 'maven-publish' + id 'signing' + +} + +group = 'org.grails.plugins' + dependencies { - compileOnly "javax.servlet:javax.servlet-api:$servletApiVersion" - compileOnly 'javax:javaee-web-api:6.0' - compile "org.grails:grails-plugin-controllers:$grailsVersion" - compile project(':grails-plugin-events') - compile project(':grails-async') - - testCompile "javax.servlet:javax.servlet-api:$servletApiVersion" - testCompile "org.springframework:spring-test:$springVersion" + + api project(':grails-async-core') + api project(':grails-events-compat') + api project(':grails-events-core') + + implementation 'org.grails:grails-core' + implementation 'org.grails:grails-logging' + implementation 'org.grails:grails-plugin-controllers' + + compileOnly 'io.micronaut:micronaut-inject-groovy' + + testImplementation 'io.micronaut:micronaut-inject-groovy' + testImplementation 'org.grails:grails-web-testing-support' + testImplementation libs.spock.core + } + +bootJar.enabled = false +jar { + enabled = true + archiveClassifier = '' +} + +apply from: file("$rootProject.projectDir/gradle/java.gradle") +apply from: file("$rootProject.projectDir/gradle/publishing.gradle") diff --git a/grails-plugin-events/build.gradle b/grails-plugin-events/build.gradle index 9e3a7f62..5d6a2310 100644 --- a/grails-plugin-events/build.gradle +++ b/grails-plugin-events/build.gradle @@ -1,5 +1,38 @@ +plugins { + + id 'groovy' + id 'java-library' + + id 'org.grails.grails-web' + id 'org.grails.grails-plugin' + + id 'maven-publish' + id 'signing' + +} + +group = 'org.grails.plugins' + dependencies { - compile "org.grails:grails-core:$grailsVersion" - compile project(":grails-events"), project(":grails-events-transform") - compile project(":grails-events-compat") + + api project(':grails-events-core') + api project(':grails-events-compat') + api project(':grails-events-transform') + + implementation 'org.grails:grails-core' + implementation 'org.grails:grails-logging' + implementation 'org.grails:grails-plugin-controllers' + + compileOnly 'io.micronaut:micronaut-inject-groovy' + + testImplementation libs.spock.core +} + +bootJar.enabled = false +jar { + enabled = true + archiveClassifier = '' } + +apply from: file("$rootProject.projectDir/gradle/java.gradle") +apply from: file("$rootProject.projectDir/gradle/publishing.gradle") \ No newline at end of file diff --git a/grails-plugin-events/src/main/groovy/org/grails/plugins/events/EventBusGrailsPlugin.groovy b/grails-plugin-events/src/main/groovy/org/grails/plugins/events/EventBusGrailsPlugin.groovy index 28079aa7..fab26277 100644 --- a/grails-plugin-events/src/main/groovy/org/grails/plugins/events/EventBusGrailsPlugin.groovy +++ b/grails-plugin-events/src/main/groovy/org/grails/plugins/events/EventBusGrailsPlugin.groovy @@ -18,23 +18,20 @@ package org.grails.plugins.events import grails.config.Config import grails.plugins.Plugin -import groovy.util.logging.Slf4j import org.grails.events.bus.spring.EventBusFactoryBean import org.grails.events.gorm.GormDispatcherRegistrar import org.grails.events.spring.SpringEventTranslator import reactor.bus.EventBus - /** * A plugin that integrates Reactor into Grails * * @author Graeme Rocher * @since 3.0 */ -@Slf4j class EventBusGrailsPlugin extends Plugin { - def grailsVersion = "3.3.0 > *" + def grailsVersion = "6.0.0 > *" /** * Whether to translate GORM events into reactor events diff --git a/settings.gradle b/settings.gradle index 30b48311..59def7bd 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,10 +1,21 @@ +pluginManagement { + repositories { + maven { url 'https://repo.grails.org/grails/core' } + gradlePluginPortal() + } + plugins { + id 'org.grails.grails-web' version "$grailsVersion" + id 'org.grails.plugins.views-json' version "$viewsVersion" + } +} + rootProject.name = 'grails-async' -include 'grails-async' +include 'grails-async-core' include 'grails-async-gpars' include 'grails-async-rxjava' include 'grails-async-rxjava2' -include 'grails-events' +include 'grails-events-core' include 'grails-events-compat' include 'grails-events-transform' include 'grails-events-gpars' @@ -15,5 +26,5 @@ include 'grails-plugin-async' include 'grails-plugin-events' // examples -// include 'pubsub-demo' -// project(":pubsub-demo").projectDir = new File(settingsDir, "examples/pubsub-demo") \ No newline at end of file +include 'pubsub-demo' +project(':pubsub-demo').projectDir = new File(settingsDir, 'examples/pubsub-demo') diff --git a/travis-build.sh b/travis-build.sh deleted file mode 100755 index ed439238..00000000 --- a/travis-build.sh +++ /dev/null @@ -1,74 +0,0 @@ -#!/bin/bash -set -e - -EXIT_STATUS=0 - -echo "Check for branch $TRAVIS_BRANCH JDK: $TRAVIS_JDK_VERSION" - -./gradlew clean check || EXIT_STATUS=$? - -if [ $EXIT_STATUS -ne 0 ]; then - exit $EXIT_STATUS -fi - -if [ "${TRAVIS_JDK_VERSION}" == "openjdk11" ] ; then - exit $EXIT_STATUS -fi - -echo "Publishing archives for branch $TRAVIS_BRANCH JDK: $TRAVIS_JDK_VERSION" -if [[ -n $TRAVIS_TAG ]] || [[ $TRAVIS_BRANCH =~ ^master$ && $TRAVIS_PULL_REQUEST == 'false' ]]; then - - echo "Publishing archives" - if [[ -n $TRAVIS_TAG ]]; then - ./gradlew bintrayUpload --no-daemon --stacktrace || EXIT_STATUS=$? - else - ./gradlew publish --no-daemon --stacktrace || EXIT_STATUS=$? - fi - - ./gradlew --no-daemon docs || EXIT_STATUS=$? - - git config --global user.name "$GIT_NAME" - git config --global user.email "$GIT_EMAIL" - git config --global credential.helper "store --file=~/.git-credentials" - echo "https://$GH_TOKEN:@github.com" > ~/.git-credentials - - git clone https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git -b gh-pages gh-pages --single-branch > /dev/null - cd gh-pages - - # If this is the master branch then update the snapshot - if [[ $TRAVIS_BRANCH =~ ^master|[12]\..\.x$ ]]; then - mkdir -p snapshot - cp -r ../build/docs/. ./snapshot/ - - git add snapshot/* - fi - - # If there is a tag present then this becomes the latest - if [[ -n $TRAVIS_TAG ]]; then - mkdir -p latest - cp -r ../build/docs/. ./latest/ - git add latest/* - - version="$TRAVIS_TAG" - version=${version:1} - majorVersion=${version:0:4} - majorVersion="${majorVersion}x" - - mkdir -p "$version" - cp -r ../build/docs/. "./$version/" - git add "$version/*" - - mkdir -p "$majorVersion" - cp -r ../build/docs/. "./$majorVersion/" - git add "$majorVersion/*" - - fi - - git commit -a -m "Updating docs for Travis build: https://travis-ci.org/$TRAVIS_REPO_SLUG/builds/$TRAVIS_BUILD_ID" && { - git push origin HEAD || true - } - cd .. - rm -rf gh-pages -fi - -exit $EXIT_STATUS From 4722d0515158d3a6257b8e5f84644112b09e9f2e Mon Sep 17 00:00:00 2001 From: Mattias Reichel Date: Fri, 17 Nov 2023 19:51:12 +0100 Subject: [PATCH 2/9] Fix error in publishing that caused the wrong groupId --- gradle/publishing.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/publishing.gradle b/gradle/publishing.gradle index a3046458..62de36e9 100644 --- a/gradle/publishing.gradle +++ b/gradle/publishing.gradle @@ -3,7 +3,7 @@ ext['signing.secretKeyRingFile'] = project.findProperty("signing.secretKeyRingFi ext['signing.password'] = project.findProperty("signing.password") ?: System.getenv('SIGNING_PASSPHRASE') def isExample = project.projectDir.parentFile.name == 'examples' -def isGrailsPlugin = project.group = 'org.grails.plugins' +def isGrailsPlugin = project.group == 'org.grails.plugins' def isCoreModule = project.name.endsWith('-core') def pomInfo = { From 4b7170e2523bb3c7da1345e371c6abf06756d963 Mon Sep 17 00:00:00 2001 From: Mattias Reichel Date: Fri, 17 Nov 2023 19:51:30 +0100 Subject: [PATCH 3/9] Clarify comment --- gradle.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 7b754a49..fb6a6ef8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ projectVersion=5.0.0-SNAPSHOT grailsVersion=6.1.0 viewsVersion=3.1.0 -# This prevents the Grails Gradle Plugin from unnecessarily excluding SLF4J in the generated POMs +# This prevents the Grails Gradle Plugin from unnecessarily excluding slf4j-simple in the generated POMs # https://github.com/grails/grails-gradle-plugin/issues/222 slf4jPreventExclusion=true @@ -22,4 +22,4 @@ gparsdocs=http://gpars.org/1.2.1/groovydoc rxjavadocs=https://reactivex.io/RxJava/1.x/javadoc rxjava2docs=https://reactivex.io/RxJava/2.x/javadoc -org.gradle.caching=true \ No newline at end of file +org.gradle.caching=false \ No newline at end of file From dda2c2e6e3d5392ac2b672f9f97301ea0ceb790c Mon Sep 17 00:00:00 2001 From: Mattias Reichel Date: Fri, 17 Nov 2023 20:03:03 +0100 Subject: [PATCH 4/9] Simplify publishing script --- gradle/publishing.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/publishing.gradle b/gradle/publishing.gradle index 62de36e9..ebc08eab 100644 --- a/gradle/publishing.gradle +++ b/gradle/publishing.gradle @@ -48,7 +48,7 @@ if (!isExample) { // don't publish examples username = System.getenv('ARTIFACTORY_USERNAME') ?: project.findProperty('artifactoryPublishUsername') ?: '' password = System.getenv('ARTIFACTORY_PASSWORD') ?: project.findProperty('artifactoryPublishPassword') ?: '' } - url = (project.group == 'org.grails.plugins') ? + url = isGrailsPlugin ? uri('https://repo.grails.org/grails/plugins3-snapshots-local') : uri('https://repo.grails.org/grails/libs-snapshots-local') From d97b896b6fce51fc283681cf88f739b77c797db7 Mon Sep 17 00:00:00 2001 From: Mattias Reichel Date: Fri, 17 Nov 2023 20:03:18 +0100 Subject: [PATCH 5/9] Turn on Gradle caching --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index fb6a6ef8..f5f783b0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,4 +22,4 @@ gparsdocs=http://gpars.org/1.2.1/groovydoc rxjavadocs=https://reactivex.io/RxJava/1.x/javadoc rxjava2docs=https://reactivex.io/RxJava/2.x/javadoc -org.gradle.caching=false \ No newline at end of file +org.gradle.caching=true \ No newline at end of file From 14bdbdafb503cfe7c3a10006ebac71ccd75c8b85 Mon Sep 17 00:00:00 2001 From: Mattias Reichel Date: Sat, 18 Nov 2023 08:16:11 +0100 Subject: [PATCH 6/9] Strip leading/trailing whitespace on developer name in pom. --- gradle/publishing.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/publishing.gradle b/gradle/publishing.gradle index ebc08eab..76962ef2 100644 --- a/gradle/publishing.gradle +++ b/gradle/publishing.gradle @@ -30,7 +30,7 @@ def pomInfo = { for (dev in developers.split(',')) { delegate.developer { delegate.id dev.toLowerCase().replace(' ', '') - delegate.name dev + delegate.name dev.strip() } } } From 5fca5b7a6a390c82468b3592f4a29f17642c9ad4 Mon Sep 17 00:00:00 2001 From: Mattias Reichel Date: Sat, 18 Nov 2023 09:05:20 +0100 Subject: [PATCH 7/9] Configure jar and bootJar lazily in plugin projects --- grails-plugin-async/build.gradle | 6 ++++-- grails-plugin-events/build.gradle | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/grails-plugin-async/build.gradle b/grails-plugin-async/build.gradle index 71fc6727..a2af4651 100644 --- a/grails-plugin-async/build.gradle +++ b/grails-plugin-async/build.gradle @@ -31,8 +31,10 @@ dependencies { } -bootJar.enabled = false -jar { +tasks.named('bootJar').configure { + enabled = false +} +tasks.named('jar').configure { enabled = true archiveClassifier = '' } diff --git a/grails-plugin-events/build.gradle b/grails-plugin-events/build.gradle index 5d6a2310..d396b6e9 100644 --- a/grails-plugin-events/build.gradle +++ b/grails-plugin-events/build.gradle @@ -28,8 +28,10 @@ dependencies { testImplementation libs.spock.core } -bootJar.enabled = false -jar { +tasks.named('bootJar').configure { + enabled = false +} +tasks.named('jar').configure { enabled = true archiveClassifier = '' } From dd365a8e1624ad501873d182037fed91d1119360 Mon Sep 17 00:00:00 2001 From: Mattias Reichel Date: Sat, 18 Nov 2023 11:22:33 +0100 Subject: [PATCH 8/9] Dry up the build by extracting common grails plugin configuration. --- gradle/grails-plugin-config.gradle | 9 +++++++++ gradle/{java.gradle => java-config.gradle} | 0 grails-async-core/build.gradle | 2 +- grails-async-gpars/build.gradle | 2 +- grails-async-rxjava/build.gradle | 2 +- grails-async-rxjava2/build.gradle | 2 +- grails-events-compat/build.gradle | 2 +- grails-events-core/build.gradle | 2 +- grails-events-gpars/build.gradle | 2 +- grails-events-rxjava/build.gradle | 2 +- grails-events-rxjava2/build.gradle | 2 +- grails-events-spring/build.gradle | 2 +- grails-events-transform/build.gradle | 2 +- grails-plugin-async/build.gradle | 13 ++----------- grails-plugin-events/build.gradle | 13 ++----------- 15 files changed, 24 insertions(+), 33 deletions(-) create mode 100644 gradle/grails-plugin-config.gradle rename gradle/{java.gradle => java-config.gradle} (100%) diff --git a/gradle/grails-plugin-config.gradle b/gradle/grails-plugin-config.gradle new file mode 100644 index 00000000..2efa468a --- /dev/null +++ b/gradle/grails-plugin-config.gradle @@ -0,0 +1,9 @@ +group = 'org.grails.plugins' + +tasks.named('bootJar').configure { + enabled = false // Grails plugins shouldn't produce a boot jar +} +tasks.named('jar').configure { + enabled = true + archiveClassifier = '' // Skip the '-plain' suffix on the jar file name +} diff --git a/gradle/java.gradle b/gradle/java-config.gradle similarity index 100% rename from gradle/java.gradle rename to gradle/java-config.gradle diff --git a/grails-async-core/build.gradle b/grails-async-core/build.gradle index 6838f14c..2f2bc18c 100644 --- a/grails-async-core/build.gradle +++ b/grails-async-core/build.gradle @@ -21,6 +21,6 @@ dependencies { } -apply from: file("$rootProject.projectDir/gradle/java.gradle") +apply from: file("$rootProject.projectDir/gradle/java-config.gradle") apply from: file("$rootProject.projectDir/gradle/publishing.gradle") diff --git a/grails-async-gpars/build.gradle b/grails-async-gpars/build.gradle index d728b731..bebe29fc 100644 --- a/grails-async-gpars/build.gradle +++ b/grails-async-gpars/build.gradle @@ -26,7 +26,7 @@ dependencies { } -apply from: file("$rootProject.projectDir/gradle/java.gradle") +apply from: file("$rootProject.projectDir/gradle/java-config.gradle") apply from: file("$rootProject.projectDir/gradle/publishing.gradle") diff --git a/grails-async-rxjava/build.gradle b/grails-async-rxjava/build.gradle index 20fa8db1..ff54da1f 100644 --- a/grails-async-rxjava/build.gradle +++ b/grails-async-rxjava/build.gradle @@ -23,7 +23,7 @@ dependencies { } -apply from: file("$rootProject.projectDir/gradle/java.gradle") +apply from: file("$rootProject.projectDir/gradle/java-config.gradle") apply from: file("$rootProject.projectDir/gradle/publishing.gradle") diff --git a/grails-async-rxjava2/build.gradle b/grails-async-rxjava2/build.gradle index 90864c19..8ebc4538 100644 --- a/grails-async-rxjava2/build.gradle +++ b/grails-async-rxjava2/build.gradle @@ -22,5 +22,5 @@ dependencies { } -apply from: file("$rootProject.projectDir/gradle/java.gradle") +apply from: file("$rootProject.projectDir/gradle/java-config.gradle") apply from: file("$rootProject.projectDir/gradle/publishing.gradle") diff --git a/grails-events-compat/build.gradle b/grails-events-compat/build.gradle index 690dd6e4..e1f74c07 100644 --- a/grails-events-compat/build.gradle +++ b/grails-events-compat/build.gradle @@ -22,5 +22,5 @@ dependencies { } -apply from: file("$rootProject.projectDir/gradle/java.gradle") +apply from: file("$rootProject.projectDir/gradle/java-config.gradle") apply from: file("$rootProject.projectDir/gradle/publishing.gradle") diff --git a/grails-events-core/build.gradle b/grails-events-core/build.gradle index c1f0c940..c984eac5 100644 --- a/grails-events-core/build.gradle +++ b/grails-events-core/build.gradle @@ -22,5 +22,5 @@ dependencies { } -apply from: file("$rootProject.projectDir/gradle/java.gradle") +apply from: file("$rootProject.projectDir/gradle/java-config.gradle") apply from: file("$rootProject.projectDir/gradle/publishing.gradle") diff --git a/grails-events-gpars/build.gradle b/grails-events-gpars/build.gradle index 010528e1..916c1ebd 100644 --- a/grails-events-gpars/build.gradle +++ b/grails-events-gpars/build.gradle @@ -27,5 +27,5 @@ dependencies { } -apply from: file("$rootProject.projectDir/gradle/java.gradle") +apply from: file("$rootProject.projectDir/gradle/java-config.gradle") apply from: file("$rootProject.projectDir/gradle/publishing.gradle") diff --git a/grails-events-rxjava/build.gradle b/grails-events-rxjava/build.gradle index 8f903ac1..44d7a35c 100644 --- a/grails-events-rxjava/build.gradle +++ b/grails-events-rxjava/build.gradle @@ -28,5 +28,5 @@ dependencies { } -apply from: file("$rootProject.projectDir/gradle/java.gradle") +apply from: file("$rootProject.projectDir/gradle/java-config.gradle") apply from: file("$rootProject.projectDir/gradle/publishing.gradle") diff --git a/grails-events-rxjava2/build.gradle b/grails-events-rxjava2/build.gradle index b242dfa1..dc22daa1 100644 --- a/grails-events-rxjava2/build.gradle +++ b/grails-events-rxjava2/build.gradle @@ -24,5 +24,5 @@ dependencies { } -apply from: file("$rootProject.projectDir/gradle/java.gradle") +apply from: file("$rootProject.projectDir/gradle/java-config.gradle") apply from: file("$rootProject.projectDir/gradle/publishing.gradle") diff --git a/grails-events-spring/build.gradle b/grails-events-spring/build.gradle index 4d3fcc26..688f1882 100644 --- a/grails-events-spring/build.gradle +++ b/grails-events-spring/build.gradle @@ -24,5 +24,5 @@ dependencies { } -apply from: file("$rootProject.projectDir/gradle/java.gradle") +apply from: file("$rootProject.projectDir/gradle/java-config.gradle") apply from: file("$rootProject.projectDir/gradle/publishing.gradle") diff --git a/grails-events-transform/build.gradle b/grails-events-transform/build.gradle index 60d50a1c..479f2694 100644 --- a/grails-events-transform/build.gradle +++ b/grails-events-transform/build.gradle @@ -28,5 +28,5 @@ dependencies { } -apply from: file("$rootProject.projectDir/gradle/java.gradle") +apply from: file("$rootProject.projectDir/gradle/java-config.gradle") apply from: file("$rootProject.projectDir/gradle/publishing.gradle") diff --git a/grails-plugin-async/build.gradle b/grails-plugin-async/build.gradle index a2af4651..d3ce8a67 100644 --- a/grails-plugin-async/build.gradle +++ b/grails-plugin-async/build.gradle @@ -11,8 +11,6 @@ plugins { } -group = 'org.grails.plugins' - dependencies { api project(':grails-async-core') @@ -31,13 +29,6 @@ dependencies { } -tasks.named('bootJar').configure { - enabled = false -} -tasks.named('jar').configure { - enabled = true - archiveClassifier = '' -} - -apply from: file("$rootProject.projectDir/gradle/java.gradle") +apply from: file("$rootProject.projectDir/gradle/grails-plugin-config.gradle") +apply from: file("$rootProject.projectDir/gradle/java-config.gradle") apply from: file("$rootProject.projectDir/gradle/publishing.gradle") diff --git a/grails-plugin-events/build.gradle b/grails-plugin-events/build.gradle index d396b6e9..45da285c 100644 --- a/grails-plugin-events/build.gradle +++ b/grails-plugin-events/build.gradle @@ -11,8 +11,6 @@ plugins { } -group = 'org.grails.plugins' - dependencies { api project(':grails-events-core') @@ -28,13 +26,6 @@ dependencies { testImplementation libs.spock.core } -tasks.named('bootJar').configure { - enabled = false -} -tasks.named('jar').configure { - enabled = true - archiveClassifier = '' -} - -apply from: file("$rootProject.projectDir/gradle/java.gradle") +apply from: file("$rootProject.projectDir/gradle/grails-plugin-config.gradle") +apply from: file("$rootProject.projectDir/gradle/java-config.gradle") apply from: file("$rootProject.projectDir/gradle/publishing.gradle") \ No newline at end of file From f92368ad8f4189d33aa6853662652d677b3a341c Mon Sep 17 00:00:00 2001 From: Mattias Reichel Date: Sat, 18 Nov 2023 16:38:03 +0100 Subject: [PATCH 9/9] Remove unused dependencies from grails plugin projects --- grails-plugin-async/build.gradle | 5 ----- grails-plugin-events/build.gradle | 5 ----- 2 files changed, 10 deletions(-) diff --git a/grails-plugin-async/build.gradle b/grails-plugin-async/build.gradle index d3ce8a67..61223553 100644 --- a/grails-plugin-async/build.gradle +++ b/grails-plugin-async/build.gradle @@ -18,13 +18,8 @@ dependencies { api project(':grails-events-core') implementation 'org.grails:grails-core' - implementation 'org.grails:grails-logging' implementation 'org.grails:grails-plugin-controllers' - compileOnly 'io.micronaut:micronaut-inject-groovy' - - testImplementation 'io.micronaut:micronaut-inject-groovy' - testImplementation 'org.grails:grails-web-testing-support' testImplementation libs.spock.core } diff --git a/grails-plugin-events/build.gradle b/grails-plugin-events/build.gradle index 45da285c..7bc6f50e 100644 --- a/grails-plugin-events/build.gradle +++ b/grails-plugin-events/build.gradle @@ -3,7 +3,6 @@ plugins { id 'groovy' id 'java-library' - id 'org.grails.grails-web' id 'org.grails.grails-plugin' id 'maven-publish' @@ -18,10 +17,6 @@ dependencies { api project(':grails-events-transform') implementation 'org.grails:grails-core' - implementation 'org.grails:grails-logging' - implementation 'org.grails:grails-plugin-controllers' - - compileOnly 'io.micronaut:micronaut-inject-groovy' testImplementation libs.spock.core }