{"id":37027251,"url":"https://github.com/reactiverse/reactive-contexts","last_synced_at":"2026-01-14T03:13:45.399Z","repository":{"id":57736115,"uuid":"127290258","full_name":"reactiverse/reactive-contexts","owner":"reactiverse","description":"Reactive context propagation library migrated to https://github.com/smallrye/smallrye-context-propagation/","archived":true,"fork":false,"pushed_at":"2019-09-12T12:46:34.000Z","size":102,"stargazers_count":16,"open_issues_count":0,"forks_count":5,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-07-04T14:52:41.292Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/reactiverse.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-03-29T12:46:53.000Z","updated_at":"2024-09-11T06:38:34.000Z","dependencies_parsed_at":"2022-08-23T22:40:27.480Z","dependency_job_id":null,"html_url":"https://github.com/reactiverse/reactive-contexts","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/reactiverse/reactive-contexts","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reactiverse%2Freactive-contexts","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reactiverse%2Freactive-contexts/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reactiverse%2Freactive-contexts/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reactiverse%2Freactive-contexts/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/reactiverse","download_url":"https://codeload.github.com/reactiverse/reactive-contexts/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reactiverse%2Freactive-contexts/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28408816,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T01:52:23.358Z","status":"online","status_checked_at":"2026-01-14T02:00:06.678Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":[],"created_at":"2026-01-14T03:13:43.600Z","updated_at":"2026-01-14T03:13:45.390Z","avatar_url":"https://github.com/reactiverse.png","language":"Java","readme":"# Reactive Contexts\n\n[![Build Status](https://travis-ci.com/reactiverse/reactive-contexts.svg?branch=master)](https://travis-ci.com/reactiverse/reactive-contexts)\n\nReactive Contexts is a library that allows you to capture contexts from various providers ([RESTEasy](https://resteasy.github.io), \n[Redpipe](http://redpipe.net), [Weld](http://weld.cdi-spec.org))\nand propagate them along the reactive flow of various propagators ([RxJava1, RxJava2](https://github.com/ReactiveX/RxJava)).\n\n# License\n\nReactive Contexts is licensed under the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0) terms.\n\n# The problem with contexts and reactive libraries\n\nMany libraries (for example: RESTEasy, [CDI](http://cdi-spec.org)) use a thread-local context that holds information required by user-executing\ncode (in the case of RESTEasy: the current request, response and many other useful information so that the resource \nmethod can look them up when executed).\n\nThis works well in settings where you have one thread per operation, but stops working if the operation spawns other \nthreads, or gets executed in other threads, or simply later, as is the case in many Reactive libraries, such as RxJava,\nwhere users code is spread between the subscriber, operations such as filter/map, and producers, all of which can be\nexecuted in different thread schedulers.\n\nFor example, the following RESTEasy/RxJava code works out of the box with the latest version of RESTEasy:\n\n    @GET\n    @Path(\"reactive-nodelay\")\n    public Single\u003cString\u003e reactiveNoDelay(@Context UriInfo uriInfo){\n        return Single.just(\"hello\")\n                .map(str -\u003e str + \" from: \"+uriInfo.getAbsolutePath());\n    }\n\nAnd will display something like `hello from: http://localhost:8081/reactive-nodelay`.\n\nBut if you introduce a delay, which is only one of the many ways to introduce a thread-switch from RxJava:\n\n    @GET\n    @Path(\"reactive-delay\")\n    public Single\u003cString\u003e reactiveDelay(@Context UriInfo uriInfo){\n        return Single.just(\"hello\")\n                .delay(1, TimeUnit.SECONDS)\n                .map(str -\u003e str + \" from: \"+uriInfo.getAbsolutePath());\n    }\n\nThen it breaks down with `RESTEASY003880: Unable to find contextual data of type: javax.ws.rs.core.UriInfo`.\n\nThis is due to the fact that RESTEasy doesn't know that RxJava is going to schedule the delayed `map` operation\ninto a different scheduler, on a thread which doesn't have the RESTEasy context set-up in a thread-local.\n\nThis is not RESTEasy's fault: the exact same error will happen if you try to use CDI at that point, for the exact\nsame reason. Many existing libraries rely on thread-locals for context propagation, and it does not work in the\nasync/Reactive world.\n\nRxJava supports a system of hooks/plugins that we can use to capture and restore context, but there can only be\none such hook/plugin, so if RESTEasy uses it, CDI cannot use it. Also, there would be a lot of code duplication\nas propagating contexts with RxJava hooks/plugins is not trivial.\n\n# The solution\n\nIn order to enable automatic context propagation of any number of contexts, Reactive Context uses a system of plugins\nfor saving/restoring contexts: `ContextProvider`, and plugins that hook into Reactive libraries/schedulers in\norder to use the `Context` API for saving/restoring all contexts: `ContextPropagator`.\n\nIf your context-using framework is supported, and your Reactive library/scheduler is supported too, then all your\ncontexts will be automatically propagated and your code will look great. If not, add support for your \n`ContextProvider` or `ContextPropagator`. If it's not possible, you can still use manual context\npropagation by accessing directly the `Context` API.\n\nTo get back to our original problematic code:\n\n    @GET\n    @Path(\"reactive-delay\")\n    public Single\u003cString\u003e reactiveDelay(@Context UriInfo uriInfo){\n        return Single.just(\"hello\")\n                .delay(1, TimeUnit.SECONDS)\n                .map(str -\u003e str + \" from: \"+uriInfo.getAbsolutePath());\n    }\n\nWill work fine if you use the RESTEasy `ContextProvider` with the RxJava2 `ContextPropagator`, without any change\nin your code. Automatic context propagation FTW!\n\n# Usage\n\nImport the following Maven module:\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eio.reactiverse\u003c/groupId\u003e\n    \u003cartifactId\u003ereactive-contexts-core\u003c/artifactId\u003e\n    \u003cversion\u003e1.0.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nThen call `io.reactiverse.reactivecontexts.core.Context.load();` and your contexts will be propagated, depending on the\npresence of the following optional plugins in your classpath:\n\nartifactId | Description\n--- | ---\n`reactive-contexts-core` | Core engine\n`reactive-contexts-propagators-rxjava1` | Propagates contexts for RxJava1\n`reactive-contexts-propagators-rxjava2` | Propagates contexts for RxJava2\n\nIf you are using RxJava 1 or 2, you don't need anything to propagate your contexts: every RxJava type (`Single`,\n`Flowable`…) will have the right contexts automatically propagated. If you are using reactive types that don't\nhave a `reactive-contexts-propagator` plugin, such as `CompletionStage` in the JDK, see [below for how to manually\npropagate contexts](#manual-context-propagation) \n\n# Building\n\nClone this repository, and run:\n\n```shell\n$ mvn clean install\n```\n\n# For context providers\n\nIf you have a context that your library provides and requires, which is often stored in thread-local\nvariables, it's very likely it won't work with reactive applications that register callbacks and\ninvoke them later in various threads.\n\nIn order for your library to have its context propagated to all supported reactive libraries, you\ncan implement the `io.reactiverse.reactivecontexts.core.ContextProvider` interface and specify how\nyou can save and restore your context:\n\n```java\npackage my.library;\n\npublic class MyContextProvider implements ContextProvider\u003cMyState\u003e {\n\n    @Override\n    public MyState install(MyState state) {\n        MyState previousState = MyContext.getState();\n        MyContext.setState(state);\n        return previousState;\n    }\n\n    @Override\n    public void restore(MyState previousState) {\n        MyContext.setState(previousState);\n    }\n\n    @Override\n    public MyState capture() {\n        return MyContext.getState();\n    }\n}\n```\n\nThen you declare a `META-INF/services/io.reactiverse.reactivecontexts.core.ContextProvider` file which\nlists your fully-qualified class name implementing the `ContextProvider` interface (in this case\n`my.library.MyContextProvider`) and include it in your classpath.\n\nUpon initialisation, your context provider implementation will automatically be loaded and your\ncontext will be propagated to all supported reactive libraries. \n\n# For context propagators\n\nIf you have a reactive library that supports scheduling of callbacks on various threads, you will need\nto register a `ContextPropagator` implementation that will be called by the `reactive-contexts` library,\nwhere you will register any required plumbing on the reactive library, to make sure it will properly\npropagate all contexts during scheduling.\n\nFor example, here is how the RxJava1 propagator is implemented:\n\n```java\npublic class RxJava1ContextPropagator implements ContextPropagator {\n\n    public void setup() {\n        RxJavaHooks.setOnSingleCreate(new ContextPropagatorOnSingleCreateAction());\n        // ...\n    }\n}\n```\n\nDon't forget to list your context propagator's fully-qualified class names in the\n`META-INF/services/io.reactiverse.reactivecontexts.core.ContextPropagator` file, and to include it in\nyour classpath.\n\nYour plugin can capture all current contexts with `Context.capture()`, then install captured contexts with\n`Context.install(contexts)` and restore them with `Context.restore(contexts)`.\n\nFor example, here is how contexts are propagated for RxJava1 `Single`:\n\n```java\npublic class ContextPropagatorOnSingleCreateAction implements Func1\u003cOnSubscribe, OnSubscribe\u003e {\n\n    @Override\n    public OnSubscribe call(OnSubscribe t) {\n        return new ContextCapturerSingle(t);\n    }\n    \n    final static class ContextCapturerSingle\u003cT\u003e implements Single.OnSubscribe\u003cT\u003e {\n\n        final Single.OnSubscribe\u003cT\u003e source;\n\n        private ContextState states;\n\n        public ContextCapturerSingle(Single.OnSubscribe\u003cT\u003e source) {\n            this.source = source;\n            // capture the context\n            states = Context.capture();\n        }\n\n        @Override\n        public void call(SingleSubscriber\u003c? super T\u003e t) {\n            // restore the context for subscription\n            ContextState previousStates = states.install();\n            try {\n                source.call(new OnAssemblySingleSubscriber\u003cT\u003e(t, states));\n            }finally {\n                previousStates.restore();\n            }\n        }\n\n        static final class OnAssemblySingleSubscriber\u003cT\u003e extends SingleSubscriber\u003cT\u003e {\n\n            final SingleSubscriber\u003c? super T\u003e actual;\n            private final ContextState states;\n\n\n            public OnAssemblySingleSubscriber(SingleSubscriber\u003c? super T\u003e actual, ContextState states) {\n                this.actual = actual;\n                this.states = states;\n                actual.add(this);\n            }\n\n            @Override\n            public void onError(Throwable e) {\n                // propagate the context for listeners\n                ContextState previousStates = states.install();\n                try {\n                    actual.onError(e);\n                }finally {\n                    previousStates.restore();\n                }\n            }\n\n            @Override\n            public void onSuccess(T t) {\n                // propagate the context for listeners\n                ContextState previousStates = states.install();\n                try {\n                    actual.onSuccess(t);\n                }finally {\n                    previousStates.restore();\n                }\n            }\n        }\n    }\n\n}\n```\n\n## Manual context propagation\n\nIf you have a library that uses reactive types that don't support hooks, such as the JDK's `CompletionStage`,\nyou will have to manually capture and restore contexts, for example:\n\n```java\nCompletionStage\u003cResponse\u003e userResponse = invokeUserAction();\nContextState states = Context.capture();\nuserResponse.thenAccept(response -\u003e {\n    ContextState previousStates = states.install();\n    try {\n        writeResponse(response);\n    }finally {\n        previousStates.restore();\n    }\n});\n```\n\nAlternately, you can use `Context.wrap` to propagate reactive contexts to many functional interfaces, for example:\n\n```java\nCompletionStage\u003cResponse\u003e userResponse = Context.wrap(invokeUserAction());\nuserResponse.thenAccept(response -\u003e writeResponse(response));\n```\n\n# Threads, class loaders\n\nIf you are using a flat classpath, this is all you need to know. If you're using a modular class loader,\nor need to have several independent `Context` objects, each with their own list of providers and \npropagators, then you need to stop using the global `Context` instance and create your own.\n\nYou can create your own Context with `new Context()`, then set it as a thread-local with\n`Context.setThreadInstance(Context)`, and when you're done you can clear the thread-local with\n`Context.clearThreadInstance()`.\n\nNote that each captured context state will restore the proper Context thread-local when\ncalling `ContextState.install()` and `ContextState.restore()`, so as to avoid\ninterference.\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freactiverse%2Freactive-contexts","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Freactiverse%2Freactive-contexts","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freactiverse%2Freactive-contexts/lists"}