{"id":23424244,"url":"https://github.com/joshlong-attic/a-feign-like-rsocket-client","last_synced_at":"2025-04-12T16:35:10.001Z","repository":{"id":137252798,"uuid":"280113859","full_name":"joshlong-attic/a-feign-like-rsocket-client","owner":"joshlong-attic","description":"@Mario5Gray had a great idea - a Feign for RSocket. So, here it is.","archived":false,"fork":false,"pushed_at":"2020-07-21T04:04:12.000Z","size":154,"stargazers_count":18,"open_issues_count":2,"forks_count":5,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-26T10:52:24.688Z","etag":null,"topics":["reactive","rsocket","spring"],"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/joshlong-attic.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","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":"2020-07-16T09:37:33.000Z","updated_at":"2024-04-02T17:47:12.000Z","dependencies_parsed_at":null,"dependency_job_id":"b194571a-fc01-4222-8752-6164a0f5b0eb","html_url":"https://github.com/joshlong-attic/a-feign-like-rsocket-client","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joshlong-attic%2Fa-feign-like-rsocket-client","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joshlong-attic%2Fa-feign-like-rsocket-client/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joshlong-attic%2Fa-feign-like-rsocket-client/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joshlong-attic%2Fa-feign-like-rsocket-client/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/joshlong-attic","download_url":"https://codeload.github.com/joshlong-attic/a-feign-like-rsocket-client/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248597070,"owners_count":21130803,"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":["reactive","rsocket","spring"],"created_at":"2024-12-23T04:14:44.452Z","updated_at":"2025-04-12T16:35:09.985Z","avatar_url":"https://github.com/joshlong-attic.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# A Feign-like RSocket Client \n\n## Inspiration \n\nIt'd be nice to have easy Feign-like RSocket clients. This is a thing [@Mario5Gray](http://github.com/Mario5Gray) has talked about, and it seems like a great idea. So here it is. \n\n## Installation\n\nAdd the following dependency to your build: \n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.joshlong.rsocket\u003c/groupId\u003e\n    \u003cartifactId\u003eclient\u003c/artifactId\u003e\n    \u003cversion\u003e0.0.1-SNAPSHOT\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nIn your Java code you need to enable the RSocket client support. Use the `@EnableRSocketClient` annotation. You'll also need to define an `RSocketRequester` bean. \n\n```java\n@SpringBootApplication\n@EnableRSocketClient\nclass RSocketClientApplication {\n \n @Bean RSocketRequester requester (RSocketRequester.Builder builder) {\n  return builder.connectTcp(\"localhost\", 8888).block();\n }\n}\n\n``` \n\nthen, define an RSocket client interface, like this:\n\n\n```java \n\n\n@RSocketClient\npublic interface GreetingClient {\n\n\t@MessageMapping(\"supplier\")\n\tMono\u003cGreetingResponse\u003e greet();\n\n\t@MessageMapping(\"request-response\")\n\tMono\u003cGreetingResponse\u003e requestResponse(Mono\u003cString\u003e name);\n\n\t@MessageMapping(\"fire-and-forget\")\n\tMono\u003cVoid\u003e fireAndForget(Mono\u003cString\u003e name);\n\n\t@MessageMapping(\"destination.variables.and.payload.annotations.{name}.{age}\")\n\tMono\u003cString\u003e greetMonoNameDestinationVariable(\n            @DestinationVariable(\"name\") String name,\n\t    @DestinationVariable(\"age\") int age, \n            @Payload Mono\u003cString\u003e payload);\n}\n\n```\n\nIf you invoke methods on this interface it'll in turn invoke endpoints using the configured `RSocketRequester` for you, turning destination variables into route variables and turning your payload into the data for the request.\n\n\n## Mapping Headers (RSocket metadata) to the RSocket request \n\nYou can map `@Header` elements to parameters in the method invocation. The header parameters get sent as composite RSocket metadata. Normal invocations of RSocket metadata would require two parts - a mime type and a value tht can be encoded. The encoding is a separate issue - Spring ships with a ton of encoders/decoders out of the box, but by default Spring Framework's built in support uses something called `CBOR`. There is still the question of how to communicate the mimetype. We expect the mime-type to be specified as the `value()` attribute for the `@Header` annotation. Thus:\n\n```java\nimport com.joshlong.rsocket.client.RSocketClient;\nimport org.springframework.messaging.handler.annotation.Header;\nimport org.springframework.messaging.handler.annotation.MessageMapping;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport reactor.core.publisher.Mono;\n\n@RSocketClient\ninterface GreetingClient {\n\n\t@MessageMapping(\"greetings\")\n\tMono\u003cString\u003e greet(@Header( \"messaging/x.bootiful.client-id\") String clientId, @Payload Mono\u003cString\u003e name);\n\n}\n```\n\nThis needs to line up with the expectations for composite metadata on the responder side of course. \n\n\n\n\n## Pairing `RSocketRequesters` to `@RSocketClient` interfaces \n\nYou can annotate your interfaces with a `@Qualifier` annotation (or a meta-annotated qualifier of your own making ) and then annotate an `RSocketRequester` and this module will use that `RSocketRequester` when servicing methods on a particular interface. \n\nThe following demonstrates the concept in action. RSocket connections are stateful. Once they've connected, they stay connected and all subsequent interactions are assumed to be against the already established connection. Therefore, each `RSocketRequester` talks to a different logical (and physical) service, unlike, e.g., a `WebClient` which may be used to talk to any arbitrary host and port. \n\n```java\n\n@RSocketClient\n@Qualifier(Constants.QUALIFIER_2)\ninterface GreetingClient {\n\n\t@MessageMapping(\"greetings-with-name\")\n\tMono\u003cGreeting\u003e greet(Mono\u003cString\u003e name);\n\n}\n\n@RSocketClient\n@PersonQualifier\ninterface PersonClient {\n\n\t@MessageMapping(\"people\")\n\tFlux\u003cPerson\u003e people();\n\n}\n\n@EnableRSocketClients\n@SpringBootApplication\nclass RSocketClientConfiguration {\n\n\t@Bean\n\t@PersonQualifier // meta-annotation\n\t// @Qualifier(Constants.QUALIFIER_1)\n\tRSocketRequester one(@Value(\"${\" + Constants.QUALIFIER_1 + \".port}\") int port, RSocketRequester.Builder builder) {\n\t\treturn builder.connectTcp(\"localhost\", port).block();\n\t}\n\n\t\n\t@Bean \n\t@Qualifier(Constants.QUALIFIER_2) // direct-annotation\n\tRSocketRequester two(@Value(\"${\" + Constants.QUALIFIER_2 + \".port}\") int port, RSocketRequester.Builder builder) {\n\t\treturn builder.connectTcp(\"localhost\", port).block();\n\t}\n\n}\n\n@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER })\n@Retention(RetentionPolicy.RUNTIME)\n@Qualifier(Constants.QUALIFIER_1)\n@interface PersonQualifier {\n}\n\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoshlong-attic%2Fa-feign-like-rsocket-client","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjoshlong-attic%2Fa-feign-like-rsocket-client","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoshlong-attic%2Fa-feign-like-rsocket-client/lists"}