In this post, using -jar option ignores all the -cp and $CLASSPATH.
In this post, using -cp option also ignores the $CLASSPATH.
Is there any good reason for them?
It's to avoid clashes in the classpath whenever you distribute your application to run on different environments. In most of the cases you'd like your application to be independent from the platform specific configuration. If the $CLASSPATH contains references to classes with (either unawarely or awarely) the same package and classname, it would get precedence in classloading before the classes which you included in your application's classpath. This may lead to unexpected application behaviour or potential security holes.
jar is supposed to be a standalone program with self-contained libraries. If you want to include other classpaths, you may need to do something like
java -cp jar1:jar2:$CLASSPATH some.class.with.main
BalusC answered the other question.
In both cases, the reason for the restrictions it to avoid1 accidental or wanton / ill-considered overriding of the effective classpath.
If you really want an application to be launchable using "-jar" and to also pick up classes via the user's $CLASSPATH environment variable, you should be able to do this by having the application create its own classloader, or using a custom launcher. (You could even make your application look for a "-cp" argument after the "-jar" argument.)
Likewise, you could modify the behavior in the first case.
However, I think it would be bad idea to do that. The main point of executable JAR files is to isolate the application from the vagaries of the environment in which the user happens to launch the application.
If you want to do hacky things with your application classpath, a simpler approach is to create a wrapper script that assembles the effective classpath however you want to, then launches the application with a "-cp" option. You could even pull the "Class-path" out of various JAR files' manifests and incorporate that ...
1 - Clearly, it doesn't stop someone changing the classpath entirely. But stopping that would be a bad idea, and probably isn't technically possible if we assume that the user can get local admin privilege, etcetera.
There are several reasons why the environment variable CLASSPATH is (and should be) ignored:
A global CLASSPATH for all projects makes no sense at all. It can't be the same for all projects, and you don't want one massive one that's reapplied to all projects.
You can't count on it being set, so depending on it is a bad idea. Code that works on one machine suddenly doesn't work when it's moved. How do you communicate the necessary environment settings? Better not to use them.
Java EE app servers all have their own conventions (e.g., all JARs in WEB-INF/lib and all .class files in WEB-INF/classes are automatically in the CLASSPATH for a web app).
Java EE app servers all ignore global CLASSPATH. They don't count on it.
Java IDEs all have their own conventions for setting a project CLASSPATH. Learn them.
All Java IDEs ignore global CLASSPATH. They don't count on it.
I don't have a global CLASSPATH on any machine that I use. It's not necessary. I'd recommend learning how CLASSPATH works and stop relying on environment variables.
Correct or not, I long for a -jar-cp flag. That would be obvious and direct enough to not be a security risk or break current behavior.
With APIs like java.util.ServiceLoader, it is entirely reasonable to wish to add/remove services from the classpath. You shouldn't have to loose that functionality because you used Main-Class in your manifest.
There is no sane enough reason to explain this apparent "absurdity", in my words. From one of the bugs in the Sun bug database, one can only infer that a developer did not account for the fact that the classpath could be specified via the CLASSPATH environment variable, or via the -cp option. By the time the issue was found, the release was more or less public, with the effect that the fix would cause backward compatibility issues.
Related
Can someone please help me, I'm almost desperate because I can't find solutions for a Mac.
I have written two .java files that I would like to use as a library for all new projects.
For this I found out that I have to set the CLASSPATH.
The java files path:
/Users/thomas/Soft/IN_OUT/In.java
/Users/thomas/Soft/IN_OUT/Out.java
Don't bother with it. The concept of CLASSPATH is misguided in the first place (because you can run more than one java app, ever, on your system, setting the classpath globally is misguided as a concept), and on most platforms, libraries and tools, this isn't the intended way to configure classpaths.
For example, in IDEs, you configure classpaths on a per project basis.
When compiling code or with javac or running code with java, you can supply the classpath with the -classpath option (or shorten it to -cp). For running jar files (java -jar foo.jar, or double clicking a jar), the classpath is taken solely from the Class-Path manifest entry in that jar file; both the -cp and the global CLASSPATH variable are ignored.
I get that you want these files to be available to 'all projects', but that's just not how it works. If you update these files, all your old projects would break, or at least you'd have to consider if they might. You'd also be extending these files to eclipse, intellij, and any other java apps on your system which makes no sense. That's why this notion of 'make these available to everything' isn't a particularly sensible thing to want.
Just for the sake of curiosity , I want to know is there a way I can edit/modify Java core classes inside rt.jar. I am searching everywhere in the internet but got no relevant answers. I have also referred to Covert Java(book) but I am unable to understand it.
Note: I don't want to distribute my app. I just want to do it for my curiosity.
Another answer suggested patching rt.jar
I believe this is not the best approach - you effectively cannot distribute your app (unless you have your app contenerized and you deliver Java with your app).
The way to go is to learn about bootstrap classpath
This should cover most of reasonable use cases.
The normal Java classloaders look for classes first in the bootstrap classpath, before checking for extensions and the application classpath. By default, the bootstrap classpath consists of the "rt.jar" file and some other important JAR files that are supplied by the JRE installation. These provide all of the classes in the standard Java SE class library, along with various "internal" implementation classes.
Under normal circumstances, you don't need to concern yourself with this. By default, commands like java, javac and so on will use the appropriate versions of the runtime libraries.
Very occasionally, it is necessary to override the normal behavior of the Java runtime by using an alternative version of a class in the standard libraries. For example, you might encounter a "show stopper" bug in the runtime libraries that you cannot work around by normal means. In such a situation, it is possible to create a JAR file containing the altered class and then add it to the bootstrap classpath which launching the JVM.
Better approach would be extending those Class and modifying the specific methods. This will not affect other project where you just wanted Java provided Class
Files in that jar are compiled .class files from .java source files. You cannot modify the binaries directly that easy, but if you have a source code (e.g. https://github.com/openjdk/) you can compile your own .class files and replace, since JAR is basically an archive with binaries.
Not the most scalable approach, but possible.
I'm a Java veteran, but I've been using JDK8 for a long time. I've decided I finally want to upgrade, so I've been using Java11. I've been enjoying the new features, but I've gotten the point where I need to deploy my software.
In the past I would export a runnable JAR from Eclipse and bundle it with an appropriate JRE. Then I'd use both to run the software from an OS-specific program (e.g. an EXE file that fires up the bundled JRE with the given JAR). Of course, now this isn't really an option because JREs are a thing of the past. Supposedly the new system in its place is much lighter weight and straight forward, the only problem is I can't figure out how to actually use it.
I've been reading about how to deploy programs with JDK9 and above and have seen people mention JLink and link documentation to it, but I can't seem to find a straight answer on how to just simply export a runnable JAR. The thing is - I don't really need all of the module support and don't really want to have to configure it. Is there a tool for simply exporting something I can run? How is this done now?
Sorry if this is a dumb question, I'm just genuinely confused at how this all works and can't really find anything online that lays it out in a clear and concise way. There's a lot of documentation on JLink and what it does, but I haven't really found anything that explains the root purpose for all of it.
TL;DR; how do I export working Java programs with JDK9 and above using Eclipse (latest version)?
Thank you for your time!
...how to just simply export a runnable JAR.
Well, if you're not planning on using modules for your application classes, the JAR part should be the same as before. You basically just have to create your own JRE using jlink, e.g.:
jlink --add-modules java.se --output jre
That would create a jre folder with a runtime image that includes all the java.se modules.
You can then bundle that with your JAR like before.
The interesting part here is that you can pick and choose which modules go into this runtime image. java.se is an aggregator module that transitively includes a bunch of other modules. But you could also specify your own specific list of modules, leaving out some of the ones you don't need, making the final runtime image smaller.
I've spent literally months on trying to figure out how to create a single, clickable executable using Java 9+. I now conclude that it is impossible. Whether you use jlink directly yourself, or indirectly via Maven or Gradle, the result is that jlink produces a full directory structure that you are left to "distribute to your users" (somehow, I guess magically, since nothing in Java 9+ tells you anything about how you're supposed to do this). Then, once your user (somehow) has this directory on their machine, they are forced to invoke runtime-image-directory/bin/your-program-name. As though your user is a programmer who is happy to have to install a directory structure, and drill down to invoke a specific file name, which is buried in a directory with lots of other files, rather than being a user who USED to be able to simply double-click on a .jar file to run it. This makes me really wonder if whomever designed all this thought at all about the "user experience". The fact that there appears, as of Java 9+, to be NO WAY to simply deliver ONE file (such as a .jar) to users, together with the fact that just BUILDING this "runtime image" is fantastically complicated, means that developers are going to abandon Java in droves. It really seems to me that the brainiacs at Oracle simply didn't think this through. They have created a death knell to Java by making something onerous for developers to build and distribute, and onerous for users to invoke. I don't see how this situation can be allowed to remain without Java ultimately dying off. Somebody please correct me if I am missing something here, but I've spent months now trying to figure out how to create a runnable jar in Java 9+, USING MODULES, and it appears there is no way to do it.
I'm using a mac, and I would like to know if there is a directory in which I can put all my java libraries that I use often so that I can compile/run the code that uses them without explicitly setting the classpath each time? I want to do something analogous to what a package manager would do in python, but it doesn't seem like there are any package managers for java.
I'd strongly advise that you avoid doing this. While it might be convenient for you personally, it's terrible from a repeatability standpoint. If you have trouble with your code and want to send your project to somebody else to look at, they shouldn't have to reproduce your entire environment to be able to compile it.
For package management in Java, look at the Maven project. This will allow you to describe your project dependencies and have the tool automatically download the appropriate JARs and add them to your project's classpath.
You can put them anywhere you like, and set the CLASSPATH environment variable so that it includes that path. javac uses the environment variable, so you don't have to specify it each time.
As you're using a Mac, I'll point you to this SO question and its answers about how to set environment variables. To include multiple directories in your CLASSPATH, separate them with the standard path separator (which I believe is : on Mac OS X).
Two options:
1 - set a CLASSPATH in your ~/.profile file. Then every time you open a Terminal, it will be there.
2 - You can also put the JAR files into the lib/ext directory for your JDK. See this article for the details on that although note that Apple recommends you not do this.
Is there anything similar to Python virtualenv for Java or JVM Languages?
From what I understand, virtualenv enables you to have separate library installation paths, effectively separate "virtual" Python installations.
Java doesn't have the concept of a "system-wide installed" library(*): It always searches the classpath for the libraries to be loaded. Since the classpath can be (and needs to be!) defined for each application, each application can pick-and-choose which libraries and which versions it wants to load.
If you go down one level deeper and have a single application that somehow needs two different versions of the same library at the same time, then you can do even that with some classpath trickery. It can get complicated, but it's definitely possible (OSGi is one example where this is supported, even Tomcat with two separate webapplications does this).
I've seens some references to security in the virtualenv description: Java has a pretty thorough security system built in. In server applications it's often turned off because it's just easier to configure this way, but you can easily configure what exactly a Java application is allowed to do.
(*) Almost, there are extensions or extension libraries, but they aren't used a lot and even those can easily be loaded from arbitrary directories.
Build tools like Ant, Maven, and gradle are the the closest thing to pip or easy_install.
The concept of virtualenv is done by the classpath. So there is no real need of virtualenv for Java
Yes(see http://www.jenv.be/), like many other languages (Ruby, Python, Go, R, Php, etc. etc.).
I know this may be a little late , but Groovy/Java has gvm http://gvmtool.net/ which is the Groovy version of Ruby's renv.
I would respectfully agree with Gautam K, luthur. Dependency and package version management for projects is not the same as an isolated self-contained virtual environment to maintain different project.
My 2 cents
-W
I have also been looking for a similar solution to simplify switching context between projects that use different Maven versions/settings and/or Java SDKs without having to modify M2_HOME and JAVA_HOME settings every time.
To this end, I developed a solution that helps execute mvn commands with the appropriate configuration based on per-project settings (stored in a .mvn folder).
See: https://github.com/AlejandroRivera/maven-env
Be aware that this only helps if you're using Maven to build and/or run your project.
I'm confused by the assertion that "Java doesn't have the concept of a 'system-wide installed' library". What would you call the jar files in $JAVA_HOME/jre/lib and $JAVA_HOME/jre/lib/ext?
Regardless of whether or not Java "needs" a tool like virtualenv, it seems that something that allowed you to quickly switch between different Java environments (e.g. Java 6 with such-and-such security extensions, Java 7, etc.) would be handy - even if all it was actually doing under the covers was manipulating the PATH, JAVA_HOME, and CLASSPATH env variables.
Maven, you can explicitly specify which packages you would use in a java project
Java as a language does not need the sandboxing features of virtualenv but a JVM Language like Jython can have VirtualEnv to use different environments without any conflict.
It is outlined in this blog post
Quote:
Get virtualenv installed for Jython. Just type "jeasy_install
virtualenv". Once that finishes you should have a 'virtualenv' tool in
the Jython installation's bin folder.
So when using Jython different frameworks and packages can be used without any conflict with global packages.