10 ms·
Show HN: Java REST without annotations, DI nor reactive streams
grumpyrest is a Java REST server framework that does not use annotations, automatic dependency injection or reactive streams, and minimizes the use of reflection. I created this because I got fed up with annotation-mad frameworks that you cannot easily understand, step into or reason about. grumpyrest uses the type system to guide JSON mapping and validation, and (possibly virtual) threads for parallelism. It's for grumpy people who don't like what REST server programming in Java has become.
I made this because I intend to use it in one of my own projects, but at the same time I want to make it available to others to (hopefully) get some good ideas on how to extend it.
- rco8786 3y agoKudos to you for trying this. Every time I open a Java project after spending time in literally any other language I get saddened by just how...cluttered? wordy? tedious? it is...hard to find the right word, but it's not good.
- winrid 3y agoVertx is pretty good. I have two vertx services in prod without a single annotation.
- exp-prohibited 3y ago[dead]
- mhd 3y agoI've been doing some Angular-/Nest-ish TypeScript projects, and it's basically Spring with more bugs and = signs.
- Traubenfuchs 3y agoAs a year long spring (boot) dev, I felt right at home with angular 2+ -it‘s literally the spring framework of frontend frameworks.
- webosdude 3y agoI haven’t used Angular but I have used Nest and I agree with this sentiment. Java Spring’s annotations are similar to Nest’s custom decorators.
- re-thc 3y ago> basically Spring with more bugs and = signs The worst parts of Spring is Spring. Nest lacks the IDE, the performance in the JVM, etc... there is nothing to gain.
- nunobrito 3y agoSure, other languages without semi-colons and using invisible tabs for syntax are fantastic. What I love best are magic object types where you only discover invalid type casting when running the code. And lest we forget the fabulous unit testing for those non-tedious languages. Oh.. I forgot testing is tedious and therefore unavailable for most of those languages. But hey, just look at that really short code without a single comment. The word you're looking for is: Fantasticc! :-)
- rco8786 3y agoThis doesn’t describe any language or framework I’ve worked in so hard not to conclude that it’s some sort of straw man
- jayd16 3y agoWouldn't this add more cluttered boilerplate? Annotations are pretty clean looking but the backlash is from the "magic" they provide, not the clutter.
- olavgg 3y agoOf all web frameworks, with perhaps the exception of Ruby on Rails. I really struggle with lack of documentation or the quality of documentation. And lesser popular frameworks has a lot less volunteers sharing knowledge. Spring Boot is one of the most popular web frameworks in the world. It is by far easier to figure out how to do build something that solves business problems in Spring / Hibernate than alternatives(except maybe RoR). For example, do you want to use Micronaut, the Spring Boot killer? Good luck fetching a collection with string type. I takes basically 2 minutes to google how do this with Spring Boot / Hibernate, and it will take you a day or two to figure out it isn't possible at all with Micronaut without writing massive amount of code that binds the results from your SQL to your objects. This is one example, and it shows the madness in web frameworks. Why are we switching frameworks that only ends up slowing us down? Spring Framework is a big framework, that solves many problems. It may be overwhelming the first year, but with a few years experience you do not want to switch, because other frameworks lacks or has ugly hacks for many Spring Framework features. Other advantages, massive access to experts for hire. There are literally thousands of Spring Framework developers in my area. Also, since I am an expert myself, I can share my knowledge between these people, and together we are improving our knowledge at a rate other frameworks cannot offer and this gives us a massive innovation pace that brings increased business value. The next time I will try another web framework, I need to see that my productivity gets improved immediately. Otherwise, is just noise.
- nprateem 3y agoPlus it's old enough that chat gpt is quite helpful. Would be good if they could update it for spring boot 3 though.
- winrid 3y agoNeeds more code examples in the readme.
- moring 3y agoThank you for the feedback. I'll keep that in mind for the next version.
- rattray 3y agoAgreed, it's hard to get a sense for what using this would actually be like. Prose describing code just doesn't work for most developers, myself including.
- moring 3y agoI added a simple example to the README that shows for to build a single-endpoint API and also how to do things in JSON with the type system instead of annotations.
- nunobrito 3y agoMoring, this is looking really simple to adopt, good work. First question: is there maven already available for this library? Mostly to ease upgrades in the future. Second question: what strategy would you recommend for adding https on the communication channel? Thanks.
- moring 3y agoHi, thanks for your feedback. Maven integration is not there yet. Gradle makes it easy to generate a POM, but I have honestly never published anything to central, and I don't even know what is necessary for that. I have added this to my to-do list. HTTPS is something I won't have to deal with myself, because I'll be running it in a context (Cloud Run on Google Cloud Platform) with external HTTPS termination. If you have any chance to do the same, I'd highly recommend it, because you can use standardized solutions. Other than that, the current version runs on embedded Jetty, but is actually just a servlet code-wise, so any servlet container would do. Again, this is not possible out-of-the-box (unless you don't mind cloning grumpyrest and modifying the code), but likely will be in the future. Once you can use a standard servlet container, such as Tomcat, you can just follow the normal how-tos on how to add HTTPS.
- faangsticle 3y ago> it calls constructors to create dependency objects, and passes constructor parameters to inject them So, basically DI. This is the recommended mode of operation of Guice & Spring.
- moring 3y agoKeep in mind that I had to cram as much useful information as possible into a single headline. The GitHub page says "without _automatic_ dependency injection". What grumpyrest does is sometimes called "poor man's DI", although this term has been discouraged because it is quite useful. The important thing is that it does not rely on any DI framework / container, and especially not on automatic construction of dependencies or annotations.
- faangsticle 3y agoTo be clear, most (all?) DI frameworks are available with plain classes without annotations. They do use reflection to figure out which parameter goes where, but that's it. public class Foo { public Foo(Bar bar, Baz baz) {} } Is a perfectly valid class as far as most DI frameworks are concerned.
- jwfy2342___ 3y agoI don't find value in runtime DI frameworks, however I'm assuming there is a valid use case for them since they exist.
- faangsticle 3y agoInteroperability with libraries that can be loaded at runtime, and in general skipping a build step that would make development annoying to do.
- suchar 3y agoThe main issue with Java-based projects (possibly using Spring) is the amount of existing resources: there are thousands/millions of example projects, code snippets, answers on StackOverflow etc. and majority of them is very old (as far as software development is considered). Even fresh resource are often using outdated techniques. Modern Java is pretty good (although Kotlin is a bit cleaner IMO), but you should really use Spring documentation (if you are using Spring) and avoid code snippets from SO/Github.
- moring 3y agoThe issues that I see with Spring aren't really code snippets from SO, but the fact that annotations drop all the advantages that Java had as a language. Just two examples: When you create an Object of the wrong class and try to pass it to a method, you get a compile-time error. When you use the wrong annotation, nothing happens during startup of your application... but you don't even know at which point in time something should happen. Or if. When a method you call throws an exception, you can run the application in a debugger and single-step into the method, then single-step until the exception gets thrown. This doesn't always just solve the problem but more often than not it gives you a good indication. If an exception gets thrown due to an annotation, you get an enormous stacktrace from some code you have never seen and didn't even know was run, or why it was run, from a thread you have never seen, complaining about wrong parameters that you have never seen, passed from another method you have never seen.
- nunobrito 3y agoThese is just (another) reason for avoiding Spring. Whenever I'm tempted because of XY module that looks good, I'm reminded with feedback like yours that things are still the same. Oh well, will continue using java far away from that framework.
- delusional 3y agoAnother one of my favorites. When you make an API for a library it's usually pretty clear what the user can do with it. When you make an annotation API for a spring library it's evidently impossible to expect how the user will use it. Random feature of a library won't work because the context you're using their annotation in aren't compatible with whatever assumptions they made.
- social_ism 3y agoThis does nothing that important. I do detest annotation soup, but what would be more useful is to simply generate a competent rest service and client from the openai spec. There is nothing performant or magical about using Java here. In fact the comments on performance are pretty dumb. The JIT compiler is the only source of performance in the jvm and you will never make a performance optimization that beats the JIT
- moring 3y agoThanks for your feedback. My experience with code generation from an OpenAPI spec wasn't that great, to be honest. The comments about performance are based on earlier experience with a similar scenario in which code generation _did_ improve performance. Mostly in that the generated code was "de-convoluted" to such an extent that the JIT could actually do its work. I'm expecting a similar thing here, especially with JSON serialization/deserialization, because of the heavy amount of reflection involved: The generated code would then no longer use reflection, but access fields and methods directly, and can therefore be optimized by the JIT.
- exp-prohibited 3y ago[dead]
- Phelinofist 3y agoLooks a bit like Spark - https://github.com/perwendel/spark https://github.com/perwendel/spark
- moring 3y agoI don't know why this was downvoted. Spark was actually one of the alternatives I considered before I concluded that I couldn't find any framework that suits me, and started writing grumpyrest. The main issue I had with spark was, from what I could see, the lack of good JSON support. A major part of grumpyrest is its JSON serialization/deserialization framework (it's roughly half of the whole codebase!) which applies the same principles as grumpyrest does for REST, to JSON. (I even called it grumpyjson in anticipation that I might one day break this out as a standalone project). Now don't get me wrong, I consider Jackson a high-quality framework, and I like very much how its author takes care of even the smallest details. It's not at all like Spring. However, in making it work as I wanted there were too many things that I could not solve to my satisfaction -- I could not abstract them in a way that was truly re-usable, and every API method would have to deal with these things again. This was even more true for Gson. So in the end, I used them (Gson, to be precise) as a low-level JSON library that basically translates between a JSON AST and serialized JSON, and did the high-level mapping to application classes myself.
- marginalia_nu 3y agoHmm, a lot of my endpoints (in Spark) tend to look like public SomeService() { Spark.post("some/endpoint", this::someEndpoint, gson::toJson); Spark.get("another/endpoint/:id". this::anotherEndpoint, gson::toJson); //... } private ResponseType someEndpoint(Request request, Response response) { Something some = gson.fromJson(request.body(), Something.class); return new ResponseType(some.a, logic(some)); } private ResponseType2 anotherEndpoint(Request request, Response response) { return new ResponseType2(otherLogic(request.param("id")); } Like yeah I guess it could be have more well integrated json support, but it's not like this is heavy in boilerplate, and I do think it balances the need to occasionally access more low-level aspects of the HTTP stack well. Not to detract from your project, of course.
- dimgl 3y agoHey, I'd be interested in seeing what this looks like but the README.md is just a bunch of wordiness. I worked on a beanstalkd client for Node.js and Golang, here's an example of going straight to the point: https://github.com/getjackd/jackd https://github.com/getjackd/jackd I'd say making a better README.md is your #1 priority.
- moring 3y agoThanks for your feedback. The README has been criticized in another comment already, so I'll definitely take that serious.
- bedobi 3y agoI'm a dev with 10+ years professional Java experience and something more like this is what I wish Java had always had. Ignore the other commenters who are being predictably and characteristically harsh and uncharitable.
- misja111 3y agoFrameworks such as Spring used to be much more like this, until the annotation hype started to kick in somewhere around 2010. I always thought this was a bad idea. Sure, annotations work nice in a small demo app, but in a large application you want to have everything explicit and under your direct control.
- krzyk 3y agoSpring used xml to configure that so no that similar.
- ElectricalUnion 3y agoThe alternative was "effectively stringly-typed XML forest" that was only checked at runtime; even worse for tooling that the current annotation (and recursive/nested annotation in the case of Spring) thing modern Java is.
- elric 3y agoI think spring's giant XML config file was preferable to the annotation garbage that most people use these days. But both are shitty solutions in my book. I'm still not convinced that doing all this configuration and magic at runtime is a good idea. Most of what spring does in terms of dependency injection (and probably a bunch of configuration related things as well) should really be compile time steps. There are very few cases where you want to change the implementation of $foo at runtime, and being explicit about those few cases would be nice.
- bedobi 3y ago> Frameworks such as Spring used to be much more like this Not really. Spring has always had problems with pointless complexity and turning what should be compile time errors into runtime ones. Its original schtick was DI, which you've always been able to do by just... sending dependencies in through the constructor, lol? No framework or magic needed. The emperor really has no clothes and I wish more people would admit that.
- social_ism 3y agoI find this article weird. Aping Sinatra is not an innovation. Java reflection is essentially zero cost after the JIt runs. The idea of "this is what you type" to make a REST endpoint or client call is a non-starter and non-issue. This code in any shop worth its salt has been generate for years off of metadata. If you are typing in rest code into any language, you are holding it wrong.
- re-thc 3y ago> Java reflection is essentially zero cost after the JIt runs. It is slower and it is cached so uses more memory.
- fsdjkflsjfsoij 3y agoIn Spring apps the same reflection caches are also often duplicated many times.
- hamandcheese 3y agoAll of the harm from reflection happens well before runtime.
- krzyk 3y agoThis is quite similar to Takes (https://github.com/yegor256/takes https://github.com/yegor256/takes). I like both, look cleaner then all the annotation based ones.
- moring 3y agoI did not know Takes, and I'll definitely have a closer look. Thanks a lot!
- AlexITC 3y agoI think we need more simple alternatives like this, https://javalin.io/ https://javalin.io/ is another project I have been following which looks simpler to me. In any case, thanks for sharing.
- doctorpangloss 3y agoHelidon Nima may wind up performing similarly to Netty. It has a straightforward API. https://www.infoq.com/news/2022/09/introducing-helidon-nima/ https://www.infoq.com/news/2022/09/introducing-helidon-nima/ https://github.com/tomas-langer/helidon-nima-example/blob/main/nima/src/main/java/io/examples/helidon/nima/NimaMain.java https://github.com/tomas-langer/helidon-nima-example/blob/ma...
- AugustoCAS 3y agoSomething that people are missing about annotations: in order to test the annotated class/method one needs to start running extra code, sometimes a lot of extra code. Using spring as a toxic example, many things have to be tested using @SpringBootTest which is incredibly slow to start. On top of that because of the use/abuse of @MockBean tests stop being thread safe. So one ends up with slow test that need to be run sequencially. I'm working in a 'start up' that went the spring boot way and quite simple services take 15+ minutes to run all their tests, which is insane. On top of that, annotations make it impossible/very dificcult to know what code is actually executed (and also it not possible to navigate to the code in an IDE). As I rule of thumb, I'm always happy to swap one annotation for one or two lines code.
- ivan_gammel 3y ago> On top of that, annotations make it impossible/very dificcult to know what code is actually executed (and also it not possible to navigate to the code in an IDE) I have never understood this argument. What is exactly the problem with identifying the executed code?
- jakewins 3y agoIf you use a “dumber” imperative style framework, the way to find out what code is executed is to go-to-definition of the library code when you call it. With an annotation-based framework, how do you do the same? You could find usages of the annotations, and then manually read the thousands of places “@GET” is referenced?
- ivan_gammel 3y agoSearch by usages is usually grouped by library/package, sk it is quite easy to identify the code in the framework responsible for processing it.
- moring 3y agoHow do you identify the executed code? I don't even know how to start. You cannot single-step into it from the annotation, nor can you select the annotation, "go to definition" and see the code. You can "find usages" for the annotation which gives you a lot of places that may or may not be the code that gets executed. Imagine I'm looking at a class that is annotated. What is the next step to find the code that gets executed for the annotations?
- The_Colonel 3y ago> Unknown properties in records cause an error too. This kills the forward compatibility.
- ivan_gammel 3y agoIt’s fail fast vs fail randomly because unknown property was expected by client to be handled in a certain way. Not every case deserves forward compatibility.
- The_Colonel 3y ago> Not every case deserves forward compatibility. REST APIs do. SOAP has the same "feature" of failing on unknown properties and it made API evolution very hard.
- revskill 3y agoJava is an OOP language, and OOP is enough to implement DI in a consistent way. It's how enterprise NodeJS projects are structured, too (without frameworks like NestJS). We might not need annotation for DI stuffs, it's an overkill.
- KronisLV 3y agoIt's always nice to see projects attempting to explore something a bit different than the status quo (which in Java's case is Spring or hopefully at least Spring Boot). Personally, I've also had good experiences with Dropwizard: https://www.dropwizard.io/en/latest/ https://www.dropwizard.io/en/latest/ Any smaller project will suffer at least somewhat from a lack of documentation/examples, but Dropwizard takes many packages that might be considered idiomatic in the Java ecosystem (Jetty, Jersey, Jackson, Metrics, Logback, Hibernate/JDBI3 and others) and joins them without too much hidden complexity or "magic". In contrast, something like Spring might suffer from too many examples, many of which are outdated, no longer relevant or considered best practices, as well as there are far too many ways to get something done, leading to lots of confusion. Though the last time I used Dropwizard, I still ran into some issues with adding dependency injection (which is optional), since I had to write some of the code for that myself and couldn't find something that worked nicely enough for my needs. Otherwise it was fine, though probably could benefit from additional tooling like Spring Initializr. Either way, anything that lets you put a breakpoint on some initialization code is worth a look in my book, vs having multiple layers of indirection and logic dictated by annotations that are not easy to debug, or even understand. That's also why I'm not necessarily the biggest fun of Laravel, Rails (convention over configuration/code) or a few other frameworks, though I acknowledge their usefulness otherwise.
- vips7L 3y agoMy problem with Drop Wizard is the manual wiring you have to do for everything. I would much prefer dropwizard-guicy being the default DI resolution mechanism.
- ivan_gammel 3y agoIt is an interesting experiment that would require some work before I would see it in my production environments. First of all, in bigger projects dependency injection is going to happen one or another way. Having all controllers implementing some simple interface and thus avoiding overhead of runtime mapping is fine. How can we map 50-100 endpoints and wire persistence etc without a god class or a lot of user code? Looks like a case for at least compile-time reflection and source level annotations (micronaut). Second, a small suggestion: the only difference between null and empty string or any other special value is that null fails faster. Client code that is not fully aware of all allowed special cases is going to fail at some more obscure point, so user of the data model must ensure that all cases are handled adequately. Offering to null of all those cases a preferential treatment with a special class is IMO just adding verbosity to the code with longer declarations and unwrapping. I would keep only OptionalField and rename it to Maybe<T> - it does have semantics justifying a wrapper. Constructors can enforce non-null constraint and user code can handle null or other cases in a traditional way.
- moring 3y agoThanks for your feedback. grumpyrest is not averse to dependency injection, but rather trying to "get out of the way". In particular it does not rely on (automatic) DI. My intention is that you can totally use it in a project that has a DI framework, and at worst it should require you to write the same few lines of glue code that you would need without the DI framework, i.e. calling some constructors. > Offering to null of all those cases a preferential treatment with a special class is IMO just adding verbosity to the code with longer declarations and unwrapping. There was a specific reason to give null / missing fields a preferential treatment. If you use a data class like Username that wraps a String, then that class can do all the validation necessary, before the Username reaches the handler method. _Except_ for null and missing fields because the two popular JSON frameworks, Jackson and Guice, already try to bypass that class in those cases. You can configure Jackson to pass null to a Deserializer, but you still cannot distinguish null/missing because they are both a null reference at that point. Also, AFAIK you cannot say "pass null to the Deserializer only for this type". You have to configure it for the whole ObjectMapper, which is problematic in a DI context where that same ObjectMapper gets used by a lot of code.
- stickfigure 3y agoThis misses out on the main benefit of using annotations: The associated Java code is "just Java". Testing JAX-RS endpoints is just a matter of instantiating Java objects and calling methods on them. Testing grumpyrest endpoints would require mocking the requestCycle other low-level http-oriented classes. Here's the JAX-RS equivalent of the demo: public class GreetingResource { @POST @Path("/make-greeting") public MakeGreetingResponse greet(final MakeGreetingRequest request) { if (request.addendum.isPresent()) { return new MakeGreetingResponse("Hello, " + request.name + "! " + request.addendum.getValue()); } else { return new MakeGreetingResponse("Hello, " + request.name + "!"); } } } This is less noisy (no explicit parseBody) and much easier to test - you can just instantiate a GreetingResource and call the method, no mocking required.
- moring 3y agoThanks for your feedback. I'll take that into account. You are totally right about having to mock RequestCycle, which isn't good. Furthermore, RequestCycle isn't really mockable in its current state. I'm planning to solve both problems in a future version by making it mockable, providing standard mock implementations, and removing the dependency on other web objects (such as the servlet API) from code which is just using RequestCycle. The main point behind grumpyrest is that a lot is _not_ "just Java": While the handler method you presented is, the way it is mounted isn't, nor how dependencies are located and injected, nor how the JSON is validated and mapped to objects and back.
- chii 3y ago> the way it is mounted isn't, nor how dependencies are located and injected, nor how the JSON is validated and mapped to objects and back. the point of a framework is to hide some of that from you. The point of abstraction is that you _should not_ have to know how the server mounts the handlers, just that it does and it does it via a contract specified in the documentation.
- yardstick 3y ago
- rbanffy 3y agoNot being snarky at all, and saying this as a compliment, this looks like a very pythonic way of building Java services, in particular the “explicit is better than implicit” core principle. I love this.
- oppositelock 3y agoThis framework looks wonderful. Some people have an aversion to "goto" statements, and Java annotations are even worse, they're a "comefrom" statement, where you end up executing code before or after your function based on this annotation, so it makes the code really annoying to follow. Java examples which show trivial code annotated with @POST or @Path are not representative of production systems, where you may have a lot more annotations for your DOM, documentation, and in some cases, you actually have more annotation boilerplate than you have code in the handler/controller. Having annotations interleaved within your logic makes it difficult to provide good API documentation, and it's hard to automatically refactor, because your boilerplate is interleaved with real code. With an approach like this grumpyrest, you can put all your machine generated code into a package, and simply connect it to your hand written code with a little bit of glue. It makes spec-driven development much easier. OpenAPI is very popular, and annotation based frameworks make it more difficult to integrate with it. If you generate API docs automatically from code, as with JAX-RS, it's easy to break things by accident because nobody audits machine generated docs. If you reverse the approach, and do spec-driven development, you code review the API behavior, and the code follows, which is a better model, in my opinion. Grumpyrest looks like it makes integration with spec-driven workflows quite easy. A word of caution to the author; if this takes off, you will be inundated with issues and PR's, since people will use this in ways you never dreamed of. I'm experiencing that kind of onslaught in something I open-sourced for Go for REST API's.
- moring 3y ago> Some people have an aversion to "goto" statements, and Java annotations are even worse, they're a "comefrom" statement, where you end up executing code before or after your function based on this annotation, so it makes the code really annoying to follow. I like this comment so much because I would have described annotations as a "comefrom" myself... but then I probably read that somewhere and forgot about it. > Grumpyrest looks like it makes integration with spec-driven workflows quite easy. This is interesting to me because I always thought about it the code-first-generate-documentation way, and I always wondered if/how I can derive all the meta-data from the code which, admitted, annotations make much easier because they are statically accessible. Doing it the spec-first way is something I should definitely consider. > A word of caution to the author; if this takes off, you will be inundated with issues and PR's, since people will use this in ways you never dreamed of. I'm experiencing that kind of onslaught in something I open-sourced for Go for REST API's. Thank you for the warning. Do you have any advice on how to prepare for that?
- Patrol8394 3y agoAnnotations are a great way to get rid of boilerplate and noisy code. For instance, I find Spring @Transactional a much more effective, less bug prone and productive way of dealing with db tx vs having to manual code and handle exceptions, rollback, connections etc .. Same goes with JAX-RS. I would choose annotations over having to code all that manually any day. You can easily test and debug. Yes, it will cost some time to understand how aspects work and how it is all wired up, but it is worth learning.
- peanball 3y agoThe amount of times I’ve put or seen @Transactional in the wrong place where it was never called and silently ignored is pretty high. It always led to very long debugging sessions. Especially if you touch that code once a year.
- robert_dipaolo 3y agoThe problem with an annotation for that is it causes people to keep transactions open for too long. It's best practice in DB programming to keep tx's open for as short a time as possible. The longer it's open the more scope for deadlocks and contention. So we never use it in our prod code. We've put all the boiler plate code for creating the transaction, error handling, retires, etc in a helper function that takes a lambda as a param (that being the code to run in the tx). That way you can only wrap the code that needs to be in the tx, without copying and pasting all the boiler plate everywhere.
- hardware2win 3y agoTransactional is funniest shit All that magic causes weekly questions from people about how does this work or about debugging it >You can easily test and debug. As easy as putting breakpoint in actual code?
- Patrol8394 3y ago> weekly questions from people about how does this work reading the spring documentation usually help :)
- exabrial 3y agoI do find projects like this interesting, even if I wouldn't use it. I found the bit of the codebase I read fairly clean and easy to read. Nice work.
- minebreaker 3y agoI have the exact same problem and have been working on my own one. Annotation is horrible. Looks like yours is great. Congrats on shipping.
- moring 3y agoDo you have your code publicly available? I'd like to have a look on how you did it.
- minebreaker 3y agoI hate shameless plugs but if you ask: https://github.com/minebreaker/Abukuma/tree/master/documentation https://github.com/minebreaker/Abukuma/tree/master/documenta... It's a half-baked project and may never be finished.
- rizalp 3y agoSo you replace annotation and di with callback hell, got it
- maxthegeek1 3y agoThank you. :')
- debarshri 3y agoThis project reminds me of java spark [1]. I was always a cheerleader of the project. We even used it in production, loved the non-annotation centric approach. [1]https://github.com/perwendel/spark https://github.com/perwendel/spark
- sunshinerag 3y agoI like it. Annotations--, Reactive Streams--. On that note, what are the views of HNers on Reactive Streams everywhere ... is it a dream come true or a debugging nightmare?
- nesarkvechnep 3y agoIt’s also without REST. I see no hypermedia controls.
- EdwardDiego 3y agoThere's always one. Yes, REST the theory is far different to REST the reality, but we all know what they meant.
- haspok 3y agoHow about error handling? How about security? Also, just peeking into the demo code, this is the first thing that caught my eye: int id = requestCycle.getPathArguments().get(0).getValue(Integer.class); This is something I would never write like this. get(0) is a big no-no, and this style also makes it hard to follow what's happening (what parameters / arguments there are), because the URL pattern is not next to this. In comparison, an annotation next to the method declaration is far superior for readability.
- moring 3y ago> How about error handling? This is a very general question, so I have to give a general answer. Application-level errors are expected to be thrown as exceptions. Unknown exceptions will result in a 500, as usual, in particular to not leak any sensitive data. A FinishRequestException can be thrown to show a different response; it tells the framework: Act as if the handler returned this object. Errors in the incoming JSON (syntax errors as well as validation errors / mismatched structure) are reported to the client as 400, with validation messages and offending field paths included. Feel free to ask a more specific question, otherwise it's hard to tell what you are referring to. > How about security? This was not a focus for the first version. You can use servlet filters, but that's going to be like driving a screw with a hammer. The next version is intended to have framework-level filters that have access to higher level information than servlet filters, such as the matched route. Low-level security, such as resistance against malformed syntax in the HTTP request is the job of the servlet container. > This is something I would never write like this. get(0) is a big no-no, and this style also makes it hard to follow what's happening (what parameters / arguments there are), because the URL pattern is not next to this. This is something I'm working on to find a good way to express in code. Mostly, the basic idea is that the URL pattern is next to that code, because the two are tied to each other anyway. Note that using a String-named field in the URL is better for readability, but just as likely to break when the code is changed.
- okeuro49 3y agoReminds me of https://javalin.io https://javalin.io
- flashharry 3y agoThis looks pretty much like Javalin https://github.com/javalin/javalin https://github.com/javalin/javalin , which has been around for a while. Also, libraries that have a dependency on Google Guava always make me feel a bit queasy....
- moring 3y agoThank you for your feedback. I'm using Guava because of its immutable collections. There are alternatives, so changing this is probably not that hard. However, it would help to know what exactly you don't like about Guava, to avoid replacing one problem with another problem.
- vips7L 3y agoWhy not just use the modern List.of, Set.of, Map.of immutable apis from the standard library?
- moring 3y agoI didn't know this was a thing... what a major oversight! Now one big reason for Guava has just disappeared. Thanks a lot!
- flashharry 3y agoI'm just a bit allergic to unnecessary dependencies and Guava is a massive blob of a jar. It's not properly modularised so you can't just pick the bits you need, if you want to use a single feature from it you have to include all of them. This can cause problems if someone who wants to use your framework also wants to use a different version of guava. In general the fewer dependencies your framework has the easier it is for people to use it.