├── .git.commit.template ├── .github ├── ISSUE_TEMPLATE │ ├── BUG_REPORT.yml │ └── config.yml ├── release.yml └── workflows │ ├── build.yml │ ├── ci.yml │ ├── lts.yml │ ├── plugins-tests.yml │ ├── previous-lts.yml │ ├── publish.yml │ ├── qa.yml │ └── tests.yml ├── .gitignore ├── CHANGELOG.md ├── CODE-OF-CONDUCT.md ├── CONTRIBUTING.md ├── KurrentLogo-Black.png ├── KurrentLogo-Plum.png ├── KurrentLogo-White.png ├── LICENSE ├── README.md ├── build.gradle ├── configure-tls-for-tests.yml ├── configure-user-certs-for-tests.yml ├── docker-compose.yml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle ├── src ├── main │ ├── java │ │ └── io │ │ │ └── kurrent │ │ │ └── dbclient │ │ │ ├── AbortProjection.java │ │ │ ├── AbortProjectionOptions.java │ │ │ ├── AbstractCreatePersistentSubscription.java │ │ │ ├── AbstractDeletePersistentSubscription.java │ │ │ ├── AbstractPersistentSubscriptionSettingsBuilder.java │ │ │ ├── AbstractRead.java │ │ │ ├── AbstractRegularSubscription.java │ │ │ ├── AbstractSubscribePersistentSubscription.java │ │ │ ├── AbstractUpdatePersistentSubscription.java │ │ │ ├── Acl.java │ │ │ ├── Acls.java │ │ │ ├── AppendToStream.java │ │ │ ├── AppendToStreamOptions.java │ │ │ ├── Checkpointer.java │ │ │ ├── ClientCertificate.java │ │ │ ├── ClientFeatureFlags.java │ │ │ ├── ClientTelemetry.java │ │ │ ├── ClientTelemetryAttributes.java │ │ │ ├── ClientTelemetryConstants.java │ │ │ ├── ClientTelemetryTags.java │ │ │ ├── ClusterDiscovery.java │ │ │ ├── ClusterInfo.java │ │ │ ├── ConnectionMetadata.java │ │ │ ├── ConnectionService.java │ │ │ ├── ConnectionSettingsBuilder.java │ │ │ ├── ConnectionShutdownException.java │ │ │ ├── ConnectionState.java │ │ │ ├── ConnectionStringParsingException.java │ │ │ ├── Consts.java │ │ │ ├── ContentType.java │ │ │ ├── CreateChannel.java │ │ │ ├── CreatePersistentSubscriptionToAll.java │ │ │ ├── CreatePersistentSubscriptionToAllOptions.java │ │ │ ├── CreatePersistentSubscriptionToStream.java │ │ │ ├── CreatePersistentSubscriptionToStreamOptions.java │ │ │ ├── CreateProjection.java │ │ │ ├── CreateProjectionOptions.java │ │ │ ├── CustomAclCodec.java │ │ │ ├── DeletePersistentSubscriptionOptions.java │ │ │ ├── DeletePersistentSubscriptionToAll.java │ │ │ ├── DeletePersistentSubscriptionToStream.java │ │ │ ├── DeleteProjection.java │ │ │ ├── DeleteProjectionOptions.java │ │ │ ├── DeleteResult.java │ │ │ ├── DeleteStream.java │ │ │ ├── DeleteStreamOptions.java │ │ │ ├── Direction.java │ │ │ ├── DisableProjection.java │ │ │ ├── DisableProjectionOptions.java │ │ │ ├── Discovery.java │ │ │ ├── EnableProjection.java │ │ │ ├── EnableProjectionOptions.java │ │ │ ├── EventData.java │ │ │ ├── EventDataBuilder.java │ │ │ ├── EventFilter.java │ │ │ ├── EventTypeFilter.java │ │ │ ├── FeatureFlags.java │ │ │ ├── GetPersistentSubscriptionInfo.java │ │ │ ├── GetPersistentSubscriptionInfoOptions.java │ │ │ ├── GetProjectionResult.java │ │ │ ├── GetProjectionResultOptions.java │ │ │ ├── GetProjectionState.java │ │ │ ├── GetProjectionStateOptions.java │ │ │ ├── GetProjectionStatistics.java │ │ │ ├── GetProjectionStatisticsOptions.java │ │ │ ├── GetProjectionStatus.java │ │ │ ├── GetProjectionStatusOptions.java │ │ │ ├── GossipClient.java │ │ │ ├── GrpcClient.java │ │ │ ├── GrpcUtils.java │ │ │ ├── HttpUtils.java │ │ │ ├── KurrentDBClient.java │ │ │ ├── KurrentDBClientBase.java │ │ │ ├── KurrentDBClientSettings.java │ │ │ ├── KurrentDBConnectionString.java │ │ │ ├── KurrentDBPersistentSubscriptionsClient.java │ │ │ ├── KurrentDBProjectionManagementClient.java │ │ │ ├── ListPersistentSubscriptions.java │ │ │ ├── ListPersistentSubscriptionsOptions.java │ │ │ ├── ListProjections.java │ │ │ ├── ListProjectionsOptions.java │ │ │ ├── ListProjectionsResult.java │ │ │ ├── Msg.java │ │ │ ├── NackAction.java │ │ │ ├── NamedConsumerStrategy.java │ │ │ ├── NoClusterNodeFoundException.java │ │ │ ├── NodePreference.java │ │ │ ├── NodeSelector.java │ │ │ ├── NotLeaderException.java │ │ │ ├── OperationKind.java │ │ │ ├── OptionsBase.java │ │ │ ├── OptionsWithBackPressure.java │ │ │ ├── OptionsWithPositionAndResolveLinkTosBase.java │ │ │ ├── OptionsWithResolveLinkTosBase.java │ │ │ ├── OptionsWithStartRevisionAndResolveLinkTosBase.java │ │ │ ├── OptionsWithStreamStateBase.java │ │ │ ├── PersistentSubscription.java │ │ │ ├── PersistentSubscriptionConnectionInfo.java │ │ │ ├── PersistentSubscriptionInfo.java │ │ │ ├── PersistentSubscriptionListener.java │ │ │ ├── PersistentSubscriptionSettings.java │ │ │ ├── PersistentSubscriptionStats.java │ │ │ ├── PersistentSubscriptionToAllInfo.java │ │ │ ├── PersistentSubscriptionToAllSettings.java │ │ │ ├── PersistentSubscriptionToAllStats.java │ │ │ ├── PersistentSubscriptionToStreamInfo.java │ │ │ ├── PersistentSubscriptionToStreamSettings.java │ │ │ ├── PersistentSubscriptionToStreamStats.java │ │ │ ├── Position.java │ │ │ ├── PrefixFilterExpression.java │ │ │ ├── ProjectionDetails.java │ │ │ ├── ReadAll.java │ │ │ ├── ReadAllOptions.java │ │ │ ├── ReadMessage.java │ │ │ ├── ReadResponseObserver.java │ │ │ ├── ReadResult.java │ │ │ ├── ReadStream.java │ │ │ ├── ReadStreamConsumer.java │ │ │ ├── ReadStreamOptions.java │ │ │ ├── ReadSubscriber.java │ │ │ ├── RecordedEvent.java │ │ │ ├── RegularFilterExpression.java │ │ │ ├── ReplayParkedMessages.java │ │ │ ├── ReplayParkedMessagesOptions.java │ │ │ ├── ResetProjection.java │ │ │ ├── ResetProjectionOptions.java │ │ │ ├── ResolvedEvent.java │ │ │ ├── ResourceNotFoundException.java │ │ │ ├── RestartPersistentSubscriptionSubsystem.java │ │ │ ├── RestartPersistentSubscriptionSubsystemOptions.java │ │ │ ├── RestartProjectionSubsystem.java │ │ │ ├── RestartProjectionSubsystemOptions.java │ │ │ ├── RevisionOrPosition.java │ │ │ ├── RunWorkItem.java │ │ │ ├── ServerFeatures.java │ │ │ ├── ServerInfo.java │ │ │ ├── ServerVersion.java │ │ │ ├── Shutdown.java │ │ │ ├── SingleNodeDiscovery.java │ │ │ ├── StreamAcl.java │ │ │ ├── StreamConsumer.java │ │ │ ├── StreamDeletedException.java │ │ │ ├── StreamFilter.java │ │ │ ├── StreamMetadata.java │ │ │ ├── StreamNotFoundException.java │ │ │ ├── StreamPosition.java │ │ │ ├── StreamState.java │ │ │ ├── SubscribePersistentSubscriptionOptions.java │ │ │ ├── SubscribePersistentSubscriptionToAll.java │ │ │ ├── SubscribePersistentSubscriptionToStream.java │ │ │ ├── SubscribeToAll.java │ │ │ ├── SubscribeToAllOptions.java │ │ │ ├── SubscribeToStream.java │ │ │ ├── SubscribeToStreamOptions.java │ │ │ ├── Subscription.java │ │ │ ├── SubscriptionFilter.java │ │ │ ├── SubscriptionFilterBuilder.java │ │ │ ├── SubscriptionListener.java │ │ │ ├── SubscriptionStreamConsumer.java │ │ │ ├── SubscriptionTracingCallback.java │ │ │ ├── SystemMetadataKeys.java │ │ │ ├── SystemStreamAcl.java │ │ │ ├── SystemStreams.java │ │ │ ├── ThrowingBiFunction.java │ │ │ ├── ThrowingFunction.java │ │ │ ├── Tuple.java │ │ │ ├── UnsupportedFeatureException.java │ │ │ ├── UpdatePersistentSubscriptionToAll.java │ │ │ ├── UpdatePersistentSubscriptionToAllOptions.java │ │ │ ├── UpdatePersistentSubscriptionToStream.java │ │ │ ├── UpdatePersistentSubscriptionToStreamOptions.java │ │ │ ├── UpdateProjection.java │ │ │ ├── UpdateProjectionOptions.java │ │ │ ├── UserCredentials.java │ │ │ ├── UserStreamAcl.java │ │ │ ├── WorkItem.java │ │ │ ├── WorkItemArgs.java │ │ │ ├── WriteResult.java │ │ │ ├── WrongExpectedVersionException.java │ │ │ └── resolution │ │ │ ├── DeferredNodeResolution.java │ │ │ ├── DeprecatedNodeResolution.java │ │ │ ├── FixedSeedsNodeResolution.java │ │ │ └── NodeResolution.java │ └── proto │ │ ├── code.proto │ │ ├── gossip.proto │ │ ├── persistent.proto │ │ ├── projectionmanagement.proto │ │ ├── serverfeatures.proto │ │ ├── shared.proto │ │ ├── status.proto │ │ └── streams.proto └── test │ ├── java │ └── io │ │ └── kurrent │ │ └── dbclient │ │ ├── Action.java │ │ ├── BazEvent.java │ │ ├── ClientTracker.java │ │ ├── ConnectionAware.java │ │ ├── Database.java │ │ ├── DatabaseFactory.java │ │ ├── Exceptions.java │ │ ├── Foo.java │ │ ├── MiscTests.java │ │ ├── PersistentSubscriptionsTests.java │ │ ├── PluginsTests.java │ │ ├── StreamsTests.java │ │ ├── TelemetryTests.java │ │ ├── connection │ │ └── ConnectionShutdownTests.java │ │ ├── databases │ │ ├── DockerContainerDatabase.java │ │ └── ExternallyCreatedCluster.java │ │ ├── misc │ │ ├── EventDataTests.java │ │ ├── ExpectedRevisionTests.java │ │ ├── NodeSelectorTest.java │ │ ├── OfflineMetadataTests.java │ │ ├── ParseInvalidConnectionStringTests.java │ │ ├── ParseValidConnectionStringTests.java │ │ ├── PositionTests.java │ │ └── ServerVersionTests.java │ │ ├── persistentsubscriptions │ │ ├── CreatePersistentSubscriptionTests.java │ │ ├── DeletePersistentSubscriptionToStreamTests.java │ │ ├── PersistentSubscriptionManagementTests.java │ │ ├── PersistentSubscriptionToAllWithFilterTests.java │ │ ├── SubscribePersistentSubscriptionToStreamTests.java │ │ └── UpdatePersistentSubscriptionToStreamTests.java │ │ ├── plugins │ │ └── ClientCertificateAuthenticationTests.java │ │ ├── samples │ │ ├── TestEvent.java │ │ ├── appending_events │ │ │ └── AppendingEvents.java │ │ ├── authentication │ │ │ └── UserCertificate.java │ │ ├── opentelemetry │ │ │ └── Instrumentation.java │ │ ├── persistent_subscriptions │ │ │ └── PersistentSubscriptions.java │ │ ├── projection_management │ │ │ └── ProjectionManagement.java │ │ ├── quick_start │ │ │ └── QuickStart.java │ │ ├── reading_events │ │ │ └── ReadingEvents.java │ │ ├── server_side_filtering │ │ │ └── ServerSideFiltering.java │ │ └── subscribing_to_stream │ │ │ └── SubscribingToStream.java │ │ ├── streams │ │ ├── AppendTests.java │ │ ├── ClientLifecycleTests.java │ │ ├── DeadlineTests.java │ │ ├── DeleteTests.java │ │ ├── InterceptorTests.java │ │ ├── MetadataTests.java │ │ ├── ReadStreamTests.java │ │ └── SubscriptionTests.java │ │ └── telemetry │ │ ├── PersistentSubscriptionsTracingInstrumentationTests.java │ │ ├── SpanProcessorSpy.java │ │ ├── StreamsTracingInstrumentationTests.java │ │ ├── TelemetryAware.java │ │ └── TracingContextInjectionTests.java │ └── resources │ ├── all-back-c3386-p3386.json │ ├── all-back-e0-e10.json │ ├── all-c1788-p1788.json │ ├── all-e0-e10.json │ ├── all-positions-filtered-stream194-e0-e30.json │ ├── all-versions-filtered-stream194-e0-e30.json │ ├── count-events-partitioned-projection.js │ ├── count-events-projection.js │ ├── dataset20M-1800-e0-e10.json │ ├── dataset20M-1800-e1999-e1990.json │ ├── junit-platform.properties │ ├── simplelogger.properties │ └── state-with-unknown-keynames.js └── vars.env /.git.commit.template: -------------------------------------------------------------------------------- 1 | Describe change in active voice, ideally in under 60 chars 2 | 3 | Provide a more detailed summary including the motivations for the change, any 4 | downstream impacts, and limitations of the approaches taken. If other 5 | competing approaches were considered, include a summary of those and why they 6 | were not used instead. The body should describe _why_ the changes were made 7 | rather than the detail of what changes were made, which is easily identifiable 8 | from the diff. 9 | 10 | Wrap the body at 72 characters. 11 | 12 | For more information on writing Git commit messages which are likely to be 13 | merged without modification, see see https://chris.beams.io/posts/git-commit/ 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Feature Request 4 | url: https://github.com/kurrent-io/KurrentDB-Client-Java/discussions/new?category=ideas&labels=triage&title=Feature%20request%3A%20 5 | about: Request a feature to add to the client 6 | - name: Ask a Question 7 | url: https://github.com/kurrent-io/KurrentDB-Client-Java/discussions/new?category=q-a&labels=triage 8 | about: Ask questions and discuss with other community members. -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | exclude: 3 | labels: 4 | - ignore-for-release 5 | categories: 6 | - title: Added 7 | labels: 8 | - enhancement 9 | - title: Fixed 10 | labels: 11 | - bug 12 | - title: Changed 13 | labels: 14 | - "*" 15 | - title: Deprecated 16 | labels: 17 | - deprecated 18 | - title: Breaking Changes 19 | labels: 20 | - breaking 21 | - title: Documentation 22 | labels: 23 | - documentation -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build and Run Tests 2 | on: 3 | workflow_call: 4 | 5 | jobs: 6 | build: 7 | name: Build 8 | strategy: 9 | fail-fast: false 10 | matrix: 11 | java: [ 8, 11, 17 ] 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Check Out Sources 16 | uses: actions/checkout@v4 17 | 18 | - name: Set up JDK ${{ matrix.java }} 19 | uses: actions/setup-java@v3 20 | with: 21 | java-version: ${{ matrix.java }} 22 | distribution: 'temurin' 23 | 24 | - name: Setup Gradle 25 | uses: gradle/gradle-build-action@v3 26 | with: 27 | gradle-version: 8.13 28 | 29 | - name: Build 30 | run: ./gradlew compileTest 31 | 32 | # Tests that do not require a database connection. 33 | - name: Misc tests 34 | run: ./gradlew ci --tests MiscTests 35 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - trunk 7 | schedule: 8 | - cron: '0 0 * * 0' # Run every Sunday at midnight UTC 9 | 10 | jobs: 11 | build: 12 | uses: ./.github/workflows/build.yml 13 | 14 | ci: 15 | name: Tests (CI) 16 | uses: ./.github/workflows/tests.yml 17 | with: 18 | image: ${{ fromJSON(vars.KURRENTDB_DOCKER_IMAGES).ci.fullname }} 19 | secrets: inherit 20 | -------------------------------------------------------------------------------- /.github/workflows/lts.yml: -------------------------------------------------------------------------------- 1 | name: LTS 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - trunk 7 | schedule: 8 | - cron: '0 0 * * 0' # Run every Sunday at midnight UTC 9 | 10 | jobs: 11 | build: 12 | uses: ./.github/workflows/build.yml 13 | 14 | lts: 15 | name: Tests (LTS) 16 | uses: ./.github/workflows/tests.yml 17 | with: 18 | image: ${{ fromJSON(vars.KURRENTDB_DOCKER_IMAGES).lts.fullname }} 19 | secrets: inherit 20 | 21 | # Will be removed in the future 22 | plugins-tests: 23 | name: Plugins Tests 24 | 25 | uses: ./.github/workflows/plugins-tests.yml 26 | with: 27 | image: "docker.eventstore.com/eventstore-ee/eventstoredb-commercial:24.2.0-jammy" 28 | secrets: inherit 29 | -------------------------------------------------------------------------------- /.github/workflows/previous-lts.yml: -------------------------------------------------------------------------------- 1 | name: Previous LTS 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - trunk 7 | schedule: 8 | - cron: '0 0 * * 0' # Run every Sunday at midnight UTC 9 | 10 | jobs: 11 | build: 12 | uses: ./.github/workflows/build.yml 13 | 14 | previous-lts: 15 | name: Tests (Previous LTS) 16 | uses: ./.github/workflows/tests.yml 17 | with: 18 | image: ${{ fromJSON(vars.KURRENTDB_DOCKER_IMAGES)['previous-lts'].fullname }} 19 | secrets: inherit 20 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'release/*' 7 | workflow_dispatch: 8 | inputs: 9 | dry_run: 10 | description: 'Perform a dry run release' 11 | type: boolean 12 | required: false 13 | default: true 14 | 15 | jobs: 16 | publish: 17 | name: Publish 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v4 21 | 22 | - name: Set up JDK 8 23 | uses: actions/setup-java@v3 24 | with: 25 | java-version: '8' 26 | distribution: 'temurin' 27 | 28 | - name: Setup Gradle 29 | uses: gradle/gradle-build-action@v3 30 | with: 31 | gradle-version: 8.13 32 | 33 | - name: Release 34 | env: 35 | JRELEASER_GPG_PASSPHRASE: ${{ secrets.JRELEASER_GPG_PASSPHRASE }} 36 | JRELEASER_GPG_PUBLIC_KEY: ${{ secrets.JRELEASER_GPG_PUBLIC_KEY }} 37 | JRELEASER_GPG_SECRET_KEY: ${{ secrets.JRELEASER_GPG_SECRET_KEY }} 38 | JRELEASER_MAVENCENTRAL_USERNAME: ${{ secrets.JRELEASER_MAVENCENTRAL_USERNAME }} 39 | JRELEASER_MAVENCENTRAL_PASSWORD: ${{ secrets.JRELEASER_MAVENCENTRAL_PASSWORD }} 40 | JRELEASER_GENERIC_TOKEN: ${{ secrets.GH_PAT }} 41 | run: | 42 | if [ "${{ inputs.dry_run }}" = "true" ]; then 43 | ./gradlew publish jreleaserFullRelease -S --dryrun 44 | else 45 | ./gradlew publish jreleaserFullRelease -S 46 | fi 47 | -------------------------------------------------------------------------------- /.github/workflows/qa.yml: -------------------------------------------------------------------------------- 1 | name: QA 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | image: 7 | description: Docker full image name 8 | required: true 9 | type: string 10 | default: "docker.kurrent.io/eventstore/eventstoredb-ee:lts" 11 | 12 | jobs: 13 | test: 14 | name: Test 15 | uses: ./.github/workflows/tests.yml 16 | with: 17 | image: ${{ inputs.image }} 18 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | This changelog is no longer maintained. The information has been moved to the [GitHub release notes](https://github.com/kurrent-io/KurrentDB-Client-Java/releases) page. 2 | -------------------------------------------------------------------------------- /KurrentLogo-Black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kurrent-io/KurrentDB-Client-Java/575cf9617cfdffb615beeaff828cd696fb1f77b3/KurrentLogo-Black.png -------------------------------------------------------------------------------- /KurrentLogo-Plum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kurrent-io/KurrentDB-Client-Java/575cf9617cfdffb615beeaff828cd696fb1f77b3/KurrentLogo-Plum.png -------------------------------------------------------------------------------- /KurrentLogo-White.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kurrent-io/KurrentDB-Client-Java/575cf9617cfdffb615beeaff828cd696fb1f77b3/KurrentLogo-White.png -------------------------------------------------------------------------------- /configure-tls-for-tests.yml: -------------------------------------------------------------------------------- 1 | version: '3.5' 2 | 3 | services: 4 | volumes-provisioner: 5 | image: "hasnat/volumes-provisioner" 6 | environment: 7 | PROVISION_DIRECTORIES: "1000:1000:0755:/tmp/certs" 8 | volumes: 9 | - "./certs:/tmp/certs" 10 | network_mode: "none" 11 | 12 | setup: 13 | image: docker.eventstore.com/eventstore-utils/es-gencert-cli:latest 14 | entrypoint: bash 15 | user: "1000:1000" 16 | command: > 17 | -c "mkdir -p ./certs && cd /certs 18 | && es-gencert-cli create-ca 19 | && es-gencert-cli create-node -out ./node --dns-names localhost 20 | && es-gencert-cli create-ca -out ./untrusted-ca 21 | && find . -type f -print0 | xargs -0 chmod 666" 22 | container_name: setup 23 | volumes: 24 | - ./certs:/certs 25 | depends_on: 26 | - volumes-provisioner 27 | -------------------------------------------------------------------------------- /configure-user-certs-for-tests.yml: -------------------------------------------------------------------------------- 1 | version: '3.5' 2 | 3 | services: 4 | volumes-provisioner: 5 | image: "hasnat/volumes-provisioner" 6 | environment: 7 | PROVISION_DIRECTORIES: "1000:1000:0755:/tmp/certs" 8 | volumes: 9 | - "./certs:/tmp/certs" 10 | network_mode: "none" 11 | 12 | setup: 13 | image: docker.eventstore.com/eventstore-utils/es-gencert-cli:latest 14 | entrypoint: bash 15 | user: "1000:1000" 16 | command: > 17 | -c "mkdir -p ./certs && cd /certs 18 | && es-gencert-cli create-user -username admin 19 | && es-gencert-cli create-user -username invalid 20 | && find . -type f -print0 | xargs -0 chmod 666" 21 | container_name: setup 22 | volumes: 23 | - ./certs:/certs 24 | depends_on: 25 | - volumes-provisioner 26 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | grpcVersion=1.71.0 3 | protocVersion=4.28.2 4 | protobufVersion=4.28.2 5 | annotationApiVersion=1.3.2 6 | validationApiVersion=2.0.1.Final 7 | reactiveStreamsApiVersion=1.0.4 8 | junitVersion=5.10.0 9 | openTelemetryVersion=1.37.0 10 | openTelemetrySemConvVersion=1.25.0-alpha 11 | 12 | # Test Dependencies 13 | slf4jNopVersion=1.7.29 14 | testcontainersVersion=1.20.6 15 | jacksonVersion=2.18.3 16 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kurrent-io/KurrentDB-Client-Java/575cf9617cfdffb615beeaff828cd696fb1f77b3/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was generated by the Gradle 'init' task. 3 | * 4 | * The settings file is used to specify which projects to include in your build. 5 | * 6 | * Detailed information about configuring a multi-project build in Gradle can be found 7 | * in the user manual at https://docs.gradle.org/6.6.1/userguide/multi_project_builds.html 8 | */ 9 | 10 | rootProject.name = 'kurrentdb-client' 11 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/AbortProjection.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import io.kurrent.dbclient.proto.projections.Projectionmanagement; 4 | import io.kurrent.dbclient.proto.projections.ProjectionsGrpc; 5 | 6 | import java.util.concurrent.CompletableFuture; 7 | 8 | class AbortProjection { 9 | private final GrpcClient client; 10 | private final String projectionName; 11 | private final AbortProjectionOptions options; 12 | 13 | public AbortProjection(final GrpcClient client, final String projectionName, final AbortProjectionOptions options) { 14 | this.client = client; 15 | this.projectionName = projectionName; 16 | this.options = options; 17 | } 18 | 19 | public CompletableFuture execute() { 20 | return this.client.run(channel -> { 21 | Projectionmanagement.DisableReq.Options.Builder optionsBuilder = 22 | Projectionmanagement.DisableReq.Options.newBuilder() 23 | .setName(this.projectionName) 24 | .setWriteCheckpoint(false); 25 | 26 | Projectionmanagement.DisableReq request = Projectionmanagement.DisableReq.newBuilder() 27 | .setOptions(optionsBuilder) 28 | .build(); 29 | 30 | ProjectionsGrpc.ProjectionsStub client = 31 | GrpcUtils.configureStub(ProjectionsGrpc.newStub(channel), this.client.getSettings(), this.options); 32 | 33 | CompletableFuture result = new CompletableFuture<>(); 34 | 35 | client.disable(request, GrpcUtils.convertSingleResponse(result)); 36 | 37 | return result; 38 | }); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/AbortProjectionOptions.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Options of the abort projection request. 5 | */ 6 | public class AbortProjectionOptions extends OptionsBase { 7 | private AbortProjectionOptions() { 8 | } 9 | 10 | /** 11 | * Returns options with default values. 12 | * @return options 13 | */ 14 | public static AbortProjectionOptions get() { 15 | return new AbortProjectionOptions(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/AbstractDeletePersistentSubscription.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import io.kurrent.dbclient.proto.persistentsubscriptions.Persistent; 4 | import io.kurrent.dbclient.proto.persistentsubscriptions.PersistentSubscriptionsGrpc; 5 | 6 | import java.util.concurrent.CompletableFuture; 7 | 8 | abstract class AbstractDeletePersistentSubscription { 9 | private final GrpcClient client; 10 | private final String group; 11 | private final DeletePersistentSubscriptionOptions options; 12 | 13 | public AbstractDeletePersistentSubscription(GrpcClient client, String group, DeletePersistentSubscriptionOptions options) { 14 | this.client = client; 15 | this.group = group; 16 | this.options = options; 17 | } 18 | 19 | protected abstract Persistent.DeleteReq.Options.Builder createOptions(); 20 | 21 | @SuppressWarnings("unchecked") 22 | public CompletableFuture execute() { 23 | return this.client.runWithArgs(args -> { 24 | CompletableFuture result = new CompletableFuture(); 25 | PersistentSubscriptionsGrpc.PersistentSubscriptionsStub client = 26 | GrpcUtils.configureStub(PersistentSubscriptionsGrpc.newStub(args.getChannel()), this.client.getSettings(), this.options); 27 | 28 | Persistent.DeleteReq req = Persistent.DeleteReq.newBuilder() 29 | .setOptions(createOptions() 30 | .setGroupName(group)) 31 | .build(); 32 | 33 | if (req.getOptions().hasAll() && !args.supportFeature(FeatureFlags.PERSISTENT_SUBSCRIPTION_TO_ALL)) { 34 | result.completeExceptionally(new UnsupportedFeatureException()); 35 | } else { 36 | client.delete(req, GrpcUtils.convertSingleResponse(result)); 37 | } 38 | 39 | return result; 40 | }); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/AbstractRead.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import io.kurrent.dbclient.proto.shared.Shared; 4 | import io.kurrent.dbclient.proto.streams.StreamsGrpc; 5 | import io.kurrent.dbclient.proto.streams.StreamsOuterClass; 6 | import org.reactivestreams.Publisher; 7 | import org.reactivestreams.Subscriber; 8 | 9 | abstract class AbstractRead implements Publisher { 10 | protected static final StreamsOuterClass.ReadReq.Options.Builder defaultReadOptions; 11 | 12 | private final GrpcClient client; 13 | private final OptionsWithBackPressure options; 14 | 15 | protected AbstractRead(GrpcClient client, OptionsWithBackPressure options) { 16 | this.client = client; 17 | this.options = options; 18 | } 19 | 20 | static { 21 | defaultReadOptions = StreamsOuterClass.ReadReq.Options.newBuilder() 22 | .setUuidOption(StreamsOuterClass.ReadReq.Options.UUIDOption.newBuilder() 23 | .setStructured(Shared.Empty.getDefaultInstance())); 24 | } 25 | 26 | public abstract StreamsOuterClass.ReadReq.Options.Builder createOptions(); 27 | 28 | @Override 29 | public void subscribe(Subscriber subscriber) { 30 | ReadResponseObserver observer = new ReadResponseObserver(options, new ReadStreamConsumer(subscriber)); 31 | 32 | this.client.getWorkItemArgs().whenComplete((args, error) -> { 33 | if (error != null) { 34 | observer.onError(error); 35 | return; 36 | } 37 | 38 | StreamsOuterClass.ReadReq request = StreamsOuterClass.ReadReq.newBuilder() 39 | .setOptions(createOptions()) 40 | .build(); 41 | 42 | StreamsGrpc.StreamsStub client = GrpcUtils.configureStub(StreamsGrpc.newStub(args.getChannel()), this.client.getSettings(), this.options); 43 | observer.onConnected(args); 44 | subscriber.onSubscribe(observer.getSubscription()); 45 | client.read(request, observer); 46 | }); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/Acl.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Common access control list (ACL) interface. 5 | */ 6 | public interface Acl {} 7 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/Acls.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Access control list (ACL) utility class. 5 | */ 6 | public final class Acls { 7 | 8 | /** 9 | * Returns a default stream ACL. 10 | * @see StreamAcl 11 | * @return acl 12 | */ 13 | public static StreamAcl newStreamAcl() { 14 | return new StreamAcl(); 15 | } 16 | 17 | /** 18 | * Returns a default user ACL. 19 | * @see UserStreamAcl 20 | * @return acl 21 | */ 22 | public static Acl newUserStreamAcl() { 23 | return UserStreamAcl.getInstance(); 24 | } 25 | 26 | /** 27 | * Returns a default system ACL. 28 | * @see SystemStreamAcl 29 | * @return acl 30 | */ 31 | public static Acl newSystemStreamAcl() { 32 | return SystemStreamAcl.getInstance(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/AppendToStreamOptions.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Options of the append stream request. 5 | */ 6 | public class AppendToStreamOptions extends OptionsWithStreamStateBase { 7 | private AppendToStreamOptions() { 8 | } 9 | 10 | /** 11 | * Returns options with default values. 12 | */ 13 | public static AppendToStreamOptions get() { 14 | return new AppendToStreamOptions(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/Checkpointer.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import java.util.concurrent.CompletableFuture; 4 | 5 | /** 6 | * Callback type when a checkpoint is reached. 7 | */ 8 | @FunctionalInterface 9 | public interface Checkpointer { 10 | /** 11 | * Called everytime a checkpoint is reached. 12 | * @param subscription Subscription handle. 13 | * @param position Transaction log position. 14 | */ 15 | CompletableFuture onCheckpoint(Subscription subscription, Position position); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/ClientCertificate.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import java.util.Objects; 4 | 5 | /** 6 | * Holds a certificate and key for authenticated requests. 7 | */ 8 | public final class ClientCertificate { 9 | private final String certFile; 10 | private final String keyFile; 11 | 12 | public ClientCertificate(String certFile, String keyFile) { 13 | this.certFile = certFile; 14 | this.keyFile = keyFile; 15 | } 16 | 17 | /** 18 | * Certificate for user authentication. 19 | */ 20 | public String getCertFile() { 21 | return certFile; 22 | } 23 | 24 | /** 25 | * Certificate key for user authentication. 26 | */ 27 | public String getKeyFile() { 28 | return keyFile; 29 | } 30 | 31 | @Override 32 | public boolean equals(Object obj) { 33 | if (this == obj) { 34 | return true; 35 | } 36 | if (obj == null || getClass() != obj.getClass()) { 37 | return false; 38 | } 39 | 40 | ClientCertificate other = (ClientCertificate) obj; 41 | 42 | return Objects.equals(certFile, other.certFile) 43 | && Objects.equals(keyFile, other.keyFile); 44 | } 45 | 46 | @Override 47 | public int hashCode() { 48 | return Objects.hash(certFile, keyFile); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/ClientFeatureFlags.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | public final class ClientFeatureFlags { 4 | /** 5 | * Enables direct DNS name resolution, retrieving all IP addresses associated with a given hostname. This 6 | * functionality was initially implemented to support the now-deprecated TCP API. It is particularly useful in 7 | * scenarios involving clusters, where node discovery is enabled. 8 | */ 9 | public static final String DNS_LOOKUP = "dns-lookup"; 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/ClientTelemetryAttributes.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import io.opentelemetry.semconv.ExceptionAttributes; 4 | import io.opentelemetry.semconv.ServerAttributes; 5 | import io.opentelemetry.semconv.incubating.DbIncubatingAttributes; 6 | 7 | public class ClientTelemetryAttributes { 8 | public static class Database { 9 | public static final String USER = DbIncubatingAttributes.DB_USER.getKey(); 10 | public static final String SYSTEM = DbIncubatingAttributes.DB_SYSTEM.getKey(); 11 | public static final String OPERATION = DbIncubatingAttributes.DB_OPERATION.getKey(); 12 | } 13 | 14 | public static class Server { 15 | public static final String ADDRESS = ServerAttributes.SERVER_ADDRESS.getKey(); 16 | public static final String PORT = ServerAttributes.SERVER_PORT.getKey(); 17 | } 18 | 19 | public static class Exceptions { 20 | public static final String TYPE = ExceptionAttributes.EXCEPTION_TYPE.getKey(); 21 | public static final String MESSAGE = ExceptionAttributes.EXCEPTION_MESSAGE.getKey(); 22 | public static final String STACK_TRACE = ExceptionAttributes.EXCEPTION_STACKTRACE.getKey(); 23 | } 24 | 25 | public static class KurrentDB { 26 | public static final String STREAM = "db.kurrentdb.stream"; 27 | public static final String SUBSCRIPTION_ID = "db.kurrentdb.subscription.id"; 28 | public static final String EVENT_ID = "db.kurrentdb.event.id"; 29 | public static final String EVENT_TYPE = "db.kurrentdb.event.type"; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/ClientTelemetryConstants.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | public class ClientTelemetryConstants { 4 | public static final String INSTRUMENTATION_NAME = "kurrentdb"; 5 | 6 | public static class Metadata { 7 | public static final String TRACE_ID = "$traceId"; 8 | public static final String SPAN_ID = "$spanId"; 9 | } 10 | 11 | public static class Operations { 12 | public static final String APPEND = "streams.append"; 13 | public static final String SUBSCRIBE = "streams.subscribe"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/ConnectionMetadata.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import io.grpc.Metadata; 4 | 5 | import java.util.Map; 6 | 7 | class ConnectionMetadata { 8 | private Metadata metadata; 9 | 10 | public ConnectionMetadata() { 11 | this.metadata = new Metadata(); 12 | } 13 | 14 | public ConnectionMetadata authenticated(UserCredentials credentials) { 15 | this.metadata.put(Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER), credentials.basicAuthHeader()); 16 | return this; 17 | } 18 | 19 | public boolean hasUserCredentials() { 20 | return this.metadata.containsKey(Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER)); 21 | } 22 | 23 | public String getUserCredentials() { 24 | return this.metadata.get(Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER)); 25 | } 26 | 27 | public ConnectionMetadata requiresLeader() { 28 | this.metadata.put(Metadata.Key.of("requires-leader", Metadata.ASCII_STRING_MARSHALLER), String.valueOf(true)); 29 | return this; 30 | } 31 | 32 | public ConnectionMetadata headers(Map headers) { 33 | for (Map.Entry entry : headers.entrySet()) 34 | this.metadata.put(Metadata.Key.of(entry.getKey(), Metadata.ASCII_STRING_MARSHALLER), entry.getValue()); 35 | 36 | return this; 37 | } 38 | 39 | public Metadata build() { 40 | return this.metadata; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/ConnectionShutdownException.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * When a connection is already closed. 5 | */ 6 | public class ConnectionShutdownException extends RuntimeException { 7 | ConnectionShutdownException() { 8 | super("The connection is closed"); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/ConnectionStringParsingException.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * When the provided connection string is malformed. 5 | */ 6 | public class ConnectionStringParsingException extends Exception { 7 | ConnectionStringParsingException(String connectionString, int from, int to, String expected) { 8 | super("Unexpected " + connectionString.substring(from, from == to ? from + 1 : to) + " at position " + from + ", expected " + expected); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/Consts.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | class Consts { 4 | public static long DEFAULT_KEEP_ALIVE_TIMEOUT_IN_MS = 10000; // 10secs 5 | public static long DEFAULT_KEEP_ALIVE_INTERVAL_IN_MS = 10000; // 10secs 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/ContentType.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | class ContentType { 4 | public static final String JSON = "application/json"; 5 | public static final String BYTES = "application/octet-stream"; 6 | } -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/CreateChannel.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import java.net.InetSocketAddress; 4 | import java.util.StringJoiner; 5 | import java.util.UUID; 6 | 7 | class CreateChannel implements Msg { 8 | final InetSocketAddress channel; 9 | final UUID previousId; 10 | 11 | public CreateChannel(UUID previousId) { 12 | this.channel = null; 13 | this.previousId = previousId; 14 | } 15 | 16 | public CreateChannel(UUID previousId, InetSocketAddress endpoint) { 17 | this.channel = endpoint; 18 | this.previousId = previousId; 19 | } 20 | 21 | @Override 22 | public String toString() { 23 | return new StringJoiner(", ", CreateChannel.class.getSimpleName() + "[", "]") 24 | .add("endpoint=" + (channel != null ? channel.toString() : "NOT_SET")) 25 | .toString(); 26 | } 27 | 28 | @Override 29 | public void accept(ConnectionService connectionService) { 30 | connectionService.createChannel(this.previousId, this.channel); 31 | } 32 | } -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/CreatePersistentSubscriptionToAll.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import io.kurrent.dbclient.proto.persistentsubscriptions.Persistent; 4 | import io.kurrent.dbclient.proto.shared.Shared; 5 | 6 | class CreatePersistentSubscriptionToAll extends AbstractCreatePersistentSubscription { 7 | private final CreatePersistentSubscriptionToAllOptions options; 8 | 9 | public CreatePersistentSubscriptionToAll(GrpcClient client, String group, 10 | CreatePersistentSubscriptionToAllOptions options) { 11 | super(client, group, options.getSettings(), options); 12 | this.options = options; 13 | } 14 | 15 | @Override 16 | protected Persistent.CreateReq.Options.Builder createOptions() { 17 | Persistent.CreateReq.Options.Builder optionsBuilder = Persistent.CreateReq.Options.newBuilder(); 18 | Persistent.CreateReq.AllOptions.Builder allOptionsBuilder = Persistent.CreateReq.AllOptions.newBuilder(); 19 | StreamPosition position = this.options.getSettings().getStartFrom(); 20 | 21 | if (position instanceof StreamPosition.Start) { 22 | allOptionsBuilder.setStart(Shared.Empty.newBuilder()); 23 | } else if (position instanceof StreamPosition.End) { 24 | allOptionsBuilder.setEnd(Shared.Empty.newBuilder()); 25 | } else { 26 | Position pos = position.getPositionOrThrow(); 27 | allOptionsBuilder.setPosition(Persistent.CreateReq.Position.newBuilder() 28 | .setCommitPosition(pos.getCommitUnsigned()) 29 | .setPreparePosition(pos.getPrepareUnsigned())); 30 | } 31 | 32 | SubscriptionFilter filter = options.getFilter(); 33 | if (filter != null) { 34 | filter.addToWirePersistentCreateReq(allOptionsBuilder); 35 | } else { 36 | allOptionsBuilder.setNoFilter(Shared.Empty.getDefaultInstance()); 37 | } 38 | 39 | optionsBuilder.setAll(allOptionsBuilder); 40 | 41 | return optionsBuilder; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/CreatePersistentSubscriptionToStream.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import io.kurrent.dbclient.proto.persistentsubscriptions.Persistent; 4 | import io.kurrent.dbclient.proto.shared.Shared; 5 | import com.google.protobuf.ByteString; 6 | 7 | class CreatePersistentSubscriptionToStream extends AbstractCreatePersistentSubscription { 8 | private final String stream; 9 | private final PersistentSubscriptionToStreamSettings settings; 10 | 11 | public CreatePersistentSubscriptionToStream(GrpcClient client, String stream, String group, 12 | CreatePersistentSubscriptionToStreamOptions options) { 13 | super(client, group, options.getSettings(), options); 14 | this.stream = stream; 15 | this.settings = options.getSettings(); 16 | } 17 | 18 | @Override 19 | protected Persistent.CreateReq.Settings.Builder createSettings() { 20 | return Persistent.CreateReq.Settings.newBuilder(); 21 | } 22 | 23 | @Override 24 | @SuppressWarnings("deprecation") 25 | // We have to support the setStreamIdentifier call while 20.10LTS is still supported. 26 | protected Persistent.CreateReq.Options.Builder createOptions() { 27 | Persistent.CreateReq.Options.Builder optionsBuilder = Persistent.CreateReq.Options.newBuilder(); 28 | Shared.StreamIdentifier.Builder streamIdentifierBuilder = Shared.StreamIdentifier.newBuilder(); 29 | Persistent.CreateReq.StreamOptions.Builder streamOptionsBuilder = Persistent.CreateReq.StreamOptions 30 | .newBuilder(); 31 | StreamPosition position = settings.getStartFrom(); 32 | if (position instanceof StreamPosition.Start) { 33 | streamOptionsBuilder.setStart(Shared.Empty.newBuilder()); 34 | } else if (position instanceof StreamPosition.End) { 35 | streamOptionsBuilder.setEnd(Shared.Empty.newBuilder()); 36 | } else { 37 | streamOptionsBuilder.setRevision(position.getPositionOrThrow()); 38 | } 39 | 40 | streamIdentifierBuilder.setStreamName(ByteString.copyFromUtf8(stream)); 41 | streamOptionsBuilder.setStreamIdentifier(streamIdentifierBuilder); 42 | optionsBuilder.setStream(streamOptionsBuilder); 43 | optionsBuilder.setStreamIdentifier(streamIdentifierBuilder); 44 | 45 | return optionsBuilder; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/CreatePersistentSubscriptionToStreamOptions.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Options for the create persistent subscription to stream request. 5 | */ 6 | public class CreatePersistentSubscriptionToStreamOptions 7 | extends AbstractPersistentSubscriptionSettingsBuilder { 8 | CreatePersistentSubscriptionToStreamOptions() { 9 | super(PersistentSubscriptionSettings.defaultRegular()); 10 | } 11 | 12 | /** 13 | * Returns options with default values. 14 | */ 15 | public static CreatePersistentSubscriptionToStreamOptions get() { 16 | return new CreatePersistentSubscriptionToStreamOptions(); 17 | } 18 | 19 | /** 20 | * Starts the subscription from the beginning of the given stream. 21 | 22 | */ 23 | public CreatePersistentSubscriptionToStreamOptions fromStart() { 24 | getSettings().setStartFrom(StreamPosition.start()); 25 | return this; 26 | } 27 | 28 | /** 29 | * Starts the subscription from the end of the given stream. 30 | 31 | */ 32 | public CreatePersistentSubscriptionToStreamOptions fromEnd() { 33 | getSettings().setStartFrom(StreamPosition.end()); 34 | return this; 35 | } 36 | 37 | /** 38 | * Starts the subscription from the given stream revision. 39 | */ 40 | public CreatePersistentSubscriptionToStreamOptions startFrom(long revision) { 41 | getSettings().setStartFrom(StreamPosition.position(revision)); 42 | return this; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/CreateProjectionOptions.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Options for create projection request. 5 | */ 6 | public class CreateProjectionOptions extends OptionsBase { 7 | private boolean trackEmittedStreams; 8 | private boolean emitEnabled; 9 | 10 | private CreateProjectionOptions() { 11 | this.trackEmittedStreams = false; 12 | } 13 | 14 | /** 15 | * Returns options with default values. 16 | */ 17 | public static CreateProjectionOptions get() { 18 | return new CreateProjectionOptions(); 19 | } 20 | 21 | boolean isTrackingEmittedStreams() { 22 | return trackEmittedStreams; 23 | } 24 | 25 | boolean isEmitEnabled() { 26 | return emitEnabled; 27 | } 28 | 29 | /** 30 | * If true, the projection tracks all streams it creates. 31 | */ 32 | public CreateProjectionOptions trackEmittedStreams(boolean trackEmittedStreams) { 33 | this.trackEmittedStreams = trackEmittedStreams; 34 | return this; 35 | } 36 | 37 | /** 38 | * If true, allows the projection to emit events. 39 | */ 40 | public CreateProjectionOptions emitEnabled(boolean value) { 41 | this.emitEnabled = value; 42 | return this; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/DeletePersistentSubscriptionOptions.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Options of the delete persistent subscription request. 5 | */ 6 | public class DeletePersistentSubscriptionOptions extends OptionsBase { 7 | private DeletePersistentSubscriptionOptions() { 8 | } 9 | 10 | /** 11 | * Returns options with default values. 12 | */ 13 | public static DeletePersistentSubscriptionOptions get() { 14 | return new DeletePersistentSubscriptionOptions(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/DeletePersistentSubscriptionToAll.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import io.kurrent.dbclient.proto.persistentsubscriptions.Persistent; 4 | import io.kurrent.dbclient.proto.shared.Shared; 5 | 6 | class DeletePersistentSubscriptionToAll extends AbstractDeletePersistentSubscription { 7 | public DeletePersistentSubscriptionToAll(GrpcClient client, String group, 8 | DeletePersistentSubscriptionOptions options) { 9 | super(client, group, options); 10 | } 11 | 12 | @Override 13 | protected Persistent.DeleteReq.Options.Builder createOptions() { 14 | return Persistent.DeleteReq.Options.newBuilder() 15 | .setAll(Shared.Empty.newBuilder()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/DeletePersistentSubscriptionToStream.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import io.kurrent.dbclient.proto.persistentsubscriptions.Persistent; 4 | import io.kurrent.dbclient.proto.shared.Shared; 5 | import com.google.protobuf.ByteString; 6 | 7 | class DeletePersistentSubscriptionToStream extends AbstractDeletePersistentSubscription { 8 | private String stream; 9 | 10 | public DeletePersistentSubscriptionToStream(GrpcClient client, String stream, String group, 11 | DeletePersistentSubscriptionOptions options) { 12 | super(client, group, options); 13 | this.stream = stream; 14 | } 15 | 16 | @Override 17 | protected Persistent.DeleteReq.Options.Builder createOptions() { 18 | Shared.StreamIdentifier.Builder streamIdentifier = 19 | Shared.StreamIdentifier.newBuilder() 20 | .setStreamName(ByteString.copyFromUtf8(stream)); 21 | 22 | return Persistent.DeleteReq.Options.newBuilder() 23 | .setStreamIdentifier(streamIdentifier); 24 | } 25 | } -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/DeleteProjection.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import io.kurrent.dbclient.proto.projections.Projectionmanagement; 4 | import io.kurrent.dbclient.proto.projections.ProjectionsGrpc; 5 | 6 | import java.util.concurrent.CompletableFuture; 7 | 8 | class DeleteProjection { 9 | private final GrpcClient client; 10 | private final String projectionName; 11 | private final DeleteProjectionOptions options; 12 | 13 | public DeleteProjection(final GrpcClient client, final String projectionName, final DeleteProjectionOptions options) { 14 | this.client = client; 15 | this.projectionName = projectionName; 16 | this.options = options; 17 | } 18 | 19 | public CompletableFuture execute() { 20 | return this.client.run(channel -> { 21 | Projectionmanagement.DeleteReq.Options reqOptions = 22 | Projectionmanagement.DeleteReq.Options.newBuilder() 23 | .setName(this.projectionName) 24 | .setDeleteCheckpointStream(options.getDeleteCheckpointStream()) 25 | .setDeleteEmittedStreams(options.getDeleteEmittedStreams()) 26 | .setDeleteStateStream(options.getDeleteStateStream()) 27 | .build(); 28 | 29 | Projectionmanagement.DeleteReq request = Projectionmanagement.DeleteReq.newBuilder() 30 | .setOptions(reqOptions) 31 | .build(); 32 | 33 | ProjectionsGrpc.ProjectionsStub client = 34 | GrpcUtils.configureStub(ProjectionsGrpc.newStub(channel), this.client.getSettings(), this.options); 35 | 36 | CompletableFuture result = new CompletableFuture<>(); 37 | 38 | client.delete(request, GrpcUtils.convertSingleResponse(result)); 39 | 40 | return result; 41 | }); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/DeleteProjectionOptions.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Options of the delete projection request. 5 | */ 6 | public class DeleteProjectionOptions extends OptionsBase { 7 | private boolean deleteEmittedStreams; 8 | private boolean deleteStateStream; 9 | private boolean deleteCheckpointStream; 10 | 11 | private DeleteProjectionOptions() { 12 | } 13 | 14 | /** 15 | * Returns options with default values. 16 | */ 17 | public static DeleteProjectionOptions get() { 18 | return new DeleteProjectionOptions(); 19 | } 20 | 21 | /** 22 | * Deletes emitted streams if the projections had track emitted streams enabled. 23 | */ 24 | public DeleteProjectionOptions deleteEmittedStreams() { 25 | deleteEmittedStreams = true; 26 | return this; 27 | } 28 | 29 | /** 30 | * Deletes the projection state stream. 31 | */ 32 | public DeleteProjectionOptions deleteStateStream() { 33 | deleteStateStream = true; 34 | return this; 35 | } 36 | 37 | /** 38 | * Deletes the projection checkpoint stream. 39 | */ 40 | public DeleteProjectionOptions deleteCheckpointStream() { 41 | deleteCheckpointStream = true; 42 | return this; 43 | } 44 | 45 | /** 46 | * If true, deletes emitted streams if the projections had track emitted streams enabled. 47 | */ 48 | public DeleteProjectionOptions deleteEmittedStreams(boolean delete) { 49 | deleteEmittedStreams = delete; 50 | return this; 51 | } 52 | 53 | /** 54 | * If true, deletes the projection state stream. 55 | */ 56 | public DeleteProjectionOptions deleteStateStream(boolean delete) { 57 | deleteStateStream = delete; 58 | return this; 59 | } 60 | 61 | /** 62 | * If true, deletes the projection checkpoint stream. 63 | */ 64 | public DeleteProjectionOptions deleteCheckpointStream(boolean delete) { 65 | deleteCheckpointStream = delete; 66 | return this; 67 | } 68 | 69 | boolean getDeleteEmittedStreams() { 70 | return deleteEmittedStreams; 71 | } 72 | 73 | boolean getDeleteStateStream() { 74 | return deleteStateStream; 75 | } 76 | 77 | boolean getDeleteCheckpointStream() { 78 | return deleteCheckpointStream; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/DeleteResult.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import javax.validation.constraints.NotNull; 4 | import java.util.Objects; 5 | 6 | /** 7 | * Object returned on a successful stream deletion request. 8 | */ 9 | public class DeleteResult { 10 | private final Position logPosition; 11 | 12 | DeleteResult(@NotNull Position logPosition) { 13 | this.logPosition = logPosition; 14 | } 15 | 16 | /** 17 | * Returns the transaction log position of the stream deletion. 18 | */ 19 | public Position getPosition() { 20 | return logPosition; 21 | } 22 | 23 | @Override 24 | public boolean equals(Object o) { 25 | if (this == o) return true; 26 | if (o == null || getClass() != o.getClass()) return false; 27 | DeleteResult that = (DeleteResult) o; 28 | return logPosition.equals(that.logPosition); 29 | } 30 | 31 | @Override 32 | public int hashCode() { 33 | return Objects.hash(logPosition); 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return "DeleteResult{" + 39 | "logPosition=" + logPosition + 40 | '}'; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/DeleteStreamOptions.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Options of the delete or tombstone stream request. 5 | */ 6 | public class DeleteStreamOptions extends OptionsWithStreamStateBase { 7 | DeleteStreamOptions() {} 8 | 9 | /** 10 | * Returns options with default values. 11 | */ 12 | public static DeleteStreamOptions get() { 13 | return new DeleteStreamOptions(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/Direction.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Specifies the direction of a read operation. 5 | */ 6 | public enum Direction { 7 | /** 8 | * Read in the forward direction. 9 | */ 10 | Forwards, 11 | /** 12 | * Read in the backward direction. 13 | */ 14 | Backwards 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/DisableProjection.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import io.kurrent.dbclient.proto.projections.Projectionmanagement; 4 | import io.kurrent.dbclient.proto.projections.ProjectionsGrpc; 5 | 6 | import java.util.concurrent.CompletableFuture; 7 | 8 | class DisableProjection { 9 | private final GrpcClient client; 10 | private final String projectionName; 11 | private final DisableProjectionOptions options; 12 | 13 | public DisableProjection(final GrpcClient client, final String projectionName, final DisableProjectionOptions options) { 14 | this.client = client; 15 | this.projectionName = projectionName; 16 | this.options = options; 17 | } 18 | 19 | public CompletableFuture execute() { 20 | return this.client.run(channel -> { 21 | Projectionmanagement.DisableReq.Options.Builder optionsBuilder = 22 | Projectionmanagement.DisableReq.Options.newBuilder() 23 | .setName(this.projectionName) 24 | .setWriteCheckpoint(true); 25 | 26 | Projectionmanagement.DisableReq request = Projectionmanagement.DisableReq.newBuilder() 27 | .setOptions(optionsBuilder) 28 | .build(); 29 | 30 | ProjectionsGrpc.ProjectionsStub client = 31 | GrpcUtils.configureStub(ProjectionsGrpc.newStub(channel), this.client.getSettings(), this.options); 32 | 33 | CompletableFuture result = new CompletableFuture<>(); 34 | 35 | client.disable(request, GrpcUtils.convertSingleResponse(result)); 36 | 37 | return result; 38 | }); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/DisableProjectionOptions.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Options of the disable projection request. 5 | */ 6 | public class DisableProjectionOptions extends OptionsBase { 7 | private DisableProjectionOptions() { 8 | } 9 | 10 | /** 11 | * Returns options with default values. 12 | */ 13 | public static DisableProjectionOptions get() { 14 | return new DisableProjectionOptions(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/Discovery.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import java.util.concurrent.CompletableFuture; 4 | 5 | public interface Discovery { 6 | CompletableFuture run(ConnectionState state); 7 | } -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/EnableProjection.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import io.kurrent.dbclient.proto.projections.Projectionmanagement; 4 | import io.kurrent.dbclient.proto.projections.ProjectionsGrpc; 5 | 6 | import java.util.concurrent.CompletableFuture; 7 | 8 | class EnableProjection { 9 | private final GrpcClient client; 10 | private final String projectionName; 11 | private EnableProjectionOptions options; 12 | 13 | public EnableProjection(final GrpcClient client, final String projectionName, final EnableProjectionOptions options) { 14 | this.client = client; 15 | this.projectionName = projectionName; 16 | this.options = options; 17 | } 18 | 19 | public CompletableFuture execute() { 20 | return this.client.run(channel -> { 21 | Projectionmanagement.EnableReq.Options.Builder optionsBuilder = 22 | Projectionmanagement.EnableReq.Options.newBuilder() 23 | .setName(this.projectionName); 24 | 25 | Projectionmanagement.EnableReq request = Projectionmanagement.EnableReq.newBuilder() 26 | .setOptions(optionsBuilder) 27 | .build(); 28 | 29 | ProjectionsGrpc.ProjectionsStub client = 30 | GrpcUtils.configureStub(ProjectionsGrpc.newStub(channel), this.client.getSettings(), this.options); 31 | 32 | CompletableFuture result = new CompletableFuture<>(); 33 | 34 | client.enable(request, GrpcUtils.convertSingleResponse(result)); 35 | 36 | return result; 37 | }); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/EnableProjectionOptions.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Options of the enable projection request. 5 | */ 6 | public class EnableProjectionOptions extends OptionsBase { 7 | private EnableProjectionOptions() { 8 | } 9 | 10 | /** 11 | * Returns options with default values. 12 | */ 13 | public static EnableProjectionOptions get() { 14 | return new EnableProjectionOptions(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/EventFilter.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import java.util.Optional; 4 | 5 | interface EventFilter { 6 | PrefixFilterExpression[] getPrefixFilterExpressions(); 7 | 8 | RegularFilterExpression getRegularFilterExpression(); 9 | 10 | Optional getMaxSearchWindow(); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/EventTypeFilter.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import javax.validation.constraints.NotNull; 4 | import java.util.Arrays; 5 | import java.util.Objects; 6 | import java.util.Optional; 7 | 8 | class EventTypeFilter implements EventFilter { 9 | private final PrefixFilterExpression[] prefixFilterExpressions; 10 | private final RegularFilterExpression regularFilterExpression; 11 | @NotNull 12 | private final Optional maxSearchWindow; 13 | 14 | EventTypeFilter(@NotNull Optional maxSearchWindow, RegularFilterExpression regex) { 15 | this.maxSearchWindow = maxSearchWindow; 16 | this.regularFilterExpression = regex; 17 | this.prefixFilterExpressions = null; 18 | } 19 | 20 | EventTypeFilter(@NotNull Optional maxSearchWindow, PrefixFilterExpression... prefixes) { 21 | this.maxSearchWindow = maxSearchWindow; 22 | this.prefixFilterExpressions = prefixes; 23 | this.regularFilterExpression = null; 24 | } 25 | 26 | @Override 27 | public PrefixFilterExpression[] getPrefixFilterExpressions() { 28 | return this.prefixFilterExpressions; 29 | } 30 | 31 | @Override 32 | public RegularFilterExpression getRegularFilterExpression() { 33 | return regularFilterExpression; 34 | } 35 | 36 | @Override 37 | public Optional getMaxSearchWindow() { 38 | return maxSearchWindow; 39 | } 40 | 41 | @Override 42 | public boolean equals(Object o) { 43 | if (this == o) return true; 44 | if (o == null || getClass() != o.getClass()) return false; 45 | EventTypeFilter that = (EventTypeFilter) o; 46 | return Arrays.equals(prefixFilterExpressions, that.prefixFilterExpressions) && 47 | Objects.equals(regularFilterExpression, that.regularFilterExpression) && 48 | maxSearchWindow.equals(that.maxSearchWindow); 49 | } 50 | 51 | @Override 52 | public int hashCode() { 53 | int result = Objects.hash(regularFilterExpression, maxSearchWindow); 54 | result = 31 * result + Arrays.hashCode(prefixFilterExpressions); 55 | return result; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/FeatureFlags.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | class FeatureFlags { 3 | public final static int NOTHING = 0; 4 | public final static int BATCH_APPEND = 1; 5 | public final static int PERSISTENT_SUBSCRIPTION_LIST = 2; 6 | public final static int PERSISTENT_SUBSCRIPTION_REPLAY = 4; 7 | public final static int PERSISTENT_SUBSCRIPTION_RESTART_SUBSYSTEM = 8; 8 | public final static int PERSISTENT_SUBSCRIPTION_GET_INFO = 16; 9 | public final static int PERSISTENT_SUBSCRIPTION_TO_ALL = 32; 10 | public final static int PERSISTENT_SUBSCRIPTION_MANAGEMENT = PERSISTENT_SUBSCRIPTION_LIST | PERSISTENT_SUBSCRIPTION_REPLAY | PERSISTENT_SUBSCRIPTION_GET_INFO | PERSISTENT_SUBSCRIPTION_RESTART_SUBSYSTEM; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/GetPersistentSubscriptionInfoOptions.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Options of the get persistent subscription info request. 5 | */ 6 | public class GetPersistentSubscriptionInfoOptions extends OptionsBase { 7 | /** 8 | * Returns options with default values. 9 | */ 10 | public static GetPersistentSubscriptionInfoOptions get() { 11 | return new GetPersistentSubscriptionInfoOptions(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/GetProjectionResultOptions.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Options of the get projection result request. 5 | */ 6 | public class GetProjectionResultOptions extends OptionsBase { 7 | private String partition; 8 | 9 | public GetProjectionResultOptions() { 10 | this.partition = ""; 11 | } 12 | 13 | /** 14 | * Returns options with default values. 15 | */ 16 | public static GetProjectionResultOptions get() { 17 | return new GetProjectionResultOptions(); 18 | } 19 | 20 | String getPartition() { 21 | return this.partition; 22 | } 23 | 24 | /** 25 | * Specifies which partition to retrieve the result from. 26 | */ 27 | public GetProjectionResultOptions partition(String partition) { 28 | this.partition = partition; 29 | return this; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/GetProjectionStateOptions.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Options of the get projection state request. 5 | */ 6 | public class GetProjectionStateOptions extends OptionsBase { 7 | private String partition; 8 | 9 | private GetProjectionStateOptions() { 10 | this.partition = ""; 11 | } 12 | 13 | /** 14 | * Returns options with default values. 15 | */ 16 | public static GetProjectionStateOptions get() { 17 | return new GetProjectionStateOptions(); 18 | } 19 | 20 | /** 21 | * Specifies which partition to retrieve the state from. 22 | */ 23 | public GetProjectionStateOptions partition(String partition) { 24 | this.partition = partition; 25 | return this; 26 | } 27 | 28 | String getPartition() { 29 | return this.partition; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/GetProjectionStatistics.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import io.kurrent.dbclient.proto.projections.Projectionmanagement; 4 | import io.kurrent.dbclient.proto.projections.ProjectionsGrpc; 5 | 6 | import java.util.concurrent.CompletableFuture; 7 | 8 | 9 | class GetProjectionStatistics { 10 | private final GrpcClient client; 11 | private final String projectionName; 12 | private final GetProjectionStatisticsOptions options; 13 | 14 | public GetProjectionStatistics(final GrpcClient client, final String projectionName, final GetProjectionStatisticsOptions options) { 15 | this.client = client; 16 | this.projectionName = projectionName; 17 | this.options = options; 18 | } 19 | 20 | public CompletableFuture execute() { 21 | return this.client.run(channel -> { 22 | Projectionmanagement.StatisticsReq.Options.Builder optionsBuilder = 23 | Projectionmanagement.StatisticsReq.Options.newBuilder() 24 | .setName(this.projectionName); 25 | 26 | Projectionmanagement.StatisticsReq request = Projectionmanagement.StatisticsReq.newBuilder() 27 | .setOptions(optionsBuilder) 28 | .build(); 29 | 30 | ProjectionsGrpc.ProjectionsStub client = 31 | GrpcUtils.configureStub(ProjectionsGrpc.newStub(channel), this.client.getSettings(), this.options); 32 | 33 | CompletableFuture result = new CompletableFuture<>(); 34 | 35 | client.statistics(request, GrpcUtils.convertSingleResponse(result, resp -> { 36 | final Projectionmanagement.StatisticsResp.Details details = resp.getDetails(); 37 | return ProjectionDetails.fromWire(details); 38 | })); 39 | 40 | return result; 41 | }); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/GetProjectionStatisticsOptions.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Options of the get projection statistics request. 5 | */ 6 | public class GetProjectionStatisticsOptions extends OptionsBase { 7 | private GetProjectionStatisticsOptions() { 8 | } 9 | 10 | /** 11 | * Returns options with default values. 12 | */ 13 | public static GetProjectionStatisticsOptions get() { 14 | return new GetProjectionStatisticsOptions(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/GetProjectionStatus.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import io.kurrent.dbclient.proto.projections.Projectionmanagement; 4 | import io.kurrent.dbclient.proto.projections.ProjectionsGrpc; 5 | 6 | import java.util.concurrent.CompletableFuture; 7 | 8 | 9 | class GetProjectionStatus { 10 | private final GrpcClient client; 11 | private final String projectionName; 12 | private final GetProjectionStatusOptions options; 13 | 14 | public GetProjectionStatus(final GrpcClient client, final String projectionName, final GetProjectionStatusOptions options) { 15 | this.client = client; 16 | this.projectionName = projectionName; 17 | this.options = options; 18 | } 19 | 20 | public CompletableFuture execute() { 21 | return this.client.run(channel -> { 22 | Projectionmanagement.StatisticsReq.Options.Builder optionsBuilder = 23 | Projectionmanagement.StatisticsReq.Options.newBuilder() 24 | .setName(this.projectionName); 25 | 26 | Projectionmanagement.StatisticsReq request = Projectionmanagement.StatisticsReq.newBuilder() 27 | .setOptions(optionsBuilder) 28 | .build(); 29 | 30 | ProjectionsGrpc.ProjectionsStub client = 31 | GrpcUtils.configureStub(ProjectionsGrpc.newStub(channel), this.client.getSettings(), this.options); 32 | 33 | CompletableFuture result = new CompletableFuture<>(); 34 | 35 | client.statistics(request, GrpcUtils.convertSingleResponse(result, resp -> { 36 | final Projectionmanagement.StatisticsResp.Details details = resp.getDetails(); 37 | return ProjectionDetails.fromWire(details); 38 | })); 39 | 40 | return result; 41 | }); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/GetProjectionStatusOptions.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Options of the get projection status request. 5 | */ 6 | public class GetProjectionStatusOptions extends OptionsBase { 7 | private GetProjectionStatusOptions() { 8 | } 9 | 10 | /** 11 | * Returns options with default values. 12 | */ 13 | public static GetProjectionStatusOptions get() { 14 | return new GetProjectionStatusOptions(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/KurrentDBClientBase.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import java.security.Security; 8 | import java.util.Optional; 9 | import java.util.UUID; 10 | import java.util.concurrent.CompletableFuture; 11 | import java.util.concurrent.Executor; 12 | import java.util.concurrent.Executors; 13 | 14 | public class KurrentDBClientBase { 15 | final Logger logger = LoggerFactory.getLogger(KurrentDBClientBase.class); 16 | final private GrpcClient client; 17 | 18 | KurrentDBClientBase(KurrentDBClientSettings settings) { 19 | Discovery discovery; 20 | 21 | if (settings.getHosts().length == 1 && !settings.isDnsDiscover()) { 22 | discovery = new SingleNodeDiscovery(settings.getHosts()[0]); 23 | } else { 24 | discovery = new ClusterDiscovery(settings); 25 | } 26 | 27 | // Required to instruct Netty to use BouncyCastle for TLS 28 | Security.addProvider(new BouncyCastleProvider()); 29 | ConnectionService service = new ConnectionService(settings, discovery); 30 | this.client = service.getHandle(); 31 | 32 | CompletableFuture.runAsync(service, createConnectionLoopExecutor()); 33 | } 34 | private Executor createConnectionLoopExecutor() { 35 | return Executors.newSingleThreadExecutor(r -> { 36 | Thread thread = new Thread(r, "esdb-client-" + UUID.randomUUID()); 37 | thread.setDaemon(true); 38 | return thread; 39 | }); 40 | } 41 | 42 | 43 | /** 44 | * Closes a connection and cleans all its allocated resources. 45 | */ 46 | public CompletableFuture shutdown() { 47 | return this.client.shutdown(); 48 | } 49 | 50 | /** 51 | * Checks if this client instance has been shutdown. 52 | * After shutdown a client instance can no longer process new operations and 53 | * a new client instance has to be created. 54 | * @return {@code true} if client instance has been shutdown. 55 | */ 56 | public boolean isShutdown() { 57 | return this.client.isShutdown(); 58 | } 59 | 60 | public CompletableFuture> getServerVersion() { 61 | return client.getServerVersion(); 62 | } 63 | 64 | GrpcClient getGrpcClient() { 65 | return client; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/ListPersistentSubscriptionsOptions.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Options of the list persistent subscriptions request. 5 | */ 6 | public class ListPersistentSubscriptionsOptions extends OptionsBase { 7 | ListPersistentSubscriptionsOptions(){} 8 | 9 | /** 10 | * Returns options with default values. 11 | */ 12 | public static ListPersistentSubscriptionsOptions get() { 13 | return new ListPersistentSubscriptionsOptions(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/ListProjections.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import io.kurrent.dbclient.proto.projections.Projectionmanagement; 4 | import io.kurrent.dbclient.proto.projections.ProjectionsGrpc; 5 | import io.kurrent.dbclient.proto.shared.Shared; 6 | import io.grpc.stub.StreamObserver; 7 | 8 | import java.util.ArrayList; 9 | import java.util.concurrent.CompletableFuture; 10 | 11 | 12 | class ListProjections { 13 | private final GrpcClient client; 14 | private final ListProjectionsOptions options; 15 | 16 | public ListProjections(final GrpcClient client, final ListProjectionsOptions options) { 17 | this.client = client; 18 | this.options = options; 19 | } 20 | 21 | public CompletableFuture execute() { 22 | return this.client.run(channel -> { 23 | Projectionmanagement.StatisticsReq.Options.Builder optionsBuilder = 24 | Projectionmanagement.StatisticsReq.Options.newBuilder() 25 | .setContinuous(Shared.Empty.newBuilder()); 26 | 27 | Projectionmanagement.StatisticsReq request = Projectionmanagement.StatisticsReq.newBuilder() 28 | .setOptions(optionsBuilder) 29 | .build(); 30 | 31 | ProjectionsGrpc.ProjectionsStub client = 32 | GrpcUtils.configureStub(ProjectionsGrpc.newStub(channel), this.client.getSettings(), this.options); 33 | 34 | CompletableFuture future = new CompletableFuture<>(); 35 | ArrayList projections = new ArrayList<>(); 36 | 37 | client.statistics(request, new StreamObserver() { 38 | @Override 39 | public void onNext(Projectionmanagement.StatisticsResp value) { 40 | if (value.hasDetails()) { 41 | projections.add(ProjectionDetails.fromWire(value.getDetails())); 42 | } 43 | } 44 | 45 | @Override 46 | public void onCompleted() { 47 | future.complete(new ListProjectionsResult(projections)); 48 | } 49 | 50 | @Override 51 | public void onError(Throwable t) { 52 | future.completeExceptionally(t); 53 | } 54 | }); 55 | 56 | return future; 57 | }); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/ListProjectionsOptions.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Options of the list projections options. 5 | */ 6 | public class ListProjectionsOptions extends OptionsBase { 7 | private ListProjectionsOptions() { 8 | } 9 | 10 | /** 11 | * Returns options with default values. 12 | */ 13 | public static ListProjectionsOptions get() { 14 | return new ListProjectionsOptions(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/ListProjectionsResult.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import java.util.List; 4 | 5 | class ListProjectionsResult { 6 | private final List projections; 7 | 8 | public ListProjectionsResult(List projections) { 9 | this.projections = projections; 10 | } 11 | 12 | public List getProjections() { 13 | return this.projections; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/Msg.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | interface Msg { 4 | void accept(ConnectionService handler); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/NackAction.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Gathers every possible Nak actions. 5 | */ 6 | public enum NackAction { 7 | /** 8 | * Client does not know what action to take, let the server decide. 9 | */ 10 | Unknown, 11 | 12 | /** 13 | * Park message do not resend. Put on poison queue. 14 | */ 15 | Park, 16 | 17 | /** 18 | * Explicit retry the message. 19 | */ 20 | Retry, 21 | 22 | /** 23 | * Skip this message do not resend do not put in poison queue. 24 | */ 25 | Skip, 26 | 27 | /** 28 | * Stop the subscription. 29 | */ 30 | Stop, 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/NoClusterNodeFoundException.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * When no node was found based on the connection string provided. 5 | */ 6 | public class NoClusterNodeFoundException extends RuntimeException { 7 | NoClusterNodeFoundException(){} 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/NodePreference.java: -------------------------------------------------------------------------------- 1 | 2 | package io.kurrent.dbclient; 3 | 4 | /** 5 | * Indicates which order of preferred nodes for connecting to. 6 | */ 7 | public enum NodePreference { 8 | /** 9 | * When attempting connection, prefers leader nodes. 10 | */ 11 | LEADER, 12 | 13 | /** 14 | * When attempting connection, prefers follower nodes. 15 | */ 16 | FOLLOWER, 17 | 18 | /** 19 | * When attempting connection, prefers read-replica nodes. 20 | */ 21 | READ_ONLY_REPLICA, 22 | 23 | /** 24 | * When attempting connection, has no node preference. 25 | */ 26 | RANDOM 27 | } -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/NotLeaderException.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import java.net.InetSocketAddress; 4 | 5 | /** 6 | * When a request needing a leader node was executed on a follower node. 7 | * In this case the connection will reconnect automatically to the leader node. However, the request causing that 8 | * exception needs to be retried if the user really wants it to be carried out. 9 | */ 10 | public class NotLeaderException extends RuntimeException { 11 | private final InetSocketAddress leaderEndpoint; 12 | 13 | NotLeaderException(String host, int port) { 14 | leaderEndpoint = new InetSocketAddress(host, port); 15 | } 16 | 17 | public InetSocketAddress getLeaderEndpoint() { 18 | return leaderEndpoint; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/OperationKind.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | enum OperationKind { 4 | Regular, 5 | Streaming, 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/OptionsWithBackPressure.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | class OptionsWithBackPressure extends OptionsWithResolveLinkTosBase { 4 | private int batchSize; 5 | private float thresholdRatio; 6 | 7 | protected OptionsWithBackPressure(OperationKind kind) { 8 | super(kind); 9 | this.batchSize = 512; 10 | this.thresholdRatio = 0.25f; 11 | } 12 | 13 | protected OptionsWithBackPressure() { 14 | this(OperationKind.Streaming); 15 | } 16 | 17 | int getBatchSize() { 18 | return batchSize; 19 | } 20 | 21 | int computeRequestThreshold() { 22 | return (int)(batchSize * thresholdRatio); 23 | } 24 | 25 | /** 26 | * The maximum number of events to read from the server at the time. 27 | */ 28 | @SuppressWarnings("unchecked") 29 | public T batchSize(int batchSize) { 30 | this.batchSize = batchSize; 31 | return (T)this; 32 | } 33 | 34 | /** 35 | * The ratio of the batch size at which more events should be requested from the server. 36 | */ 37 | @SuppressWarnings("unchecked") 38 | public T thresholdRatio(float ratio) { 39 | this.thresholdRatio = ratio; 40 | return (T)this; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/OptionsWithPositionAndResolveLinkTosBase.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | class OptionsWithPositionAndResolveLinkTosBase extends OptionsWithBackPressure { 4 | private StreamPosition position; 5 | 6 | protected OptionsWithPositionAndResolveLinkTosBase(OperationKind kind) { 7 | super(kind); 8 | this.position = StreamPosition.start(); 9 | } 10 | 11 | protected OptionsWithPositionAndResolveLinkTosBase() { 12 | this(OperationKind.Regular); 13 | } 14 | 15 | StreamPosition getPosition() { 16 | return position; 17 | } 18 | 19 | /** 20 | * Starts from the beginning of the $all stream. 21 | */ 22 | @SuppressWarnings("unchecked") 23 | public T fromStart() { 24 | this.position = StreamPosition.start(); 25 | return (T)this; 26 | } 27 | 28 | /** 29 | * Starts from the end of the $all stream. 30 | */ 31 | @SuppressWarnings("unchecked") 32 | public T fromEnd() { 33 | this.position = StreamPosition.end(); 34 | return (T)this; 35 | } 36 | 37 | /** 38 | * Starts from the given transaction log position. 39 | * @param position transaction log position. 40 | */ 41 | @SuppressWarnings("unchecked") 42 | public T fromPosition(Position position) { 43 | this.position = StreamPosition.position(position); 44 | return (T)this; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/OptionsWithResolveLinkTosBase.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | class OptionsWithResolveLinkTosBase extends OptionsBase { 4 | private boolean resolveLinkTos; 5 | 6 | protected OptionsWithResolveLinkTosBase(OperationKind kind) { 7 | super(kind); 8 | this.resolveLinkTos = false; 9 | } 10 | 11 | protected OptionsWithResolveLinkTosBase() { 12 | this(OperationKind.Regular); 13 | } 14 | 15 | boolean shouldResolveLinkTos() { 16 | return this.resolveLinkTos; 17 | } 18 | 19 | /** 20 | * Whether the subscription should resolve linkTo events to their linked events. Default: false. 21 | */ 22 | @SuppressWarnings("unchecked") 23 | public T resolveLinkTos(boolean value) { 24 | this.resolveLinkTos = value; 25 | return (T)this; 26 | } 27 | 28 | /** 29 | * Resolve linkTo events to their linked events. 30 | */ 31 | public T resolveLinkTos() { 32 | return this.resolveLinkTos(true); 33 | } 34 | 35 | /** 36 | * Don't resolve linkTo events to their linked events. 37 | */ 38 | public T notResolveLinkTos() { 39 | return this.resolveLinkTos(false); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/OptionsWithStartRevisionAndResolveLinkTosBase.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | class OptionsWithStartRevisionAndResolveLinkTosBase extends OptionsWithBackPressure { 4 | private StreamPosition startRevision; 5 | 6 | protected OptionsWithStartRevisionAndResolveLinkTosBase(OperationKind kind) { 7 | super(kind); 8 | this.startRevision = StreamPosition.start(); 9 | } 10 | 11 | protected OptionsWithStartRevisionAndResolveLinkTosBase() { 12 | this(OperationKind.Regular); 13 | } 14 | 15 | StreamPosition getStartingRevision() { 16 | return this.startRevision; 17 | } 18 | 19 | /** 20 | * Starts from a stream position. 21 | */ 22 | @SuppressWarnings("unchecked") 23 | public T fromRevision(StreamPosition startRevision) { 24 | this.startRevision = startRevision; 25 | return (T)this; 26 | } 27 | 28 | /** 29 | * Starts from the beginning of the stream. 30 | */ 31 | public T fromStart() { 32 | return this.fromRevision(StreamPosition.start()); 33 | } 34 | 35 | /** 36 | * Starts from the end of the stream. 37 | */ 38 | public T fromEnd() { 39 | return this.fromRevision(StreamPosition.end()); 40 | } 41 | 42 | /** 43 | * Starts from the given event revision. 44 | */ 45 | public T fromRevision(long revision) { 46 | return this.fromRevision(StreamPosition.position(revision)); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/OptionsWithStreamStateBase.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | class OptionsWithStreamStateBase extends OptionsBase { 4 | private StreamState streamState; 5 | 6 | protected OptionsWithStreamStateBase() { 7 | this.streamState = StreamState.any(); 8 | } 9 | 10 | StreamState getStreamState() { 11 | return this.streamState; 12 | } 13 | 14 | /** 15 | * Asks the server to check that the stream receiving is at the expected state. 16 | 17 | * @param state - expected revision. 18 | * @return updated options. 19 | */ 20 | @SuppressWarnings("unchecked") 21 | public T streamState(StreamState state) { 22 | this.streamState = state; 23 | return (T) this; 24 | } 25 | 26 | 27 | /** 28 | * Asks the server to check that the stream receiving is at the given expected revision. 29 | 30 | * @param revision - expected revision. 31 | * @return updated options. 32 | */ 33 | public T streamRevision(long revision) { 34 | return streamState(StreamState.streamRevision(revision)); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/PersistentSubscriptionInfo.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Common persistent subscription info type. 7 | */ 8 | public abstract class PersistentSubscriptionInfo { 9 | private String eventSource; 10 | private String groupName; 11 | private String status; 12 | private List connections; 13 | 14 | PersistentSubscriptionInfo(){} 15 | 16 | /** 17 | * The source of events for the subscription. 18 | */ 19 | public String getEventSource() { 20 | return eventSource; 21 | } 22 | 23 | void setEventSource(String eventSource) { 24 | this.eventSource = eventSource; 25 | } 26 | 27 | /** 28 | * The group name given on creation. 29 | */ 30 | public String getGroupName() { 31 | return groupName; 32 | } 33 | 34 | void setGroupName(String groupName) { 35 | this.groupName = groupName; 36 | } 37 | 38 | /** 39 | * The current status of the subscription. 40 | */ 41 | public String getStatus() { 42 | return status; 43 | } 44 | 45 | void setStatus(String status) { 46 | this.status = status; 47 | } 48 | 49 | /** 50 | * Active connections to the subscription. 51 | * @see PersistentSubscriptionConnectionInfo 52 | */ 53 | public List getConnections() { 54 | return connections; 55 | } 56 | 57 | void setConnections(List connections) { 58 | this.connections = connections; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/PersistentSubscriptionListener.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Listener used to handle persistent subscription notifications raised throughout its lifecycle. 5 | */ 6 | public abstract class PersistentSubscriptionListener { 7 | /** 8 | * Called when KurrentDB sends an event to the persistent subscription. 9 | * @param subscription handle to the persistent subscription. 10 | * @param retryCount how many times the event was retried. 11 | * @param event a resolved event. 12 | */ 13 | public void onEvent(PersistentSubscription subscription, int retryCount, ResolvedEvent event) {} 14 | 15 | /** 16 | * Called when the subscription is cancelled or dropped. 17 | * @param subscription handle to the subscription. 18 | * @param exception an exception. null if the user initiated the cancellation. 19 | */ 20 | public void onCancelled(PersistentSubscription subscription, Throwable exception) {} 21 | 22 | /** 23 | * Called when the subscription is confirmed by the server. 24 | * @param subscription handle to the subscription. 25 | */ 26 | public void onConfirmation(PersistentSubscription subscription) {} 27 | } -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/PersistentSubscriptionToAllInfo.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Persistent subscription to $all info. 5 | */ 6 | public class PersistentSubscriptionToAllInfo extends PersistentSubscriptionInfo { 7 | private PersistentSubscriptionToAllSettings settings; 8 | private PersistentSubscriptionToAllStats stats; 9 | 10 | PersistentSubscriptionToAllInfo(){} 11 | 12 | /** 13 | * The settings used to create the persistent subscription. 14 | */ 15 | public PersistentSubscriptionToAllSettings getSettings() { 16 | return settings; 17 | } 18 | 19 | void setSettings(PersistentSubscriptionToAllSettings settings) { 20 | this.settings = settings; 21 | } 22 | 23 | /** 24 | * Runtime persistent subscription statistics. 25 | */ 26 | public PersistentSubscriptionToAllStats getStats() { 27 | return stats; 28 | } 29 | 30 | void setStats(PersistentSubscriptionToAllStats stats) { 31 | this.stats = stats; 32 | } 33 | 34 | @Override 35 | public String toString() { 36 | return "PersistentSubscriptionToAllInfo{" + 37 | "settings=" + settings + 38 | ", stats=" + stats + 39 | ", eventSource='" + getEventSource() + '\'' + 40 | ", groupName='" + getGroupName() + '\'' + 41 | ", status='" + getStatus() + '\'' + 42 | ", connections=" + getConnections() + 43 | '}'; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/PersistentSubscriptionToAllSettings.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Persistent subscription to $all settings. 5 | */ 6 | public class PersistentSubscriptionToAllSettings extends PersistentSubscriptionSettings { 7 | private StreamPosition startFrom; 8 | PersistentSubscriptionToAllSettings() {} 9 | 10 | /** 11 | * Return a persistent subscription settings to $all with default properties. 12 | */ 13 | public static PersistentSubscriptionToAllSettings get() { 14 | return PersistentSubscriptionSettings.defaultToAll(); 15 | } 16 | 17 | /** 18 | * Where to start subscription from. This can be from the start of the $all stream, from the end of the $all stream 19 | * at the time of creation, or from an inclusive position in $all stream. 20 | */ 21 | public StreamPosition getStartFrom() { 22 | return startFrom; 23 | } 24 | 25 | /** 26 | * Where to start subscription from. This can be from the start of the $all stream, from the end of the $all stream 27 | * at the time of creation, or from an inclusive position in $all stream. 28 | */ 29 | void setStartFrom(StreamPosition startFrom) { 30 | this.startFrom = startFrom; 31 | } 32 | 33 | @Override 34 | public String toString() { 35 | return "PersistentSubscriptionToAllSettings{" + 36 | "startFrom=" + startFrom + 37 | ", checkpointAfterInMs=" + getCheckpointAfterInMs() + 38 | ", extraStatistics=" + isExtraStatistics() + 39 | ", shouldResolveLinkTos=" + shouldResolveLinkTos() + 40 | ", historyBufferSize=" + getHistoryBufferSize() + 41 | ", liveBufferSize=" + getLiveBufferSize() + 42 | ", checkpointUpperBound=" + getCheckpointUpperBound() + 43 | ", maxRetryCount=" + getMaxRetryCount() + 44 | ", maxSubscriberCount=" + getMaxSubscriberCount() + 45 | ", messageTimeoutMs=" + getMessageTimeoutMs() + 46 | ", checkpointLowerBound=" + getCheckpointLowerBound() + 47 | ", readBatchSize=" + getReadBatchSize() + 48 | ", namedConsumerStrategy=" + getNamedConsumerStrategy() + 49 | ", resolveLinkTos=" + isResolveLinkTos() + 50 | '}'; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/PersistentSubscriptionToAllStats.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import java.util.Optional; 4 | 5 | /** 6 | * Processing-related persistent subscription to $all statistics. 7 | */ 8 | public class PersistentSubscriptionToAllStats extends PersistentSubscriptionStats { 9 | private Position lastCheckpointedEventPosition; 10 | private Position lastKnownEventPosition; 11 | 12 | PersistentSubscriptionToAllStats(){} 13 | 14 | /** 15 | * The transaction log position of the last checkpoint. 16 | */ 17 | public Optional getLastCheckpointedEventPosition() { 18 | if (lastCheckpointedEventPosition == null) 19 | return Optional.empty(); 20 | 21 | return Optional.of(lastCheckpointedEventPosition); 22 | } 23 | 24 | void setLastCheckpointedEventPosition(Position lastCheckpointedEventPosition) { 25 | this.lastCheckpointedEventPosition = lastCheckpointedEventPosition; 26 | } 27 | 28 | /** 29 | * The transaction log position of the last known event. 30 | */ 31 | public Optional getLastKnownEventPosition() { 32 | if (lastKnownEventPosition == null) 33 | return Optional.empty(); 34 | 35 | return Optional.of(lastKnownEventPosition); 36 | } 37 | 38 | void setLastKnownEventPosition(Position lastKnownEventPosition) { 39 | this.lastKnownEventPosition = lastKnownEventPosition; 40 | } 41 | 42 | @Override 43 | public String toString() { 44 | return "PersistentSubscriptionToAllStats{" + 45 | "lastCheckpointedEventPosition=" + lastCheckpointedEventPosition + 46 | ", lastKnownEventPosition=" + lastKnownEventPosition + 47 | ", averagePerSecond=" + getAveragePerSecond() + 48 | ", totalItems=" + getTotalItems() + 49 | ", countSinceLastMeasurement=" + getCountSinceLastMeasurement() + 50 | ", readBufferCount=" + getReadBufferCount() + 51 | ", liveBufferCount=" + getLiveBufferCount() + 52 | ", retryBufferCount=" + getRetryBufferCount() + 53 | ", totalInFlightMessages=" + getTotalInFlightMessages() + 54 | ", outstandingMessagesCount=" + getOutstandingMessagesCount() + 55 | ", parkedMessageCount=" + getParkedMessageCount() + 56 | '}'; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/PersistentSubscriptionToStreamInfo.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Persistent subscription to stream info. 5 | */ 6 | public class PersistentSubscriptionToStreamInfo extends PersistentSubscriptionInfo { 7 | private PersistentSubscriptionToStreamSettings settings; 8 | private PersistentSubscriptionToStreamStats stats; 9 | 10 | PersistentSubscriptionToStreamInfo(){} 11 | 12 | /** 13 | * The settings used to create the persistent subscription. 14 | */ 15 | public PersistentSubscriptionToStreamSettings getSettings() { 16 | return settings; 17 | } 18 | 19 | void setSettings(PersistentSubscriptionToStreamSettings settings) { 20 | this.settings = settings; 21 | } 22 | 23 | /** 24 | * Runtime persistent subscription statistics. 25 | */ 26 | public PersistentSubscriptionToStreamStats getStats() { 27 | return stats; 28 | } 29 | 30 | void setStats(PersistentSubscriptionToStreamStats stats) { 31 | this.stats = stats; 32 | } 33 | 34 | @Override 35 | public String toString() { 36 | return "PersistentSubscriptionToStreamInfo{" + 37 | "settings=" + settings + 38 | ", stats=" + stats + 39 | ", eventSource='" + getEventSource() + '\'' + 40 | ", groupName='" + getGroupName() + '\'' + 41 | ", status='" + getStatus() + '\'' + 42 | ", connections=" + getConnections() + 43 | '}'; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/PersistentSubscriptionToStreamSettings.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Persistent subscription to stream settings. 5 | */ 6 | public class PersistentSubscriptionToStreamSettings extends PersistentSubscriptionSettings { 7 | private StreamPosition startFrom; 8 | 9 | PersistentSubscriptionToStreamSettings() {} 10 | 11 | /** 12 | * Return a persistent subscription settings to $all with default properties. 13 | */ 14 | static public PersistentSubscriptionToStreamSettings get() { 15 | return PersistentSubscriptionSettings.defaultRegular(); 16 | } 17 | 18 | /** 19 | * Where to start subscription from. This can be from the start of the $all stream, from the end of the $all stream 20 | * at the time of creation, or from an inclusive position in $all stream. 21 | */ 22 | public StreamPosition getStartFrom() { 23 | return startFrom; 24 | } 25 | 26 | /** 27 | * Where to start subscription from. This can be from the start of the $all stream, from the end of the $all stream 28 | * at the time of creation, or from an inclusive position in $all stream. 29 | */ 30 | void setStartFrom(StreamPosition startFrom) { 31 | this.startFrom = startFrom; 32 | } 33 | 34 | @Override 35 | public String toString() { 36 | return "PersistentSubscriptionToStreamSettings{" + 37 | "startFrom=" + startFrom + 38 | ", checkpointAfterInMs=" + getCheckpointAfterInMs() + 39 | ", extraStatistics=" + isExtraStatistics() + 40 | ", shouldResolveLinkTos=" + shouldResolveLinkTos() + 41 | ", historyBufferSize=" + getHistoryBufferSize() + 42 | ", liveBufferSize=" + getLiveBufferSize() + 43 | ", checkpointUpperBound=" + getCheckpointUpperBound() + 44 | ", maxRetryCount=" + getMaxRetryCount() + 45 | ", maxSubscriberCount=" + getMaxSubscriberCount() + 46 | ", messageTimeoutMs=" + getMessageTimeoutMs() + 47 | ", checkpointLowerBound=" + getCheckpointLowerBound() + 48 | ", readBatchSize=" + getReadBatchSize() + 49 | ", namedConsumerStrategy=" + getNamedConsumerStrategy() + 50 | ", resolveLinkTos=" + isResolveLinkTos() + 51 | '}'; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/PersistentSubscriptionToStreamStats.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import java.util.Optional; 4 | 5 | /** 6 | * Processing-related persistent subscription to stream statistics. 7 | */ 8 | public class PersistentSubscriptionToStreamStats extends PersistentSubscriptionStats { 9 | private Long lastCheckpointedEventRevision; 10 | private Long lastKnownEventRevision; 11 | 12 | /** 13 | * The revision number of the last checkpoint. 14 | */ 15 | public Optional getLastCheckpointedEventRevision() { 16 | if (lastCheckpointedEventRevision == null) 17 | return Optional.empty(); 18 | 19 | return Optional.of(lastCheckpointedEventRevision); 20 | } 21 | 22 | void setLastCheckpointedEventRevision(long lastCheckpointedEventRevision) { 23 | this.lastCheckpointedEventRevision = lastCheckpointedEventRevision; 24 | } 25 | 26 | /** 27 | * The revision number of the last known event. 28 | */ 29 | public Optional getLastKnownEventRevision() { 30 | if (lastKnownEventRevision == null) 31 | return Optional.empty(); 32 | 33 | return Optional.of(lastKnownEventRevision); 34 | } 35 | 36 | void setLastKnownEventRevision(long lastKnownEventRevision) { 37 | this.lastKnownEventRevision = lastKnownEventRevision; 38 | } 39 | 40 | @Override 41 | public String toString() { 42 | return "PersistentSubscriptionToStreamStats{" + 43 | "lastCheckpointedEventRevision=" + lastCheckpointedEventRevision + 44 | ", lastKnownEventRevision=" + lastKnownEventRevision + 45 | ", averagePerSecond=" + getAveragePerSecond() + 46 | ", totalItems=" + getTotalItems() + 47 | ", countSinceLastMeasurement=" + getCountSinceLastMeasurement() + 48 | ", readBufferCount=" + getReadBufferCount() + 49 | ", liveBufferCount=" + getLiveBufferCount() + 50 | ", retryBufferCount=" + getRetryBufferCount() + 51 | ", totalInFlightMessages=" + getTotalInFlightMessages() + 52 | ", outstandingMessagesCount=" + getOutstandingMessagesCount() + 53 | ", parkedMessageCount=" + getParkedMessageCount() + 54 | '}'; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/Position.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import javax.validation.constraints.NotNull; 4 | 5 | import java.util.Objects; 6 | 7 | /** 8 | * Transaction log position. 9 | */ 10 | public class Position implements Comparable { 11 | private final long prepare; 12 | private final long commit; 13 | 14 | public Position(long commitUnsigned, long prepareUnsigned) { 15 | // Do not allow the commit position to be less than the prepare position 16 | if (Long.compareUnsigned(commitUnsigned, prepareUnsigned) < 0) { 17 | throw new IllegalArgumentException("The commit position may not be before the prepare position"); 18 | } 19 | 20 | this.prepare = prepareUnsigned; 21 | this.commit = commitUnsigned; 22 | } 23 | 24 | public Position(String prepare, String commit) { 25 | this(Long.parseUnsignedLong(commit), Long.parseUnsignedLong(prepare)); 26 | } 27 | 28 | /** 29 | * Returns the prepare position. 30 | */ 31 | public long getPrepareUnsigned() { 32 | return prepare; 33 | } 34 | 35 | /** 36 | * Returns the commit position. 37 | */ 38 | public long getCommitUnsigned() { 39 | return commit; 40 | } 41 | 42 | @Override 43 | public String toString() { 44 | return String.format("%s/%s", Long.toUnsignedString(commit), Long.toUnsignedString(prepare)); 45 | } 46 | 47 | @Override 48 | public boolean equals(Object o) { 49 | if (this == o) return true; 50 | if (o == null || getClass() != o.getClass()) return false; 51 | Position position = (Position) o; 52 | return prepare == position.prepare && 53 | commit == position.commit; 54 | } 55 | 56 | @Override 57 | public int hashCode() { 58 | return Objects.hash(prepare, commit); 59 | } 60 | 61 | @Override 62 | public int compareTo(@NotNull Position other) { 63 | if (this.commit == other.commit && this.prepare == other.prepare) { 64 | return 0; 65 | } 66 | 67 | if ((Long.compareUnsigned(this.commit, other.commit) < 0) || (Long.compareUnsigned(this.commit, other.commit) == 0 && Long.compareUnsigned(this.prepare, other.prepare) < 0)) { 68 | return -1; 69 | } 70 | 71 | return 1; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/PrefixFilterExpression.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import javax.validation.constraints.NotNull; 4 | import java.util.Objects; 5 | 6 | class PrefixFilterExpression implements Comparable { 7 | @NotNull 8 | private final String value; 9 | public PrefixFilterExpression(@NotNull String value) { 10 | this.value = value; 11 | } 12 | @Override 13 | public String toString() { 14 | return this.value; 15 | } 16 | @Override 17 | public boolean equals(Object o) { 18 | if (this == o) return true; 19 | if (o == null || getClass() != o.getClass()) return false; 20 | PrefixFilterExpression that = (PrefixFilterExpression) o; 21 | return value.equals(that.value); 22 | } 23 | 24 | @Override 25 | public int hashCode() { 26 | return Objects.hash(value); 27 | } 28 | 29 | @Override 30 | public int compareTo(PrefixFilterExpression other) { 31 | return this.value.compareTo(other.value); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/ReadAll.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import io.kurrent.dbclient.proto.shared.Shared; 4 | import io.kurrent.dbclient.proto.streams.StreamsOuterClass; 5 | 6 | class ReadAll extends AbstractRead { 7 | private final ReadAllOptions options; 8 | 9 | public ReadAll(GrpcClient client, ReadAllOptions options) { 10 | super(client, options); 11 | 12 | this.options = options; 13 | } 14 | 15 | @Override 16 | public StreamsOuterClass.ReadReq.Options.Builder createOptions() { 17 | StreamsOuterClass.ReadReq.Options.AllOptions.Builder optionsOrBuilder = 18 | StreamsOuterClass.ReadReq.Options.AllOptions.newBuilder(); 19 | 20 | if (this.options.getPosition().isEnd()) { 21 | optionsOrBuilder.setEnd(Shared.Empty.getDefaultInstance()); 22 | } else if (this.options.getPosition().isStart()) { 23 | optionsOrBuilder.setStart(Shared.Empty.getDefaultInstance()); 24 | } else { 25 | StreamPosition.Position position = (StreamPosition.Position) this.options.getPosition(); 26 | optionsOrBuilder.setPosition(StreamsOuterClass.ReadReq.Options.Position.newBuilder() 27 | .setCommitPosition(position.getPositionOrThrow().getCommitUnsigned()) 28 | .setPreparePosition(position.getPositionOrThrow().getPrepareUnsigned())); 29 | } 30 | 31 | StreamsOuterClass.ReadReq.Options.Builder builder = defaultReadOptions.clone() 32 | .setAll(optionsOrBuilder) 33 | .setResolveLinks(this.options.shouldResolveLinkTos()) 34 | .setControlOption(StreamsOuterClass.ReadReq.Options.ControlOption.newBuilder().setCompatibility(1)) 35 | .setCount(this.options.getMaxCount()) 36 | .setNoFilter(Shared.Empty.getDefaultInstance()) 37 | .setReadDirection(this.options.getDirection() == Direction.Forwards ? 38 | StreamsOuterClass.ReadReq.Options.ReadDirection.Forwards : 39 | StreamsOuterClass.ReadReq.Options.ReadDirection.Backwards); 40 | 41 | return builder; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/ReadAllOptions.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Options of the read $all stream request. 5 | */ 6 | public class ReadAllOptions extends OptionsWithPositionAndResolveLinkTosBase { 7 | private Direction direction; 8 | private long maxCount; 9 | 10 | private ReadAllOptions() { 11 | super(OperationKind.Streaming); 12 | this.direction = Direction.Forwards; 13 | this.maxCount = Long.MAX_VALUE; 14 | } 15 | 16 | /** 17 | * Returns options with default values. 18 | */ 19 | public static ReadAllOptions get() { 20 | return new ReadAllOptions(); 21 | } 22 | 23 | Direction getDirection() { 24 | return this.direction; 25 | } 26 | 27 | long getMaxCount() { 28 | return this.maxCount; 29 | } 30 | 31 | /** 32 | * Reads stream in the given direction. 33 | */ 34 | public ReadAllOptions direction(Direction direction) { 35 | this.direction = direction; 36 | return this; 37 | } 38 | 39 | /** 40 | * Reads stream in revision-ascending order. 41 | */ 42 | public ReadAllOptions forwards() { 43 | return direction(Direction.Forwards); 44 | } 45 | 46 | /** 47 | * Reads stream in revision-descending order. 48 | */ 49 | public ReadAllOptions backwards() { 50 | return direction(Direction.Backwards); 51 | } 52 | 53 | /** 54 | * The maximum event count KurrentDB will return. 55 | */ 56 | public ReadAllOptions maxCount(long maxCount) { 57 | this.maxCount = maxCount; 58 | return this; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/ReadResult.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Returned after a successful read operation. 7 | */ 8 | public class ReadResult { 9 | private final List events; 10 | private final long firstStreamPosition; 11 | private final long lastStreamPosition; 12 | private final Position lastAllStreamPosition; 13 | 14 | ReadResult(List events, long firstStreamPosition, long lastStreamPosition, Position lastAllStreamPosition) { 15 | this.events = events; 16 | this.firstStreamPosition = firstStreamPosition; 17 | this.lastStreamPosition = lastStreamPosition; 18 | this.lastAllStreamPosition = lastAllStreamPosition; 19 | } 20 | 21 | /** 22 | * Returns all the events of the read operation. 23 | */ 24 | public List getEvents() { 25 | return this.events; 26 | } 27 | 28 | /** 29 | * When reading from a regular stream, returns the first event revision number of the stream. 30 | */ 31 | public long getFirstStreamPosition() { 32 | return firstStreamPosition; 33 | } 34 | 35 | /** 36 | * When reading from a regular stream, returns the last event revision number of the stream. 37 | */ 38 | public long getLastStreamPosition() { 39 | return lastStreamPosition; 40 | } 41 | 42 | /** 43 | * When reading from $all stream, returns the last event position. 44 | * @return null if reading from a regular stream. 45 | */ 46 | public Position getLastAllStreamPosition() { 47 | return lastAllStreamPosition; 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return "ReadResult{" + 53 | "events=" + events + 54 | ", firstStreamPosition=" + firstStreamPosition + 55 | ", lastStreamPosition=" + lastStreamPosition + 56 | ", lastAllStreamPosition=" + lastAllStreamPosition + 57 | '}'; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/ReadStream.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import io.kurrent.dbclient.proto.shared.Shared; 4 | import io.kurrent.dbclient.proto.streams.StreamsOuterClass; 5 | 6 | class ReadStream extends AbstractRead { 7 | private final String streamName; 8 | private final ReadStreamOptions options; 9 | 10 | public ReadStream(GrpcClient client, String streamName, ReadStreamOptions options) { 11 | super(client, options); 12 | 13 | this.streamName = streamName; 14 | this.options = options; 15 | } 16 | 17 | @Override 18 | public StreamsOuterClass.ReadReq.Options.Builder createOptions() { 19 | return defaultReadOptions.clone() 20 | .setStream(GrpcUtils.toStreamOptions(this.streamName, this.options.getStartingRevision())) 21 | .setResolveLinks(this.options.shouldResolveLinkTos()) 22 | .setCount(this.options.getMaxCount()) 23 | .setControlOption(StreamsOuterClass.ReadReq.Options.ControlOption.newBuilder().setCompatibility(1)) 24 | .setNoFilter(Shared.Empty.getDefaultInstance()) 25 | .setReadDirection(this.options.getDirection() == Direction.Forwards ? 26 | StreamsOuterClass.ReadReq.Options.ReadDirection.Forwards : 27 | StreamsOuterClass.ReadReq.Options.ReadDirection.Backwards); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/ReadStreamConsumer.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import org.reactivestreams.Subscriber; 4 | 5 | import java.time.Instant; 6 | 7 | class ReadStreamConsumer implements StreamConsumer { 8 | private final Subscriber subscriber; 9 | 10 | public ReadStreamConsumer(Subscriber subscriber) { 11 | this.subscriber = subscriber; 12 | } 13 | 14 | @Override 15 | public void onEvent(ResolvedEvent event) { 16 | this.subscriber.onNext(ReadMessage.fromEvent(event)); 17 | } 18 | 19 | @Override 20 | public void onSubscriptionConfirmation(String subscriptionId) {} 21 | 22 | @Override 23 | public void onCheckpoint(long commit, long prepare) {} 24 | 25 | @Override 26 | public void onStreamNotFound(String streamName) { 27 | this.subscriber.onError(new StreamNotFoundException(streamName)); 28 | } 29 | 30 | @Override 31 | public void onFirstStreamPosition(long position) { 32 | this.subscriber.onNext(ReadMessage.fromFirstStreamPosition(position)); 33 | } 34 | 35 | @Override 36 | public void onLastStreamPosition(long position) { 37 | this.subscriber.onNext(ReadMessage.fromLastStreamPosition(position)); 38 | } 39 | 40 | @Override 41 | public void onLastAllStreamPosition(long commit, long prepare) { 42 | this.subscriber.onNext(ReadMessage.fromLastAllPosition(commit, prepare)); 43 | } 44 | 45 | @Override 46 | public void onCaughtUp(Instant timestamp, Long streamRevision, Position position) {} 47 | 48 | @Override 49 | public void onFellBehind(Instant timestamp, Long streamRevision, Position position) {} 50 | 51 | @Override 52 | public void onCancelled(Throwable exception) { 53 | this.subscriber.onError(exception); 54 | } 55 | 56 | @Override 57 | public void onComplete() { 58 | this.subscriber.onComplete(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/ReadStreamOptions.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Options of the read stream request. 5 | */ 6 | public class ReadStreamOptions extends OptionsWithStartRevisionAndResolveLinkTosBase { 7 | private Direction direction; 8 | private long maxCount; 9 | 10 | private ReadStreamOptions() { 11 | super(OperationKind.Streaming); 12 | this.direction = Direction.Forwards; 13 | this.maxCount = Long.MAX_VALUE; 14 | } 15 | 16 | /** 17 | * Returns options with default values. 18 | */ 19 | public static ReadStreamOptions get() { 20 | return new ReadStreamOptions(); 21 | } 22 | 23 | Direction getDirection() { 24 | return this.direction; 25 | } 26 | 27 | long getMaxCount() { 28 | return this.maxCount; 29 | } 30 | 31 | /** 32 | * Reads stream in the given direction. 33 | 34 | */ 35 | public ReadStreamOptions direction(Direction direction) { 36 | this.direction = direction; 37 | return this; 38 | } 39 | 40 | /** 41 | * Reads stream in revision-ascending order. 42 | 43 | */ 44 | public ReadStreamOptions forwards() { 45 | return direction(Direction.Forwards); 46 | } 47 | 48 | /** 49 | * Reads stream in revision-descending order. 50 | 51 | */ 52 | public ReadStreamOptions backwards() { 53 | return direction(Direction.Backwards); 54 | } 55 | 56 | /** 57 | * The maximum event count EventStoreDB will return. 58 | */ 59 | public ReadStreamOptions maxCount(long maxCount) { 60 | this.maxCount = maxCount; 61 | return this; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/ReadSubscriber.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import org.reactivestreams.Subscriber; 4 | import org.reactivestreams.Subscription; 5 | 6 | abstract class ReadSubscriber implements Subscriber { 7 | 8 | private Subscription subscription; 9 | 10 | @Override 11 | public final void onSubscribe(Subscription s) { 12 | this.subscription = s; 13 | } 14 | 15 | public final void request(long n) { 16 | this.subscription.request(n); 17 | } 18 | 19 | @Override 20 | public final void onNext(ReadMessage resolvedEvent) { 21 | onEvent(resolvedEvent); 22 | } 23 | 24 | public abstract void onEvent(ReadMessage resolvedEvent); 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/RegularFilterExpression.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import javax.validation.constraints.NotNull; 4 | import java.util.Objects; 5 | import java.util.regex.Pattern; 6 | 7 | class RegularFilterExpression implements Comparable { 8 | @NotNull 9 | private final Pattern value; 10 | 11 | public RegularFilterExpression(@NotNull Pattern value) { 12 | this.value = value; 13 | } 14 | 15 | @Override 16 | public String toString() { 17 | return this.value.pattern(); 18 | } 19 | 20 | @Override 21 | public boolean equals(Object o) { 22 | if (this == o) return true; 23 | if (o == null || getClass() != o.getClass()) return false; 24 | RegularFilterExpression that = (RegularFilterExpression) o; 25 | return value.equals(that.value); 26 | } 27 | 28 | @Override 29 | public int hashCode() { 30 | return Objects.hash(value); 31 | } 32 | 33 | @Override 34 | public int compareTo(RegularFilterExpression other) { 35 | String ours = this.value.pattern(); 36 | String theirs = other.value.pattern(); 37 | 38 | return ours.compareTo(theirs); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/ReplayParkedMessagesOptions.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Options of the replay parked messages request. 5 | */ 6 | public class ReplayParkedMessagesOptions extends OptionsBase { 7 | Long stopAt = null; 8 | 9 | ReplayParkedMessagesOptions(){} 10 | 11 | Long getStopAt() { 12 | return stopAt; 13 | } 14 | 15 | /** 16 | * Replay the parked messages until the event revision within the parked messages stream is reached. 17 | */ 18 | public ReplayParkedMessagesOptions stopAt(long value) { 19 | this.stopAt = value; 20 | return this; 21 | } 22 | 23 | /** 24 | * Options with default values. 25 | */ 26 | public static ReplayParkedMessagesOptions get() { 27 | return new ReplayParkedMessagesOptions(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/ResetProjection.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import io.kurrent.dbclient.proto.projections.Projectionmanagement; 4 | import io.kurrent.dbclient.proto.projections.ProjectionsGrpc; 5 | 6 | import java.util.concurrent.CompletableFuture; 7 | 8 | class ResetProjection { 9 | private final GrpcClient client; 10 | private final String projectionName; 11 | private final ResetProjectionOptions options; 12 | 13 | public ResetProjection(final GrpcClient client, final String projectionName, final ResetProjectionOptions options) { 14 | this.client = client; 15 | this.projectionName = projectionName; 16 | this.options = options; 17 | } 18 | 19 | public CompletableFuture execute() { 20 | return this.client.run(channel -> { 21 | Projectionmanagement.ResetReq.Options.Builder optionsBuilder = 22 | Projectionmanagement.ResetReq.Options.newBuilder() 23 | .setName(this.projectionName); 24 | 25 | Projectionmanagement.ResetReq request = Projectionmanagement.ResetReq.newBuilder() 26 | .setOptions(optionsBuilder) 27 | .build(); 28 | 29 | ProjectionsGrpc.ProjectionsStub client = 30 | GrpcUtils.configureStub(ProjectionsGrpc.newStub(channel), this.client.getSettings(), this.options); 31 | 32 | CompletableFuture result = new CompletableFuture<>(); 33 | 34 | client.reset(request, GrpcUtils.convertSingleResponse(result)); 35 | 36 | return result; 37 | }); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/ResetProjectionOptions.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Options of the reset projection request. 5 | */ 6 | public class ResetProjectionOptions extends OptionsBase { 7 | private ResetProjectionOptions() { 8 | } 9 | 10 | /** 11 | * Options with default values. 12 | */ 13 | public static ResetProjectionOptions get() { 14 | return new ResetProjectionOptions(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/ResourceNotFoundException.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * A remote resource was not found or because its access was denied. Could only happen when a request was performed 5 | * through HTTP. 6 | */ 7 | public class ResourceNotFoundException extends RuntimeException { 8 | ResourceNotFoundException(){ 9 | super("Resource not found exception"); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/RestartPersistentSubscriptionSubsystem.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import io.kurrent.dbclient.proto.persistentsubscriptions.PersistentSubscriptionsGrpc; 4 | import io.kurrent.dbclient.proto.shared.Shared; 5 | 6 | import java.io.IOException; 7 | import java.net.HttpURLConnection; 8 | import java.util.concurrent.CompletableFuture; 9 | 10 | import static io.kurrent.dbclient.HttpUtils.checkForError; 11 | 12 | class RestartPersistentSubscriptionSubsystem { 13 | @SuppressWarnings("unchecked") 14 | public static CompletableFuture execute(GrpcClient client, RestartPersistentSubscriptionSubsystemOptions options) { 15 | return client.runWithArgs(args -> { 16 | CompletableFuture result = new CompletableFuture(); 17 | 18 | if (args.supportFeature(FeatureFlags.PERSISTENT_SUBSCRIPTION_MANAGEMENT)) { 19 | PersistentSubscriptionsGrpc.PersistentSubscriptionsStub stub = 20 | GrpcUtils.configureStub(PersistentSubscriptionsGrpc.newStub(args.getChannel()), client.getSettings(), options); 21 | 22 | stub.restartSubsystem(Shared.Empty.getDefaultInstance(), GrpcUtils.convertSingleResponse(result, resp -> 42)); 23 | } else { 24 | HttpURLConnection http = args.getHttpConnection(options, client.getSettings(), "/subscriptions/restart"); 25 | 26 | try { 27 | http.setDoOutput(true); 28 | http.setRequestMethod("POST"); 29 | http.setFixedLengthStreamingMode(0); 30 | 31 | Exception error = checkForError(http.getResponseCode()); 32 | if (error != null) { 33 | result.completeExceptionally(error); 34 | } else { 35 | result.complete(42); 36 | } 37 | } catch (IOException e) { 38 | throw new RuntimeException(e); 39 | } finally { 40 | http.disconnect(); 41 | } 42 | } 43 | 44 | return result; 45 | }); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/RestartPersistentSubscriptionSubsystemOptions.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Options of the restart persistent subscription subsystem request. 5 | */ 6 | public class RestartPersistentSubscriptionSubsystemOptions extends OptionsBase { 7 | RestartPersistentSubscriptionSubsystemOptions(){} 8 | 9 | /** 10 | * Options with the default values. 11 | */ 12 | public static RestartPersistentSubscriptionSubsystemOptions get() { 13 | return new RestartPersistentSubscriptionSubsystemOptions(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/RestartProjectionSubsystem.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import io.kurrent.dbclient.proto.projections.ProjectionsGrpc; 4 | import io.kurrent.dbclient.proto.shared.Shared; 5 | 6 | import java.util.concurrent.CompletableFuture; 7 | 8 | class RestartProjectionSubsystem { 9 | private final GrpcClient client; 10 | private final RestartProjectionSubsystemOptions options; 11 | 12 | public RestartProjectionSubsystem(final GrpcClient client, final RestartProjectionSubsystemOptions options) { 13 | this.client = client; 14 | this.options = options; 15 | } 16 | 17 | public CompletableFuture execute() { 18 | return this.client.run(channel -> { 19 | ProjectionsGrpc.ProjectionsStub client = 20 | GrpcUtils.configureStub(ProjectionsGrpc.newStub(channel), this.client.getSettings(), this.options); 21 | 22 | CompletableFuture result = new CompletableFuture<>(); 23 | 24 | Shared.Empty request = Shared.Empty.newBuilder().build(); 25 | 26 | client.restartSubsystem(request, GrpcUtils.convertSingleResponse(result)); 27 | 28 | return result; 29 | }); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/RestartProjectionSubsystemOptions.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Options of the restart projection subsystem request. 5 | */ 6 | public class RestartProjectionSubsystemOptions extends OptionsBase { 7 | private RestartProjectionSubsystemOptions() { 8 | } 9 | 10 | /** 11 | * Options with default values. 12 | */ 13 | public static RestartProjectionSubsystemOptions get() { 14 | return new RestartProjectionSubsystemOptions(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/RevisionOrPosition.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import java.util.Optional; 4 | 5 | /** 6 | * Holds a stream revision number or transaction log position. 7 | */ 8 | public class RevisionOrPosition { 9 | private Optional revision = Optional.empty(); 10 | private Optional position = Optional.empty(); 11 | 12 | RevisionOrPosition() {} 13 | 14 | /** 15 | * Returns a stream revision number. 16 | */ 17 | public Optional getRevision() { 18 | return revision; 19 | } 20 | 21 | /** 22 | * Returns a transaction log position. 23 | */ 24 | public Optional getPosition() { 25 | return position; 26 | } 27 | 28 | void setRevision(long revision) { 29 | this.revision = Optional.of(revision); 30 | } 31 | 32 | void setPosition(Position position) { 33 | this.position = Optional.of(position); 34 | } 35 | 36 | /** 37 | * Checks if the object holds a stream revision number. 38 | */ 39 | public boolean isRevisionPresent() { 40 | return revision.isPresent(); 41 | } 42 | 43 | /** 44 | * Checks if the object holds a transaction log position. 45 | */ 46 | public boolean isPositionPresent(){ 47 | return !isRevisionPresent(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/RunWorkItem.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import java.util.UUID; 4 | 5 | class RunWorkItem implements Msg { 6 | final UUID msgId; 7 | final WorkItem item; 8 | 9 | public RunWorkItem(UUID msgId, WorkItem item) { 10 | this.msgId = msgId; 11 | this.item = item; 12 | } 13 | 14 | public UUID getMsgId() { 15 | return msgId; 16 | } 17 | 18 | public WorkItem getItem() { 19 | return item; 20 | } 21 | 22 | public void reportError(Exception e) { 23 | this.item.accept(null, e); 24 | } 25 | 26 | @Override 27 | public String toString() { 28 | return "RunWorkItem[" + msgId + "]"; 29 | } 30 | 31 | @Override 32 | public void accept(ConnectionService connectionService) { 33 | connectionService.process(this); 34 | } 35 | } -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/ServerInfo.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | class ServerInfo { 4 | private final ServerVersion version; 5 | private final int features; 6 | 7 | ServerInfo(ServerVersion version, int features) { 8 | this.version = version; 9 | this.features = features; 10 | } 11 | 12 | public boolean supportFeature(int feature) { 13 | return (features & feature) != 0; 14 | } 15 | public ServerVersion getServerVersion() { return version; } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/ServerVersion.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | public class ServerVersion { 3 | private final int major; 4 | private final int minor; 5 | private final int patch; 6 | 7 | public ServerVersion(int major, int minor, int patch) { 8 | this.major = major; 9 | this.minor = minor; 10 | this.patch = patch; 11 | } 12 | 13 | public int getMajor() { 14 | return major; 15 | } 16 | 17 | public int getMinor() { 18 | return minor; 19 | } 20 | 21 | public int getPatch() { 22 | return patch; 23 | } 24 | 25 | public boolean isGreaterThan(int major, int minor, int patch) { 26 | return !isLessThan(major, minor, patch) && 27 | !equals(major, minor, patch); 28 | } 29 | 30 | public boolean equals(int major, int minor, int patch) { 31 | return this.major == major 32 | && this.minor == minor 33 | && this.patch == patch; 34 | } 35 | 36 | public boolean isLessThan(int major, int minor, int patch) { 37 | int cmp; 38 | if ((cmp = Integer.compare(this.major, major)) != 0) 39 | return cmp < 0; 40 | 41 | if ((cmp = Integer.compare(this.minor, minor)) != 0) 42 | return cmp < 0; 43 | 44 | if ((cmp = Integer.compare(this.patch, patch)) != 0) 45 | return cmp < 0; 46 | 47 | return false; 48 | } 49 | 50 | public boolean isLessOrEqualThan(int major, int minor) { 51 | int cmp; 52 | if ((cmp = Integer.compare(this.major, major)) != 0) 53 | return cmp < 0; 54 | 55 | if ((cmp = Integer.compare(this.minor, minor)) != 0) 56 | return cmp < 0; 57 | 58 | return true; 59 | } 60 | 61 | @Override 62 | public String toString() { 63 | return "ServerVersion{" + 64 | "major=" + major + 65 | ", minor=" + minor + 66 | ", patch=" + patch + 67 | '}'; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/Shutdown.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import java.util.function.Consumer; 4 | 5 | class Shutdown implements Msg { 6 | final Consumer completed; 7 | 8 | public Shutdown(Consumer completed) { 9 | this.completed = completed; 10 | } 11 | 12 | public void complete() { 13 | completed.accept(null); 14 | } 15 | 16 | @Override 17 | public String toString() { 18 | return "Shutdown"; 19 | } 20 | 21 | @Override 22 | public void accept(ConnectionService handler) { 23 | handler.shutdown(this); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/SingleNodeDiscovery.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import java.net.InetSocketAddress; 4 | import java.util.concurrent.CompletableFuture; 5 | 6 | class SingleNodeDiscovery implements Discovery { 7 | private final InetSocketAddress endpoint; 8 | 9 | SingleNodeDiscovery(InetSocketAddress endpoint) { 10 | this.endpoint = endpoint; 11 | } 12 | 13 | @Override 14 | public CompletableFuture run(ConnectionState state) { 15 | return CompletableFuture.runAsync(() -> state.connect(endpoint)); 16 | } 17 | } -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/StreamConsumer.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import java.time.Instant; 4 | 5 | public interface StreamConsumer { 6 | default void onSubscribe(org.reactivestreams.Subscription subscription) {} 7 | void onEvent(ResolvedEvent event); 8 | void onSubscriptionConfirmation(String subscriptionId); 9 | void onCheckpoint(long commit, long prepare); 10 | void onStreamNotFound(String streamName); 11 | void onFirstStreamPosition(long position); 12 | void onLastStreamPosition(long position); 13 | void onLastAllStreamPosition(long commit, long prepare); 14 | void onCaughtUp(Instant timestamp, Long streamRevision, Position position); 15 | void onFellBehind(Instant timestamp, Long streamRevision, Position position); 16 | void onCancelled(Throwable exception); 17 | void onComplete(); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/StreamDeletedException.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * When a read or write operation was performed on a deleted stream. 5 | */ 6 | final public class StreamDeletedException extends RuntimeException { 7 | private final String streamName; 8 | 9 | StreamDeletedException(String streamName) { 10 | super(String.format("Stream '%s' is deleted", streamName)); 11 | 12 | this.streamName = streamName; 13 | } 14 | 15 | public String getStreamName() { 16 | return streamName; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/StreamFilter.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import javax.validation.constraints.NotNull; 4 | import java.util.Arrays; 5 | import java.util.Objects; 6 | import java.util.Optional; 7 | 8 | class StreamFilter implements EventFilter { 9 | private final PrefixFilterExpression[] prefixFilterExpressions; 10 | private final RegularFilterExpression regularFilterExpression; 11 | @NotNull 12 | private final Optional maxSearchWindow; 13 | 14 | public StreamFilter(@NotNull Optional maxSearchWindow, RegularFilterExpression regex) { 15 | this.maxSearchWindow = maxSearchWindow; 16 | this.regularFilterExpression = regex; 17 | this.prefixFilterExpressions = null; 18 | } 19 | 20 | public StreamFilter(@NotNull Optional maxSearchWindow, PrefixFilterExpression... prefixes) { 21 | this.maxSearchWindow = maxSearchWindow; 22 | this.prefixFilterExpressions = prefixes; 23 | this.regularFilterExpression = null; 24 | } 25 | 26 | @Override 27 | public PrefixFilterExpression[] getPrefixFilterExpressions() { 28 | return this.prefixFilterExpressions; 29 | } 30 | 31 | @Override 32 | public RegularFilterExpression getRegularFilterExpression() { 33 | return regularFilterExpression; 34 | } 35 | 36 | @Override 37 | public Optional getMaxSearchWindow() { 38 | return maxSearchWindow; 39 | } 40 | 41 | @Override 42 | public boolean equals(Object o) { 43 | if (this == o) return true; 44 | if (o == null || getClass() != o.getClass()) return false; 45 | StreamFilter that = (StreamFilter) o; 46 | return Arrays.equals(prefixFilterExpressions, that.prefixFilterExpressions) && 47 | Objects.equals(regularFilterExpression, that.regularFilterExpression) && 48 | maxSearchWindow.equals(that.maxSearchWindow); 49 | } 50 | 51 | @Override 52 | public int hashCode() { 53 | int result = Objects.hash(regularFilterExpression, maxSearchWindow); 54 | result = 31 * result + Arrays.hashCode(prefixFilterExpressions); 55 | return result; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/StreamNotFoundException.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * When a stream is not found. 5 | */ 6 | public class StreamNotFoundException extends RuntimeException { 7 | private final String streamName; 8 | 9 | StreamNotFoundException(String streamName){ 10 | this.streamName = streamName; 11 | } 12 | 13 | public String getStreamName() { 14 | return this.streamName; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/SubscribePersistentSubscriptionOptions.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Options of the subscribe persistent subscription request. 5 | */ 6 | public class SubscribePersistentSubscriptionOptions extends OptionsBase { 7 | private int bufferSize; 8 | 9 | private SubscribePersistentSubscriptionOptions() { 10 | super(OperationKind.Streaming); 11 | this.bufferSize = 10; 12 | } 13 | 14 | /** 15 | * Returns options with default values. 16 | */ 17 | public static SubscribePersistentSubscriptionOptions get() { 18 | return new SubscribePersistentSubscriptionOptions(); 19 | } 20 | 21 | int getBufferSize() { 22 | return bufferSize; 23 | } 24 | 25 | /** 26 | * Persistent subscription's buffer size. 27 | */ 28 | public SubscribePersistentSubscriptionOptions bufferSize(int value) { 29 | bufferSize = value; 30 | return this; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/SubscribePersistentSubscriptionToAll.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import io.kurrent.dbclient.proto.persistentsubscriptions.Persistent; 4 | import io.kurrent.dbclient.proto.shared.Shared; 5 | 6 | class SubscribePersistentSubscriptionToAll extends AbstractSubscribePersistentSubscription { 7 | public SubscribePersistentSubscriptionToAll(GrpcClient connection, String group, 8 | SubscribePersistentSubscriptionOptions options, 9 | PersistentSubscriptionListener listener) { 10 | super(connection, group, options, listener); 11 | } 12 | 13 | @Override 14 | protected Persistent.ReadReq.Options.Builder createOptions() { 15 | return defaultReadOptions.clone() 16 | .setAll(Shared.Empty.newBuilder()); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/SubscribePersistentSubscriptionToStream.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import io.kurrent.dbclient.proto.persistentsubscriptions.Persistent; 4 | import io.kurrent.dbclient.proto.shared.Shared; 5 | import com.google.protobuf.ByteString; 6 | 7 | class SubscribePersistentSubscriptionToStream extends AbstractSubscribePersistentSubscription { 8 | private final String stream; 9 | 10 | public SubscribePersistentSubscriptionToStream(GrpcClient connection, String stream, String group, 11 | SubscribePersistentSubscriptionOptions options, 12 | PersistentSubscriptionListener listener) { 13 | super(connection, group, options, listener); 14 | 15 | this.stream = stream; 16 | } 17 | 18 | @Override 19 | protected Persistent.ReadReq.Options.Builder createOptions() { 20 | Shared.StreamIdentifier streamIdentifier = 21 | Shared.StreamIdentifier.newBuilder() 22 | .setStreamName(ByteString.copyFromUtf8(stream)) 23 | .build(); 24 | 25 | return defaultReadOptions.clone() 26 | .setStreamIdentifier(streamIdentifier); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/SubscribeToAll.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import io.kurrent.dbclient.proto.shared.Shared; 4 | import io.kurrent.dbclient.proto.streams.StreamsOuterClass; 5 | 6 | class SubscribeToAll extends AbstractRegularSubscription { 7 | private final SubscribeToAllOptions options; 8 | 9 | public SubscribeToAll(GrpcClient client, SubscriptionListener listener, SubscribeToAllOptions options) { 10 | super(client, options); 11 | 12 | this.options = options; 13 | this.listener = listener; 14 | } 15 | 16 | @Override 17 | protected StreamsOuterClass.ReadReq.Options.Builder createOptions() { 18 | StreamsOuterClass.ReadReq.Options.AllOptions.Builder allOptions = StreamsOuterClass.ReadReq.Options.AllOptions.newBuilder(); 19 | 20 | if (this.options.getPosition().isEnd()) { 21 | allOptions.setEnd(Shared.Empty.getDefaultInstance()); 22 | } else if (this.options.getPosition().isStart()) { 23 | allOptions.setStart(Shared.Empty.getDefaultInstance()); 24 | } else { 25 | StreamPosition.Position position = (StreamPosition.Position) this.options.getPosition(); 26 | allOptions.setPosition(StreamsOuterClass.ReadReq.Options.Position.newBuilder() 27 | .setCommitPosition(position.getPositionOrThrow().getCommitUnsigned()) 28 | .setPreparePosition(position.getPositionOrThrow().getPrepareUnsigned())); 29 | } 30 | StreamsOuterClass.ReadReq.Options.Builder options = 31 | defaultSubscribeOptions.clone() 32 | .setResolveLinks(this.options.shouldResolveLinkTos()) 33 | .setAll(allOptions); 34 | 35 | if (this.options.getFilter() != null) { 36 | this.options.getFilter().addToWireStreamsReadReq(options); 37 | this.checkpointer = this.options.getFilter().getCheckpointer(); 38 | } else { 39 | options.setNoFilter(Shared.Empty.getDefaultInstance()); 40 | } 41 | 42 | return options; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/SubscribeToAllOptions.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Options of a subscription to $all request. 5 | */ 6 | public class SubscribeToAllOptions extends OptionsWithPositionAndResolveLinkTosBase { 7 | private SubscriptionFilter filter; 8 | 9 | private SubscribeToAllOptions() { 10 | super(OperationKind.Streaming); 11 | } 12 | 13 | /** 14 | * Returns options with default values. 15 | */ 16 | public static SubscribeToAllOptions get() { 17 | return new SubscribeToAllOptions(); 18 | } 19 | 20 | SubscriptionFilter getFilter() { 21 | return filter; 22 | } 23 | 24 | /** 25 | * Applies a server-side filter to determine if an event of the subscription should be yielded. 26 | */ 27 | public SubscribeToAllOptions filter(SubscriptionFilter filter) { 28 | this.filter = filter; 29 | return this; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/SubscribeToStream.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import io.kurrent.dbclient.proto.shared.Shared; 4 | import io.kurrent.dbclient.proto.streams.StreamsOuterClass; 5 | 6 | class SubscribeToStream extends AbstractRegularSubscription { 7 | private final SubscribeToStreamOptions options; 8 | private final String streamName; 9 | 10 | public SubscribeToStream(GrpcClient client, String streamName, SubscriptionListener listener, SubscribeToStreamOptions options) { 11 | super(client, options); 12 | 13 | this.streamName = streamName; 14 | this.options = options; 15 | 16 | this.listener = listener; 17 | } 18 | 19 | 20 | @Override 21 | protected StreamsOuterClass.ReadReq.Options.Builder createOptions() { 22 | return defaultSubscribeOptions.clone() 23 | .setResolveLinks(this.options.shouldResolveLinkTos()) 24 | .setNoFilter(Shared.Empty.getDefaultInstance()) 25 | .setStream(GrpcUtils.toStreamOptions(this.streamName, this.options.getStartingRevision())); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/SubscribeToStreamOptions.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Options of the subscribe to stream request. 5 | */ 6 | public class SubscribeToStreamOptions extends OptionsWithStartRevisionAndResolveLinkTosBase { 7 | private SubscribeToStreamOptions() { 8 | super(OperationKind.Streaming); 9 | } 10 | 11 | /** 12 | * Returns options with default values. 13 | */ 14 | public static SubscribeToStreamOptions get() { 15 | return new SubscribeToStreamOptions(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/Subscription.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import io.kurrent.dbclient.proto.streams.StreamsOuterClass; 4 | import io.grpc.stub.ClientCallStreamObserver; 5 | 6 | /** 7 | * Subscription handle. 8 | */ 9 | public class Subscription { 10 | private final org.reactivestreams.Subscription internal; 11 | private final String subscriptionId; 12 | private final Checkpointer checkpointer; 13 | 14 | Subscription(org.reactivestreams.Subscription internal, String subscriptionId, Checkpointer checkpointer) { 15 | this.internal = internal; 16 | this.subscriptionId = subscriptionId; 17 | this.checkpointer = checkpointer; 18 | } 19 | 20 | /** 21 | * Returns subscription's id. 22 | */ 23 | public String getSubscriptionId() { 24 | return this.subscriptionId; 25 | } 26 | 27 | /** 28 | * Drops the subscription. 29 | */ 30 | public void stop() { 31 | this.internal.cancel(); 32 | } 33 | 34 | Checkpointer getCheckpointer() { 35 | return this.checkpointer; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/SubscriptionListener.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import java.time.Instant; 4 | 5 | /** 6 | * Listener used to handle catch-up subscription notifications raised throughout its lifecycle. 7 | */ 8 | public abstract class SubscriptionListener { 9 | /** 10 | * Called when EventStoreDB sends an event to the subscription. 11 | * @param subscription handle to the subscription. 12 | * @param event a resolved event. 13 | */ 14 | public void onEvent(Subscription subscription, ResolvedEvent event) {} 15 | 16 | /** 17 | * Called when the subscription is cancelled or dropped. 18 | * @param subscription handle to the subscription. 19 | * @param exception an exception. null if the user initiated the cancellation. 20 | */ 21 | public void onCancelled(Subscription subscription, Throwable exception) {} 22 | 23 | /** 24 | * Called when the subscription is confirmed by the server. 25 | * @param subscription handle to the subscription. 26 | */ 27 | public void onConfirmation(Subscription subscription) {} 28 | 29 | /** 30 | * Called when the subscription has reached the head of the stream. 31 | * @param subscription handle to the subscription. 32 | */ 33 | public void onCaughtUp(Subscription subscription, Instant timestamp, Long streamRevision, Position position) {} 34 | 35 | /** 36 | * Called when the subscription has fallen behind, meaning it's no longer keeping up with the 37 | * stream's pace. 38 | * @param subscription handle to the subscription. 39 | */ 40 | public void onFellBehind(Subscription subscription, Instant timestamp, Long streamRevision, Position position) {} 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/SubscriptionTracingCallback.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | @FunctionalInterface 4 | public interface SubscriptionTracingCallback { 5 | void trace(String subscriptionId, RecordedEvent event, Runnable action); 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/SystemMetadataKeys.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | class SystemMetadataKeys { 4 | static final String CONTENT_TYPE = "content-type"; 5 | static final String CREATED = "created"; 6 | static final String IS_JSON = "is-json"; 7 | static final String TYPE = "type"; 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/SystemStreamAcl.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Admin stream access control list (ACL). 5 | */ 6 | class SystemStreamAcl implements Acl { 7 | public static final String ACL_NAME = "$systemStreamAcl"; 8 | private static final SystemStreamAcl SINGLETON = new SystemStreamAcl(); 9 | 10 | private SystemStreamAcl() {} 11 | 12 | static SystemStreamAcl deserialize(String source) { 13 | SystemStreamAcl acl = null; 14 | 15 | if (source.equals(ACL_NAME)) 16 | acl = SINGLETON; 17 | 18 | return acl; 19 | } 20 | 21 | static SystemStreamAcl getInstance() { 22 | return SINGLETON; 23 | } 24 | 25 | @Override 26 | public int hashCode() { 27 | return ACL_NAME.hashCode(); 28 | } 29 | 30 | @Override 31 | public boolean equals(Object obj) { 32 | return (obj instanceof SystemStreamAcl); 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return ACL_NAME; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/SystemStreams.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | class SystemStreams { 4 | public static final String ALL_STREAM = "$all"; 5 | public static final String STREAMS_STREAM = "$streams"; 6 | public static final String SETTINGS_STREAM = "$settings"; 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/ThrowingBiFunction.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | @FunctionalInterface 4 | interface ThrowingBiFunction { 5 | 6 | TResult apply(TFirst first, TSecond second) throws TException; 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/ThrowingFunction.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | @FunctionalInterface 4 | interface ThrowingFunction { 5 | 6 | TResult apply(TInput first) throws TException; 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/Tuple.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | class Tuple { 4 | private final A _1; 5 | private final B _2; 6 | 7 | public Tuple(A _1, B _2) { 8 | this._1 = _1; 9 | this._2 = _2; 10 | } 11 | 12 | public A get_1() { 13 | return this._1; 14 | } 15 | 16 | public B get_2() { 17 | return this._2; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/UnsupportedFeatureException.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * A request not supported by the targeted KurrentDB node was sent. 5 | */ 6 | public class UnsupportedFeatureException extends RuntimeException { 7 | UnsupportedFeatureException(){ 8 | super("Unsupported feature exception"); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/UpdatePersistentSubscriptionToAll.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import io.kurrent.dbclient.proto.persistentsubscriptions.Persistent; 4 | import io.kurrent.dbclient.proto.shared.Shared; 5 | 6 | class UpdatePersistentSubscriptionToAll extends AbstractUpdatePersistentSubscription { 7 | private final UpdatePersistentSubscriptionToAllOptions options; 8 | public UpdatePersistentSubscriptionToAll(GrpcClient connection, String group, 9 | UpdatePersistentSubscriptionToAllOptions options) { 10 | super(connection, group, options.getSettings(), options); 11 | 12 | this.options = options; 13 | } 14 | 15 | @Override 16 | protected Persistent.UpdateReq.Options.Builder createOptions() { 17 | Persistent.UpdateReq.Options.Builder optionsBuilder = Persistent.UpdateReq.Options.newBuilder(); 18 | Persistent.UpdateReq.AllOptions.Builder allOptionsBuilder = Persistent.UpdateReq.AllOptions.newBuilder(); 19 | 20 | StreamPosition startFrom = options.getSettings().getStartFrom(); 21 | 22 | if (startFrom instanceof StreamPosition.Start) { 23 | allOptionsBuilder.setStart(Shared.Empty.newBuilder()); 24 | } else if (startFrom instanceof StreamPosition.End) { 25 | allOptionsBuilder.setEnd(Shared.Empty.newBuilder()); 26 | } else { 27 | Position position = startFrom.getPositionOrThrow(); 28 | allOptionsBuilder.setPosition(Persistent.UpdateReq.Position.newBuilder() 29 | .setCommitPosition(position.getCommitUnsigned()) 30 | .setPreparePosition(position.getPrepareUnsigned())); 31 | } 32 | 33 | optionsBuilder.setAll(allOptionsBuilder); 34 | 35 | return optionsBuilder; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/UpdatePersistentSubscriptionToStream.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import io.kurrent.dbclient.proto.persistentsubscriptions.Persistent; 4 | import io.kurrent.dbclient.proto.shared.Shared; 5 | import com.google.protobuf.ByteString; 6 | 7 | class UpdatePersistentSubscriptionToStream extends AbstractUpdatePersistentSubscription { 8 | private final PersistentSubscriptionToStreamSettings settings; 9 | private final String stream; 10 | 11 | public UpdatePersistentSubscriptionToStream(GrpcClient connection, String stream, String group, 12 | UpdatePersistentSubscriptionToStreamOptions options) { 13 | super(connection, group, options.getSettings(), options); 14 | 15 | this.stream = stream; 16 | this.settings = options.getSettings(); 17 | } 18 | 19 | @Override 20 | protected Persistent.UpdateReq.Settings.Builder createSettings() { 21 | return Persistent.UpdateReq.Settings.newBuilder(); 22 | } 23 | 24 | @Override 25 | @SuppressWarnings("deprecation") 26 | // As long as the 20.10LTS, we have to use that setStreamIdentifier deprecated call. 27 | protected Persistent.UpdateReq.Options.Builder createOptions() { 28 | Persistent.UpdateReq.Options.Builder optionsBuilder = Persistent.UpdateReq.Options.newBuilder(); 29 | Shared.StreamIdentifier.Builder streamIdentifierBuilder = Shared.StreamIdentifier.newBuilder(); 30 | Persistent.UpdateReq.StreamOptions.Builder streamOptionsBuilder = Persistent.UpdateReq.StreamOptions 31 | .newBuilder(); 32 | 33 | StreamPosition position = settings.getStartFrom(); 34 | if (position instanceof StreamPosition.Start) { 35 | streamOptionsBuilder.setStart(Shared.Empty.newBuilder()); 36 | } else if (position instanceof StreamPosition.End) { 37 | streamOptionsBuilder.setEnd(Shared.Empty.newBuilder()); 38 | } else { 39 | streamOptionsBuilder.setRevision(position.getPositionOrThrow()); 40 | } 41 | 42 | streamIdentifierBuilder.setStreamName(ByteString.copyFromUtf8(stream)); 43 | streamOptionsBuilder.setStreamIdentifier(streamIdentifierBuilder); 44 | optionsBuilder.setStream(streamOptionsBuilder); 45 | optionsBuilder.setStreamIdentifier(streamIdentifierBuilder); 46 | 47 | return optionsBuilder; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/UpdatePersistentSubscriptionToStreamOptions.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Options of the update persistent subscription to stream request. 5 | */ 6 | public class UpdatePersistentSubscriptionToStreamOptions 7 | extends AbstractPersistentSubscriptionSettingsBuilder { 8 | UpdatePersistentSubscriptionToStreamOptions() { 9 | this(PersistentSubscriptionSettings.defaultRegular()); 10 | } 11 | 12 | UpdatePersistentSubscriptionToStreamOptions(PersistentSubscriptionToStreamSettings settings) { 13 | super(settings); 14 | } 15 | 16 | /** 17 | * Returns options with default values. 18 | */ 19 | public static UpdatePersistentSubscriptionToStreamOptions get() { 20 | return new UpdatePersistentSubscriptionToStreamOptions(); 21 | } 22 | 23 | /** 24 | * Returns options from a persistent subscription to stream settings. 25 | * @see PersistentSubscriptionToStreamSettings 26 | */ 27 | public static UpdatePersistentSubscriptionToStreamOptions from(PersistentSubscriptionToStreamSettings settings) { 28 | return new UpdatePersistentSubscriptionToStreamOptions(settings); 29 | } 30 | 31 | /** 32 | * Starts the persistent subscription from the beginning of the stream. 33 | */ 34 | public UpdatePersistentSubscriptionToStreamOptions fromStart() { 35 | getSettings().setStartFrom(StreamPosition.start()); 36 | return this; 37 | } 38 | 39 | /** 40 | * Starts the persistent subscription from the end of the stream. 41 | */ 42 | public UpdatePersistentSubscriptionToStreamOptions fromEnd() { 43 | getSettings().setStartFrom(StreamPosition.end()); 44 | return this; 45 | } 46 | 47 | /** 48 | * Starts the persistent subscription from a specific revision number. 49 | */ 50 | public UpdatePersistentSubscriptionToStreamOptions startFrom(long revision) { 51 | getSettings().setStartFrom(StreamPosition.position(revision)); 52 | return this; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/UpdateProjection.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import io.kurrent.dbclient.proto.projections.Projectionmanagement; 4 | import io.kurrent.dbclient.proto.projections.ProjectionsGrpc; 5 | import io.kurrent.dbclient.proto.shared.Shared; 6 | 7 | import java.util.concurrent.CompletableFuture; 8 | 9 | class UpdateProjection { 10 | private final GrpcClient client; 11 | private final String projectionName; 12 | private final String query; 13 | private final Boolean emitEnabled; 14 | private final UpdateProjectionOptions options; 15 | 16 | public UpdateProjection(final GrpcClient client, final String projectionName, final String query, 17 | final UpdateProjectionOptions options) { 18 | this.client = client; 19 | this.projectionName = projectionName; 20 | this.query = query; 21 | this.emitEnabled = options.isEmitEnabled(); 22 | this.options = options; 23 | } 24 | 25 | public CompletableFuture execute() { 26 | return this.client.run(channel -> { 27 | Projectionmanagement.UpdateReq.Options.Builder optionsBuilder = 28 | Projectionmanagement.UpdateReq.Options.newBuilder() 29 | .setName(this.projectionName) 30 | .setQuery(this.query); 31 | 32 | if(this.emitEnabled == null){ 33 | optionsBuilder.setNoEmitOptions(Shared.Empty.newBuilder()); 34 | } else { 35 | optionsBuilder.setEmitEnabled(this.emitEnabled); 36 | } 37 | 38 | Projectionmanagement.UpdateReq request = Projectionmanagement.UpdateReq.newBuilder() 39 | .setOptions(optionsBuilder) 40 | .build(); 41 | 42 | ProjectionsGrpc.ProjectionsStub client = 43 | GrpcUtils.configureStub(ProjectionsGrpc.newStub(channel), this.client.getSettings(), this.options); 44 | 45 | CompletableFuture result = new CompletableFuture<>(); 46 | 47 | client.update(request, GrpcUtils.convertSingleResponse(result)); 48 | 49 | return result; 50 | }); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/UpdateProjectionOptions.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Options of the update projection request. 5 | */ 6 | public class UpdateProjectionOptions extends OptionsBase { 7 | private boolean emitEnabled; 8 | 9 | private UpdateProjectionOptions() { 10 | } 11 | 12 | /** 13 | * Returns options with default values. 14 | */ 15 | public static UpdateProjectionOptions get() { 16 | return new UpdateProjectionOptions(); 17 | } 18 | 19 | /** 20 | * Allows the projection to write events. 21 | */ 22 | public UpdateProjectionOptions emitEnabled(boolean value) { 23 | this.emitEnabled = value; 24 | return this; 25 | } 26 | 27 | boolean isEmitEnabled() { 28 | return emitEnabled; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/UserCredentials.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import java.nio.charset.StandardCharsets; 4 | import java.util.Base64; 5 | 6 | /** 7 | * Holds a login and a password for authenticated requests. 8 | */ 9 | public final class UserCredentials { 10 | private final String username; 11 | private final String base64Encoded; 12 | 13 | public UserCredentials(String username, String password) { 14 | this.username = username; 15 | 16 | byte[] credentialsBytes = String.format("%s:%s", username, password).getBytes(StandardCharsets.US_ASCII); 17 | base64Encoded = String.format("Basic %s", Base64.getEncoder().encodeToString(credentialsBytes)); 18 | } 19 | 20 | String getUsername() { return username; } 21 | 22 | String basicAuthHeader() { 23 | return base64Encoded; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/UserStreamAcl.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | /** 4 | * Default user stream access control list (ACL). 5 | */ 6 | class UserStreamAcl implements Acl { 7 | public static final String ACL_NAME = "$userStreamAcl"; 8 | private static final UserStreamAcl SINGLETON = new UserStreamAcl(); 9 | 10 | private UserStreamAcl() {} 11 | 12 | static UserStreamAcl deserialize(String source) { 13 | UserStreamAcl acl = null; 14 | 15 | if (source.equals(ACL_NAME)) 16 | acl = SINGLETON; 17 | 18 | return acl; 19 | } 20 | 21 | static UserStreamAcl getInstance() { 22 | return SINGLETON; 23 | } 24 | 25 | @Override 26 | public int hashCode() { 27 | return ACL_NAME.hashCode(); 28 | } 29 | 30 | @Override 31 | public boolean equals(Object obj) { 32 | return (obj instanceof UserStreamAcl); 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return ACL_NAME; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/WorkItem.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | interface WorkItem { 4 | void accept(WorkItemArgs args, Exception error); 5 | } -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/WriteResult.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import java.util.Objects; 4 | 5 | /** 6 | * Returned after writing to a stream. 7 | */ 8 | public class WriteResult { 9 | private final StreamState nextExpectedRevision; 10 | private final Position logPosition; 11 | 12 | WriteResult(StreamState nextExpectedRevision, Position logPosition) { 13 | this.nextExpectedRevision = nextExpectedRevision; 14 | this.logPosition = logPosition; 15 | } 16 | 17 | /** 18 | * Next expected version of the stream. 19 | */ 20 | public StreamState getNextExpectedRevision() { 21 | return nextExpectedRevision; 22 | } 23 | 24 | /** 25 | * Transaction log position of the write. 26 | */ 27 | public Position getLogPosition() { 28 | return logPosition; 29 | } 30 | 31 | @Override 32 | public boolean equals(Object o) { 33 | if (this == o) return true; 34 | if (o == null || getClass() != o.getClass()) return false; 35 | WriteResult that = (WriteResult) o; 36 | return nextExpectedRevision == that.nextExpectedRevision && 37 | logPosition.equals(that.logPosition); 38 | } 39 | 40 | @Override 41 | public int hashCode() { 42 | return Objects.hash(nextExpectedRevision, logPosition); 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | return "WriteResult{" + 48 | "nextExpectedRevision=" + nextExpectedRevision + 49 | ", logPosition=" + logPosition + 50 | '}'; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/WrongExpectedVersionException.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import javax.validation.constraints.NotNull; 4 | 5 | /** 6 | * When append request failed the optimistic concurrency on the server. 7 | */ 8 | public class WrongExpectedVersionException extends RuntimeException { 9 | private final String streamName; 10 | private final StreamState expectedState; 11 | private final StreamState actualState; 12 | 13 | WrongExpectedVersionException( 14 | @NotNull String streamName, 15 | @NotNull StreamState expectedState, 16 | @NotNull StreamState actualState) { 17 | super(String.format("Expected %s but got %s instead", expectedState, actualState)); 18 | this.streamName = streamName; 19 | this.expectedState = expectedState; 20 | this.actualState = actualState; 21 | } 22 | 23 | /** 24 | * Returns on which stream the error occurred. 25 | */ 26 | public String getStreamName() { 27 | return streamName; 28 | } 29 | 30 | /** 31 | * Returns the expected stream state by the request. 32 | */ 33 | public StreamState getExpectedState() { 34 | return expectedState; 35 | } 36 | 37 | /** 38 | * Returns the actual stream state when the check was performed. 39 | */ 40 | public StreamState getActualState() { 41 | return actualState; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/resolution/DeferredNodeResolution.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient.resolution; 2 | 3 | import java.net.InetSocketAddress; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | public class DeferredNodeResolution implements NodeResolution { 8 | private final InetSocketAddress address; 9 | 10 | public DeferredNodeResolution(InetSocketAddress address) { 11 | this.address = address; 12 | } 13 | 14 | @Override 15 | public List resolve() { 16 | return Collections.singletonList(address); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/resolution/DeprecatedNodeResolution.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient.resolution; 2 | 3 | import java.net.InetAddress; 4 | import java.net.InetSocketAddress; 5 | import java.net.UnknownHostException; 6 | import java.util.Arrays; 7 | import java.util.List; 8 | import java.util.stream.Collectors; 9 | 10 | public class DeprecatedNodeResolution implements NodeResolution { 11 | private final InetSocketAddress address; 12 | 13 | public DeprecatedNodeResolution(InetSocketAddress address) { 14 | this.address = address; 15 | } 16 | 17 | @Override 18 | public List resolve() { 19 | try { 20 | return Arrays.stream(InetAddress.getAllByName(address.getHostName())) 21 | .map(addr -> new InetSocketAddress(addr, address.getPort())) 22 | .collect(Collectors.toList()); 23 | } catch (UnknownHostException e) { 24 | throw new RuntimeException(e); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/resolution/FixedSeedsNodeResolution.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient.resolution; 2 | 3 | import java.net.InetSocketAddress; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | 7 | public class FixedSeedsNodeResolution implements NodeResolution { 8 | private final InetSocketAddress[] seeds; 9 | 10 | public FixedSeedsNodeResolution(InetSocketAddress[] seeds) { 11 | this.seeds = seeds; 12 | } 13 | 14 | @Override 15 | public List resolve() { 16 | return Arrays.asList(seeds); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/io/kurrent/dbclient/resolution/NodeResolution.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient.resolution; 2 | 3 | import java.net.InetSocketAddress; 4 | import java.util.List; 5 | 6 | public interface NodeResolution { 7 | List resolve(); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/proto/gossip.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package event_store.client.gossip; 3 | option java_package = "io.kurrent.dbclient.proto.gossip"; 4 | 5 | import "shared.proto"; 6 | 7 | service Gossip { 8 | rpc Read (event_store.client.Empty) returns (ClusterInfo); 9 | } 10 | 11 | message ClusterInfo { 12 | repeated MemberInfo members = 1; 13 | } 14 | 15 | message EndPoint { 16 | string address = 1; 17 | uint32 port = 2; 18 | } 19 | 20 | message MemberInfo { 21 | enum VNodeState { 22 | Initializing = 0; 23 | DiscoverLeader = 1; 24 | Unknown = 2; 25 | PreReplica = 3; 26 | CatchingUp = 4; 27 | Clone = 5; 28 | Follower = 6; 29 | PreLeader = 7; 30 | Leader = 8; 31 | Manager = 9; 32 | ShuttingDown = 10; 33 | Shutdown = 11; 34 | ReadOnlyLeaderless = 12; 35 | PreReadOnlyReplica = 13; 36 | ReadOnlyReplica = 14; 37 | ResigningLeader = 15; 38 | } 39 | event_store.client.UUID instance_id = 1; 40 | int64 time_stamp = 2; 41 | VNodeState state = 3; 42 | bool is_alive = 4; 43 | EndPoint http_end_point = 5; 44 | } -------------------------------------------------------------------------------- /src/main/proto/serverfeatures.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package event_store.client.server_features; 3 | option java_package = "io.kurrent.dbclient.proto.serverfeatures"; 4 | import "shared.proto"; 5 | 6 | service ServerFeatures { 7 | rpc GetSupportedMethods (event_store.client.Empty) returns (SupportedMethods); 8 | } 9 | 10 | message SupportedMethods { 11 | repeated SupportedMethod methods = 1; 12 | string event_store_server_version = 2; 13 | } 14 | 15 | message SupportedMethod { 16 | string method_name = 1; 17 | string service_name = 2; 18 | repeated string features = 3; 19 | } 20 | -------------------------------------------------------------------------------- /src/main/proto/shared.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package event_store.client; 3 | option java_package = "io.kurrent.dbclient.proto.shared"; 4 | import "google/protobuf/empty.proto"; 5 | 6 | message UUID { 7 | oneof value { 8 | Structured structured = 1; 9 | string string = 2; 10 | } 11 | 12 | message Structured { 13 | int64 most_significant_bits = 1; 14 | int64 least_significant_bits = 2; 15 | } 16 | } 17 | message Empty { 18 | } 19 | 20 | message StreamIdentifier { 21 | reserved 1 to 2; 22 | bytes stream_name = 3; 23 | } 24 | 25 | message AllStreamPosition { 26 | uint64 commit_position = 1; 27 | uint64 prepare_position = 2; 28 | } 29 | 30 | message WrongExpectedVersion { 31 | oneof current_stream_revision_option { 32 | uint64 current_stream_revision = 1; 33 | google.protobuf.Empty current_no_stream = 2; 34 | } 35 | oneof expected_stream_position_option { 36 | uint64 expected_stream_position = 3; 37 | google.protobuf.Empty expected_any = 4; 38 | google.protobuf.Empty expected_stream_exists = 5; 39 | google.protobuf.Empty expected_no_stream = 6; 40 | } 41 | } 42 | 43 | message AccessDenied {} 44 | 45 | message StreamDeleted { 46 | StreamIdentifier stream_identifier = 1; 47 | } 48 | 49 | message Timeout {} 50 | 51 | message Unknown {} 52 | 53 | message InvalidTransaction {} 54 | 55 | message MaximumAppendSizeExceeded { 56 | uint32 maxAppendSize = 1; 57 | } 58 | 59 | message BadRequest { 60 | string message = 1; 61 | } -------------------------------------------------------------------------------- /src/main/proto/status.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | package google.rpc; 18 | 19 | import "google/protobuf/any.proto"; 20 | import "code.proto"; 21 | 22 | option cc_enable_arenas = true; 23 | option go_package = "google.golang.org/genproto/googleapis/rpc/status;status"; 24 | option java_multiple_files = true; 25 | option java_outer_classname = "StatusProto"; 26 | option java_package = "com.google.rpc"; 27 | option objc_class_prefix = "RPC"; 28 | 29 | // The `Status` type defines a logical error model that is suitable for 30 | // different programming environments, including REST APIs and RPC APIs. It is 31 | // used by [gRPC](https://github.com/grpc). Each `Status` message contains 32 | // three pieces of data: error code, error message, and error details. 33 | // 34 | // You can find out more about this error model and how to work with it in the 35 | // [API Design Guide](https://cloud.google.com/apis/design/errors). 36 | message Status { 37 | // The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. 38 | google.rpc.Code code = 1; 39 | 40 | // A developer-facing error message, which should be in English. Any 41 | // user-facing error message should be localized and sent in the 42 | // [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. 43 | string message = 2; 44 | 45 | // A list of messages that carry the error details. There is a common set of 46 | // message types for APIs to use. 47 | google.protobuf.Any details = 3; 48 | } -------------------------------------------------------------------------------- /src/test/java/io/kurrent/dbclient/Action.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | @FunctionalInterface 4 | public interface Action { 5 | A run() throws Exception; 6 | } 7 | -------------------------------------------------------------------------------- /src/test/java/io/kurrent/dbclient/BazEvent.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import java.util.Objects; 4 | 5 | public class BazEvent { 6 | private String name; 7 | private int age; 8 | 9 | public BazEvent() {} 10 | 11 | public BazEvent(String name, int age) { 12 | this.name = name; 13 | this.age = age; 14 | } 15 | 16 | public String getName() { 17 | return name; 18 | } 19 | 20 | public int getAge() { 21 | return age; 22 | } 23 | 24 | public void setName(String name) { 25 | this.name = name; 26 | } 27 | 28 | public void setAge(int age) { 29 | this.age = age; 30 | } 31 | 32 | @Override 33 | public boolean equals(Object o) { 34 | if (this == o) return true; 35 | if (o == null || getClass() != o.getClass()) return false; 36 | BazEvent bazEvent = (BazEvent) o; 37 | return age == bazEvent.age && Objects.equals(name, bazEvent.name); 38 | } 39 | 40 | @Override 41 | public int hashCode() { 42 | return Objects.hash(name, age); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/io/kurrent/dbclient/Database.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.util.Optional; 7 | import java.util.function.Function; 8 | 9 | public interface Database { 10 | Logger logger = LoggerFactory.getLogger(Database.class); 11 | 12 | ConnectionSettingsBuilder defaultSettingsBuilder(); 13 | 14 | ClientTracker getClientTracker(); 15 | 16 | void cleanup(); 17 | 18 | default KurrentDBClient newClient() { 19 | return connectWith(Function.identity()); 20 | } 21 | 22 | default KurrentDBClient connectWith(Function mod) { 23 | return createClient(mod.apply(defaultSettingsBuilder()).buildConnectionSettings()); 24 | } 25 | 26 | default KurrentDBClient defaultClient() { 27 | return getClientTracker().getDefaultClient(this); 28 | } 29 | 30 | default KurrentDBClient createClient(KurrentDBClientSettings settings) { 31 | return getClientTracker().createClient(settings); 32 | } 33 | 34 | default boolean isTargetingBelowOrEqual21_10() { 35 | try { 36 | Optional result = defaultClient().getServerVersion().get(); 37 | return !result.isPresent() || result.get().isLessOrEqualThan(21, 10); 38 | } catch (Exception e) { 39 | logger.error("Error when retrieving the server version", e); 40 | throw new RuntimeException(e); 41 | } 42 | } 43 | 44 | default void dispose() { 45 | cleanup(); 46 | getClientTracker().dispose(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/io/kurrent/dbclient/DatabaseFactory.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import io.kurrent.dbclient.databases.DockerContainerDatabase; 4 | import io.kurrent.dbclient.databases.ExternallyCreatedCluster; 5 | 6 | import java.util.Optional; 7 | 8 | public class DatabaseFactory { 9 | public static Database spawn() { 10 | boolean secure = Boolean.parseBoolean(Optional.ofNullable(System.getenv("SECURE")).orElse("false")); 11 | boolean cluster = Boolean.parseBoolean(Optional.ofNullable(System.getenv("CLUSTER")).orElse("false")); 12 | 13 | if (cluster) 14 | return new ExternallyCreatedCluster(secure); 15 | 16 | return singleNodeBuilder() 17 | .secure(secure) 18 | .build(); 19 | } 20 | 21 | public static Database spawnEnterpriseWithPluginsEnabled(String... pluginsToEnable) { 22 | boolean secure = Boolean.parseBoolean(Optional.ofNullable(System.getenv("SECURE")).orElse("false")); 23 | boolean cluster = Boolean.parseBoolean(Optional.ofNullable(System.getenv("CLUSTER")).orElse("false")); 24 | 25 | if (cluster) 26 | return new ExternallyCreatedCluster(secure); 27 | 28 | DockerContainerDatabase.Builder builder = singleNodeBuilder(); 29 | 30 | for (String plugin : pluginsToEnable) 31 | builder.env(String.format("EventStore__Plugins__%s__Enabled", plugin), "true"); 32 | 33 | return builder.secure(secure).build(); 34 | } 35 | 36 | private static DockerContainerDatabase.Builder singleNodeBuilder() { 37 | return DockerContainerDatabase 38 | .builder() 39 | .image(Optional 40 | .ofNullable(System.getenv("KURRENTDB_IMAGE")) 41 | .orElse(DockerContainerDatabase.DEFAULT_IMAGE)); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/io/kurrent/dbclient/Exceptions.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import io.grpc.Status; 4 | import io.grpc.StatusRuntimeException; 5 | 6 | import java.util.ArrayList; 7 | import java.util.function.Predicate; 8 | 9 | public class Exceptions { 10 | final private ArrayList> exceptions; 11 | 12 | public Exceptions() { 13 | exceptions = new ArrayList<>(); 14 | } 15 | 16 | public Exceptions register(Predicate predicate) { 17 | this.exceptions.add(e -> { 18 | @SuppressWarnings("unchecked") 19 | A target = (A) e; 20 | return target != null && predicate.test(target); 21 | }); 22 | 23 | return this; 24 | } 25 | 26 | public Exceptions register(Class clazz) { 27 | this.exceptions.add(clazz::isInstance); 28 | return this; 29 | } 30 | 31 | public Exceptions registerGoAwayError() { 32 | return this.register(e -> 33 | e.getStatus().getCode() == Status.Code.INTERNAL 34 | ); 35 | } 36 | 37 | public Exceptions registerUnknownError() { 38 | return this.register(e -> 39 | e.getStatus().getCode() == Status.Code.UNKNOWN 40 | ); 41 | } 42 | 43 | public boolean contains(Throwable target) { 44 | for (Predicate predicate : this.exceptions) { 45 | if (predicate.test(target)) { 46 | return true; 47 | } 48 | } 49 | 50 | return false; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/io/kurrent/dbclient/Foo.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import java.util.Objects; 4 | 5 | public class Foo { 6 | @Override 7 | public boolean equals(Object o) { 8 | if (this == o) return true; 9 | if (o == null || getClass() != o.getClass()) return false; 10 | Foo foo1 = (Foo) o; 11 | return foo == foo1.foo; 12 | } 13 | 14 | @Override 15 | public int hashCode() { 16 | return Objects.hash(foo); 17 | } 18 | 19 | private boolean foo; 20 | 21 | public boolean isFoo() { 22 | return foo; 23 | } 24 | 25 | public void setFoo(boolean foo) { 26 | this.foo = foo; 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /src/test/java/io/kurrent/dbclient/MiscTests.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import org.junit.platform.suite.api.SelectPackages; 4 | import org.junit.platform.suite.api.Suite; 5 | 6 | @Suite 7 | @SelectPackages("io.kurrent.dbclient.misc") 8 | public class MiscTests {} 9 | -------------------------------------------------------------------------------- /src/test/java/io/kurrent/dbclient/PersistentSubscriptionsTests.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import io.kurrent.dbclient.persistentsubscriptions.*; 4 | import io.kurrent.dbclient.persistentsubscriptions.PersistentSubscriptionToAllWithFilterTests; 5 | import org.junit.jupiter.api.AfterAll; 6 | import org.junit.jupiter.api.BeforeAll; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | public class PersistentSubscriptionsTests implements 11 | CreatePersistentSubscriptionTests, 12 | PersistentSubscriptionManagementTests, 13 | DeletePersistentSubscriptionToStreamTests, 14 | UpdatePersistentSubscriptionToStreamTests, 15 | SubscribePersistentSubscriptionToStreamTests, 16 | PersistentSubscriptionToAllWithFilterTests { 17 | static private Database database; 18 | static private Logger logger; 19 | 20 | @BeforeAll 21 | public static void setup() { 22 | database = DatabaseFactory.spawn(); 23 | logger = LoggerFactory.getLogger(PersistentSubscriptionsTests.class); 24 | } 25 | 26 | @Override 27 | public Database getDatabase() { 28 | return database; 29 | } 30 | 31 | @Override 32 | public Logger getLogger() { 33 | return logger; 34 | } 35 | 36 | @AfterAll 37 | public static void cleanup() { 38 | database.dispose(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/io/kurrent/dbclient/PluginsTests.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | import io.kurrent.dbclient.plugins.ClientCertificateAuthenticationTests; 4 | import org.junit.jupiter.api.AfterAll; 5 | import org.junit.jupiter.api.BeforeAll; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | /** 10 | * PluginsTests require the enterprise edition of KurrentDB (>= 24.2.0). 11 | */ 12 | public class PluginsTests implements ClientCertificateAuthenticationTests { 13 | static private Database database; 14 | static private Logger logger; 15 | 16 | @BeforeAll 17 | public static void setup() { 18 | database = DatabaseFactory.spawnEnterpriseWithPluginsEnabled("UserCertificates"); 19 | logger = LoggerFactory.getLogger(StreamsTests.class); 20 | } 21 | 22 | @Override 23 | public Database getDatabase() { 24 | return database; 25 | } 26 | 27 | @Override 28 | public Logger getLogger() { 29 | return logger; 30 | } 31 | 32 | @AfterAll 33 | public static void cleanup() { 34 | database.dispose(); 35 | } 36 | } 37 | 38 | -------------------------------------------------------------------------------- /src/test/java/io/kurrent/dbclient/StreamsTests.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient; 2 | 3 | 4 | import io.kurrent.dbclient.streams.*; 5 | import io.kurrent.dbclient.streams.ReadStreamTests; 6 | import org.junit.jupiter.api.AfterAll; 7 | import org.junit.jupiter.api.BeforeAll; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | public class StreamsTests implements 12 | AppendTests, 13 | ReadStreamTests, 14 | SubscriptionTests, 15 | DeleteTests, 16 | DeadlineTests, 17 | InterceptorTests, 18 | MetadataTests, 19 | ClientLifecycleTests { 20 | static private Database database; 21 | static private Logger logger; 22 | 23 | @BeforeAll 24 | public static void setup() { 25 | database = DatabaseFactory.spawn(); 26 | logger = LoggerFactory.getLogger(StreamsTests.class); 27 | } 28 | 29 | @Override 30 | public Database getDatabase() { 31 | return database; 32 | } 33 | 34 | @Override 35 | public Logger getLogger() { 36 | return logger; 37 | } 38 | 39 | @AfterAll 40 | public static void cleanup() { 41 | database.dispose(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/io/kurrent/dbclient/databases/ExternallyCreatedCluster.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient.databases; 2 | 3 | import io.kurrent.dbclient.*; 4 | 5 | public class ExternallyCreatedCluster implements Database { 6 | final private boolean secure; 7 | final private ClientTracker clientTracker; 8 | 9 | public ExternallyCreatedCluster(boolean secure) { 10 | this.secure = secure; 11 | this.clientTracker = new ClientTracker(); 12 | } 13 | 14 | @Override 15 | public ConnectionSettingsBuilder defaultSettingsBuilder() { 16 | return KurrentDBClientSettings 17 | .builder() 18 | .dnsDiscover(true) 19 | .defaultCredentials("admin", "changeit") 20 | .addHost("localhost", 2_113) 21 | .tls(secure) 22 | .tlsVerifyCert(false) 23 | .maxDiscoverAttempts(50) 24 | .defaultDeadline(60_000); 25 | } 26 | 27 | @Override 28 | public ClientTracker getClientTracker() { 29 | return clientTracker; 30 | } 31 | 32 | @Override 33 | public void cleanup() { 34 | // Nothing do to here. 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/io/kurrent/dbclient/misc/EventDataTests.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient.misc; 2 | 3 | import io.kurrent.dbclient.EventData; 4 | import io.kurrent.dbclient.EventDataBuilder; 5 | import org.junit.jupiter.api.Assertions; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import java.util.UUID; 9 | 10 | public class EventDataTests { 11 | @Test 12 | public void testBinaryConstructorWithId() throws Throwable { 13 | UUID id = UUID.randomUUID(); 14 | 15 | EventData data = EventDataBuilder.binary(id, "type", new byte[]{}).build(); 16 | 17 | Assertions.assertEquals(id, data.getEventId()); 18 | } 19 | 20 | @Test 21 | public void testJsonConstructorWithId() throws Throwable { 22 | UUID id = UUID.randomUUID(); 23 | 24 | EventData data = EventDataBuilder.json(id, "type", new byte[]{}).build(); 25 | 26 | Assertions.assertEquals(id, data.getEventId()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/test/java/io/kurrent/dbclient/misc/OfflineMetadataTests.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient.misc; 2 | 3 | import io.kurrent.dbclient.Acl; 4 | import io.kurrent.dbclient.Acls; 5 | import io.kurrent.dbclient.StreamMetadata; 6 | import com.fasterxml.jackson.databind.ObjectMapper; 7 | import org.junit.jupiter.api.Assertions; 8 | import org.junit.jupiter.api.Test; 9 | 10 | import java.util.HashMap; 11 | 12 | public class OfflineMetadataTests { 13 | @Test 14 | public void testSerializationIsoMorphism() throws Throwable { 15 | StreamMetadata expected = new StreamMetadata(); 16 | HashMap custom = new HashMap<>(); 17 | 18 | custom.put("foo", "bar"); 19 | 20 | expected.setMaxAge(2L); 21 | expected.setCacheControl(15L); 22 | expected.setTruncateBefore(1L); 23 | expected.setMaxCount(12L); 24 | 25 | Acl acl = Acls.newStreamAcl() 26 | .addReadRoles("admin") 27 | .addWriteRoles("admin") 28 | .addDeleteRoles("admin") 29 | .addMetaReadRoles("admin") 30 | .addMetaWriteRoles("admin"); 31 | 32 | expected.setAcl(acl); 33 | expected.setCustomProperties(custom); 34 | 35 | ObjectMapper mapper = new ObjectMapper(); 36 | StreamMetadata actual = mapper.readValue(mapper.writeValueAsString(expected), StreamMetadata.class); 37 | 38 | Assertions.assertEquals(expected, actual); 39 | } 40 | 41 | @Test 42 | public void testNullAcl() throws Throwable { 43 | StreamMetadata expected = new StreamMetadata(); 44 | 45 | expected.setMaxAge(2L); 46 | expected.setCacheControl(15L); 47 | expected.setTruncateBefore(1L); 48 | expected.setMaxCount(12L); 49 | 50 | ObjectMapper mapper = new ObjectMapper(); 51 | StreamMetadata actual = mapper.readValue(mapper.writeValueAsString(expected), StreamMetadata.class); 52 | 53 | Assertions.assertEquals(expected, actual); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/test/java/io/kurrent/dbclient/misc/ParseInvalidConnectionStringTests.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient.misc; 2 | 3 | import io.kurrent.dbclient.ConnectionStringParsingException; 4 | import io.kurrent.dbclient.KurrentDBConnectionString; 5 | import org.junit.jupiter.api.Assertions; 6 | import org.junit.jupiter.params.ParameterizedTest; 7 | import org.junit.jupiter.params.provider.Arguments; 8 | import org.junit.jupiter.params.provider.MethodSource; 9 | 10 | import java.util.stream.Stream; 11 | 12 | public class ParseInvalidConnectionStringTests { 13 | public static Stream invalidConnectionStrings() { 14 | return Stream.of( 15 | Arguments.of("localhost"), 16 | Arguments.of("https://console.eventstore.cloud/"), 17 | Arguments.of("kurnt+discovery://localhost"), 18 | Arguments.of("kurrent://my:great@username:UyeXx8$^PsOo4jG88FlCauR1Coz25q@host?nodePreference=follower&tlsVerifyCert=false"), 19 | Arguments.of("kurrent://host1,host2:200:300?tlsVerifyCert=false"), 20 | Arguments.of("kurrent://localhost/&tlsVerifyCert=false"), 21 | Arguments.of("kurrent://localhost?tlsVerifyCert=false?nodePreference=follower"), 22 | Arguments.of("kurrent://localhost?tlsVerifyCert=false&nodePreference=any"), 23 | Arguments.of("kurrent://localhost?tlsVerifyCert=if you feel like it"), 24 | Arguments.of("kurrent://localhost?keepAliveInterval=-3"), 25 | Arguments.of("kurrent://localhost?keepAliveInterval=sdfksjsfl"), 26 | Arguments.of("kurrent://localhost?keepAliveTimeout=sdfksjsfl"), 27 | Arguments.of("kurrent://localhost?keepAliveTimeout=-3"), 28 | Arguments.of("kurrent://localhost?nodePreference=read_only_replica"), 29 | Arguments.of("kurrent://localhost?userCertFile=/path/to/cert"), 30 | Arguments.of("kurrent://localhost?userKeyFile=/path/to/key") 31 | ); 32 | } 33 | 34 | @ParameterizedTest 35 | @MethodSource("invalidConnectionStrings") 36 | public void test(String input) throws ConnectionStringParsingException { 37 | Assertions.assertThrows(RuntimeException.class, () -> { 38 | KurrentDBConnectionString.parseOrThrow(input); 39 | }); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/io/kurrent/dbclient/misc/ServerVersionTests.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient.misc; 2 | 3 | import io.kurrent.dbclient.ServerVersion; 4 | import org.junit.jupiter.api.Assertions; 5 | import org.junit.jupiter.api.Test; 6 | 7 | public class ServerVersionTests { 8 | @Test 9 | public void testEquals() { 10 | ServerVersion v = new ServerVersion(22, 6, 123); 11 | 12 | Assertions.assertTrue(v.equals(22, 6, 123)); 13 | 14 | Assertions.assertFalse(v.equals(21, 6, 123)); 15 | Assertions.assertFalse(v.equals(22, 5, 123)); 16 | Assertions.assertFalse(v.equals(22, 6, 124)); 17 | } 18 | 19 | @Test 20 | public void testIsLessThan() { 21 | ServerVersion v = new ServerVersion(22, 6, 123); 22 | 23 | Assertions.assertFalse(v.isLessThan(22, 6, 123)); 24 | 25 | Assertions.assertTrue(v.isLessThan(23, 6, 123)); 26 | Assertions.assertTrue(v.isLessThan(22, 7, 123)); 27 | Assertions.assertTrue(v.isLessThan(22, 6, 124)); 28 | 29 | Assertions.assertFalse(v.isLessThan(21, 6, 123)); 30 | Assertions.assertFalse(v.isLessThan(22, 5, 123)); 31 | Assertions.assertFalse(v.isLessThan(22, 6, 122)); 32 | 33 | Assertions.assertTrue(v.isLessThan(23, 5, 123)); 34 | } 35 | 36 | @Test 37 | public void testIsGreaterThan() { 38 | ServerVersion v = new ServerVersion(22, 6, 123); 39 | 40 | Assertions.assertFalse(v.isGreaterThan(22, 6, 123)); 41 | 42 | Assertions.assertTrue(v.isGreaterThan(21, 6, 123)); 43 | Assertions.assertTrue(v.isGreaterThan(22, 5, 123)); 44 | Assertions.assertTrue(v.isGreaterThan(22, 6, 122)); 45 | 46 | Assertions.assertFalse(v.isGreaterThan(23, 6, 123)); 47 | Assertions.assertFalse(v.isGreaterThan(22, 7, 123)); 48 | Assertions.assertFalse(v.isGreaterThan(22, 6, 124)); 49 | 50 | Assertions.assertTrue(v.isGreaterThan(21, 7, 123)); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/io/kurrent/dbclient/persistentsubscriptions/CreatePersistentSubscriptionTests.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient.persistentsubscriptions; 2 | 3 | import io.kurrent.dbclient.*; 4 | import org.junit.jupiter.api.Test; 5 | 6 | public interface CreatePersistentSubscriptionTests extends ConnectionAware { 7 | @Test 8 | default void testCreatePersistentSub() throws Throwable { 9 | KurrentDBPersistentSubscriptionsClient client = getDefaultPersistentSubscriptionClient(); 10 | 11 | client.createToStream(generateName(), generateName()) 12 | .get(); 13 | 14 | client.createToStream(generateName(), generateName(), CreatePersistentSubscriptionToStreamOptions.get().startFrom(2)) 15 | .get(); 16 | } 17 | 18 | @Test 19 | default void testCreatePersistentSubToAll() throws Throwable { 20 | KurrentDBPersistentSubscriptionsClient client = getDefaultPersistentSubscriptionClient(); 21 | 22 | client.createToAll(generateName()) 23 | .get(); 24 | 25 | client.createToAll(generateName(), CreatePersistentSubscriptionToAllOptions.get().startFrom(1, 2)) 26 | .get(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/test/java/io/kurrent/dbclient/persistentsubscriptions/DeletePersistentSubscriptionToStreamTests.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient.persistentsubscriptions; 2 | 3 | import io.kurrent.dbclient.ConnectionAware; 4 | import io.kurrent.dbclient.KurrentDBPersistentSubscriptionsClient; 5 | import org.junit.jupiter.api.Test; 6 | 7 | public interface DeletePersistentSubscriptionToStreamTests extends ConnectionAware { 8 | @Test 9 | default void testDeletePersistentSub() throws Throwable { 10 | KurrentDBPersistentSubscriptionsClient client = getDefaultPersistentSubscriptionClient(); 11 | String streamName = generateName(); 12 | String groupName = generateName(); 13 | 14 | client.createToStream(streamName, groupName) 15 | .get(); 16 | 17 | client.deleteToStream(streamName, groupName) 18 | .get(); 19 | } 20 | 21 | @Test 22 | default void testDeletePersistentSubToAll() throws Throwable { 23 | KurrentDBPersistentSubscriptionsClient client = getDefaultPersistentSubscriptionClient(); 24 | String groupName = generateName(); 25 | 26 | client.createToAll(groupName) 27 | .get(); 28 | 29 | client.deleteToAll(groupName) 30 | .get(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/io/kurrent/dbclient/persistentsubscriptions/UpdatePersistentSubscriptionToStreamTests.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient.persistentsubscriptions; 2 | 3 | import io.kurrent.dbclient.*; 4 | import org.junit.jupiter.api.Test; 5 | 6 | public interface UpdatePersistentSubscriptionToStreamTests extends ConnectionAware { 7 | @Test 8 | default void testUpdatePersistentSub() throws Throwable { 9 | String streamName = generateName(); 10 | String groupName = generateName(); 11 | 12 | KurrentDBPersistentSubscriptionsClient client = getDefaultPersistentSubscriptionClient(); 13 | client.createToStream(streamName, groupName) 14 | .get(); 15 | 16 | UpdatePersistentSubscriptionToStreamOptions updated = UpdatePersistentSubscriptionToStreamOptions.get() 17 | .checkpointAfterInMs(5_000) 18 | .startFrom(2); 19 | 20 | client.updateToStream(streamName, groupName, updated) 21 | .get(); 22 | } 23 | 24 | @Test 25 | default void testUpdatePersistentSubToAll() throws Throwable { 26 | String groupName = generateName(); 27 | KurrentDBPersistentSubscriptionsClient client = getDefaultPersistentSubscriptionClient(); 28 | 29 | client.createToAll(groupName) 30 | .get(); 31 | UpdatePersistentSubscriptionToAllOptions updatedSettings = UpdatePersistentSubscriptionToAllOptions.get() 32 | .checkpointAfterInMs(5_000) 33 | .startFrom(3,4); 34 | 35 | client.updateToAll(groupName, updatedSettings) 36 | .get(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/io/kurrent/dbclient/plugins/ClientCertificateAuthenticationTests.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient.plugins; 2 | 3 | import io.kurrent.dbclient.*; 4 | import org.junit.jupiter.api.Assertions; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import java.nio.file.Paths; 8 | 9 | public interface ClientCertificateAuthenticationTests extends ConnectionAware { 10 | @Test 11 | default void testClientCertificateAuthenticationWithValidCertificate() { 12 | Assertions.assertDoesNotThrow(() -> { 13 | KurrentDBClient client = getDatabase() 14 | .createClient(getDatabase() 15 | .defaultSettingsBuilder() 16 | .defaultClientCertificate(clientCertificate("admin"), userKey("admin")) 17 | .defaultCredentials(null) 18 | .buildConnectionSettings()); 19 | 20 | client.readAll().get(); 21 | }); 22 | } 23 | 24 | static String clientCertificate(String user) { 25 | return buildCertPath(user, "crt"); 26 | } 27 | 28 | static String userKey(String user) { 29 | return buildCertPath(user, "key"); 30 | } 31 | 32 | static String buildCertPath(String user, String extension) { 33 | String certsPath = Paths.get(System.getProperty("user.dir"), "certs").toAbsolutePath().toString(); 34 | return String.format("%s/user-%s/user-%s.%s", certsPath, user, user, extension); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/io/kurrent/dbclient/samples/TestEvent.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient.samples; 2 | 3 | public class TestEvent { 4 | private String id; 5 | private String importantData; 6 | 7 | public TestEvent(){ 8 | } 9 | 10 | public TestEvent(String id, String importantData){ 11 | this.id = id; 12 | this.importantData = importantData; 13 | } 14 | 15 | public String getId() { 16 | return id; 17 | } 18 | 19 | public void setId(String id){ 20 | this.id = id; 21 | } 22 | 23 | public String getImportantData() { 24 | return importantData; 25 | } 26 | 27 | public void setImportantData(String importantData){ 28 | this.importantData = importantData; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/io/kurrent/dbclient/samples/authentication/UserCertificate.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient.samples.authentication; 2 | 3 | import io.kurrent.dbclient.KurrentDBClient; 4 | import io.kurrent.dbclient.KurrentDBClientSettings; 5 | import io.kurrent.dbclient.KurrentDBConnectionString; 6 | 7 | public class UserCertificate { 8 | private static void tracing() { 9 | // region client-with-user-certificates 10 | KurrentDBClientSettings settings = KurrentDBConnectionString 11 | .parseOrThrow("kurrentdb://admin:changeit@{endpoint}?tls=true&userCertFile={pathToCaFile}&userKeyFile={pathToKeyFile}"); 12 | KurrentDBClient client = KurrentDBClient.create(settings); 13 | // endregion client-with-user-certificates 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/test/java/io/kurrent/dbclient/samples/quick_start/QuickStart.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient.samples.quick_start; 2 | 3 | import io.kurrent.dbclient.*; 4 | import io.kurrent.dbclient.samples.TestEvent; 5 | import com.fasterxml.jackson.core.JsonProcessingException; 6 | import com.fasterxml.jackson.databind.json.JsonMapper; 7 | 8 | import java.util.UUID; 9 | import java.util.concurrent.ExecutionException; 10 | 11 | public class QuickStart { 12 | public static void Run() throws ConnectionStringParsingException, ExecutionException, InterruptedException, JsonProcessingException { 13 | // region createClient 14 | KurrentDBClientSettings settings = KurrentDBConnectionString.parseOrThrow("{connectionString}"); 15 | KurrentDBClient client = KurrentDBClient.create(settings); 16 | // endregion createClient 17 | 18 | // region createEvent 19 | TestEvent event = new TestEvent(); 20 | JsonMapper jsonMapper = new JsonMapper(); 21 | event.setId(UUID.randomUUID().toString()); 22 | event.setImportantData("I wrote my first event!"); 23 | 24 | EventData eventData = EventData 25 | .builderAsJson("TestEvent", jsonMapper.writeValueAsBytes(event)) 26 | .build(); 27 | // endregion createEvent 28 | 29 | // region appendEvents 30 | client.appendToStream("some-stream", eventData) 31 | .get(); 32 | // endregion appendEvents 33 | 34 | // region overriding-user-credentials 35 | AppendToStreamOptions appendToStreamOptions = AppendToStreamOptions.get() 36 | .authenticated("admin", "changeit"); 37 | 38 | client.appendToStream("some-stream", appendToStreamOptions, eventData) 39 | .get(); 40 | // endregion overriding-user-credentials 41 | 42 | 43 | // region readStream 44 | ReadStreamOptions options = ReadStreamOptions.get() 45 | .forwards() 46 | .fromStart() 47 | .maxCount(10); 48 | 49 | ReadResult result = client.readStream("some-stream", options) 50 | .get(); 51 | // endregion readStream 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/test/java/io/kurrent/dbclient/streams/AppendTests.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient.streams; 2 | 3 | import io.kurrent.dbclient.*; 4 | import com.fasterxml.jackson.databind.json.JsonMapper; 5 | import org.junit.jupiter.api.Assertions; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import java.util.UUID; 9 | 10 | public interface AppendTests extends ConnectionAware { 11 | @Test 12 | default void testAppendSingleEventNoStream() throws Throwable { 13 | KurrentDBClient client = getDatabase().defaultClient(); 14 | 15 | final String streamName = generateName(); 16 | final String eventType = "TestEvent"; 17 | final String eventId = "38fffbc2-339e-11ea-8c7b-784f43837872"; 18 | final byte[] eventMetaData = new byte[]{0xd, 0xe, 0xa, 0xd}; 19 | final JsonMapper jsonMapper = new JsonMapper(); 20 | 21 | EventData event = EventData.builderAsJson(eventType, jsonMapper.writeValueAsBytes(new Foo())) 22 | .metadataAsBytes(eventMetaData) 23 | .eventId(UUID.fromString(eventId)) 24 | .build(); 25 | 26 | AppendToStreamOptions appendOptions = AppendToStreamOptions.get() 27 | .streamState(StreamState.noStream()); 28 | 29 | WriteResult appendResult = client.appendToStream(streamName, appendOptions, event) 30 | .get(); 31 | 32 | Assertions.assertEquals(StreamState.streamRevision(0), appendResult.getNextExpectedRevision()); 33 | 34 | ReadStreamOptions readStreamOptions = ReadStreamOptions.get() 35 | .fromEnd() 36 | .backwards() 37 | .maxCount(1); 38 | 39 | // Ensure appended event is readable 40 | ReadResult result = client.readStream(streamName, readStreamOptions) 41 | .get(); 42 | 43 | Assertions.assertEquals(1, result.getEvents().size()); 44 | RecordedEvent first = result.getEvents().get(0).getEvent(); 45 | JsonMapper mapper = new JsonMapper(); 46 | 47 | Assertions.assertEquals(streamName, first.getStreamId()); 48 | Assertions.assertEquals(eventType, first.getEventType()); 49 | Assertions.assertEquals(eventId, first.getEventId().toString()); 50 | Assertions.assertArrayEquals(eventMetaData, first.getUserMetadata()); 51 | Assertions.assertEquals(new Foo(), mapper.readValue(first.getEventData(), Foo.class)); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/test/java/io/kurrent/dbclient/streams/ClientLifecycleTests.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient.streams; 2 | 3 | import io.kurrent.dbclient.ConnectionAware; 4 | import io.kurrent.dbclient.ConnectionShutdownException; 5 | import io.kurrent.dbclient.KurrentDBClient; 6 | import io.kurrent.dbclient.KurrentDBClientSettings; 7 | import org.junit.jupiter.api.Test; 8 | 9 | import java.util.concurrent.ExecutionException; 10 | 11 | import static org.junit.jupiter.api.Assertions.assertFalse; 12 | import static org.junit.jupiter.api.Assertions.assertInstanceOf; 13 | import static org.junit.jupiter.api.Assertions.assertTrue; 14 | import static org.junit.jupiter.api.Assertions.fail; 15 | 16 | public interface ClientLifecycleTests extends ConnectionAware { 17 | @Test 18 | default void testProvidesRunningStatus() { 19 | KurrentDBClient client = getDatabase().newClient(); 20 | 21 | assertFalse(client.isShutdown()); 22 | } 23 | 24 | @Test 25 | default void testProvidesShutdownStatusAfterManualShutdown() throws Throwable { 26 | KurrentDBClient client = getDatabase().newClient(); 27 | 28 | client.shutdown().get(); 29 | 30 | assertTrue(client.isShutdown()); 31 | } 32 | 33 | @Test 34 | default void testProvidesShutdownStatusAfterAutomaticShutdown() throws Throwable { 35 | KurrentDBClientSettings settings = KurrentDBClientSettings.builder() 36 | .addHost("unknown.host.name", 2113) 37 | .buildConnectionSettings(); 38 | KurrentDBClient client = KurrentDBClient.create(settings); 39 | 40 | try { 41 | client.readAll().get(); 42 | fail(); 43 | } catch (ExecutionException ex) { 44 | assertInstanceOf(ConnectionShutdownException.class, ex.getCause()); 45 | } 46 | assertTrue(client.isShutdown()); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/io/kurrent/dbclient/streams/DeadlineTests.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient.streams; 2 | 3 | import io.kurrent.dbclient.*; 4 | import io.grpc.Status; 5 | import io.grpc.StatusRuntimeException; 6 | import org.junit.jupiter.api.Assertions; 7 | import org.junitpioneer.jupiter.RetryingTest; 8 | 9 | import java.util.UUID; 10 | import java.util.concurrent.ExecutionException; 11 | 12 | public interface DeadlineTests extends ConnectionAware { 13 | @RetryingTest(10) 14 | default void testDefaultDeadline() throws Throwable { 15 | KurrentDBClient client = getDatabase().connectWith(opts -> 16 | opts.defaultDeadline(1) 17 | .maxDiscoverAttempts(3)); 18 | UUID id = UUID.randomUUID(); 19 | 20 | EventData data = EventDataBuilder.binary(id, "type", new byte[]{}).build(); 21 | ExecutionException e = Assertions.assertThrows(ExecutionException.class, () -> client.appendToStream("toto", data).get()); 22 | StatusRuntimeException status = (StatusRuntimeException) e.getCause(); 23 | 24 | Assertions.assertEquals(status.getStatus().getCode(), Status.Code.DEADLINE_EXCEEDED); 25 | } 26 | 27 | @RetryingTest(3) 28 | default void testOptionLevelDeadline() throws Throwable { 29 | KurrentDBClient client = getDatabase().defaultClient(); 30 | UUID id = UUID.randomUUID(); 31 | 32 | EventData data = EventDataBuilder.binary(id, "type", new byte[]{}).build(); 33 | AppendToStreamOptions options = AppendToStreamOptions.get().deadline(1); 34 | ExecutionException e = Assertions.assertThrows(ExecutionException.class, () -> client.appendToStream("toto", options, data).get()); 35 | StatusRuntimeException status = (StatusRuntimeException) e.getCause(); 36 | 37 | Assertions.assertEquals(status.getStatus().getCode(), Status.Code.DEADLINE_EXCEEDED); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/io/kurrent/dbclient/streams/DeleteTests.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient.streams; 2 | 3 | import io.kurrent.dbclient.*; 4 | import org.junit.jupiter.api.Assertions; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import java.util.concurrent.ExecutionException; 8 | 9 | public interface DeleteTests extends ConnectionAware { 10 | @Test 11 | default void testCanDeleteStream() throws Throwable { 12 | KurrentDBClient client = getDatabase().defaultClient(); 13 | String streamName = generateName(); 14 | 15 | client.appendToStream(streamName, generateEvents(1, "foobar").iterator()).get(); 16 | 17 | client.deleteStream(streamName).get(); 18 | } 19 | 20 | @Test 21 | default void testDeleteStreamWhenAlreadyDeleted() throws Throwable { 22 | KurrentDBClient client = getDatabase().defaultClient(); 23 | String streamName = generateName(); 24 | 25 | client.appendToStream(streamName, generateEvents(1, "foobar").iterator()).get(); 26 | client.tombstoneStream(streamName, DeleteStreamOptions.get()).get(); 27 | Assertions.assertThrows(StreamDeletedException.class, () -> { 28 | try { 29 | client.tombstoneStream(streamName, DeleteStreamOptions.get()).get(); 30 | } catch (ExecutionException e) { 31 | throw e.getCause(); 32 | } 33 | }); 34 | } 35 | 36 | @Test 37 | default void testDeleteStreamWhenDoesntExist() throws Throwable { 38 | KurrentDBClient client = getDatabase().defaultClient(); 39 | 40 | String streamName = generateName(); 41 | DeleteStreamOptions options = DeleteStreamOptions.get() 42 | .streamState(StreamState.streamExists()); 43 | 44 | Assertions.assertThrows(WrongExpectedVersionException.class, () -> { 45 | try { 46 | client.tombstoneStream(streamName, options).get(); 47 | } catch (ExecutionException e) { 48 | throw e.getCause(); 49 | } 50 | }); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/io/kurrent/dbclient/streams/InterceptorTests.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient.streams; 2 | 3 | import io.kurrent.dbclient.ConnectionAware; 4 | import io.kurrent.dbclient.KurrentDBClient; 5 | import io.kurrent.dbclient.ReadStreamOptions; 6 | import io.grpc.*; 7 | import org.junit.jupiter.api.Assertions; 8 | import org.junit.jupiter.api.Test; 9 | 10 | import java.util.concurrent.atomic.AtomicInteger; 11 | 12 | public interface InterceptorTests extends ConnectionAware { 13 | @Test 14 | default void testInterceptorIsCalled() { 15 | AtomicInteger atom = new AtomicInteger(0); 16 | KurrentDBClient client = getDatabase().connectWith(opts -> opts.addInterceptor(new MyInterceptor(atom))); 17 | 18 | try { 19 | client.readStream("foobar", ReadStreamOptions.get()).get(); 20 | } catch (Exception e) { 21 | // We don't care. 22 | } 23 | 24 | Assertions.assertEquals(42, atom.get()); 25 | } 26 | 27 | class MyInterceptor implements ClientInterceptor { 28 | final AtomicInteger atom; 29 | 30 | MyInterceptor(AtomicInteger atom) { 31 | this.atom = atom; 32 | } 33 | @Override 34 | public ClientCall interceptCall(MethodDescriptor method, CallOptions callOptions, Channel next) { 35 | atom.set(42); 36 | return next.newCall(method, callOptions); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/io/kurrent/dbclient/streams/MetadataTests.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient.streams; 2 | 3 | import io.kurrent.dbclient.*; 4 | import org.junit.jupiter.api.Assertions; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import java.util.HashMap; 8 | 9 | public interface MetadataTests extends ConnectionAware { 10 | @Test 11 | default void testSetStreamMetadata() throws Throwable { 12 | KurrentDBClient client = getDatabase().defaultClient(); 13 | 14 | StreamMetadata metadata = new StreamMetadata(); 15 | 16 | metadata.setMaxAge(2L); 17 | metadata.setCacheControl(15L); 18 | metadata.setTruncateBefore(1L); 19 | metadata.setMaxCount(12L); 20 | 21 | Acl acl = Acls.newStreamAcl() 22 | .addReadRoles("admin") 23 | .addWriteRoles("admin") 24 | .addDeleteRoles("admin") 25 | .addMetaReadRoles("admin") 26 | .addMetaWriteRoles("admin"); 27 | 28 | metadata.setAcl(acl); 29 | 30 | byte[] payload = "data".getBytes(); 31 | 32 | String streamName = generateName(); 33 | 34 | client.appendToStream(streamName, EventDataBuilder.json("foo", payload).build()).get(); 35 | client.setStreamMetadata(streamName, metadata).get(); 36 | 37 | StreamMetadata got = client.getStreamMetadata(streamName).get(); 38 | 39 | Assertions.assertEquals(metadata, got); 40 | } 41 | 42 | @Test 43 | default void testReadNoExistingMetadata() throws Throwable { 44 | KurrentDBClient client = getDatabase().defaultClient(); 45 | String streamName = generateName(); 46 | client.appendToStream(streamName, EventDataBuilder.json("bar", "data".getBytes()).build()).get(); 47 | 48 | StreamMetadata got = client.getStreamMetadata(streamName).get(); 49 | 50 | Assertions.assertEquals(new StreamMetadata(), got); 51 | } 52 | 53 | @Test 54 | default void testReadMetadataAfterStreamDeletion() throws Throwable { 55 | KurrentDBClient client = getDatabase().defaultClient(); 56 | String streamName = generateName(); 57 | client.appendToStream(streamName, EventDataBuilder.json("bar", "data".getBytes()).build()).get(); 58 | 59 | client.deleteStream(streamName).get(); 60 | client.getStreamMetadata(streamName).get(); 61 | } 62 | } -------------------------------------------------------------------------------- /src/test/java/io/kurrent/dbclient/streams/ReadStreamTests.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient.streams; 2 | 3 | import io.kurrent.dbclient.*; 4 | import org.junit.jupiter.api.Assertions; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import java.util.concurrent.ExecutionException; 10 | 11 | public interface ReadStreamTests extends ConnectionAware { 12 | @Test 13 | default void testReadStreamEvents() throws Throwable { 14 | String streamName = generateName(); 15 | int count = 3; 16 | List expecteds = generateBazEvent(count); 17 | List actuals = new ArrayList<>(); 18 | List events = new ArrayList<>(); 19 | 20 | for (BazEvent event : expecteds) { 21 | events.add(serializeBazEvent(event)); 22 | } 23 | 24 | KurrentDBClient client = getDefaultClient(); 25 | 26 | client.appendToStream(streamName, events.iterator()).get(); 27 | ReadResult result = client.readStream(streamName, ReadStreamOptions.get()).get(); 28 | 29 | for (ResolvedEvent resolvedEvent : result.getEvents()) { 30 | BazEvent event = deserializeBazEvent(resolvedEvent.getOriginalEvent().getEventData()); 31 | actuals.add(event); 32 | } 33 | 34 | for (int i = 0; i < count; i++) { 35 | BazEvent actual = actuals.get(i); 36 | BazEvent expected = expecteds.get(i); 37 | Assertions.assertEquals(expected.getName(), actual.getName()); 38 | Assertions.assertEquals(expected.getAge(), actual.getAge()); 39 | } 40 | } 41 | 42 | @Test 43 | default void testNonexistentStream() throws Throwable { 44 | String streamName = generateName(); 45 | 46 | Assertions.assertThrows(StreamNotFoundException.class, () -> { 47 | try { 48 | getDefaultClient().readStream(streamName, ReadStreamOptions.get()).get(); 49 | } catch (ExecutionException e) { 50 | throw e.getCause(); 51 | } 52 | }); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/io/kurrent/dbclient/telemetry/SpanProcessorSpy.java: -------------------------------------------------------------------------------- 1 | package io.kurrent.dbclient.telemetry; 2 | 3 | import io.opentelemetry.context.Context; 4 | import io.opentelemetry.sdk.trace.ReadWriteSpan; 5 | import io.opentelemetry.sdk.trace.ReadableSpan; 6 | import io.opentelemetry.sdk.trace.SpanProcessor; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import java.util.List; 10 | import java.util.function.Consumer; 11 | 12 | public class SpanProcessorSpy implements SpanProcessor { 13 | private final List> spanEndedHooks; 14 | 15 | public SpanProcessorSpy(List> spanEndedHooks) { 16 | this.spanEndedHooks = spanEndedHooks; 17 | } 18 | 19 | @Override 20 | public void onStart(@NotNull Context context, @NotNull ReadWriteSpan readWriteSpan) { 21 | // Do nothing. 22 | } 23 | 24 | @Override 25 | public boolean isStartRequired() { 26 | return false; 27 | } 28 | 29 | @Override 30 | public void onEnd(@NotNull ReadableSpan readableSpan) { 31 | spanEndedHooks.forEach(hook -> hook.accept(readableSpan)); 32 | } 33 | 34 | @Override 35 | public boolean isEndRequired() { 36 | return true; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/resources/all-positions-filtered-stream194-e0-e30.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "prepare": 137783, 4 | "commit": 137783 5 | }, 6 | { 7 | "prepare": 167653, 8 | "commit": 167653 9 | }, 10 | { 11 | "prepare": 197523, 12 | "commit": 197523 13 | }, 14 | { 15 | "prepare": 227393, 16 | "commit": 227393 17 | }, 18 | { 19 | "prepare": 257263, 20 | "commit": 257263 21 | }, 22 | { 23 | "prepare": 287133, 24 | "commit": 287133 25 | }, 26 | { 27 | "prepare": 317003, 28 | "commit": 317003 29 | }, 30 | { 31 | "prepare": 346873, 32 | "commit": 346873 33 | }, 34 | { 35 | "prepare": 376743, 36 | "commit": 376743 37 | }, 38 | { 39 | "prepare": 406613, 40 | "commit": 406613 41 | }, 42 | { 43 | "prepare": 11629380, 44 | "commit": 11629380 45 | }, 46 | { 47 | "prepare": 11629537, 48 | "commit": 11629537 49 | }, 50 | { 51 | "prepare": 11629694, 52 | "commit": 11629694 53 | }, 54 | { 55 | "prepare": 11629851, 56 | "commit": 11629851 57 | }, 58 | { 59 | "prepare": 11630008, 60 | "commit": 11630008 61 | }, 62 | { 63 | "prepare": 11630165, 64 | "commit": 11630165 65 | }, 66 | { 67 | "prepare": 11630322, 68 | "commit": 11630322 69 | }, 70 | { 71 | "prepare": 11630479, 72 | "commit": 11630479 73 | }, 74 | { 75 | "prepare": 11630636, 76 | "commit": 11630636 77 | }, 78 | { 79 | "prepare": 11630793, 80 | "commit": 11630793 81 | }, 82 | { 83 | "prepare": 11785596, 84 | "commit": 11785596 85 | }, 86 | { 87 | "prepare": 11785753, 88 | "commit": 11785753 89 | }, 90 | { 91 | "prepare": 11785910, 92 | "commit": 11785910 93 | }, 94 | { 95 | "prepare": 11786067, 96 | "commit": 11786067 97 | }, 98 | { 99 | "prepare": 11786224, 100 | "commit": 11786224 101 | }, 102 | { 103 | "prepare": 11786381, 104 | "commit": 11786381 105 | }, 106 | { 107 | "prepare": 11786538, 108 | "commit": 11786538 109 | }, 110 | { 111 | "prepare": 11786695, 112 | "commit": 11786695 113 | }, 114 | { 115 | "prepare": 11786852, 116 | "commit": 11786852 117 | }, 118 | { 119 | "prepare": 11787009, 120 | "commit": 11787009 121 | } 122 | ] 123 | -------------------------------------------------------------------------------- /src/test/resources/all-versions-filtered-stream194-e0-e30.json: -------------------------------------------------------------------------------- 1 | [ 2 | 194, 3 | 394, 4 | 594, 5 | 794, 6 | 994, 7 | 1194, 8 | 1394, 9 | 1594, 10 | 1794, 11 | 1994, 12 | 140, 13 | 141, 14 | 142, 15 | 143, 16 | 144, 17 | 145, 18 | 146, 19 | 147, 20 | 148, 21 | 149, 22 | 340, 23 | 341, 24 | 342, 25 | 343, 26 | 344, 27 | 345, 28 | 346, 29 | 347, 30 | 348, 31 | 349 32 | ] 33 | -------------------------------------------------------------------------------- /src/test/resources/count-events-partitioned-projection.js: -------------------------------------------------------------------------------- 1 | fromStream('dataset20M-1800') 2 | .partitionBy(function(event){ 3 | return event.data.somedata % 2 == 1 ? "odd" : "even"; 4 | }) 5 | .when({ 6 | "$init": function() { 7 | return { 8 | count: 0 9 | } 10 | }, 11 | "$any": function(s, e) { 12 | s.count = s.count + 1; 13 | } 14 | }).outputState(); 15 | -------------------------------------------------------------------------------- /src/test/resources/count-events-projection.js: -------------------------------------------------------------------------------- 1 | fromStream('dataset20M-1800'). 2 | when({ 3 | "$init": function() { 4 | return { 5 | count: 0 6 | } 7 | }, 8 | "$any": function(s, e) { 9 | s.count = s.count + 1; 10 | } 11 | }).outputState(); 12 | -------------------------------------------------------------------------------- /src/test/resources/junit-platform.properties: -------------------------------------------------------------------------------- 1 | junit.jupiter.extensions.autodetection.enabled=true -------------------------------------------------------------------------------- /src/test/resources/simplelogger.properties: -------------------------------------------------------------------------------- 1 | org.slf4j.simpleLogger.defaultLogLevel=error 2 | org.slf4j.simpleLogger.log.io.kurrent.dbclient=debug -------------------------------------------------------------------------------- /src/test/resources/state-with-unknown-keynames.js: -------------------------------------------------------------------------------- 1 | fromStream('dataset20M-1800'). 2 | when({ 3 | "$any": function(s, e) { 4 | s[e.streamId] = { 5 | timeArrivedMillis: new Date().getTime() 6 | } 7 | } 8 | }).outputState(); 9 | -------------------------------------------------------------------------------- /vars.env: -------------------------------------------------------------------------------- 1 | EVENTSTORE_CLUSTER_SIZE=3 2 | EVENTSTORE_RUN_PROJECTIONS=All 3 | EVENTSTORE_INT_TCP_PORT=1112 4 | EVENTSTORE_HTTP_PORT=2113 5 | EVENTSTORE_TRUSTED_ROOT_CERTIFICATES_PATH=/etc/eventstore/certs/ca 6 | EVENTSTORE_DISCOVER_VIA_DNS=false 7 | EVENTSTORE_ENABLE_ATOM_PUB_OVER_HTTP=true 8 | EVENTSTORE_ADVERTISE_HOST_TO_CLIENT_AS=localhost 9 | --------------------------------------------------------------------------------