6 ms·
Java call stack – from HTTP upto JDBC as a picture (2006)
- rhapsodic 9y agoThis should be titled "Acegi/Spring/Hibernate" call stack. And none of those technologies are needed to build a JEE webapp.
- meddlepal 9y agoWhile that's entirely true, the glueing of those frameworks together is very common in the Enterprise Java development space especially around 2006 when this was taken. I'm a big fan of the JVM and the Java ecosystem but in many ways the JVM ecosystem is split into two worlds: frameworks or libraries. This would be an example of a framework heavy development model where god knows what is going on between the outside world, your biz logic and the database calls.
- yazr 9y ago> common in the Enterprise Java development space especially around 2006 Can you mention which frameworks and glues are common today ?
- keketi 9y agoAcegi/Spring/Hibernate :>
- gav 9y agoSpring Boot, but that includes Spring Security (was Acegi) and Spring MVC. Spring Data (JPA) has taken over from Hibernate.
- deepsun 9y agodropwizard.io
- Floegipoky 9y ago+1 for dropwizard; Spring is too big, dropwizard is just right. I've always preferred JDBI over JPA too.
- topspin 9y agoYou can get a long way with just Spark: https://github.com/perwendel/spark https://github.com/perwendel/spark
- klez 9y agoOfficial website, for those wondering http://sparkjava.com/ http://sparkjava.com/
- meddlepal 9y agoI feel like I get pretty far with just https://javalin.io/ https://javalin.io/ or http://sparkjava.com/ http://sparkjava.com/. They're pretty similar but Javalin is being built by a maintainer of Spark and inherits many of the lessons learned from it. These are basically thin wrappers on Jetty. SQL: Mybatis or JDBI. SQL Migrations: Flyway JSON: Jackson, usually Redis: Jedis
- sgt 9y agohttp://vertx.io/ http://vertx.io/ is extremely powerful and relatively popular. We've used in production for years. Note that it is a non-blocking framework so there is a bit of a learning curve coming from e.g. Java EE.
- agumonkey 9y agoMan the java era was seriously seriously wrong[1]. IMO it's only there because people invested so much, they'll never cut their losses and stay in denial for one or two decades. [1] java 8 makes thinks bearable, but even then you feel like sculpting chapter 1 lisp/haskell idioms in granite with a spoon.
- rhacker 9y agoNot sure it is any smaller today. Some additional layers not seen are the DTO transformations (not seen because they don't become part of the stack when it's hitting JDBC) but two more major subsystems. Many IDEs now automatically filter out these well known stacks from view. Personally moving from a mostly Java background to entirely typescript on the backend and front-end.
- deleted 9y ago[deleted]
- harunurhan 9y agoAlthough I like TypeScript as a language, and use Node.js, also really appreciate sharing code such as (data types/interfaces etc.) btw frontend and backend, I think call stack is much worse on the side, especially when you start using frameworks. At least here you get a huge but relevant stack, you can find your business logic, but Node.js it's not rare to get irrelevant unhelpful stack traces which you can't figure out even if you dig in.
- nickbauman 9y agoThe fact that Spring even exists should be a big red flag for the value of static typing. People routinely make mistakes using type systems. And when done correctly I see static type systems work very well solving problems that they themselves created.
- jlg23 9y agoHow do you get from deeply nested method calls to "people routinely make mistakes using type systems"?
- nickbauman 9y agoThe gambit of static typing is that the type checker can attest correctness. But the result, if indeed that the type checker does this (provide a mechanism of provable correctness), you still have to deal with protocol bloat that type systems incur. Hence the deeply nested method calls: that's a result of adopting a static type system. I'm saying that with all that overhead, people still routinely make mistakes using static typing systems. They get the compiler to tell them that they're correct but their code is still not working.
- mrits 9y agoIt sounds like you once read a blog that you half understood.
- jdmichal 9y agoThe best way to configure Spring beans is through @Bean annotations in code, precisely because it allows for static typing...
- stickfigure 9y agoI'm not sure I really see the problem. They picked a particularly pathological example, with a lot of heavyweight frameworks. It's a choice. I just counted the frames from my typical stack, a Guice/Hibernate project. It was about 100 lines from a constraint violation handler in the postgres JDBC driver up to Thread.run(). This seems pretty reasonable, especially with the various layers I've added for authentication, declarative transaction management, AOP logging, etc. Furthermore, IntelliJ is pretty good about only showing me what I care about. Overall I find the debugging experience no worse than Python or Ruby, and much better than exceptions from Python or Ruby native libraries. It's orders of magnitude better than debugging Go, which destroys stack context with every "if err != nil return err".
- arrty88 9y agoThis is beautiful. Working with ES2015 async await style promises on the server now... you don’t even get valid stacks.
- linkmotif 9y agoYep, a totally simple stack you can follow. Wouldn’t it be nice in the async world.
- wrmsr 9y agoRails code can get that deep, the difference being MRI doesn't have an inlining jit...
- danbruc 9y agoThis looks much worse than it actually is. You could implement it in a much flatter way. function HandleRequest(request : Request) : Response { LogRequest(request) CheckAuthentication(request) CheckAuthorization(request) var response = DispatchAndHandleRequest(request) LogResponse(response) return response } This way you would have no deep stack traces and all would be fine. But only until you need to do some additional work somewhere right in the middle, say patch malformed requests between authorization and dispatch. With the above implementation you would be somewhat screwed, the best you could do would be reimplementing HandleRequest() with your additional step in the middle. But with implementations like those show in the article you just have a list of steps with a common interface, each step does its work and then the next step gets called. If you need to do something new somewhere in the middle, you just add a new step to the list of steps in the right place and you are done, possibly simply by putting the class name of the new step in a configuration file. var steps = { new LogRequestStep(), new CheckAuthenticationStep(), new CheckAuthorizationStep(), // This stupid XYZ client always sends broken requests. new PatchMalformedRequestFromXyzStep(), new DispatchAndHandleRequestStep(), new LogResponseStep() } function HandleRequest(request : Request) : Response { var context = new Context(request) foreach (var step in steps) { step.Execute(context) } return context.Response } This would still avoid deep stack traces because we are iterating over all the steps but note that with this implementation we would not really be able to abort processing the request somewhere in the middle, say if the authorization check failed, but we could fix this by adding a flag to the context and check it inside the loop after a step executed. But a more serious limitation is that every step only gets one chance to act, note for example that we have two separate steps for logging the request and the response. Imagine we wanted to log the request duration, then we would need a step getting the current time at the beginning and another one getting the current time after the request was handled at the end. And the first step would have to somehow communicate the processing start time to the second one, possibly by storing it in the context. A much more elegant solution is to organize the list of steps as a linked list with all steps looking like this. function Execute(request : Request) : Response { PreprocessRequest(request) var response = GetNextStep().Execute(request) PostprocessResponse(response) return response } This creates those deep stack traces but it also creates a huge amount of flexibility and extensibility. It surly looks crazy if you do not need it, but when you need it, it is really easy with this model.
- p2t2p 9y agoLike what is the problem? Thanks to this stack trace your business logic fits into two lines. Do you want to have lines and lines of your own transaction manager? Lines and lines of your own DI framework? Do you suggest to write a HTTP web server yourself? Why don't we count stack frames in kernel space? And what do we have in JVM underneath us? And libc/Win32 layer? I'm really annoyed by this kind of things like they have any meaning except "I don't know shit about how abstractions work in computers" %s/you/picture author
- marktangotango 9y agoDebugging these stack traces is easy, just look for your packages and classes, and “caused by”. You should never be debugging framework code. Well almost never.
- peterevans 9y ago"Almost never." In my experience, the code you write within a third-party framework rarely works the way you expect it to the first time. So you often do "debug" the framework, just to see where your code goes while in the framework to see what you missed. That's not a bad thing, though--it's a great way to learn any new framework!
- givehimagun 9y agoAgreed and I will piggyback on top of it. I think we forget how reliable some of the code in this stack is. It's battle-hardened and works extremely well with lots of documentation/help. There is no sane alternative to this...that is the power of open source where we collectively pool our capacity into something worthwhile to everyone. I want more of it.
- jdmichal 9y agoUsing jBPM, you get a NullPointerException when you use a WorkItemHandler with a BPMN <serviceTask>. Not a nice one with a message, either. Had to debug into jBPM to figure out that one.
- 9y ago
- asaph 9y agoNot every java web app needs to be this bloated. It's possible (even practical) to build robust, database driven web apps/web services without spring/hibernate and with far shallower stacktraces.
- commandlinefan 9y agoIt's possible, but in my experience, frowned upon for some reason.
- augbog 9y agoWould love to see more visualizations of other frameworks in their respective languages. I wouldn't be surprised if they were just as big.
- linkmotif 9y agoI’ve seen this all over the place and it looks like a totally fine reasonable stack trace to me. Anyway while the stack (hibernate, etc) shown in this stacktrace is still heavily in use, newer async java frameworks/tools usually result in very short stacks for me. Sometimes I get a trace and it’s 5-6 frames. Of course then you may not know how you got there. So it’s really not all that great.
- ceronman 9y agoThis looks like the perfect example to illustrate the point that Rich Hickey tries to make in "Simple made easy" [1]. This huge call stack has been designed to make your life as a developer easy but the price you pay is an enormous amount of complexity. I've been working a lot with a similar Java web stack and I feel how painful this complexity is. What is worse, is that I think that a lot of this complexity is incidental. There are libraries and frameworks designed to make some things easier, but in the process end up creating a lot of problems that then requires another library or framework to overcome that problem which also has other problems and so on... The result is a huge stack like this. One concrete example of this is Hibernate. A tool designed to make it easier (apparently) to work with databases, but in the end create so many problems that the medicine ends up being much worse than the disease. Resolving an HTTP request that returns a the result of a database call should not be this complicated! HTTP is simple! Why do we need so many calls to so many things. I'm not advocating for a flat stack of course, but certainly a stack this deep is a clear sign that something is wrong. I very much agree with Rich Hickey, we need to stop thinking about how to make things easier and start thinking how to make them simpler. [1] https://www.infoq.com/presentations/Simple-Made-Easy https://www.infoq.com/presentations/Simple-Made-Easy
- realharo 9y ago>This huge call stack has been designed to make your life as a developer easy but the price you pay is an enormous amount of complexity. This particular problem could be solved by just having a good filtering UI. You don't have to analyze the stack in its raw text form. That being said, I agree that complexity in the Java world is often much higher than it needs to be, and sometimes the tradeoffs are not worth it.
- gerbilly 9y ago>One concrete example of this is Hibernate. A tool designed to make it easier (apparently) to work with databases, but in the end create so many problems that the medicine ends up being much worse than the disease. Sure. At our startup we had the choice to let 20 programmers write custom individual SQL statements for 100s of CRUD operations, or create entities and let Hibernate generate them for us. We used hibernate and it has worked out well. I can't imagine how it would have been to debug 100s of bespoke SQL queries and associated object mapping code, each written in the developers unique style after a few years. That would have been fun.
- bullen 9y agoMy application server has 8 methods from thread to your code: http://test.rupy.se/?id=2 http://test.rupy.se/?id=2 And an integrated distributed async-to-async database that is just as shallow: http://root.rupy.se http://root.rupy.se Feel free to use it: http://github.com/tinspin/rupy http://github.com/tinspin/rupy
- mozboz 9y agoOT, but anyone have similar stack traces that show call stacks or abstractions in other layers, down to transistors, up to user?