5 ms·
I'm not a java developer, but when i've had to use java I've hated the classpath mechanism. (In part because I dont feel like I can use build-tool-magic without
by mjburgess 5y ago
I'm not a java developer, but when i've had to use java I've hated the classpath mechanism. (In part because I dont feel like I can use build-tool-magic without understanding what is going on).
This article presents modules as a solution to some classpath problems, but doesnt quite give me the detail on how.
Can anyone elaborate further on how modules will augment/replace/obviate/worsen my need to care about the classpath?
- koevet 5y agoThis article explains it well, code examples included: https://www.baeldung.com/java-9-modularity https://www.baeldung.com/java-9-modularity
- watwut 5y agoYou did not elaborated what frustrated you about classpath. So it hard to answer.
- rossmohax 5y agoNot a fundamental flaw, but took some time to figure it out once I was hit by it. Jar files are iterated in a non defined (filesystem) order, meaning if the same class is found in more than one jar file, on 2 different systems different version of the class can be loaded.
- watwut 5y agoThis one will be somewhat better, because of those two will be in different modules they will be independent. If it happens within one module, then it stays the same. I agree that this one is annoying. It happens rarely, but every time it happens it is frustrating for whoever is dealing with it. I think there are ways how to automatically check for this, but yeah.
- capableweb 5y agoMy first experience with the JVM classpath was when I started using Clojure with zero Java experience. I quickly understood it more or less works the same as UNIX $PATH (although the devil is in the details, they are not identical), and since then I haven't really hit any issues. nodejs/npm does something similar by instead of being user set by default, it defaults to "./node_modules && ./node_modules/*/node_modules" (recursively). Most languages deal with paths in mostly similar ways (except Go until recently). What precisely is your issues with the Java classpath that someone who have been writing Clojure code for years haven't hit yet?
- amelius 5y agoOne issue is that you can't have a colon in your path names.
- capableweb 5y agoIs that something you come across frequently enough to hate the Java classpath? AFAIK, $PATH might be tripped up by that as well, as `:` is part of the syntax for delimiting. Just because I was curious, I tried searching for all directories containing ":" and besides /sys and /run, I got three hits. One because of "https:// https://", one because of "localhost:8080" (both mirrors made from wget) and one where it actually seems to be on purpose. It seems, to me at least, that having colon in path names is unpopular enough for this to not be a real problem. Edit: desktop I tried the command (`# find / -name ':' -type d`) on has been running daily since beginning of 2018 (2196 packages installed atm), with daily pacman updates, so should have enough data for a average workstation.
- fnord123 5y agoColon is an invalid character on Windows systems. I've used it when making log files where they have an intermediate suffix with an ISO 8601 timestamp but indeed I don't see a reason to have it in a path with class or jar files.
- aww_dang 5y agoCan't you just use an escape character?
- cjaybo 5y agoWhy is that an issue?
- fulafel 5y agoHow does this work out on Windows?
- vbezhenar 5y ago
- junon 5y agoI've had the opposite experience. The classpath system in java is, in my experience and opinion, one of the better packaging/module discovery systems among all of the languages I've ever tried.
- agumonkey 5y agocare to explain ? I have hate similar levels of hatred as OP toward java packages for years, then with age I started to appreciate the abstraction (independence from the file storage name space etc) but still i find clean python so much nicer 80% of the time. I'm really curious about your point of view.
- junon 5y agoGiven a class and its package, I know exactly how to find the original code or class file. Using the classpath, I can specify exactly the search order of classes, including overrides and whatnot, when the VM searches for them. Need to debug a Jar? Unzip it (it's just a Zip file). Jars can contain non-code resources, essentially allowing you to run an entire app using a single file, without the need to first decompress to the filesystem. Java gives you great utilities to find those resources. Classloaders can be customized and you have complete control over how the JVM sees code. It's boring, to the point, and simple to understand. I don't get how people have so many issues with them...
- barbarbar 5y agoBut if it is a class that exists in multiple jars in different versions due to dependencies dependencies depend... then it is not so easy when the wrong class is loaded.
- pjmlp 5y agoThat is why application servers use multiple classloaders isolated from each order, and in stuff like WebSphere you can even specify which overrides which.
- _old_dude_ 5y agoModules fix two issues that plagues Java for a long time, classpath resolution and monkey patching using reflection. For the former, the classpath is a colon separated list of jars (a zip containing the classes). At runtime the VM tries to find a class by scanning linearly all the jars, when needed or panic with a NoClassDefFoundError. The modulepath is a colon separated list of directories that contains some jars. At runtime, the VM revolves the dependencies (stored in the module descriptor of each jar) before starting the application. For the later, when you define a module you have to explicitly export packages and open them to reflection
- pjmlp 5y agoThat later solution won't work after Java 17, which I agree with. Java is not the language for doing monkey patching with internals. https://openjdk.java.net/jeps/403 https://openjdk.java.net/jeps/403 Notice that on Android, this kind of tricks earn you the prize of the system killing the process and be done with it.
- kaba0 5y agoModuler are almost completely orthogonal to the classpath; nothing changes for the latter. They are meant more as a way to enforce encapsulation. For example, runtime reflection is disallowed through module boundaries. From the JDK’s point of view it was really important, because some libs were depending on the internals of the OpenJDK, which are prone to change. With modules enforced, the internals can change much more liberally, while still keeping up perfectly with backwards compatibility.
- jillesvangurp 5y agoWell, if you use maven or gradle, it will manage the classpath for you. If you are doing this manually, you indeed need to read up on how that works. It isn't particularly hard. It's like a file path; but for class and jar files. It pretty much works the same way. Where the class loading mechanism gets complicated is the notion that it's a run-time thing and that Java allows for loading and unloading things at runtime as well as loading jars and classes into separate class loaders that can each have their own copies of the same; or even different versions of class files. Not a lot of applications actually do that these days but it used to be quite common. I actually used OSGI back in the day (a more powerful module system) that relied on this for managing different components that each had their own dependencies and dependency versions. Neat, but no longer that common to do these days. Osgi actually emerged out of the embedded software devices world. Later Eclipse used it to componentize the Eclipse platform and I know some companies that use OSGi for different things. These days you'd use microservices in some kind of Kubernetes container; or not (I'd consider that overkill for most of the stuff I work on). The Java module system is much simpler and not really intended for this (at least not primarily). Instead, the main use case is ahead of time compilation. Modules allow parking things that can't be compiled (e.g. because of reflection ahead of time in separate modules, which you can then exclude. Breaking up the jvm platform into modules was a big project. As far as I know, they are not that commonly used for anything else. At least, I've not encountered many projects that rely on this. It's a pretty low level mechanism. These days for servers, you package up applications as Docker containers, which contain your application and the full jdk. Both Spring Boot, Ktor, and other modern frameworks are commonly packaged up as single jar applications. Nothing magical about that; they are literally zip files with some manifest listing their content (classes, resources, more jar files). Of course modularizing a single jar application is kind of pointless. Likewise, there is no real need to tweak the java runtime you run it with either. Just not a thing. The notion of stuffing zip files in zipfiles is also a bit weird but it kind of works as a packaging mechanism.
- lmm 5y agoThe Java classpath is "flat" by default: you can only have one version of any given class (and therefore de facto only one version of any given library, unless they move the library to a different package for each major version, which some libraries do) on the classpath. This has advantages: you avoid the Rust problem of "Foo is incompatible with Foo" because you had two different versions of Foo in your (transitive) dependencies. But it means that if some library you're using uses a utility library internally, that dependency "leaks" and "pollutes" your classpath: you can't use a different version of that utility library yourself. Modules try to solve this by distinguishing between exported and internal dependencies. The idea is that if your library depends on some other library and uses that as part of its API, that should be an exported dependency (and a program that uses your library has to use the same version of that). But if your library depends on another library just for internal implementation, that gets hidden away from the application that uses your library. Personally I'm extremely doubtful of this working, because for it to work, library authors would have to specify their metadata correctly. So I suspect it's only going to lead to bigger problems than before, as the classpath gets more complicated and there are more ways to go wrong. But we'll see.
- brabel 5y agoYour analysis focuses on the wrong aspects of the Java module system. It's not meant to solve the problem of having different library versions on the same classpath, and it doesn't solve that at all as you still must ensure only one version of each class exists even if no modules expose those packages (see the split-package problem). The module system is for one thing above all: one more level of visibility, allowing Java developers to specify which packages should be visible to other packages. If you expose a package and that package has public types that expose another library's public types, then you must re-export those packages to make it clear that you have compile-time dependencies on another library, and that's checked so it's not at all hard to specify the appropriate re-exports. The only thing you need to worry about when writing a library is to expose only those packages you want, allowing you to have public classes that are not visible to the outside world unless they are in an public package. Another clarification: Java doesn't really have a single, flat classpath: you can create your own "nested" classpath if you want by instantiating your own class loaders. OSGi works by doing that for each "bundle" (library), which then does allow you to have multiple versions of the same library co-existing peacefully as long as no two packages which use different versions of that library attempt to re-export the library packages to each other (which would fail when you tried to load those bundles into the runtime). You can absolutely get the Rust problem you mentioned in Java if you use your own class loaders, and I've seen that happen several times, even when both libraries are of the same version (a class loaded by a different classloader is NOT the same as the class of the same exact name loaded by another, which is why classloaders are hierarchical and not supposed to load classes already loaded by their parents).