{"id":21097911,"url":"https://github.com/dehora/nakadi-java","last_synced_at":"2025-05-16T16:31:42.836Z","repository":{"id":44456040,"uuid":"73006755","full_name":"dehora/nakadi-java","owner":"dehora","description":"🌀 Client library for the Nakadi Event Broker  (examples: http://bit.ly/njc-examples, site: https://dehora.github.io/nakadi-java/)","archived":false,"fork":false,"pushed_at":"2023-10-24T11:53:08.000Z","size":1386,"stargazers_count":30,"open_issues_count":23,"forks_count":19,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-04-04T03:41:17.763Z","etag":null,"topics":["gson","java","nakadi","rxjava"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dehora.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-11-06T17:23:01.000Z","updated_at":"2024-03-15T22:24:40.000Z","dependencies_parsed_at":"2024-11-20T13:46:17.834Z","dependency_job_id":null,"html_url":"https://github.com/dehora/nakadi-java","commit_stats":null,"previous_names":[],"tags_count":70,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dehora%2Fnakadi-java","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dehora%2Fnakadi-java/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dehora%2Fnakadi-java/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dehora%2Fnakadi-java/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dehora","download_url":"https://codeload.github.com/dehora/nakadi-java/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254567400,"owners_count":22092760,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["gson","java","nakadi","rxjava"],"created_at":"2024-11-19T22:51:58.741Z","updated_at":"2025-05-16T16:31:41.840Z","avatar_url":"https://github.com/dehora.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n**Status**\n\n- Build: [![CircleCI](https://circleci.com/gh/dehora/nakadi-java.svg?style=svg)](https://circleci.com/gh/dehora/nakadi-java)\n- Source Release: [0.19.0](https://github.com/zalando-incubator/nakadi-java/releases/tag/0.19.0)\n- Contact: [maintainers](https://github.com/zalando-incubator/nakadi-java/blob/master/MAINTAINERS)\n\n\n\n# nakadi-java\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n**Table of Contents**  *[DocToc](https://github.com/thlorenz/doctoc)*\n\n- [About](#about)\n  - [Background](#background)\n- [Requirements and Getting Started](#requirements-and-getting-started)\n- [Status](#status)\n- [Usage](#usage)\n  - [Available Resources](#available-resources)\n  - [Creating a client](#creating-a-client)\n    - [Authorization](#authorization)\n    - [OAuth Scopes](#oauth-scopes)\n    - [HTTPS Security](#https-security)\n    - [Metric Collector](#metric-collector)\n    - [JSON](#json)\n    - [Using TypeLiterals](#using-typeliterals)\n    - [Resource Classes](#resource-classes)\n    - [Retries](#retries)\n  - [Event Types](#event-types)\n  - [Producing Events](#producing-events)\n    - [Publishing Compression](#publishing-compression)\n  - [Compacting Events](#compacting-events)\n  - [Subscriptions](#subscriptions)\n  - [Consuming Events](#consuming-events)\n    - [Named Event Type Streaming](#named-event-type-streaming)\n    - [Subscription Streaming](#subscription-streaming)\n    - [Streaming and Compression](#streaming-and-compression)\n    - [Backpressure and Buffering](#backpressure-and-buffering)\n  - [Healthchecks](#healthchecks)\n  - [Registry](#registry)\n  - [Metrics](#metrics)\n- [Installation](#installation)\n  - [Maven](#maven)\n  - [Gradle](#gradle)\n  - [SBT](#sbt)\n- [Idioms](#idioms)\n  - [Fluent](#fluent)\n  - [Iterable pagination](#iterable-pagination)\n  - [HTTP Requests](#http-requests)\n  - [Exceptions](#exceptions)\n- [Build and Development](#build-and-development)\n- [Internals](#internals)\n- [Contributing](#contributing)\n- [License](#license)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n----\n\n\n## About\n\nNakadi-java is a client driver for the [Nakadi Event Broker](https://github.com/zalando/nakadi). It was created for the following reasons:\n\n- Completeness. Provide a full reference implementation of the Nakadi API for producers and consumers.\n\n- Minimise dependencies. The client doesn't force a dependency on frameworks or libraries. The sole dependency is on the SLF4J API.\n\n- Robust HTTP handling. Request/response behaviour and consumer stream handling are given the same importance as functionality. \n\n- Operational visibility. Error handling, stream retries, logging and instrumentation are given the same importance as functionality. \n\n- Be easy to use. The client should be straightforward to use as is, or as an engine for higher level abstractions.\n\n### Background\n\nA number of JVM clients already exist and are in use - nakadi-java is not meant \nto compete with or replace them. In certain respects they solve different \ngoals. The existing JVM clients looked at as a whole, provide partial \nimplementations with larger dependencies, but which are idiomatic to certain \nframeworks, whereas the aim of nakadi-java is to provide a full client with a \nreduced dependency footprint to allow portability. \n\nNakadi-java is designed for application development. If you're just looking  \nfor a quick way to browse and examine streams, take a look at the excellent \n[Peek library](https://github.com/zalando-incubator/peek).\n\n## Requirements and Getting Started\n\nSee the [installation section](#installation) on how to add the client library \nto your project as a jar dependency. The client uses Java 1.8 or later. \n\n## Status\n\nThe client is pre 1.0.0, with the aim of getting to 1.0.0 quickly. \n\nThe client API is relatively stable and unlikely to see massive sweeping \nchanges, though some changes should be expected. The entire Nakadi API is \nimplemented.\n\nThe client's had some basic testing to verify it can handle things like \nconsumer stream connection/network failures and retries. It should not be \ndeemed robust yet, but it is a goal to produce a well-behaved production \nlevel client especially for producing and consuming events for 1.0.0. \nSee also:\n\n- The [open issues](https://github.com/zalando-incubator/nakadi-java/issues) section has a \nlist of bugs and things to get done. \n\n- The [help-wanted](https://github.com/zalando-incubator/nakadi-java/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement) has a list of things that would be pretty cool to have.\n\nAs a client that aims to provide a full implementation, it will post 1.0.0 \ncontinue to track the development of the Nakadi Event Broker's API.\n\n## Usage\n\nThis section summarizes what you can do with the client. The [nakadi-java-examples](https://github.com/zalando-incubator/nakadi-java-examples) project provides runnable examples for most of what you see here.\n\n### Available Resources\n\nThe API resources this client supports are:\n\n- [Event Types](#event-types)\n- [Events](#producing-events)\n- [Subscriptions](#subscriptions)\n- [Streams](#consuming-events)\n- [Registry](#registry)\n- [Healthchecks](#healthchecks)\n- [Metrics](#metrics)\n\n### Creating a client\n\nA new client can be created via a builder: \n\n```java\nNakadiClient client = NakadiClient.newBuilder()\n  .baseURI(\"http://localhost:9080\")\n  .build();\n```\n\nYou can create multiple clients if you wish. Every client must have a base URI \nset and can optionally have other values set (notably for token providers and \nmetrics collection). \n\nHere's a fuller configuration:\n\n```java\nNakadiClient client = NakadiClient.newBuilder()\n  .baseURI(\"http://localhost:9080\")\n  .metricCollector(myMetricsCollector)\n  .tokenProvider(myResourceTokenProvider)\n  .readTimeout(60, TimeUnit.SECONDS)\n  .connectTimeout(30, TimeUnit.SECONDS)\n  .build();\n```\n\n#### Authorization\n\nBy default the client does not send an authorization header with each request.\nThis is useful for working with a development Nakadi server, which will try \nand resolve bearer tokens if they are sent but will accept requests with no \nbearer token present. \n\nYou can define a token provider by implementing the `TokenProvider` \ninterface, which will supply the client with a string that will be \nsent to the server as the value of an Authorization header. The \n`TokenProvider` is  called on each request and thus can be implemented as \na dynamic provider to handle token refreshes and recycling.\n\n```java\nNakadiClient client = NakadiClient.newBuilder()\n  .baseURI(\"http://localhost:9080\")\n  .tokenProvider(new MyTokenProvider())\n  .build();\n```\n\nThere's a `ZignTokenProvider` that can connect to the zign process and run in \nthe background in the \n[nakadi-java-zign](https://github.com/zalando-incubator/nakadi-java/tree/master/nakadi-java-zign) \nsub-project.\n\n#### OAuth Scopes\n\nSome resources support use of OAuth scopes (where the API documents them, it's incomplete as of \n2016-11-15):\n\n- `StreamProcessor`: can be set via `StreamProcessor.Builder.scope()` before calling `start()`.\n- `EventTypeResource`: can be set via `EventTypeResource.scope()` before making an API call.\n- `EventResource`: can be set via `EventResource.scope` before making an API call.\n- `SubscriptionResource`: can be set via `EventResource.scope` before making an API call.\n\nOn each request the client will resolve the scope to a token by asking the `TokenProvider` to \nsupply a token via `authHeaderValue`. If a custom scope has been applied on the request it will \nbe used, otherwise the default scope documented by the API will be used. \n\nThe scope set on resource instances is stateful, not one-shot, and will be re-used across requests. \nTo change the scope, call `scope()` again will a new scope value, or if you wish to clear the \n custom scope and revert to defaults, call `scope()` with `null`. However the `StreamProcessor` \n scope is fixed once streaming begins after `start()` is called and can't be changed.\n \n \n#### HTTPS Security\n\nThe client checks certificates. If your target server is using a self-signed \ncertificate and for some reason you can't install that cert into the system \ntrust store using something like keytool, you can supply the cert via \nthe builder's `certificatePath` method: \n\n```java\nNakadiClient client = NakadiClient.newBuilder()\n  .baseURI(\"http://localhost:9080\")\n  .certificatePath(\"file:///var/certs\")\n  .build();\n```\n\nThis will cause the client to install any certificates it finds. There are three \nloading options\n\n- A path beginning with `\"file:///\"` will load from the supplied directory any \nfiles with `*.crt` and `*.pem` extensions\n\n- A path beginning with `\"classpath:\"` and ending with `*.crt` or `*.pem` will \nload that resource item from the classpath. \n \n- A path beginning with `\"classpath:\"` will load from the supplied classpath \ndirectory any files with `*.crt` and `*.pem` extensions.\n\n\nThe classpath option targeting a directory is for local development and not meant \nfor production/deployed situations. If you must use the classpath for deployed apps, \nuse the cert resource option as that will allow the classpath resolver to work more \n generally.\n\nIf no `certificatePath` is supplied, the system defaults are used. This is the \nstrongly recommended option for deployments.\n\n#### Metric Collector\n\nThe client emits well known metrics as meters and timers (see `MetricCollector` \nfor the available metrics). \n\nBy default the client ignores metrics, but you can supply your own collector. \nFor example, this sets the client to use `MetricsCollectorDropwizard`, from \nthe support library that integrates with \n[Dropwizard Metrics](http://metrics.dropwizard.io/3.1.0/):\n\n```java\nMetricRegistry metricRegistry = new MetricRegistry();\nMetricsCollectorDropwizard metrics =\n    new MetricsCollectorDropwizard(\"mynamespace\", metricRegistry);\n \nNakadiClient client = NakadiClient.newBuilder()\n  .baseURI(\"http://localhost:9080\")\n  .metricCollector(metrics)\n  .build();\n```\n\nTo provide your own collector implement the `MetricCollector` interface. Each \nemitted metric is based on an enum. Implementations can look at the enum and \nrecord as they wish. They can also work with them generally and ask any enum \nfor its path, which will be a dotted string.\n\nPlease note that calls to the collector are currently blocking. This may be \nchanged to asynchronous for 1.0.0, but in the meantime if your collector is \nmaking network calls or hitting disk, you might want to hand off them off \nas Callables or send them to a queue.\n\n#### JSON\n\nSome calls return `Response` objects that contain raw json. You can serialize \nthese using the `JsonSupport` helper, available from the client. `JsonSupport` \naccepts classes, and for generic bindings you can supply it with a `TypeLiteral`.\n\n#### Using TypeLiterals\n\nWhen using a `TypeLiteral`, please note the following:\n\n- TypeLiterals must be an actual subclass. This means declaring the TypeLiteral with a \npair of braces `new TypeLiteral\u003cMap\u003cString, Object\u003e\u003e() {};` and not just \n`new TypeLiteral\u003cMap\u003cString, Object\u003e\u003e();`. The latter won't work and can cause hard to debug \n errors.\n\n- TypeLiterals for the 3 category classes can't be declared with a String. For example \n`DataChangeEvent\u003cString\u003e` will cause marshalling errors, because the underlying JSON \nprocessing treats `String` as a JSON String type and not escaped JSON. The parser then \nfails when it sees structured JSON instead of a JSOn String. Typically you want to declare \nsomething like `DataChangeEvent\u003cMap\u003cString, Object\u003e\u003e` to destructure the data properly. The \nclient might add a stringified option for 1.0.0.\n\n#### Resource Classes\n\nOnce you have a client, you can access server resources via the `resources()` \nmethod. Here's an example that gets an events resource:\n\n```java \nEventResource resource = client.resources().events();\n```\n\nAll calls you make to the server will be done via these resource classes to \nmake network calls distinct from local requests.\n\n#### Retries\n\nA number of the non streaming resource classes support a backoff policy:\n\n- `EventTypeResource`\n- `SubscriptionResource`\n- `EventResource`\n- `RegistryResource`\n- `MetricsResource`\n- `HealthCheckResource`\n\nThey each take a `RetryPolicy` via a `retryPolicy()` method; there is an inbuilt `ExponentialRetry` \nthat can be used to define a maximum number of requests or maximum total time elapsed. Note that \nthe retry policy object is stateful and must be reset between results. You can disable the retries\n (the default behavior) by setting `retryPolicy` to null, or to start a new retry supplying a fresh \n `RetryPolicy` instance.  \n\n**Please be careful with EventTypeResource**: the ordering and general delivery behaviour for event \ndelivery is **undefined** under retries. That is, a delivery retry may result in out of order \nbatches being sent to the server. Also retrying a partially delivered (207) batch may result \nin one or more events being delivered multiple times. \n\n### Event Types\n\nYou can create, edit and delete event types as well as list them:\n\n```java\n// grab an event type resource\nEventTypeResource eventTypes = client.resources().eventTypes();\n \n// create a new event type, using an escaped string for the schema\nEventType requisitions = new EventType()\n  .category(EventType.Category.data)\n  .name(\"priority-requisitions\")\n  .owningApplication(\"weyland\")\n  .partitionStrategy(EventType.PARTITION_HASH)\n  .enrichmentStrategy(EventType.ENRICHMENT_METADATA)\n  .partitionKeyFields(\"id\")\n  .cleanupPolicy(\"delete\")\n  .schema(new EventTypeSchema().schema(\n      \"{ \\\"properties\\\": { \\\"id\\\": { \\\"type\\\": \\\"string\\\" } } }\"));\nResponse response = eventTypes.create(requisitions);\n \n// read the partitions for an event type\nPartitionCollection partitions = eventTypes.partitions(\"priority-requisitions\");\npartitions.iterable().forEach(System.out::println);\n \n// read a particular partition\nPartition partition = eventTypes.partition(\"priority-requisitions\", \"0\");\nSystem.out.println(partition);\n \n// list event types\nEventTypeCollection list = client.resources().eventTypes().list();\nlist.iterable().forEach(System.out::println);\n \n// find by name \nEventType byName = eventTypes.findByName(\"priority-requisitions\");\n \n// update \nResponse update = eventTypes.update(byName);\n \n// remove \nResponse delete = eventTypes.delete(\"priority-requisitions\");\n```\n\n### Producing Events\n\nYou can send one or more events to the server:\n\n```java\nEventResource resource = client.resources().events();\n \n// nb: EventMetadata.newPreparedEventMetadata sets defaults for eid, occurred at and flow id fields\nEventMetadata em = EventMetadata.newPreparedEventMetadata();\n\n// you can send flowids as strings and tracing spans as Map\u003cString, String\u003e \nEventMetadata em1 = new EventMetadata()\n  .eid(UUID.randomUUID().toString())\n  .occurredAt(OffsetDateTime.now())\n  .spanCtx(tracingSpan)\n  .flowId(\"decafbad\");\n  \n// create our domain event inside a typesafe DataChangeEvent  \nPriorityRequisition pr = new PriorityRequisition(\"22\");\nDataChangeEvent\u003cPriorityRequisition\u003e dce = new DataChangeEvent\u003cPriorityRequisition\u003e()\n  .metadata(em)\n  .op(DataChangeEvent.Op.C)\n  .dataType(\"priority-requisitions\")\n  .data(pr);\n \nResponse response = resource.send(\"priority-requisitions\", dce);\n \n// send a batch of two events\n \nDataChangeEvent\u003cPriorityRequisition\u003e dce1 = new DataChangeEvent\u003cPriorityRequisition\u003e()\n  .metadata(EventMetadata.newPreparedEventMetadata())\n  .op(DataChangeEvent.Op.C)\n  .dataType(\"priority-requisitions\")\n  .data(new PriorityRequisition(\"23\"));\n \nDataChangeEvent\u003cPriorityRequisition\u003e dce2 = new DataChangeEvent\u003cPriorityRequisition\u003e()\n  .metadata(EventMetadata.newPreparedEventMetadata())\n  .op(DataChangeEvent.Op.C)\n  .dataType(\"priority-requisitions\")\n  .data(new PriorityRequisition(\"24\"));\n\nArrayList list = new ArrayList();\nlist.add(dce1);\nlist.add(dce2);\n \nResponse batch = resource.send(\"priority-requisitions\", list);\n``` \n\n#### Publishing Compression\n\nEvent posting can be compressed by configuring the client \nwith `.enablePublishingCompression()`:\n\n```java\nNakadiClient client = NakadiClient.newBuilder()\n  .baseURI(\"http://localhost:9080\")\n  .enablePublishingCompression()  \n  .build();\n```\n\n### Compacting Events\n\nEvents can be sent with compaction information by setting their metadata. \nThis is required when the `cleanup_policy` of event type is set to `compact`. \n\n```java\n// create metadata with compaction information for an event\n\nEventMetadata compacted = EventMetadata.newPreparedEventMetadata()\n  .partitionCompactionKey(\"329ed3d2-8366-11e8-adc0-fa7ae01bbebc\");\n\nPriorityRequisition pr = new PriorityRequisition(\"23\");\nDataChangeEvent\u003cPriorityRequisition\u003e dce = new DataChangeEvent\u003cPriorityRequisition\u003e()\n  .metadata(compacted)\n  .op(DataChangeEvent.Op.C)\n  .dataType(\"priority-requisitions\")\n  .data(pr);\n \nResponse response = resource.send(\"priority-requisitions\", dce);\n```\n\n### Subscriptions\n\nYou can create, edit and delete subscriptions as well as list them:\n\n```java\n// grab a subscription resource\nSubscriptionResource resource = client.resources().subscriptions();\n \n// create a new subscription\nSubscription subscription = new Subscription()\n    .consumerGroup(\"mccaffrey-cg\")\n    .eventType(\"priority-requisitions\")\n    .owningApplication(\"shaper\");\n \nResponse response = resource.create(subscription);\n\n// create a subscription from a given offset\nCursor c0 = new Cursor(\"0\", \"000000000000002009\", \"priority-requisitions\");\nCursor c1 = new Cursor(\"1\", \"000000000000002008\", \"priority-requisitions\");\n\nSubscription offsetSubscription = new Subscription()\n    .consumerGroup(\"roja-cg\")\n    .eventType(\"priority-requisitions\")\n    .owningApplication(\"anarch\")\n    .readFrom(\"cursors\")\n    .initialCursors(Lists.newArrayList(c0, c1));    \n\n// find a subscription\nSubscription found = resource.find(\"a2ab0b7c-ee58-48e5-b96a-d13bce73d857\");\n \n// get the cursors and iterate them\nSubscriptionCursorCollection cursors = resource.cursors(found.id());\ncursors.iterable().forEach(System.out::println);\n \n// get the stats and iterate them\nSubscriptionEventTypeStatsCollection stats = resource.stats(found.id());\nstats.iterable().forEach(System.out::println);\n \n// list subscriptions\nSubscriptionCollection list = resource.list();\nlist.iterable().forEach(System.out::println);\n \n// list for an owner\nlist = resource.list(new QueryParams().param(\"owning_application\", \"shaper\"));\nlist.iterable().forEach(System.out::println);\n \n// delete a subscription\nResponse delete = resource.delete(found.id());\n```\n\n### Consuming Events\n\nYou can consume events via stream. Both the named event type and newer \nsubscription stream APIs are available via the `StreamProcessor` class.\n\nA `StreamProcessor` accepts a `StreamObserverProvider` which is a factory for \ncreating the `StreamObserver` class the events will be sent to. The \n`StreamObserver` accepts one or more `StreamBatchRecord` objects  where each \nitem in the batch has been marshalled to an instance of `T` as defined by \nit and the `StreamObserverProvider`.  \n\nA `StreamObserver` implements a number of callback methods that are invoked \nby the underlying stream processor:\n\n- `onStart()`:  Called before stream connection begins and before a retry is attempted.\n\n- `onStop()`: Called after the stream is completed and when a retry is needed.\n\n- `onCompleted()`: Called when the client is finished sending batches.\n\n- `onError(Throwable t)`: Called when there's been an error.\n\n- `onNext(StreamBatchRecord\u003cT\u003e record)`: Called for each batch of events. Also contains the current offset observer and the batch cursor.\n\n- `requestBackPressure()`: request a maximum number of emitted items from the stream. \n\n- `requestBuffer()`: Ask to have batches buffered before emitting them from the stream.\n\nThe interface is influenced by [RxJava](https://github.com/ReactiveX/RxJava) \nand the general style of `onX`  callback APIs. You can see an example in the \nsource called `LoggingStreamObserverProvider` which maps the events in a \nbatch to plain strings.\n\nThe API also supports a `StreamOffsetObserver` - the offset observer is given \nto the `StreamObserver` object with each `onNext` call. Typically the offset \nobserver is used to provide checkpointing of a consumer's partition in the \nstream. \n\n#### Named Event Type Streaming\n\nTo consume a named event type stream, configure a `StreamProcessor` and run it:\n\n```java\n\n// configure a stream for an event type from a given cursor; \n// all api settings are available\nStreamConfiguration sc = new StreamConfiguration()\n    .eventTypeName(\"priority-requisitions\")\n    .cursors(new Cursor(\"0\", \"450\"));\n\n// set up a processor with an event observer provider\nStreamProcessor processor = client.resources().streamBuilder()\n    .streamConfiguration(sc)\n    .streamObserverFactory(new LoggingStreamObserverProvider())\n    .build();\n\n// consume in the background until the app exits or stop() is called\nprocessor.start(); \n\n// configure a stream with a bounded number of events retries, keepalives, plus custom timeouts\nStreamConfiguration sc1 = new StreamConfiguration()\n    .eventTypeName(\"priority-requisitions\")\n    .cursors(new Cursor(\"0\", \"450\"))\n    .batchLimit(15)\n    .batchFlushTimeout(2, TimeUnit.SECONDS)\n    .maxRetryAttempts(256)\n    .maxRetryDelay(30, TimeUnit.SECONDS)\n    .streamLimit(1024)\n    .connectTimeout(8, TimeUnit.SECONDS)\n    .readTimeout(3, TimeUnit.MINUTES)\n    .streamKeepAliveLimit(2048)\n    .streamTimeout(1, TimeUnit.DAYS);\n \n// create a processor with an observer and an offset observer  \nStreamProcessor boundedProcessor = client.resources().streamBuilder()\n    .streamConfiguration(sc1)\n    .streamObserverFactory(new LoggingStreamObserverProvider())\n    .streamOffsetObserver(new LoggingStreamOffsetObserver())\n    .build();\n \n/*\n start in the background, stopping when the criteria are reached,\n the app exits, or stop() is called\n*/\nboundedProcessor.start(); \n```\n\nIf no offset observer is given, the default observer used is \n`LoggingStreamOffsetObserver` which simply logs when it is invoked.\n\n\n#### Subscription Streaming\n\nSubscription stream consumers allow consumers to store offsets with the server \nand work much like named event type streams:\n\n```java\n// configure a stream from a subscription id; \n// all api settings are available\nStreamConfiguration sc = new StreamConfiguration()\n    .subscriptionId(\"27302800-bc68-4026-a9ff-8d89372f8473\")\n    .maxUncommittedEvents(20L);\n\n// create a processor with an observer\nStreamProcessor processor = client.resources().streamBuilder(sc)\n    .streamObserverFactory(new LoggingStreamObserverProvider())\n    .build();\n\n// consume in the background until the app exits or stop() is called\nprocessor.start();\n```\n\nThere are some notable differences: \n\n- The `StreamConfiguration` is configured with a `subscriptionId`  instead of an `eventTypeName`.\n\n- The inbuilt offset observer for a subscription stream will call Nakadi's checkpointing API to update the offset. You can replace this with your own implementation if you wish.\n\n- A subscription stream also allows setting the `maxUncommittedEvents` as defined by the Nakadi API.\n\n#### Streaming and Compression\n\nThe default behaviour for all streaming consumers is to request a gzipped stream. This can \nbe changed to a plain stream by setting the `Accept-Encoding` header to `identity` on \n `StreamConfiguration` as follows:\n\n```java\nStreamConfiguration sc = new StreamConfiguration()\n    //  ask the server for unencoded data\n    .requestHeader(\"Accept-Encoding\", \"identity\")\n    ...;\n```\n\n#### Backpressure and Buffering\n\nA `StreamObserver` can signal for backpressure via the `requestBackPressure` \nmethod. This is applied with each `onNext` call to the `StreamObserver` and \nso can be used to adjust backpressure dynamically. The client's underlying \nstream processor will make a  best effort attempt to honor backpressure.\n\nIf the user wants events buffered into contiguous batches it can set a buffer \nsize using `requestBuffer`. This is independent of the underlying HTTP \nstream - the stream will be consumed off the wire based on the API request \nsettings - the batches are buffered in memory by the underlying processor. \nThis is applied during setup and is fixed for the processor's lifecycle.\n\nUsers that don't care about backpresure controls can subclass the\n `StreamObserverBackPressure` class.\n\n\n### Healthchecks\n\nYou can make healthcheck requests to the server:\n\n```java\nHealthCheckResource health = client.resources().health();\n \n// check returning a response object, regardless of status\nResponse healthcheck = health().healthcheck();\n \n// ask to throw if the check failed (non 2xx code)\nResponse throwable = health.healthcheckThrowing();\n\n// check with an expoential backoff retry\n\nRetryPolicy retry = ExponentialRetry.newBuilder()\n        .initialInterval(1000, TimeUnit.MILLISECONDS)\n        .maxAttempts(5)\n        .maxInterval(3000, TimeUnit.MILLISECONDS)\n        .build();\nhealth.retryPolicy(retry).healthcheckThrowing();\n```\n\n### Registry\n\nYou can view the service registry:\n\n```java\nRegistryResource resource = client.resources().registry();\n \n// get and iterate available enrichments\nEnrichmentStrategyCollection enrichments = resource.listEnrichmentStrategies();\nenrichments.iterable().forEach(System.out::println);\n \n// get and iterate available validations\nValidationStrategyCollection validations = resource.listValidationStrategies();\nvalidations.iterable().forEach(System.out::println);        \n```\n\n### Metrics\n\nYou can view service metrics:\n\n```java\nMetricsResource metricsResource = client.resources().metrics();\n \n// print service metrics\nMetricsResource metricsResource = client.resources().metrics();\nMetrics metrics = metricsResource.get();\nMap\u003cString, Object\u003e items = metrics.items();\nSystem.out.println(items);\n```\n\nNote that the structure of metrics is not defined by the server, hence it's \nreturned as as map within the `Metrics` object.\n\n## Installation\n\n### Maven\n\nAdd sonatype to the repositories element in `pom.xml` or `settings.xml` to access snapshots:\n\n```xml\n\u003crepositories\u003e\n  \u003crepository\u003e\n    \u003cid\u003esonatype-nexus-snapshots\u003c/id\u003e\n    \u003cname\u003esonatype-nexus-snapshots\u003c/name\u003e\n    \u003curl\u003ehttps://oss.sonatype.org/content/repositories/snapshots\u003c/url\u003e\n    \u003csnapshots\u003e\n      \u003cenabled\u003etrue\u003c/enabled\u003e\n    \u003c/snapshots\u003e\n  \u003c/repository\u003e\n\u003c/repositories\u003e\n```  \n\nand add the project declaration to `pom.xml`:\n\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003enet.dehora.nakadi\u003c/groupId\u003e\n  \u003cartifactId\u003enakadi-java-client\u003c/artifactId\u003e\n  \u003cversion\u003e0.19.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n### Gradle\n\nAdd sonatype to the `repositories` block for snapshots:\n\n```groovy\nrepositories {\n  maven {\n    url = 'https://oss.sonatype.org/content/repositories/snapshots/'\n  }\n}\n```\n\n```kotlin\nrepositories {\n  maven {\n    url = uri(\"https://oss.sonatype.org/content/repositories/snapshots\")\n  }\n}\n```\n\nand add the project to the `dependencies` block in `build.gradle`:\n\n```groovy\ndependencies {\n  implementation 'net.dehora.nakadi:nakadi-java-client:0.19.0'\n}  \n```\n\n```kotlin\ndependencies {\n  implementation(\"net.dehora.nakadi:nakadi-java-client:0.19.0\")\n}  \n```\n\n### SBT\n\nAdd sontaype to `resolvers` in `build.sbt` to access snapshots:\n\n```scala\nresolvers += Opts.resolver.sonatypeSnapshots\n```\n\nand add the project to `libraryDependencies` in `build.sbt`:\n\n```scala\nlibraryDependencies += \"net.dehora.nakadi\" % \"nakadi-java-client\" % \"0.19.0\"\n```\n\n\n## Idioms\n\n### Fluent\n\nThe client prefers a fluent style, setters return `this` to allow chaining. \nComplex constructors use a builder pattern where needed. The JavaBeans \nget/set prefixing idiom is not used by the API, as is increasingly typical \nwith modern Java code.\n\n### Iterable pagination\n\nAny API call that returns a collection, including ones that could be paginated \nexpose Iterable contracts, allowing `forEach` or `iterator` access:\n\n```java \nEventTypeCollection list = client.resources().eventTypes().list();\nlist.iterable().forEach(System.out::println);\n \nIterator\u003cEventType\u003e iterator = list.iterable().iterator();\nwhile (iterator.hasNext()) {\n  EventType next = iterator.next();\n  System.out.println(next);\n}\n```\n\nPagination if it happens, is done automatically by the collection's backing \niterable by following the `next` relation sent back by the server. \n\nYou can if wish work with pages and hypertext links directly via the methods \non `ResourceCollection` which each collection implements.\n\n### HTTP Requests\n\nCalls that result in HTTP requests are performed using resource classes. The \nresults can be accessed as HTTP level responses or mapped to API objects.\n\nYou don't have to deal with HTTP responses from the API directly. If there \nis a failure then a `NakadiException` or a subclass will be thrown. The \nexception will have `Problem` information that can be examined. \n\n### Exceptions\n\nClient exceptions are runtime exceptions by default. They extend from \n`NakadiException` which allows you to catch all errors under one type. The \n`NakadiException` embeds a `Problem` object which can be examined. Nakadi's \nAPI uses Problem JSON ([RFC7807](https://tools.ietf.org/html/rfc7807)) to \ndescribe errors. Local errors also contain Problem descriptions. \n\nThe client will also throw an `IllegalArgumentException` in a number of places \nwhere null fields are not accepted or sensible as values, such as required \nparameters for builder classes. However the client performs no real data \nvalidation for API requests, leaving that to the server. Invalid server \nrequests resulting in 422s will cause an `InvalidException` to be thrown \ninstead.\n\nIn a handful of circumstances the API exposes a checked exception where \nit's neccessary the user handles the error; for example some exceptions \nfrom `StreamOffsetObserver` are checked.\n\n## Build and Development\n\nThe project is built with [Gradle](http://gradle.org/) and uses the \n[Netflix Nebula](https://nebula-plugins.github.io/) plugins. The `./gradlew` \nwrapper script will bootstrap the right Gradle version if it's not already \ninstalled. \n\nThe main client jar file is build using the shadow plugin.\n\nThe main tasks are:\n\n- `./gradlew build` : run a build and test\n- `./gradlew clean` : clean down the build \n- `./gradlew clean shadow` : builds the client jar\n\n## Internals\n\nThe wiki page [Internals](https://github.com/dehora/nakadi-java/wiki/Internals)\nhas details on how the client works under the hood.\n\n## Contributing\n\nPlease see the [issue tracker](https://github.com/zalando-incubator/nakadi-java/issues) \nfor things to work on. The [help-wanted](https://github.com/zalando-incubator/nakadi-java/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement) has a list of things that would be \npretty cool to have.\n\nBefore making a contribution, please let us know by posting a comment to the \nrelevant issue. If you would like to propose a new feature, create a new issue \nfirst explaining the feature you’d like to contribute or bug you want to fix.\n\nThe codebase follows [Square's code style](https://github.com/square/java-code-styles) \nfor Java and Android projects.\n\n----\n\n## License\n\nMIT License\n\nCopyright (c) 2016 Zalando SE, https://tech.zalando.com\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdehora%2Fnakadi-java","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdehora%2Fnakadi-java","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdehora%2Fnakadi-java/lists"}