Anyone else having issues running their .jar files from the terminal after yesterday/today's updates have applied? My system no longer recognizes java as a command and I'm not sure what to do. I see I already have openjdk-8 installed on my machine, and I suspect I need to do some symlinking. Any help would be appreciated.
java command unknown after update
- Edited
#! /bin/sh
cd /usr/bin/
#sudo ln -sf /usr/lib64/jdk-14.0.1/bin/java java
#sudo ln -sf /usr/lib64/jdk-14.0.1/bin/javac javac
#sudo ln -sf /usr/lib64/jdk-14.0.1/bin/javadoc javadoc
#sudo ln -sf /usr/lib64/jdk-14.0.1/bin/javap javap
#sudo ln -sf /usr/lib64/jdk-14.0.1/bin/jar jar
#sudo ln -sf /usr/lib64/jdk-14.0.1/bin/jarsigner jarsigner
sudo ln -sf /usr/lib64/openjdk-8/bin/java java
sudo ln -sf /usr/lib64/openjdk-8/bin/javac javac
sudo ln -sf /usr/lib64/openjdk-8/bin/java javadoc
sudo ln -sf /usr/lib64/openjdk-8/bin/javah javah
sudo ln -sf /usr/lib64/openjdk-8/bin/javap javap
sudo ln -sf /usr/lib64/openjdk-8/bin/java-rmi.cgi java-rmi.cgi
sudo ln -sf /usr/lib64/openjdk-8/bin/jar jar
sudo ln -sf /usr/lib64/openjdk-8/bin/jarsigner jarsigner
save above as .sh file and provide execute permission for sh file and run in terminal
Thank you both this has been so helpful!
I'm not sure I understand this approach from a typical desktop user point of view. Now ordinary people trying to run a .jar are just going to see the archive manager open up when they try to open their apps, and conclude their Solus is broken. I mean, they'll think why use a GUI at all if we keep regressing to terminal hoop jumping for simple tasks. Why remove the symlinks before a solution to graphically supporting multiple versions is developed? Jars account for numerous popular cross-platform games, etc. What apps in the repo already require multiple versions of JDK?
JohannPopper We provide Java solely for the applications we ship and for the developers who use it. We control which version of Java our packages use and Developers are smart enough to reconfigure their terminal or IDE to switch JDKs. Right now because we don't support a newer JDK than OpenJDK 8, many developers have been forced to use unsupported methods to install newer JDKs which aren't supported for Solus.
We removed the symlinks because they can only point to one version of Java. That is harder to override than it is to not use in the first place. When we add a second JDK, there will be no conflicting paths. There also won't be some horrible stateful mess like /etc/alternatives handling switching between JDKs.
No package requires 2 versions of the JDK, but there are several packages being help back from upgrades because they need a newer JDK (See: here). At the same time, we can't just upgrade our existing JDK because things like jitsi
still need an older JDK release.
Cool. Thanks for the explanation.
- Edited
DataDrake Thanks for the explanation, but not fully clear why a default Java version causes issues. What's the problem with the approach distributions like Ubuntu take where I can select a default version?
Is the symlik to a default version breaking anything else?
My current workaround is to add the default java version to the PATH in /usr/share/defaults/etc/profile.d/java-path.sh
:
# Begin /usr/share/defaults/etc/profile.d/java-path.sh
export PATH="$PATH:/usr/lib64/openjdk-8/bin/"
# End /usr/share/defaults/etc/profile.d/java-path.sh
palto42 but not fully clear why a default Java version causes issues. What's the problem with the approach distributions like Ubuntu take where I can select a default version?
For starters, Ubuntu use an antiquated system of symlink redirection that is a part of dpkg
itself. Packages have to be known to provide these alternative commands and this all has to be heavily integrated into dpkg
, apt
, and graphical package management tools. I have personally used this system for years on Ubuntu and it's fine, until it breaks, and then it's awful to fix. That's before you assess this solution ideologically and realize that this whole process goes against the stateless approach to packaging that we strive for in Solus.
Is the symlik to a default version breaking anything else?
It actually can break things pretty easily. Let's say you have two Java programs that are started via shell script. Both scripts are hard-coded to use /usr/bin/java
. The first program requires JDK 8 and the second program requires JDK 11. Now you try to get both of them working. First you install JDK 8. update-alternatives
is run and sets it as the default java. The first program will now run fine, but the second program will not. So you install JDK 11 and update-alternatives
is run and asks you if you want to switch the default to JDK 11. You say yes. Now the second program runs, but the first program fails. It's at this point you realize that before running each program you will have to make sure you have set the correct version of Java. You also realize that anyone else using the computer will need to have sudo privileges to modify the symlinks because /usr/bin
is protected. One of the simplest possible use-cases and it already falls apart.
Now, let's try this another way. Each script is modified to look for JAVA_HOME/bin/java
. When you create the .desktop entries for the applications, you run the program with Exec=env JAVA_HOME=path/to/jdk /usr/bin/script
. Now, both the JDK 8 and JDK 11 programs can use the correct JDK. No configuration was necessary. No symlinks were created or modified. Everything just works.
DataDrake this is obviously still a fiddly approach, removing complexity from "stateful" symlinking and handing it to the user instead.
Don't get me wrong, I'm all for multiversions. It's a hard problem with interesting approaches. gentoo handles it with slots. And nix, The Stateless OS, uses symlinking. Point is, to make the most use of this stateless approach is a good goal, but java doesn't exist in a vacuum.
Being user-friendly and all, we shouldn't expect users to be setting PATH and/or handling env vars, right? If so, I think we need to make sure programs shipped within the software centre reflect those breaking changes, as to actually work when expecting a java executable in PATH or JAVA_HOME (even when it isn't in user's PATH).
Otherwise, there should be a sane default java version for those programs that expect it, and then for outliers that explicitly cannot use that version could be overridden in their commandline wrappers or desktop entries. It could be as simple as /usr/share/defaults/etc/profile.d/90-java-(path|home|something).sh
shipped with openjdk-xyz, really.
hyphens I could care less how other distributions handle this. This debate has been going on for almost two years now. I made a judgment call and committed to it. No matter what I chose, someone was going to end up being upset with the solution I came up with. Multi-version support for languages is messy and hardly a science. The simple reality is that we never should have shipped those symlinks to begin with. We also should have experimented with having multiple JDK releases a long time ago. At the end of the day though, OpenJDK was only added to the repository to support the Java applications we ship and for developers to use. If had the foresight to not ship those symlinks in the first place, people would already be using executables in the /usr/lib64/openjdk-8/bin
however they like.
hyphens It could be as simple as /usr/share/defaults/etc/profile.d/90-java-(path|home|something).sh shipped with openjdk-xyz, really.
That won't work. As soon as you have two JDKs installed simultaneously they will fight for PATH ordering or control of JAVA_HOME.
I'm open to suggestions, but I've had years to think this through and there was never going to be a solution to this that kept everyone happy forever. I decided to rip off the band-aid now so that people are less frustrated in the future.
For the average user, there is another way to solve this issue, although it raises even more debate.
A shared java runtime never really works well for any OS, and in my long experience, those java apps that "always" work flawlessly have bundled the java they need in with the app, rather than rely on a shared version.
Since drive space isn't a problem now day's, it seems to still be the cleanest and most reliable approach and collisions are avoided.
Of course, the java you require and it's licencing can make this more challenging, but if it likes OpenJDK, then things do look better
mark0x01 openjdk-8
is currently 340MB installed and openjfx-8
is another 80MB. That's an awful lot of code to bundle with an application. In a distro, we try to increase reuse of things wherever possible to avoid using disk space unnecessarily. For us there is no advantage to building and bundling multiple copies of the same OpenJDK. Some vendors do indeed ship a JRE with their application, but in Third-Party
we go so far as to remove the bundled Java in order to further make use of our JDKs which have also been compiled using our optimized compiler flags and against Solus libraries instead of say Ubuntu or RHEL.
The issue we are discussing here is not one of bundling or shipping vendor-provided JRE/JDK. The issue here is how do we support OpenJDK 8 alongside 11 or some future OpenJDK release. There would have been file collisions or tooling problems if we kept the symlinks in /usr/bin
around, so we removed them. Now, multiple JDK versions can be installed and we have an established set of methods for packaging Java applications which target a specific version of the JDK.
Thank you so much