7 ms·
An Opinionated Guide to Modern Java, Part 2
- vidar 12y agoThis feels like a different universe.
- anjanb 12y agocool article. I wish if someone can give a Java update like this once every year!
- derengel 12y agoAs someone not very familiar with Java can someone expand on what he means by this: "Every library is usually packaged into its own JAR, and merging all dependencies into a single JAR, might cause collisions, especially with packaged resources (non-class files)."
- jacobheric 12y agoWhen you merge multiple libraries into a single jar, it is possible that two libraries might have two (unpackaged) resources with the same name that will collide on the JVM's classpath. Or, perhaps more annoying, you might end up with an unexpected version of a (packaged) library defined class than you expected.
- saryant 12y agoA common example is with logging config files (e.g., logback.xml). Some libraries ship with their own (or a library's dependency). It's not uncommon in a large project to wind up with several logback.xml files littered around from libraries that included their own. These are called "resources" because they aren't code but they are included with the compiled distributable and are placed on the classpath. Of course, your project probably includes its own logback.xml in your resources directory. Libraries are typically distributed as JAR files, so when you include their JAR on your classpath you're also including their logback.xml in that JAR. Now, when you go to package up an uber-JAR for your project, whatever packaging tool you use has to merge all those files into one classpath. So now it's found collisions: there are multiple, different logback.xml files on your classpath but you can only have one. How does the tool pick? So now you have to specify a "merge strategy" to make sure that only your logback.xml is merged in and the rest are discarded. logback.xml is just one example. Typically Java classes themselves merge just fine because the packaging tool can dedup by fully-qualified class name which is almost never shared across projects. So even if two different dependencies have a Utils class, the FQCN is different. com.foo.bar.Utils is not the same as com.bar.baz.Utils, so there's no conflict. Conflicts can also arise from libraries which include different versions of the same transitive dependency. Generally the fix here is to exclude a transitive dependency from one of your direct dependencies so as to only include one version of that library. A common culprit is Apache Commons since so many Java libraries rely on it.
- pivo 12y agoSpeaking of Java logging, that's one area that drives me batty. The lack of a good standard Java logging library caused a multitude of logging libraries to be written. Different libraries then of course use different logging libraries, so each logging library generally has a way to wrap all the others, and they all work in different ways. Getting consistent and controllable logging (e.g. ability to turn on or off logging for a given thing) can sometimes be almost impossible to understand.
- twic 12y agoIt's a sad story. The proliferation of logging libraries was the problem that Commons Logging aimed to solve by providing a facade over all of them. But because it screws up classloading (i forget the details, but it's serious), eventually a critical mass of people needed to move off it. java.util.logging tried to fix the problems and canonise a standard interface by putting in the JDK, but it got so many things wrong that it wasn't widely adopted. SLF4J finally came along as a very well-implemented facade that has gained wide adoption. It would say it is a de facto standard. Only then JBoss decided that all their stuff (including Hibernate) needed a facade of its own, which manages to not quite properly interact with SLF4J! It's almost enough to drive me to printlns.
- FeloniousHam 12y agoAgree, and will add that part of my frustration comes from the fact that nobody really cares about logging. You just want it to work. OT, but I think this is my biggest complaint with Javascript. There's an explosion of invention and creativity with the language (because of its privileged position as the foundation of browser programming), but little standardization for the things I don't really care about.
- philbarr 12y agoEach JAR is just a zip file. It can be convenient to merge all those JAR files into one for deployment so you don't have to have all the JAR files lying around in the system and upgrades are just "replace this one JAR file" (among other reasons). Since each JAR is just a zip if you have two files in seperate JARs with the same path you'll have a problem when you try to merge them.
- jpollock 12y agoClass loaders work in a tree, and there typically isn't sharing between sibling branches. I believe that visibility is up the tree, and never across. So, you can end up with two different versions of a library existing quite happily in the same process space. Collapsing them down into a single [ejsw?]ar file results in the tree being collapsed down too. All of a sudden all of the log4j.xml files that everyone has sprinkled everywhere end up overwriting each other...
- twic 12y agoFWIW, classloaders don't have to work as a tree. The JDK itself has a simple model with three classloaders in a hierarchy (bootstrap, extension, and system), which is a degenerate tree, and application servers have traditionally had a more general tree, but other topologies are possible. But OSGi and JBoss Modules have directed graphs of classloaders.
- benjaminpv 12y ago"Java application servers are dead" and since there's no alternative to Java application servers here's a solution I cooked up myself. Like I mentioned last time I appreciate an overview of modern Java practices but boy howdy I can't discern if this is clever trolling or a cheap way to make me read Part 3.
- UK-AL 12y agoI'm guessing tomcat.
- philbarr 12y agoI skimmed through Part 3 and it's basically saying that app servers are hard to deploy, maintain and dev against and you should be using Spring Boot or Dropwizard instead to create applications with the services you require.
- twic 12y agoI used Spring Boot the other day. It was really easy! Up until things just didn't work for no apparent reason, and neither the error messages nor documentation gave me any clues as to how to fix it. App servers have historically been hard to deploy, maintain and dev against. There has been a huge amount of progress in the last few years. Based on my experience, Java EE / Wildfly is actually easier to develop with than Spring Boot.
- john-waterwood 12y agoApp servers like TomEE, JBoss and GlassFish are not hard to install at all, and certainly not to dev against. What's the author smoking? These servers are just unzip == fully installed things. We have them checked in and just deploy them automatically whenever there's a need for an update, pretty much as every other library out there. I wonder are we missing something or do people just wrongly think there's "something" difficult, where there's no need at all for things to be difficult?
- Patient0 12y agoWhere did you find part 3?
- jebblue 12y agoI'm a fan of simplifying the server side, one I'd recommend for the latter part of the slideshow he referenced: http://www.sparkjava.com/ http://www.sparkjava.com/
- danieldk 12y ago...or just use JAX-RS, which is simple, standardised, and has good implementations (such as RESTEasy and Jersey).
- jebblue 12y agoI suppose but SparkJava has as its core philosophy, build as a runnable jar from the start which is in line with what the article was discussing in its referential prohibition of conventional app server approaches. edit: Further to the point of getting Java server technology to a simpler to understand, design, debug and maintain place, SparkJava makes it insanely easy to do REST services eliminating both Annotations and heavy configuration. I've also speed tested my side projects on SparkJava and they are unreal fast.
- cmicali 12y agoNice article - Many of the suggestions (single-jar deployment, metrics, slf4j logging, etc) are all wrapped up for you in Dropwizard http://dropwizard.io http://dropwizard.io, which we use and love.
- asdf1234 12y agoDo you just use it for producing a REST API or do you use it to render HTML, handle session management etc.?
- zodvik 12y agoI happen to use Dropwizard as well and purely for REST APIs that serve as internal micro services.
- badlogic 12y agoWe use it for both REST APIs on top our services and rendering templated HTML. We had to bastardize the asset bundle extension considerably for that though. Once we got things working we didn't want to go back.
- cmicali 12y agoWe use dropwizard only for rest APIs currently. Our HTML and JS are served up with an in-house framework that has plugins for less, minimization, svg inlining, etc. We have open tasks to both publish it and make it hostable in dropwizard directly.
- ytsejammer 12y agoSharing a lot of the Dropwizard philosophy, there is Spring Boot (http://projects.spring.io/spring-boot/ http://projects.spring.io/spring-boot/), which we use and love as well.
- jebblue 12y agoI'm not clear, just did a ton of Googling and I think maybe spring.io is related to the Spring Framework at VMware. That would mean it's dependency injection.
- eshvk 12y ago> While I personally prefer Gradle’s nice DSL and the ability to use imperative code for non-common build operations, I can understand the preference for the fully declarative Maven, even if it requires lots of plugins. The modern Java developer, then, might prefer Maven to Gradle. I find Maven's abstractions surprisingly hard to understand. It is a major part of my annoyance with Java as a dev setup. I miss Make/Ant. Not sure if Gradle will be a good replacement or not but would be curious to try.
- krschultz 12y agoI really enjoy Gradle. I have done significant work with Ant, Maven, and a host of tool chains in the C/C++ world. Gradle seems to have the cleanest DSL. Not every single thing under the sun is supported (Maven seems to be the most comprehensive tool I have used), but Gradle is nice to work with.
- Groxx 12y agoI've been struggling to find out what's even available in Gradle. How does one debug it, or print available methods, or really anything? My woes are mostly around Android's gradle integration, which appears poorly documented at best. There are tons of magic commands that appear out of nowhere and with no explanation beyond "use this.". And Gradle's massive guide covers things that barely see the light after being seemingly wrapped in several layers by Android's plugin. I want to give it a real try, but the Android projects I deal with are reasonably complex, would use code generation (`scopes.PROVIDED.plus += configurations.provided }`? wtf does that even do and how in the world would I have discovered I could use it?), and need multiple products. I would have to know what and why before considering switching over for real, but there seem to be some huge walls preventing me from finding out.
- lmm 12y agoWhat abstractions? You have a project which has a name and some other metadata and it has a bunch of dependencies. If your build does something weird then it also has plugins, but you should try to avoid those as much as possible. I guess profiles could be confusing, but again, try to avoid them. Maven is the simplest build tool I've used, in any language.
- badlogic 12y agoI'm glad someone took the time to write this, it's actually a very nice resource to get new coworkers up to speed. Shouldn't be tought as dogma of course (e.g. Gradle vs. Maven). Also, thanks for mentioning Packr even though noone has used it in production yet. It's only a week old. Assuming OP is the original author: are you by any chance following JGO? I see Quasar is actually using Matthias Mann's green thread lib which i don't think was advertised anywhere outside JGO.
- pron 12y agoYep. JGO. And Quasar now uses a heavily modified fork of Matthias Mann's code.
- pjmlp 12y agoI like the overview given to JVM tooling, many developers are fully unaware of what JVMs (not only the official one from Oracle) offer in terms of monitoring. If you want to go really low level, a few of them even show the generated assembly code by the JIT.
- sehugg 12y agoYou can do this from the JVM command line with "-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly". (it's kind of a mess, though)
- pjmlp 12y agoYes, it still requires the plugins which aren't delivered with the JDKs and each JVM does it a bit differently, but is possible. As complement of it, check JIT Watch https://skillsmatter.com/skillscasts/5243-chris-newland-hotspot-profiling-with-jit-watch https://skillsmatter.com/skillscasts/5243-chris-newland-hots...
- pron 12y agoOh, I forgot to mention JIT Watch! (I actually use it sometimes). I'll give it a quick mention next time.
- dave3773 12y agoVery nice article. Capsule looks promising in regards to packaging. I'm going to have give 'er a try. One could also use the fatjar[1] or application[2] plugins (within the context of gradle). [1] https://github.com/musketyr/gradle-fatjar-plugin https://github.com/musketyr/gradle-fatjar-plugin [2] http://www.gradle.org/docs/current/userguide/application_plugin.html http://www.gradle.org/docs/current/userguide/application_plu...
- eeperson 12y agoAs a Java developer, I thought this article had some interesting information about logging and monitoring. However, the deployment section had my scratching my head a little. I've never totally understood why people want to make fat jars. It seems like a process full of headaches since you can't have jars in jars. Wouldn't it be much easier to create a regular zip file with a small script to set the classpath and and run the project? I'm not sure I understand the motivation for embedded instead of standalone servlet container. The article linked to some slides but they mostly seemed be demonstrating that you can function using an embedded container rather than providing clear benefits. Maybe it would have made more sense with the associated talk. Can anyone provide more insight to these?
- pron 12y agoAuthor here. A capsule is not necessarily a fat jar. It can point to Maven dependencies that are downloaded on the first launch, and can later be shared by other capsules. A zip with startup scripts is OK, but it requires installation. As to full blown app servers vs embedded servers, I think it's the other way around. It's the big app servers that require justification, as they are a lot more cumbersome to set up and deploy.
- derengel 12y agounzipping a folder and running a <10 line bash script requires installation? is that what you mean by installation?
- pron 12y agoWell, if it works for you then great. But doing that with every version (Capsule gives you automatic upgrades) might become annoying, and even dangerous, and if you can do the same thing with a single file that doesn't even require installation, why not make life even easier.
- derengel 12y agoOk, not disagreeing, just wondering what you meant by installation, Capsule sure looks cool.
- gsmethells 12y agoAs far as packaging and deployment of a native executable goes, I thought it odd to not mention Excelsior JET (http://www.excelsiorjet.com http://www.excelsiorjet.com) as I believe that's the only Java compiler that can handle all of the JDK (gcj isn't there yet).
- pron 12y agoNative executables are not always what you want. For example, they don't get security patches.
- Pacabel 12y agoThat's total nonsense. Binaries (or shared libraries they depend on) can be and are updated, on all sorts of systems. In many cases it's as simple as overwriting the existing binary with the updated version. Heck, the runtimes you're advocating are generally installed as binaries. If they can get updated, then so can any other binary. And if a user doesn't bother to update a given application's binary when a critical flaw of some sort is found, then there's a very good chance they wouldn't bother to update any runtimes that are installed, either. This is true even when some sort of update notification and installation process is offered. The end result is the same in either case: the update is not applied. Runtimes shouldn't be portrayed as any better in this case, when they're generally no different than any other binary.
- pron 12y agoAnything can be done in many different ways. It all comes down to convenience. When a library is found to have a security flaw, you can either upgrade all binaries, or just upgrade one runtime. You could say that your OS is just a binary, too, and that's true. But it's a binary that adds a lot of convenience. A lot of people think runtimes add convenience, too. You obviously don't agree, and that's fine: use whatever works best for you. http://xkcd.com/378/ http://xkcd.com/378/
- pjmlp 12y agogcj is dead since 2009. The only alternatives, besides Excelsior, is the new RoboVM, Websphere Realtime JVM, Aicas, Aonix among a few other ones. And if Graal eventually replaces Hotspot on the reference JVM, it might be that Truffle comes along as well.
- 12345678123 12y agoThis article does not mention OSGI or Jigsaw with one word but claims to guide to modern Java development. Seems they never run any large scale EE projects yet :)
- twic 12y agoAh, OSGI. OSGI is a bit of an enigma. People who use it think it's great, and, by the sound of it, assume everyone else is using it. People who don't use it think it's some weird thing from the turn of the century that nobody actually uses. I do think a well-rounded Java developer should know about OSGI (i have to confess that i don't). But it would be mistaken to think that it's mainstream.
- pswenson 12y agoI've found OSGi to be an absolute nightmare. Classloader hacks is not the way to solve Java's dependency problem, it needs to be baked in the language IMO. I think it's one of those ideas that sounds great in theory, but in reality it all falls apart. Even if it worked as advertised, fact is many 3rd party libraries have all sorts of issues with OSGi. For example, my company is stuck on Jersey 1.x because 2.x doesn't work properly with OSGi. Testing is much slower and harder to write. Testing seems to be an afterthought.... I think you can get most of the benefits of OSGi simply by using proper dependency management (Gradle). Use the Single Responsibility Principle and IoC and you'll get good modular code. These are much easer to do, have low risk, and are easy to test. JMO
- vorg 12y ago> I personally prefer Gradle’s nice DSL [...] in order to use Gradle one does not need to know Groovy, even if one wishes to do some non-standard stuff [...] I just learned a few useful Groovy expressions that I found in Gradle examples online The DSL is Groovy syntax from Groovy's antiquated Antlr 2.7 grammar, so simply by using Gradle you're using Groovy along with all its warts. Underneath, Gradle isn't so much a DSL as an API shipping with a programming language. You could just as easily write the first Gradle example from the article in most other JVM languages. If it was in Clojure... (require gradle :as g) (g/apply :plugin "java") (g/apply :plugin "application") (g/source-compatibility "1.8") (g/main-class-name "jmodern.Main") (g/repositories (g/maven-central)) (g/configurations g/quasar) (g/dependencies (g/compile "co.paralleluniverse:quasar-core:0.5.0:jdk8") (g/compile "co.paralleluniverse:quasar-actors:0.5.0") (g/quasar "co.paralleluniverse:quasar-core:0.5.0:jdk8") (g/test-compile "junit:junit:4.11")) (g/run (g/jvm-args (str "-javaagent:" (-> (configurations.quasar.iterator) next))))
- lmm 12y agoDoes anyone actually swap out their log backend? To me slf4j felt like an overcomplicated, enterprisey step with no clear advantage over log4j.
- paukiatwee 12y agoThink slf4j as API, and logj4 as implementation. slf4j has multiple implementations, e.g. logj4, logback, apache common logging bridge, so you can swap implementation easily without change your code(except configuration)
- agibsonccc 12y agoSLF4j is already the standard wrt logging. I think one thing to think of here is libraries. If there is one logging engine to depend on that libs can use, this means less dependency hell. Fighting it is a losing battle with how much traction it has among all the different libs.
- EdwardDiego 12y agoYes, we did, from log4j to logback. Due to using slf4j, no code changes were required.
- ddeck 12y agoI just switched from Logback to Log4j2 for an application and since I was using SLF4J, there were no code changes required. Also consider the case of third-party libraries. I have dependencies in my application that are hard coded to log to about three different logging implementations. If they instead used SLF4J, the end user could choose whichever implementation they were already using in their app, instead of having multiple implementations or needing to redirect the output via an SLF4J bridging module.
- digaozao 12y agoslf4j was useful for me in a switch from log4j to logback. Other useful tool for logging, that integrate with logback or log4j is sentry: https://github.com/getsentry/sentry https://github.com/getsentry/sentry And to use it with java: https://github.com/kencochrane/raven-java https://github.com/kencochrane/raven-java
- skybrian 12y agoThe article mentions a tool called Flight Recorder for profiling, but it appears this is commercial and not available in the openjdk?
- kbcv 12y agoHow does profiling Java applications work with JIT. Should you wait until the code has been optimized before profiling, or these tools smart enough to realize that the performance of code changes as the application runs?
- thejdude 12y agoI think as soon as any method takes up a noticeable part of execution time, it has been optimized all the way by the Hotspot or whatever your JVM uses. After all, you want to profile your app's hot spots ;-)