I have a Maven-Springboot project setup. After doing a mvn install I can run the jar file in command prompt using java -jar <my-jar-file.jar>
There was a dependency to a jar say as-common-1.0.0.jar that is there in my-jar-file.jar. Now I want to override the version of this dependent jar at run time by giving an external jar. something like:
java -jar my-jar-file.jar using as-common-1.0.1.jar
I went through many SO posts like thisinclude external jar when running java -jar but they didn't help.
is it achievable?
EDIT: The issue we have is many of our applications depend upon one of our internal framework jar which gets updated(version) often.
So every time changing the pom file and re-deploying all the apps doesn't look feasible to us. We want somehow the dependency of this particular jar is given at run time. Any idea regarding best possible way to manage this scenario?
No, this is not sensible. At best, you would load classes twice and this may/may not work.
If you build a complete jar containing everything, you cannot swap content at runtime. You need to rebuild it.
Related
I am working on a project, when I run maven test, I get all the dependencies as jars, so I can use methods of classes in those jars when editing the code, however I was wondering when running maven deploy to package the code as a jar and put it on a remote repository, how does this jar execute? doesn't it need all the jars mentioned in the pom? because when reading the contents of the jar it only includes the compiled classes of the code which are under src/main/java.
I think there is a point that I have misunderstood. please if there are some basics that I have missed or I should have known refer me with a good guide.
thanks in advance
It's like you said, to run a jar you need each dependency. But to put it on repository you don't need that.
So basically when you want to create runnable jar you need some maven build plugins which will pack every dependency inside your .jar file like maven-shade-plugin.
If you want to pack a jar to upload it to remote repository for other people to use you don't need that because now it is on the side of someone who will use your dependency to download each dependant jar of your created one (maven does it automatically).
Example
It all depends on what you need to do.
Imagine that you have a console application which shows you weather for Chicago, that application uses dependency for getting weather lets call it weather-dep so your application needs a dependency of weather-dep inside.
And now if you want your user to just run it (jar can be run from console "java -jar yourWeatherApp.jar") you need to package it in a way that yourWeatherApp.jar will have inside weather-dep.jar which maven will download on packaging process.
Second option is when you know that someone want's to show weather in Chicago using yourWeatherApp.jar so that person makes his application lets call it usaWeatherApp.jar and he will include your dependency inside his application he will then be able to use classes from yourWeatherApp.jar but also from weather-dep.jar because it's dependency inside your's app.
You just need to know which use case is best suited for you.
Short answer you package your jar including dependencies when someone wan't to just run your application and not include it's functions/classes etc. inside their app.
I have created a JavaFX application and have a generated Jar file (generated with Gradle) that will launch.
When the application does launch, it doesn't connect to the embedded database though. I feel as though I am missing something very simple but after a lot of research, I haven't been able to figure it out. When running the jar file from the command prompt, I get the following error:
java.lang.ClassNotFoundException: org.apache.derby.jdbc.EmbeddedDriver
From the reading I have done, I understand I may be able to add this to my classpath but I have not been successful with this after multiple attempts and I have made this application to be run on another computer. This is possible, right?
If possilbe, I would love to change something in my build.gradle file or surround the .jar in a folder or something like that that would make this simple for the person running the program. Program size is not a concern short of multiple gigabytes.
You've got two problems
The driver class is not in your app jar
If you embed that db into the app jar, you're not going to be able to write to it
You can make a 'fat jar' but the isn't going to solved the second problem. You really need to make an installer to do this, in order to leave the db in the file system, so it can be written to.
Problem
Part of what Gradle does is dependency management. That means it knows what dependencies you need and how to find them (based on configurations in the build script). When you execute/build your application via Gradle the tool will automatically search repositories, download+cache dependencies, and place those dependencies on the class-path/module-path. Once you deploy your JAR file Gradle is no longer involved, so your deployment is responsible for including the needed dependencies.
In other words, you need to ship your application's dependencies with your application JAR file.
Solutions
You basically just need to make sure you include your application's dependencies with your application. Here are at least three ways to do that.
Copy Dependencies
Copy the dependencies into a build folder as part of the build process. Here's an example of such a task, using the Kotlin DSL:
tasks {
val jar by existing(Jar::class)
val copyDependencies by registering(Copy::class) {
from(configurations.runtimeClasspath)
into(jar.get().destinationDirectory)
}
jar.configure {
finalizedBy(copyDependencies)
}
}
Now if you execute ./gradlew jar Gradle will create the JAR file and then copy the dependencies into the same directory as the JAR file. Then you just need to make sure all the JAR files are deployed together.
If I remember correctly, the default class-path is the working directory. But to specify the class-path you would use -cp, -classpath, or --class-path when executing your application. The module-path, if needed, is set with -p or --module-path.
Fat JAR
Create a so-called "fat" or "uber" JAR file. That's a JAR file that includes not just your own application code but all your application's dependencies as well. You could configure the jar task for this, but it would probably be easier for you to simply apply the Gradle Shadow Plugin.
// Kotlin DSL
plugins {
id("com.github.johnrengelman.shadow") version "<version>"
// other plugins...
}
And then you'd create the fat JAR with ./gradlew shadowJar. See the user guide for more information.
Self-Contained Application
Create a self-contained executable using a tool like jpackage. This tool gives you an application that has all its code and the JRE embedded, and then gives you an installer or native executable (e.g. exe on Windows). Here's the user guide for jpackage. There are Gradle plugins to make using jpackage from Gradle easier, such as The Badass JLink Plugin.
Note jpackage was added in Java 14 and was incubating until Java 16. Also note that jpackage can't "cross-package". That is to say, if you build your application on Windows then you can only create installers/executables for Windows; same for MacOS and Linux. If you need to package for multiple platforms then you'll need access to each platform.
JavaFX
Since you've tagged this question with JavaFX I want to give a note of caution. Though if you're not using JavaFX 9+ then this is not relevant to you.
Technically JavaFX only supports being loaded as named modules. That means it needs to be placed on the module-path, either via --module-path or by including it in the custom-runtime image built by jlink / jpackage. As of JavaFX 16 a warning is now emitted if JavaFX is loaded from unnamed modules (i.e. the class-path).
Executable JAR files are placed on the class-path. That includes fat JARs. And if you are not using a JDK that includes JavaFX—meaning you have Gradle pull in the JavaFX dependencies—then JavaFX will be included in your fat JAR and be placed on the class-path. Now, despite not being supported and now emitting a warning, nothing seems to currently break if JavaFX is on the class-path. Except for one caveat: Your main class must not be a subclass of javafx.application.Application. You'd have to create a separate main class that simply launches JavaFX.
Because of all this, I would highly recommend using jpackage to deploy JavaFX applications. You may want to read this Q&A as well.
I'm curious to know how Intellij can resolve the dependency conflicts? Let me explain my situation. I should work on the spring boot application. It uses Maven. IntelliJ can build and run the application without any problem, but when I make a jar file,
mvn clean package
and run the jar file
java -jar xxx.jar
I faced a java.lang.NoSuchMethodError. Some conflicts on dependencies caused it, and my application uses the wrong version of a jar file.
I want to know how IntelliJ can find the correct jar file which contains the method, while it uses the same pom.xml, which I face error while using with the mvn command.
And is it possible to find that which version of every jar file used by IntelliJ? (I want to use this for correcting the pom file)
Thanks
There is a big difference between runtime and compile time when it comes down to such things.
compiling code (such as mvn package), and writing code (as in, what IntelliJ is doing to ensure that your editing experience is nice; auto-complete dialogs for all the libraries you use, errors if you try to invoke non-existent methods, etcetera) are one thing (let's call it 'write time').
Running your code, as with java -jar xxx.jar is something completely different.
maven and your dependency list is a write time thing. Thus, maven and intellij know where to look for your dependencies, but when you run java -jar xxx.jar, that does not know where to look, and thus, your dependencies aren't found, and thus, NoClassDefFoundError occurs.
That's because that jar file that maven makes just contains your code, it is completely disconnected from your maven file (your maven stuff is not looked up when you run your code at all), and it does not contain your dependencies.
You'd have to ship them separately. There are 3 solutions to this problem:
Preferred solution: jar files contain a so-called manifest, which tells the JVM for example what the name of the main class is within that jar file. It can also contain the class-path (this is in fact the only class-path checked when using java -jar to run jar files; you can't specify a -classpath parameter). This lets you deploy your app such that your app installation looks like:
/usr/local/projects/YourApp/main-app.jar
/usr/local/projects/YourApp/dep/guava.jar
/usr/local/projects/YourApp/dep/mysql-jdbc-connector.jar
/usr/local/projects/YourApp/dep/jdbi.jar
etcetera, and to run this application, just run main-app.jar. This requires the manifest of main-app.jar to contain the entry:
Class-Path: dep/guava.jar dep/mysql-jdbc-connector.jar dep/jdbi.jar
When running the jar, the Class-Path entry is split on spaces, and then each entry is looked up relative to the directory that contains main-app.jar. By shipping the jars separately, it's easy to separately update them or replace them, and deploying a new version is much faster (you just ship the jar(s) that were changed, not all of them. Many apps have hundreds of MBs of deps, whereas their own jar is a few MB at most, makes a big difference for example when pushing deps from your dev machine to the test server!)
This leads to the question: How do you make maven put that Class-Path entry in the output jar file's manifest? The maven-jar-plugin can do the job - see this answer for more details.
Shade in your deps (also called striping, or fatjar, or bigjar)
This is the notion of taking all your deps, rewriting their name to avoid version conflicts, and then making one humongous jar file that contains everything. It has the considerable downside of being a much slower process, especially if you need to push this out to another system for testing. Use the shade plugin to do so.
Don't use java -jar.
java -jar x.jar cannot work unless the jar file either has a Class-Path entry in its manifest, or contains every dep it needs. However, you can also run your java code like so: java -cp main-app.jar:dep/guava.jar:dep/jdbi.jar:/dep/other-deps-here com.foo.YourMainApp. This is.. not convenient, but you could presumably write a shell script or some such. This isn't a very java-like solution, I don't recommend it.
Today at work I came across something interesting. Say i have an old java project that were compiled with an ant build file and we have converted this project into a maven project. So now to build this project, we only need to do a mvn install.
When i do call
mvn install
I get a myproject.jar under the target folder, along with all the dependencies under a lib folder inside the target folder.
To run the executable of this jar I need to do something like :
java -classpath $classpath com.myproject.Mainclass $myArgs
Where $classpath is the path to all of my external libs and where $myArgs is the arguments that is passed to the main function.
I came across this website and I'm really considering to use the spring boot maven plugin to package my executable jar.
Wouldn't it be easier to execute it if all the dependencies are packaged in a single jar file ?
Why would I use the manual configuration vs the Spring Boot Maven Plugin for the executable jar ?
What are the pro and the cons of doing this ?
As the article you had linked covers with pros and cons how to do such single jar file packaging, I'll write out things that you need to consider if you want to use this approach.
Pros:
1. Simplicity of deployment
Users don't have to maintain any dependencies. All they need to do to run the app is get the jar file and execute java -jar file.jar.
2. No easy way to update dependencies by user
If your app uses some external dependencies, you can be sure they are in version that you have chosen. Using "classic" approach user can easily update it by himself to the version that may require some migration steps in your app.
Cons:
1. Size of final package
If your app has large dependencies, every update will require users to download the whole package,
even if dependencies haven't changed.
2. No easy way to update dependencies by user
To change a version of any dependency you will need to update the whole package, where using the old way you could update only the dependency jar.
Summarizing, if your app doesn't have any heavy (in sense of file size) dependencies, I'll personally use single jar file approach. Even if your dependencies changes frequently. It's a lot easier to change a single file, no matter if your app has to be updated or some of its dependency.
So I've been pigeon-holed into writing some Jython code. I've been using the latest version of Eclipse IDE with the PyDev plugin for development. Up until now, things have been moderately tolerable. I've gotten all my Python scripts working and I'm successfully including a couple of JAR files and the class directory of another Java project as external dependencies. Everything seems to run fine through the Eclipse IDE.
Now I need to package everything up and deploy it. From what I can gather, the best way to do this would be to package everything up in a JAR file. The Jython documentation suggests starting out with the jython.jar file and adding to it. OK. So I modify my main python module and start adding all my python source to the JAR.
It executes but of course can't find all the external dependencies.
How is one supposed to add the external JAR files so that they are correctly seen by the Jython interpreter? How is one supposed to manage more complex dependencies in a setup like this?
Is there a plugin for Eclipse or maybe something like Ant or Maven that can handle all of these steps for me with the push of a button?
I can't be the first person that has needed to deploy Jython code with complex dependencies can I?
I've made some headway on getting this all working so I thought I would put some notes here in case they help anyone else out. I'd still like to hear from others on their experiences trying to put together something like this.
It turns out that Eclipse as of 3.5 has a project export option for Java -> Runnable JAR File. If you use this option, you can point to a Java main class in the export wizard. You also have the option to have it repackage all the JARs that you are dependent on in your new JAR file. Make sure to check the box to save the export as an ANT build so that you can repeat the process quickly. NOTE that the first time you do this through the interface, it may fail, but it will still have created a JAR file.
Now here's where it gets strange. To track all the dependencies, I am still using a mostly incomplete Maven build in my project. I create the Maven .POM file. And I told Maven what my external JAR dependency was. I then told Maven to do a dependency update for me. It pulled everything into my Maven repository as expected.
Now when I do my ANT build, it appears that it is getting its list of JARs to include in the final build from Maven. I'm not really sure if it is supposed to work that way. I'm also not 100% sure that it is working. I guess I'll find out when I have to add another external JAR to the project.
Anyways, if you follow this question you'll see that you can take the latest builds of Jython and pull the org.python.util.JarRunner.java file out and use it in your own project. This is you Java.main class that you will need to point your ANT build at. From there, convert your main Python/Jython script to be the run script that was talked about in that question.
Next, build up another copy of the Jython JAR file in your Jython directory. This one should have the /Lib directory pulled into the JAR. Save that off and then point your Eclipse IDE Jave Build option for your PyDev project at that JAR as an external dependency. Your JarRunner will now work and execute the run.py file under Jython.
If all that works, you should then be able to rerun the ANT exported build from earlier and you will end up with a single JAR file that you can execute on the command line as:
java -jar {yourjar} args
And distribute to your customers without any additional dependencies.
If that all seems a little bit convoluted, it is. If anyone has a better way of setting this all up using Eclipse, please let me know.
Make your life easier and just use Maven and the mavenjython compile plugin.
See my answer of this question: Using Jython with Maven
You get full automation of the build and deploy process, and the result is a jar that includes jython and all other dependencies.