{"id":18113874,"url":"https://github.com/jezza/restbolt","last_synced_at":"2026-06-19T01:31:13.720Z","repository":{"id":69523352,"uuid":"159417445","full_name":"Jezza/RestBolt","owner":"Jezza","description":"High Performance Java 11 Rest Client in the spirit of Retrofit.","archived":false,"fork":false,"pushed_at":"2019-01-07T17:45:11.000Z","size":46,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-06T09:14:30.477Z","etag":null,"topics":["http-client","java-11","rest-client"],"latest_commit_sha":null,"homepage":null,"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/Jezza.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":"2018-11-28T00:01:40.000Z","updated_at":"2019-01-07T17:45:13.000Z","dependencies_parsed_at":"2023-02-26T21:31:34.511Z","dependency_job_id":null,"html_url":"https://github.com/Jezza/RestBolt","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Jezza/RestBolt","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jezza%2FRestBolt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jezza%2FRestBolt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jezza%2FRestBolt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jezza%2FRestBolt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Jezza","download_url":"https://codeload.github.com/Jezza/RestBolt/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jezza%2FRestBolt/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34514282,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-18T02:00:06.871Z","response_time":128,"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":["http-client","java-11","rest-client"],"created_at":"2024-11-01T02:10:10.154Z","updated_at":"2026-06-19T01:31:13.686Z","avatar_url":"https://github.com/Jezza.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"So, the basic idea behind this isn't to revolutionise REST clients, or arguably even improve them.  \nThe main goal was to... eh.... I've forgotten at this point, but it was something like:  \n\"I want to use the new Java 11 client stuff, but I like not having to write code.\"  \nI have a pretty good grasp of java bytecode and the ASM library that makes it a breeze to write.  \nI also knew of of Retrofit and knew that it mapped a collection of endpoints against an interface (Note: I haven't actually used it...).  \nI also knew that with Java 9, we got a cool little improvement to Lookups that slipped through without too many people noticing it.  \n`defineClass` can be used to, well, define a class at runtime, instead of having to jump through hoops with classloaders and the like, you just use this method.  \nSo, with all of that in mind and smooshing them together in a way not many people would approve,  \nI thought why bother writing code, when I can just vaguely describe what I endpoints I want to use and generate code based off of that.  \n\nAnd?  \nIt works.  \nIt's easy to use, and surprisingly fast.  \nA preliminary test showed that it outperformed okHttp3.  \nI'll do a proper benchmark when this is a bit more evolved, and, I guess, if there's enough interest.  \nFrom my shitty testing, I was able to send 10,000+ requests within a second.  \nRapidoid started rejecting the requests up around 30,000, so that might have something to do with it.  \n\nBasically, you start with something like this:  \n\n```java\ninterface Service {\n\t// A method declared with a void or wildcard (eg, HttpResponse\u003c?\u003e) discards the response's body.\n\t@GET(\"/ping\")\n\tvoid ping0() throws SyncException;\n\n\t// SyncException replaces IOException, because the HttpClient can technically interrupt as well, so this exception covers both.\n\t@GET(\"/ping\")\n\tHttpResponse\u003c?\u003e ping1() throws SyncException;\n\n\t// All of the methods above are synchronous, to mark a method as async, you need to return a CompletableFuture.\n\t@GET(\"/ping\")\n\tCompletableFuture\u003cHttpResponse\u003c?\u003e\u003e ping2();\n\n\t// A bit more extreme of an example, but this basically does all of the stuff you'd expect.\n\t@POST(\"/users/{id}/create\")\n\tCompletableFuture\u003cHttpResponse\u003c?\u003e\u003e list(@Path(\"id\") String path, @Query(\"admin\") boolean admin, @Body(\"first_name\") String firstName, @Body(\"last_name\") String lastName);\n}\n```\n\nSide-note: All bodies are currently published with a URL encoded scheme by default.  \nIf you wish to see more implementations you can either wait until they get implemented by someone, or do them yourself.  \n(eg, I want to implement multipart, so that'll probably be implemented at some point)  \nIf you want to implement it yourself, go take a look at the bottom of this document.  \nIt describes how you can implement a publisher.  \n\nAnother side-note:  \nCurrently, the only handled return type is String.  \nSupport still needs to come, so hold your horses...  \nI'll get around to it at some point...  \n\n```java\nclass Main {\n\t// These implementations are thread safe. \n\tprivate static final Service SERVICE = RestBolt.bind(\"http://localhost:8080\", Service.class, MethodHandles.lookup());\n\n\tpublic static void main(String[] args) throws SyncException {\n\t\t// You can call methods on them, blah, all the usual magic stuff.\n\t\tSERVICE.ping();\n\t}\n}\n```\n\nThat's basically all that's remotely interesting for now.  \nOr at least, all that I can remember.  \nIf you're interested in what it generates behind the scenes, there's two methods.  \nFirst, you can take a look in \"Main\".  \nThat was what I based my idea off.  \nI wrote the first implementation by hand, and got an idea of what I needed to do.  \n\nThe second way is a bit more \"advance\", and that's to use the debug option inside of RestBolt itself.  \nThere's a property at the top of the class that you can define.  \nPoint it to a folder, and it'll write the generated class to it, then just use \"javap\" to take a peek at it. My recommended flags are \"-p -v -c\".  \n\n\nThe following part is more of a note to myself and anyone wanting to implement a custom body serialiser as there's some hoops you have to jump through.  \nSo, if you're not interested in that, you can stop reading here...  \n\n---\n\nAssuming I've already managed to explain the general concept, annotate interface method with annotation that corresponds to verb to want to execute.  \nGood.  \nSimple.  \nNow, some of these annotations take a body.  \nSuch as POST or PUT.  \nThese Body parts need to be serialised and how that is defined is through this \"publisher\" value on the annotation.  \n\nThis value is basically just a \"target\" for RestBolt.  \nTake, for example, POST's default value. (At the time of writing this, it's the URL_ENCODED impl)  \nThis string contains 2 parts.  \nThe class name and the method name.  \nThe signature itself is actually statically defined: {@link me.jezza.restbolt.RestBolt#PUBLISHER_FACTORY_SIGNATURE}  \nRestBolt will try its best to load the class, and then locate the method.  \nThe method obviously needs to match the given signature.  \n\nOnce located, the method will be invoked and expected to perform 1 task.  \nConstruct the publisher.  \n\nYou will receive a MethodVisitor, which is the core component.  \nThis is the method implementation itself.  \nBe careful, as one wrong step will break things, you're writing bytecode after all.  \n\nSo, on the stack ready for you is the {@link java.net.http.HttpRequest.Builder}.  \n(There's obviously more on the stack, because I'm a lazy fuck and don't bother using locals all that much. I should probably go through it and improve it...)  \nSo, you have the request builder on the stack.  \nIn the factory, you have the impl, the method itself, the types, the names, and the slotMax.  \n\nThe impl is obviously the MethodVisitor and is used to write the method implementation.  \nThe method just a {@link java.lang.reflect.Method} that is the interface's method.  \nThe types is a bit more complex and I can't be fucked explaining it yet... (TL;DR: it contains information about the method parameters. Flags, slot indices, and typing information)  \nThe names is kind of an aux data structure that the types array uses. (If a type is flagged as a Body, the value inside of the array at that location is the constant that was written.)  \nAnd the slotMax is the highwater mark of the parameters. This means that if you want to store local variables do so ABOVE this mark. (eg, ASTORE slotMax + 1).  \nIf you don't do that, you're gonna stomp on the parameters that were given to the method. Potentially breaking things.  \n  \nIn this factory, you have one job.  \nPlace a {@link java.net.http.HttpRequest.BodyPublisher} onto the stack.  \nThat's it.  \nUsing all of the information I give you, create a BodyPublisher that does what is needed.  \nDon't touch anything else on the stack. (eg, don't remove the builder, as we still need that...)  \n\nYou can take a look at {@link me.jezza.restbolt.RestBolt#buildNoBody(java.lang.reflect.Method, org.objectweb.asm.MethodVisitor, java.lang.String[], int[], int)} for a simple impl  \nor {@link me.jezza.restbolt.RestBolt#buildURLEncoded(java.lang.reflect.Method, org.objectweb.asm.MethodVisitor, java.lang.String[], int[], int)} for a more complex one.  \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjezza%2Frestbolt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjezza%2Frestbolt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjezza%2Frestbolt/lists"}